fix(nav): stop the small-screen layout overflowing and give navigation real touch targets #170
Inga granskare
Etiketter
Inga etiketter
area/backups
area/ci
area/control-panel
area/identity
area/infra
area/observability
area/payments
area/security
area/storage
area/web
blocked
needs-info
needs-triage
ready-for-implementation
type
bug
type
chore
type
docs
type
epic
type
feature
type
task
wontfix
Ingen milstolpe
Inget projekt
Inga tilldelade
1 deltagare
Notiser
Förfallodatum
Inget förfallodatum satt.
Beroenden
Inga beroenden satta
Referens
bitborg/bitborg-web!170
Läser in…
Hänvisa till i nytt ärende
Ingen beskrivning angiven.
Ta bort grenen "fix/nav-small-screen"
Borttagning av en gren är permanent. Även om den borttagna grenen kan fortsätta existera en kort tid innan den faktiskt tas bort, kan det INTE ångras i de flesta fall. Vill du fortsätta?
Closes #153, closes #160, closes #155, closes #159. Refs #156.
Small-screen layout, reflow and target size. Deliberately adds no disclosure or hamburger — the
header has no JavaScript today and that is worth keeping.
Ordering was load-bearing: the row has to wrap (#153) before targets grow (#155), or the 320 px
overflow gets worse rather than better.
Reflow — measured,
scrollWidthvsclientWidth//docs//pricing//en/pricing/Zero overflow across 8 widths × 6 pages. 768 px and above is unchanged.
A second overflow source only became visible once the first was fixed: at 320 px the
Dokumentationheading is 321 px in a 272 px column. Fixed with
overflow-wrap: break-wordon headings.#160 restacks the add-ons table below
smallrather than adding a scroller — each cell carriesits column heading as a visible label and
theadis dropped, so there is no unlabelled tab stop.#155 gives every header, footer and breadcrumb target 48 px on the block axis, honouring
--min-touch-target, which the repo already applies to buttons and the logo but to no navigationlink. It previously passed SC 2.5.8 only through the spacing exception — compliance that breaks the
moment any gap is tightened.
#159 adds
env(safe-area-inset-*)behind@supports. Verified with CDPEmulation.setSafeAreaInsetsOverrideat 780×375 landscape: link boxes moved from30..750— twelvelinks inside the 44 px notch band — to
74..706, none inside. Values are byte-identical where noinsets exist.
#156 is partial, and deliberately so
Header height at 375 px: 278 baseline → 210. The issue asks for ≤170 px, which is not reachable
while every target is 48 px. The header is now three rows — brand band plus two navigation rows —
which is 144 px of targets; the remaining 66 px is padding and gaps. Reaching 170 means cutting
~40 px more of padding (brand 32→8, nav 16→8, gap 16→8), which is a visual-density decision rather
than a layout one.
Per the operator decision, the small-screen logo now matches the footer's 24 px, which is what
freed the third row.
#156stays open for the padding question.Verification
pnpm lint,pnpm check,pnpm test(206 passing).src/lib/nav.test.tsuntouched;aria-current="page"still marks the current page in both languages.The two child navigations each paid `padding-inline: var(--page-margin)`, so a single 320 px row spent 96 px on page margins, and `flex-wrap: nowrap` meant the row could never be narrower than the sum of their min-content widths (163 + 160 + 24 = 347 px). The whole document scrolled sideways on every page — WCAG 2.2 SC 1.4.10 (Reflow, AA). The page margin is now applied once, on `.main-navigation`, and the row wraps below `small` so the two navigations move onto separate rows. Headings also get `overflow-wrap: break-word`: the 40 px `Dokumentation` h1 on /docs/ is 321 px wide inside a 272 px column at 320 px, which was masked by the larger navigation overflow. Measured at 320 x 780, isMobile, deviceScaleFactor 2: / 347 -> 320 /en/ 347 -> 320 /docs/ 348 -> 320 /docs/faq/ 348 -> 320 Layout at 768 px and above is unchanged (header 221.6 px before and after on every page). Closes #153.The add-ons table holds a full sentence in its third column, so its min-content width is 413 px: `table { max-inline-size: 100% }` cannot shrink that, and /pricing/ scrolled the page sideways at every width up to 414 px — WCAG 2.2 SC 1.4.10 (Reflow, AA). Below `small` the table now restacks into one bordered block per row. Each cell carries its column heading as a visible label, so the relationship the header row expressed travels with the cell and `thead` is dropped rather than duplicated; above the breakpoint the label is hidden again and the real header row does the work. The shared rule lives next to the other `src/style/shared` partials, keyed off the `.table` class both pricing pages already used. Measured, documentElement.scrollWidth vs clientWidth: 320 px /pricing/ 437 -> 320 /en/pricing/ 387 -> 320 375 px /pricing/ 438 -> 375 /en/pricing/ 388 -> 375 414 px /pricing/ 437 -> 414 /en/pricing/ 414 -> 414 No scroll container was introduced, so there is no unlabelled tab stop. The table at 768 px and above is byte-for-byte the same layout. Closes #160.Nothing in the header responded to a breakpoint: the 2 rem that gives a desktop header its air was the same 2 rem on a 320 px phone, and the top band was three stacked full-width rows — account links, a 112 px brand band, main navigation — before any content. Below `medium`: - the brand and the user menu share one row, logo left and account links right. A `.header-band` wrapper holds them; it is `display: contents` above the breakpoint, so the desktop header stays exactly the column it was. - `.brand` padding drops from 2 rem to 1 rem and the header logo from 48 to 40 px. The logo may also scale below its intrinsic width rather than force the row wider than the viewport. - the main navigation's 1 em band is halved: its links now carry their own 48 px target, so that padding was height paid twice. - the language switcher moves to the footer (operator decision). It is relocated, not concealed — the footer copy is shown at exactly the widths where the header copy is hidden, and `display: none` keeps only one of the two in the accessibility tree. Accepted trade-off: with no Accept-Language detection, a reader who lands in the wrong language must scroll to change it; the mitigation, if it bites, is detection rather than moving the control back. Below `small` the two child navigations also stop generating boxes, so their five links wrap as one flow instead of the app link always claiming a row of its own. Verified against the Chrome accessibility tree at 375 px and 768 px: both landmarks keep their labels and their links, and exactly one language navigation is exposed at each width. Measured header height, px: width before after #155 now 320 354.9 459 242 (306 on /en/, one row more) 360 312.3 387 242 375 277.6 387 242 414 277.6 323 242 600 258.9 275 146 768 221.6 221.6 221.6 (unchanged, as at 900 and 1280) Header height no longer varies between pages at the same width. Two acceptance numbers are NOT met and need a human decision: 170 px at 375 px, and <main> starting within 220 px on a page with breadcrumbs (it starts at 319 px). See the note on the issue — with 48 px targets, eight visible targets and a 327 px usable row, four rows of navigation is the floor. Closes #156.base.astro has asked for `viewport-fit=cover` all along, but nothing under src/ ever referenced `env(safe-area-inset-*)`. The page margin is 24 px and the inset on a notched phone in landscape is about 44 px, so the first navigation link, the account links and the footer's first column sat partly under the notch or a rounded corner; in portrait the footer's last row sat under the home indicator. The inline insets are now honoured by .site-header, .breadcrumbs, .page-content and .site-footer — breadcrumbs included because they are navigation too — and the block-end inset by .site-footer, which is the only one of the four that ever meets the bottom of the viewport. Where the element already pays a page margin the inset is added to it rather than replacing it. Everything sits behind `@supports (padding: env(safe-area-inset-left))`. Verified in Chromium at 780 x 375 (landscape) with CDP Emulation.setSafeAreaInsetsOverride at left 44, right 44, bottom 21: insets none link boxes span 30..750 12 links inside the 44 px notch band insets set link boxes span 74..706 0 links inside the 44 px notch band header padding-inline-start 44px, main 44px, footer padding-inline 68px/68px, padding-block-end 53px With no insets the computed values are unchanged: header 0, main 0, footer 24/24 and 32. Reflow, target sizes and header heights at every measured width are identical to before this commit. Closes #159.45c61631b6958050d3f5958050d3f5316f645a45