Change top bar to only appear whene scrolling up

This commit is contained in:
2026-08-28 07:42:59 +02:00
parent abb6002078
commit a2c947f870
2 changed files with 36 additions and 29 deletions
+12 -23
View File
@@ -29,11 +29,14 @@ function onFilterChange(event) {
}
// Scroll-driven show/hide: the header slides out of view on scroll-down and
// back in on scroll-up. This is show/hide via `transform` (not the old
// resize behaviour) — the header is position:fixed, so translating it never
// reflows content, and the app's programmatic scrolls resolve to sensible
// states: scrollTo(0, 0) lands near the top → shown; the list-view
// read-correction scrollBy moves only a few px → stays under the threshold.
// back in only on scroll-up — never merely by proximity to the top. This is
// show/hide via `transform` (not the old resize behaviour) — the header is
// position:fixed, so translating it never reflows content. The app's
// programmatic scrolls resolve to sensible states without any top-of-page
// special case: scrollTo(0, 0) (article paging, layout toggle) is itself a
// large upward jump, so it crosses REVEAL_THRESHOLD like a real scroll-up and
// reveals the header that way; the list-view read-correction scrollBy moves
// only a few px → stays under the threshold and leaves a hidden header hidden.
const hidden = ref(false)
const REVEAL_THRESHOLD = 12 // px of accumulated travel before toggling
// When the feed list mutates itself (read-correction scrollBy + array-splice
@@ -45,25 +48,13 @@ const REVEAL_THRESHOLD = 12 // px of accumulated travel before toggling
const PROGRAMMATIC_SUPPRESS_MS = 300
let lastY = 0
let accumulated = 0
// Cached in onMounted() below instead of re-read via headerRef.offsetHeight
// on every scroll event. The header is a fixed size (see onMounted), so
// re-measuring it here was pure waste — worse, a DevTools performance trace
// showed that offsetHeight read landing right after an unrelated DOM write
// elsewhere on the page (e.g. the article view swapping in a new article)
// forces the browser into a synchronous, forced-reflow layout pass right
// there mid-script, instead of its normal scheduled one. Stays 0 until
// onMounted() below sets it (before the scroll listener is attached, so
// onScroll() never runs against the unset value) — keep that ordering if
// this ever changes.
let headerHeight = 0
function onScroll() {
const y = Math.max(0, window.scrollY)
// Always reveal near the very top, and keep it visible while the menu is
// open (the dropdown is anchored to the header, so hiding it would slide the
// open menu off-screen).
if (y <= headerHeight || menuOpen.value) {
// Keep the header visible while the menu is open (the dropdown is anchored
// to the header, so hiding it would slide the open menu off-screen).
if (menuOpen.value) {
hidden.value = false
accumulated = 0
lastY = y
@@ -99,11 +90,9 @@ function onScrollRaf() {
onMounted(() => {
// Drives #app's padding-top / RssFeeds' scroll-margin-top so content below
// the fixed header isn't hidden behind it at scroll position 0. The header is
// a fixed size, so this is measured once on mount and never changes — and
// onScroll() reuses this same measurement instead of re-reading it.
// a fixed size, so this is measured once on mount and never changes.
const h = headerRef.value?.getBoundingClientRect().height ?? 0
document.documentElement.style.setProperty('--app-nav-height', `${h}px`)
headerHeight = h
lastY = Math.max(0, window.scrollY)
window.addEventListener('scroll', onScrollRaf, { passive: true })