fix(ui): admin top nav scrolls horizontally instead of wrapping (LC-219) #262

Merged
nrupard merged 2 commits from feat/lc-219-admin-nav-scroll into main 2026-05-29 18:51:01 +02:00
Owner

Summary

Reported on staging: "The top navbar is clunky for the admin settings. The navbar should be able to scroll so text doesn't have to wrap around."

admin_layout.html's <nav> had flex gap-4 but no flex-nowrap / overflow-x-auto / whitespace-nowrap modifiers, so the 17 links wrapped INSIDE each anchor onto two or three lines on any viewport narrower than the total link width. Multi-word labels (Outgoing webhooks, Anti-spam, Link filter, Backup & restore) broke mid-anchor.

Fix:

  • Wrap the <nav> in <div class="relative border-b border-border"> so the version chip can sit outside the scroll container.
  • <nav> gains flex-nowrap overflow-x-auto. Each <a> gains whitespace-nowrap shrink-0. Bar stays one row at every viewport; horizontal scrollbar / touch / Shift+wheel scroll when content overflows.
  • Version chip pinned absolute right-2 top-1/2 -translate-y-1/2 with bg-surface mask + pointer-events-none. Visible at all scroll positions; click-through to links beneath.
  • hidden md:inline-block hides the chip on phone widths where horizontal space is most constrained.
  • <nav aria-label="{{ "admin-nav-label"|t }}"> (new Fluent key in en + es) + aria-current="page" on the active link.

Test plan

  • cargo check -p lets-chat-server clean.
  • just build-css clean.
  • Visual smoke on staging: open /admin/* pages at narrow viewport (Spanish locale + 768 px width is the canonical overflow trigger). Bar stays one row, scrollbar reveals, version chip stays right-pinned.
  • Keyboard Tab through the nav: focus rings still visible on each link.
  • Screen reader smoke: <nav> is announced as "Admin navigation"; active link announced with current-page state.
  • Phone width (320 px): nav still scrolls; version chip hides; LC-199 mobile drawer unaffected (different surface).
## Summary Reported on staging: "The top navbar is clunky for the admin settings. The navbar should be able to scroll so text doesn't have to wrap around." `admin_layout.html`'s `<nav>` had `flex gap-4` but no `flex-nowrap` / `overflow-x-auto` / `whitespace-nowrap` modifiers, so the 17 links wrapped INSIDE each anchor onto two or three lines on any viewport narrower than the total link width. Multi-word labels (Outgoing webhooks, Anti-spam, Link filter, Backup & restore) broke mid-anchor. Fix: - Wrap the `<nav>` in `<div class="relative border-b border-border">` so the version chip can sit outside the scroll container. - `<nav>` gains `flex-nowrap overflow-x-auto`. Each `<a>` gains `whitespace-nowrap shrink-0`. Bar stays one row at every viewport; horizontal scrollbar / touch / Shift+wheel scroll when content overflows. - Version chip pinned `absolute right-2 top-1/2 -translate-y-1/2` with `bg-surface` mask + `pointer-events-none`. Visible at all scroll positions; click-through to links beneath. - `hidden md:inline-block` hides the chip on phone widths where horizontal space is most constrained. - `<nav aria-label="{{ "admin-nav-label"|t }}">` (new Fluent key in en + es) + `aria-current="page"` on the active link. ## Test plan - [x] `cargo check -p lets-chat-server` clean. - [x] `just build-css` clean. - [ ] Visual smoke on staging: open `/admin/*` pages at narrow viewport (Spanish locale + 768 px width is the canonical overflow trigger). Bar stays one row, scrollbar reveals, version chip stays right-pinned. - [ ] Keyboard Tab through the nav: focus rings still visible on each link. - [ ] Screen reader smoke: `<nav>` is announced as "Admin navigation"; active link announced with current-page state. - [ ] Phone width (320 px): nav still scrolls; version chip hides; LC-199 mobile drawer unaffected (different surface).
fix(ui): admin top nav scrolls horizontally instead of wrapping (LC-219)
All checks were successful
check-secrets / Kingfisher (push) Successful in 8s
check-secrets / TruffleHog (push) Successful in 8s
check-secrets / Nosey parker (push) Successful in 5s
check-secrets / TruffleHog (pull_request) Successful in 3s
check-secrets / Nosey parker (pull_request) Successful in 3s
check-secrets / Kingfisher (pull_request) Successful in 5s
Check / clippy + fmt + tests (pull_request) Successful in 2m8s
6a3f140231
User on staging: "The top navbar is clunky for the admin settings. The navbar should be able to scroll so text doesn't have to wrap around."

The admin layout's `<nav>` had no `flex-nowrap` / `overflow-x-auto` modifier and no `whitespace-nowrap` on the 17 links. On any viewport narrower than the sum of the link widths (most desktop windows in Spanish, every laptop screen at <1500 px, every phone), the labels wrapped INSIDE each anchor onto two or three lines (e.g. "Outgoing webhooks" -> "Outgoing" newline "webhooks"). The bar became a multi-line wall of broken phrases.

Wrap the existing `<nav>` in a `<div class="relative border-b border-border">` so the version chip can sit absolute-positioned outside the scroll container. Switch the `<nav>` to `flex-nowrap overflow-x-auto`; switch each `<a>` to `whitespace-nowrap shrink-0`. The bar now stays one row at every viewport width and reveals a horizontal scrollbar / accepts touch + Shift-wheel scrolling when total link width overflows. Multi-word labels never break inside the anchor.

Move the version chip OUT of the scroll container. Absolute-pin it to the right edge with `bg-surface` masking underlying link text that would otherwise show through when the strip is scrolled all the way right. `pointer-events-none` lets a clicked link "pass through" the chip if a hit area happens to sit beneath it. `hidden md:inline-block` hides the chip on phone widths where horizontal space is most constrained; the version is still reachable via `/settings` About panel.

Accessibility improvements while editing this surface:
- `<nav aria-label="{{ "admin-nav-label"|t }}">` names the landmark (new Fluent key `admin-nav-label` in en + es).
- `aria-current="page"` on the active link gives SR + browser-feature parity with the existing `font-semibold` visual affordance. Generated by the same `{% if section == "..." %}` predicate so no second source of truth.

Verified: `cargo check -p lets-chat-server` clean (Fluent keys resolve), `just build-css` clean.

#LC-219
fix(ui): reserve right padding for the version chip on admin nav (LC-219)
All checks were successful
check-secrets / Nosey parker (pull_request) Successful in 4s
check-secrets / Kingfisher (pull_request) Successful in 4s
check-secrets / TruffleHog (push) Successful in 4s
check-secrets / TruffleHog (pull_request) Successful in 5s
check-secrets / Nosey parker (push) Successful in 5s
check-secrets / Kingfisher (push) Successful in 6s
Check / clippy + fmt + tests (pull_request) Successful in 2m1s
Create release / Create release from merged PR (pull_request) Has been skipped
8db5ba0ea6
Code-review finding: with the chip absolute-positioned at `right-2` and `pointer-events-none`, clicks pass through to a scrolled-under link correctly, but the chip's `bg-surface` mask visually hides the link's label. A user scrolling the nav all the way right on desktop sees "...Bridges" obscured by "v0.1.0 (a1b2c3d4e5f6)" and can't read the label even though clicking still works. Phone widths already hide the chip via `hidden md:inline-block` so only desktop is affected.

Fix: add `md:pr-48` (12 rem ~= 192 px, a slight overshoot of the chip's ~176 px width including its `px-2` padding and `right-2` offset) to the `<nav>`. Reserved padding sits at the end of the scrollable content, so when the user scrolls all the way right the last link parks before the chip's footprint instead of behind it. Phone unaffected because the chip is hidden there.

While editing the comment block, fixed a doc-accuracy nit: the original LC-219 commit comment said `flex-nowrap` was the load-bearing class. CSS flex default is already `nowrap`; the actual bug was text wrapping INSIDE each anchor (multi-word labels broke onto two lines). The fix that matters is `whitespace-nowrap` on each `<a>`; dropped `flex-nowrap` from the `<nav>` since it was redundant.

Verified: `just build-css` clean, `cargo check -p lets-chat-server` clean.

#LC-219
nrupard deleted branch feat/lc-219-admin-nav-scroll 2026-05-29 18:51:01 +02:00
Sign in to join this conversation.
No reviewers
No labels
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
psa-systems/lets-chat!262
No description provided.