The header scrolls away: the navigation should float at the top of the page #195

Closed
opened 2026-09-13 08:09:26 +00:00 by tiagoagueda · 0 comments
Owner

Observation

The header scrolls away with the page. On any page longer than a screen — the listings
table, the companies table, Your career, Your details, a report — the wordmark, the
main navigation, the search shortcut and the account menu are gone after the first flick,
and getting to another part of Postulo means scrolling back to the top first.

It should float: stay at the top of the window while the page scrolls under it.

What is there today

templates/base.html:32:

<header class="border-b border-ink-200 bg-white dark:border-ink-800 dark:bg-ink-900">

No sticky, no top-0, no z-index. Inside it (:37) the row is
relative flex flex-wrap … px-4 py-3, which is what the narrow-screen menu panel pins
itself to (absolute inset-x-4 top-full z-20, :70) and what the account menu drops
from (absolute end-0 z-20, :98). Both would keep working inside a sticky header; they
are positioned against the row, not the document.

Two things on the page are already sticky, and both assume the header is not:

  • The section sidebars on Your career (resume/overview.html:29) and Your details
    (accounts/profile.html:24) are lg:sticky lg:top-4. With a sticky header they would
    slide under it and stop four pixels below the top edge, behind the masthead.
  • The skip link becomes fixed start-4 top-4 z-50 on focus (assets/css/app.css:704).
    That is above whatever z-index the header takes, so it still shows, but it lands on top
    of the wordmark rather than beside it.

And two things measure from the top of the window:

  • Anchors. #main (the skip link's target) and every #section-… the sidebars point at
    would scroll to the top edge and end up under the header. scroll-margin-top on the
    targets, or scroll-padding-top on the root, is the fix, and nothing sets either today.
  • The section observer in app.js (:920 onwards) marks the section "whose top has
    passed a line two-fifths of the way down the window". The header would cover the top
    of that window, so the line reads lower on the page than it looks; either the line
    moves down by the header's height or the reading drifts one section early.

The header's height is not a constant. The row is flex-wrap: on a narrow screen,
or in a language whose labels are longer, it wraps to two lines (#165 fought exactly this
in the dashboard arrange page). So the offsets above — the sidebars' top, the anchors'
scroll margin, the observer's line — cannot be a hard-coded number of pixels; they have
to follow the header's actual height, which means either a CSS custom property the header
sets, or measuring it.

What a fix has to settle

  • sticky top-0 on the header with a z-index above the page and below the skip link's
    z-50; the menus' z-20 inside it keep their stacking.
  • A background that is opaque in both themes (it already is: bg-white dark:bg-ink-900)
    and a border or shadow so the page is visibly passing under it.
  • The two sticky sidebars offset by the header's height rather than top-4.
  • scroll-margin-top on #main and on every anchored section, or scroll-padding-top
    on the root, equal to the header's height.
  • The observer's line moved down by the same amount.
  • On a phone the header takes a larger share of a short screen; worth deciding whether it
    is sticky below md at all, or whether only the row that holds the Menu button is.
  • The browser suite's axe run (tests/e2e/test_accessibility.py) walks every page; a
    sticky header that overlaps focused content would show up there as a target-size or
    contrast failure, which is the check to keep green.

Not in this issue: any change to what the header holds.

## Observation The header scrolls away with the page. On any page longer than a screen — the listings table, the companies table, *Your career*, *Your details*, a report — the wordmark, the main navigation, the search shortcut and the account menu are gone after the first flick, and getting to another part of Postulo means scrolling back to the top first. It should float: stay at the top of the window while the page scrolls under it. ## What is there today `templates/base.html:32`: <header class="border-b border-ink-200 bg-white dark:border-ink-800 dark:bg-ink-900"> No `sticky`, no `top-0`, no z-index. Inside it (`:37`) the row is `relative flex flex-wrap … px-4 py-3`, which is what the narrow-screen menu panel pins itself to (`absolute inset-x-4 top-full z-20`, `:70`) and what the account menu drops from (`absolute end-0 z-20`, `:98`). Both would keep working inside a sticky header; they are positioned against the row, not the document. **Two things on the page are already sticky, and both assume the header is not:** - The section sidebars on *Your career* (`resume/overview.html:29`) and *Your details* (`accounts/profile.html:24`) are `lg:sticky lg:top-4`. With a sticky header they would slide under it and stop four pixels below the top edge, behind the masthead. - The skip link becomes `fixed start-4 top-4 z-50` on focus (`assets/css/app.css:704`). That is above whatever z-index the header takes, so it still shows, but it lands on top of the wordmark rather than beside it. **And two things measure from the top of the window:** - Anchors. `#main` (the skip link's target) and every `#section-…` the sidebars point at would scroll to the top edge and end up under the header. `scroll-margin-top` on the targets, or `scroll-padding-top` on the root, is the fix, and nothing sets either today. - The section observer in `app.js` (`:920` onwards) marks the section "whose top has passed a line two-fifths of the way down the window". The header would cover the top of that window, so the line reads lower on the page than it looks; either the line moves down by the header's height or the reading drifts one section early. **The header's height is not a constant.** The row is `flex-wrap`: on a narrow screen, or in a language whose labels are longer, it wraps to two lines (#165 fought exactly this in the dashboard arrange page). So the offsets above — the sidebars' `top`, the anchors' scroll margin, the observer's line — cannot be a hard-coded number of pixels; they have to follow the header's actual height, which means either a CSS custom property the header sets, or measuring it. ## What a fix has to settle - `sticky top-0` on the header with a z-index above the page and below the skip link's `z-50`; the menus' `z-20` inside it keep their stacking. - A background that is opaque in both themes (it already is: `bg-white dark:bg-ink-900`) and a border or shadow so the page is visibly passing under it. - The two sticky sidebars offset by the header's height rather than `top-4`. - `scroll-margin-top` on `#main` and on every anchored section, or `scroll-padding-top` on the root, equal to the header's height. - The observer's line moved down by the same amount. - On a phone the header takes a larger share of a short screen; worth deciding whether it is sticky below `md` at all, or whether only the row that holds the Menu button is. - The browser suite's axe run (`tests/e2e/test_accessibility.py`) walks every page; a sticky header that overlaps focused content would show up there as a target-size or contrast failure, which is the check to keep green. Not in this issue: any change to what the header holds.
tiagoagueda added this to the 0.3.0 milestone 2026-09-13 08:09:26 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
Postulo/postulo#195
No description provided.