Skip to content

fix(ui): stop the header sliding sideways between routes - #75

Merged
guarzo merged 1 commit into
mainfrom
worktree-header-width-review
Aug 4, 2026
Merged

fix(ui): stop the header sliding sideways between routes#75
guarzo merged 1 commit into
mainfrom
worktree-header-width-review

Conversation

@guarzo

@guarzo guarzo commented Aug 4, 2026

Copy link
Copy Markdown
Owner

The bug

The shell bar took a measure prop and tracked whichever column the page under it used: 960px on /account and /payouts/new, 1248px everywhere else. Every crossing between the two moved the seal and the nav 144px sideways.

Reported as "the header seems quite narrow in general, but also seems to change between page nav" — both halves were real, and they were the same cause. At 1920 the narrow bar's contents occupy exactly half the viewport, which is what reads as stranded.

Why it was like that

The trade was made deliberately in #39 and documented: the bar tracked the page column so the seal landed on the H1's vertical. It was priced as an admin-only cost, on the premise that a member only ever sees /account.

Payouts (#65) falsified that premise 32 commits later. /payouts is wide, /payouts/new is narrow, and a plain flygd member walks that list -> form path in sequence — watching the chrome slide under them mid-task.

Worth knowing before reopening this: #39 shipped the fixed measure first and reversed itself to the tracking one within the same PR. This is the third position, not a fresh idea.

The change

Pin .shell__bar to --measure-page on every route and drop the prop.

  • globals.css — one measure for the bar; .shell__bar--narrow deleted.
  • ui.tsxmeasure prop removed. JSX excess-property checking is the compile-time guard against a stale caller.
  • account/page.tsx, payouts/new/page.tsx, error.tsx — dropped measure="narrow".

Page measures are untouched. .page--narrow still holds the reading column at 60rem on the routes that want it; only the chrome stopped moving.

The cost is symmetric and it is the whole of it: on the two narrow routes the seal sits 144px outboard of the H1, and the nav's right edge 144px outboard of the content's right edge. The ground was always full-bleed, so the bar's contents were never read as sitting inside the page column to begin with.

Test

e2e/shell.spec.ts asserts the bar's width and centring are identical on all seven routes that render one.

It measures the rect rather than the absence of a class name — the class was one of several ways to reintroduce the shift (a --narrow variant, a :has() rule, a per-page override), and only the geometry is the property that matters. Slack is computed against document.documentElement.clientWidth rather than a hardcoded 1440, because nothing sets scrollbar-gutter: stable.

Three guards inside the loop stop it measuring a bar that isn't the one asked for, since all three failure modes otherwise end in a passing assertion:

  • an access redirect lands on /account, whose bar is already 1248
  • the error boundary renders its own SiteHeader — and this same commit dropped its measure="narrow", so its bar is now exactly 1248 too
  • a missing bar makes boundingBox() null, and a bare box!.x throws a TypeError naming neither route nor reason

Mutation-tested rather than trusted green: flipping .shell__bar to the narrow measure fails on all seven routes at once, which is the "everything moved together" case the spelled-out literal exists to catch.

Verification

npm run format:check  → All matched files use Prettier code style!
npm run typecheck     → clean
npm run lint          → 0 errors, 3 warnings (pre-existing no-img-element)
npm test              → 710 passed (67 files)
npx playwright test   → 90 passed

Where to look

src/app/_components/ui.tsx — the SiteHeader docblock carries the argument that has to be beaten before this is reversed a third time.

Not in scope

e2e/admin.spec.ts:76 has a pre-existing flake: getByRole("alert") matches both the .notice--bad paragraph and Next's dev-only __next-route-announcer__. Confirmed unrelated to this diff by running the suite at c688880. One selector change fixes it; left for a separate PR.

🤖 Generated with Claude Code

The shell bar took a `measure` prop and tracked whichever column the page
under it used, so it was 960px on /account and /payouts/new and 1248px
everywhere else. Every crossing moved the seal and the nav 144px sideways.

That trade was made in #39 and priced as admin-only, on the premise that a
member only ever sees /account. Payouts falsified it: /payouts is wide,
/payouts/new is narrow, and a plain member walks that list -> form path in
sequence, watching the chrome slide under them mid-task.

Pin the bar to --measure-page on every route and drop the prop. The page
measures are untouched: .page--narrow still holds the reading column at
60rem. The cost is that on the two narrow routes the seal sits 144px
outboard of the H1, and the nav's right edge 144px outboard of the
content's — symmetric, and less than chrome that won't hold still.

e2e/shell.spec.ts asserts the bar's width and centring are identical on all
seven routes that render one, measuring the rect rather than the absence of
a class name.
@coderabbitai

coderabbitai Bot commented Aug 4, 2026

Copy link
Copy Markdown

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

Next review available in: 32 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 7f88d980-9b0c-4999-9085-aaf117fcf1a4

📥 Commits

Reviewing files that changed from the base of the PR and between c688880 and 3c556c3.

📒 Files selected for processing (6)
  • e2e/shell.spec.ts
  • src/app/_components/ui.tsx
  • src/app/account/page.tsx
  • src/app/error.tsx
  • src/app/globals.css
  • src/app/payouts/new/page.tsx

Comment @coderabbitai help to get the list of available commands.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant