Skip to content

The navigation bar speaks the language of the page it sits on, and a phone gets one button - #392

Merged
Apolloccrypt merged 3 commits into
mainfrom
fe/nav-rustig
Sep 3, 2026
Merged

The navigation bar speaks the language of the page it sits on, and a phone gets one button#392
Apolloccrypt merged 3 commits into
mainfrom
fe/nav-rustig

Conversation

@Apolloccrypt

@Apolloccrypt Apolloccrypt commented Sep 3, 2026

Copy link
Copy Markdown
Owner

Two buyer reviews, a phone and a laptop, named the shared navigation bar as the biggest remaining distraction. On 390px it held five elements: logo, HELP, SIGN IN, CREATE ACCOUNT and the hamburger, with the button and the hamburger both starting at x=330 and no air between them. All of it in mono capitals with letter-spacing, over a homepage set in paper, ink, sentences and one cobalt button.

What changed

Typography. PRODUCT SECURITY PRICING DOCS is the voice of a terminal. Same four destinations, same order, now in the reading face at 14px, sentence case, no letter-spacing: Product, Security, Pricing, Docs, and Help, Sign in, Create account beside them. Same in the drawer, at 16px.

The phone bar. One primary action next to the menu button. Sign in and Help move to a strip under the drawer, 48px tall, where a thumb reaches them, and they come back into the bar at 700px so a tablet keeps what it had room for. A gap on the bar sets a 12px floor between every pair of elements instead of hoping space-between leaves some: measured 98px and 12px where it used to be 0 and 0. Every target in the bar is at least 44px tall.

The button. The one filled button was lime, which is the punctuation colour of this brand and not the colour of its primary action; next to the cobalt hero button it read as a second, competing offer. It is cobalt with white text (6.3:1), the same .hp-btn-fill the homepage uses. app-2026.css forced #0A1626 on lime with !important for the app screens and now follows, at the same weight. The drawer's current row loses its lime wash for the same cobalt the desktop bar uses.

One home for the bar. .nav-auth, .nav-signin, .nav-cta, .nav-user and .nav-help lived in design-system.css while the rest of the bar lived in nav.css, and the half in design-system.css answered a narrow phone by shrinking: 9px type, 5px padding, the Help border dropped. Small is not roomy. It all sits in nav.css now. No page loads one without the other (vault.html, billing/checkout.html and claim.html load design-system.css without nav.css and carry none of these classes).

Signed in is the same bar: the email control in the reading face, the drawer carrying Documents, Send, Sign, Verify, Settings, and no tail, since the user menu already holds Help and Sign out.

The test

tests/navigation-shell pinned support on a phone to a visible /help link in the closed bar. That pin is why Help was the fifth element up there, shrunk to 9px type to fit. The requirement is that a visitor looking for support finds a working route, not which of the two surfaces it lives on, so the check now opens the menu and demands a 48px target instead of "not display:none". The static contract below it still demands the /help link in the stamped markup of all 51 pages, so it cannot quietly disappear.

Three checks were added that measure what the reviews saw by eye:

  • the phone bar carries one action beside the menu button (at most 3 elements)
  • nothing in the phone bar touches its neighbour (every gap >= 12px)
  • every target in the phone bar is finger-sized (every height >= 44px)

29 checks, all green. The drawer contract is untouched: #nav-mobile still mirrors the four desktop links exactly, which is why the tail is a sibling and not a child, and why apply-nav.py had to learn to strip an old tail before stamping a new one or the second run would leave two.

Versions

design-system.css v25, nav.css v20, nav.js v15, nav-auth.js v6, app-2026.css v3. Stamped by frontend/apply-nav.py over 51 pages, and by hand on the pages the generator does not own (developer.html, co-sign.html, vault.html, claim.html, billing/checkout.html, setup.html, request-key.html, all-systems-go.html, changelog.html) plus the fixture in tests/access-log-visitors.test.mjs.

Gates

All green locally: first-screen, ui-truthfulness, site-claims, seo-contract, links, navigation-shell, frontend-loading-contract, pricing-fold, cache-bust, csp-inline, static-sanity, check-test-declarations, eslint, theme-contrast, motion-safety, app-contrast, app-theme, access-log-visitors, apply-nav-idempotent, apply_seo_head --check.

Screenshots

Taken with deviceScaleFactor: 2, session mocked at /api/user/session/verify. Paths on the NUC, under /tmp/claude-1000/-home-mick-vault/aaa93ca4-46a3-4e08-bb7e-b16bfb35e53c/scratchpad/navbar2026/shots/:

view before after
390, signed out, menu closed before/390-out-closed.png after/390-out-closed.png
390, signed out, menu open before/390-out-open.png after/390-out-open.png
390, signed in, menu closed before/390-in-closed.png after/390-in-closed.png
390, signed in, menu open before/390-in-open.png after/390-in-open.png
1440, signed out before/1440-out-closed.png after/1440-out-closed.png
1440, signed in before/1440-in-closed.png after/1440-in-closed.png

The measured bar at 390, signed out, from the test output:

  • before: ParaMANT x16-83, HELP x95-134, SIGN IN x150-203, CREATE ACCOUNT x219-356, Open menu x368-412
  • after: ParaMANT x16-83, Create account x181-318, Open menu x330-374, gaps 98 and 12, all heights 44

Round 2: the page the generator does not own

Review found the hole. developer.html keeps its own navigation (KEEP_OWN_NAV in frontend/apply-nav.py), is stamped by hand, and carries a hamburger. This round moves Sign in and Help out of the bar below 700px and into the strip under the drawer, and that page never got a strip: on 390px both links measured 0x0 there, where main still had them in the bar.

Three things, because any one of them alone leaves the same gap open:

  1. The markup. developer.html carries the strip now, like every stamped page.
  2. The script. nav.js builds the strip when a page has a menu button and no strip. A page can forget the markup; this cannot forget to build it. A floor under the live site, not a substitute for the markup.
  3. The check. tests/navigation-shell now reads every .html in frontend/, not only the 51 the generator writes, and fails on any page with id="nav-hamburger" and no id="nav-mobile-tail". 52 pages carry the button. Sabotage run: taking the strip back out of developer.html turns the suite red and names the file, while the runtime check stays green on the nav.js fallback, which is the pair working as intended.

The measurement moved too. The three phone-bar checks ran on the homepage alone, which is exactly why they were green while /developer was broken. They run on /, /pricing and /developer now, each with a fourth check that the strip really holds /auth/login and /help at 44px or more. /developer measures logo x16-95, Create account x181-318, menu x330-374, gaps 86 and 12, all heights 44. 39 checks, all green, and the 20 gates re-run clean after the rebase (now on main with #379, #390, #391).

Extra screenshots: after/390-developer-out-closed.png, after/390-developer-out-open.png.

Follow-up, not in this PR

Two observations from the review that are real and out of scope here:

  • The cool seam. The nav background is rgb(248, 250, 252) while the homepage paper is #FAF8F3. Identical to main, and pinned by navigation-shell ("mobile navigation is opaque before opening the menu"), so changing it is its own round with its own contrast measurement.
  • Two filled cobalt buttons on /pricing. Create account in the bar plus the CTA in the copy now read as two primary actions on one screen. That is the cost of moving the bar button from lime to cobalt, and the fix belongs with the page, not with the bar: either the page CTA becomes an outline button, or the bar drops to outline on pages that carry their own primary action.

…phone gets one button

Two buyer reviews, a phone and a laptop, named the shared bar as the biggest
remaining distraction. On 390px it held five elements: logo, HELP, SIGN IN,
CREATE ACCOUNT and the hamburger, with the button and the hamburger both
starting at x=330 and no air between them. And all of it in mono capitals with
letter-spacing, over a homepage set in paper, ink, sentences and one cobalt
button.

Typography. PRODUCT SECURITY PRICING DOCS is the voice of a terminal. Same four
destinations, same order, now in the reading face at 14px, sentence case, no
letter-spacing: Product, Security, Pricing, Docs, and Help, Sign in, Create
account beside them.

The phone bar. One primary action next to the menu button. Sign in and Help
move to a strip under the drawer, 48px tall, where a thumb reaches them, and
they come back into the bar at 700px, so a tablet keeps what it had room for.
The bar has a 12px floor between every pair of elements now, set with a gap
instead of hoping space-between leaves some: 98px and 12px where it used to be
0. Every target in it is at least 44px tall.

The button. The one filled button was lime, which is the punctuation colour of
this brand and not the colour of its primary action; next to the cobalt hero
button it read as a second, competing offer. It is cobalt with white text
(6.3:1), the same .hp-btn-fill the homepage uses. app-2026.css forced
#0A1626 on lime with !important for the app screens and now follows, at the
same weight.

One home for the bar. .nav-auth, .nav-signin, .nav-cta, .nav-user and .nav-help
lived in design-system.css while the rest of the bar lived in nav.css, and the
half in design-system.css answered a narrow phone by shrinking: 9px type, 5px
padding, the Help border dropped. Small is not roomy. It all sits in nav.css
now. No page loads one without the other.

The drawer keeps mirroring the four destinations exactly, because
js/nav-auth.js rewrites its contents after the session check and
tests/navigation-shell pins them; that is why the tail is a sibling and not a
child, and why apply-nav.py had to learn to strip an old tail before stamping a
new one, or the second run would leave two.

Signed in is the same bar: the email control in the reading face, the drawer
carrying Documents, Send, Sign, Verify, Settings, and no tail, since the user
menu already holds Help and Sign out.

tests/navigation-shell: support on a phone was pinned to a visible /help link
in the closed bar, which is why Help was the fifth element up there at 9px.
The requirement is that a visitor looking for support finds a working route,
not which surface it lives on, so the check now opens the menu and demands a
48px target. It gained three checks that measure what the reviews saw by eye:
what is in the phone bar, how far apart, and how big. 29 checks, all green.

Versions: design-system.css v25, nav.css v20, nav.js v15, nav-auth.js v6,
app-2026.css v3, stamped by frontend/apply-nav.py over 51 pages and by hand on
the pages the generator does not own.

Gates: first-screen, ui-truthfulness, site-claims, seo-contract, links,
navigation-shell, frontend-loading-contract, pricing-fold, cache-bust,
csp-inline, static-sanity, check-test-declarations, eslint, theme-contrast,
motion-safety, app-contrast, app-theme, access-log-visitors,
apply-nav-idempotent, apply_seo_head --check. All green.
…e bar is measured on three pages

Review of #392 found the hole in the first round. developer.html keeps its own
navigation (KEEP_OWN_NAV in frontend/apply-nav.py), is stamped by hand, and
carries a hamburger. The round moved Sign in and Help out of the bar below
700px and into a strip under the drawer, and that page never got a strip. So on
a 390px screen both links measured 0x0 there, where main still had them in the
bar. A page the generator does not own is a page a generated fix does not
reach.

Three things, because one of them alone would leave the same gap open.

The markup. developer.html carries the strip now, like every stamped page.

The script. nav.js builds the strip when a page has a menu button and no strip.
A page can forget the markup; this cannot forget to build it. That is a floor
under the live site, not a substitute for the markup, which is why it comes
with the check below rather than instead of it.

The check. tests/navigation-shell reads every .html in frontend/, not only the
51 the generator writes, and fails on any page that has id="nav-hamburger"
without id="nav-mobile-tail". 52 pages carry the button. Taking the strip back
out of developer.html turns it red and names the file.

And the measurement moved. The three phone-bar checks ran on the homepage
alone, which is exactly why they were green while /developer was broken. They
run on /, /pricing and /developer now, each with a fourth check that the strip
really holds /auth/login and /help at 44px or more. /developer measures logo
x16-95, Create account x181-318, menu x330-374, gaps 86 and 12, all heights 44.

39 checks, all green. Rebased on main with #379, #390 and #391.
@Apolloccrypt
Apolloccrypt merged commit 32ed241 into main Sep 3, 2026
13 checks passed
Apolloccrypt pushed a commit that referenced this pull request Sep 3, 2026
…jurybesluit

Het oude document beschreef een richting die op drie punten is teruggedraaid
en op een vierde nooit is uitgevoerd. Het wees naar een tokenbestand dat door
niets geladen wordt en naar elf screenshots van prototypepagina's die niet
bestaan. Elke regel is nu getraceerd naar de PR die hem heeft besloten.

De richting leeft in vier lagen en niet in een. design-system.css v4.1 op alle
58 paginas, licht only; nav.css; app-2026.css op elf app-schermen, met donker
als keuze op /account onder paramant.theme.v1; en een eigen --hp-palet in de
kritieke CSS van de homepage. Het document zei dat er een globale tokenlaag
zou komen. Die is er niet en het bestand dat hem zou dragen is hier weg.

Drie dingen zijn verworpen en staan nu met hun reden in het document. De
STIL-balk in mono hoofdletters (#392: de stem van een terminal boven een
pagina in papier, inkt en zinnen, en Help stond daardoor op 9px). Lime als
primaire knop (#392: lime is de leestekenkleur van dit merk en las naast de
kobalt-heroknop als een tweede aanbod). En donker op marketing (#380:
onbereikbaar achter [data-theme] zonder schakelaar, onleesbaar wit op lime op
1.07:1, en 31 KB op elke pagina zonder gzip).

frontend/design-tokens-2026.css is weg. Geen pagina, build, test of nginx-conf
laadde hem, en hij was in tegenspraak met wat wel draait: hij schreef donker
als :root:not([data-theme="light"]), precies de sprong van creme naar zwart
die #381 heeft weggehaald en die docs/site-claims.md rij 28 en
tests/app-theme.test.mjs bewaken. Zijn aliaslaag verschoof --space-8/9/10 en
liet --space-10 en --space-11 ongedefinieerd, en --navy: var(--ink) zou elke
navy op 58 paginas in een keer verzetten. De levende bron is app-2026.css.

De elf prototypebeelden zijn vervangen door twee van de echte homepage, met
scripts/brand-shots-direction.mjs. Dat script volgt de regel uit #393: een
tempmap standaard, docs/brand/assets alleen met PARAMANT_WRITE_BRAND_SHOTS=1,
en het hergebruikt de vlag en de docs-check uit scripts/brand-shots-dir.mjs in
plaats van er een tweede kopie van te houden.

Twee merkbesluiten staan open in sectie 8. Een gevuld accent per scherm, want
/pricing draagt sinds de balkwissel zeven btn-primary plus de knop in de balk,
en op het eerste scherm staan er twee naast elkaar. En de koele naad: de balk
op rgb(248,250,252) tegen homepagepapier #FAF8F3, nu verborgen achter een
gradient over de eerste 160px, met de balkkleur gepind door
tests/navigation-shell.test.mjs.

Poorten groen: tests/static-sanity.sh (twaalf checks),
scripts/check-test-declarations.sh (134 suites), node --test
tests/links.test.mjs, npx eslint@9 . (exit 0). Extra:
tests/brand-shots-optin.test.mjs 10/10, scripts/check-cache-bust.sh 363 links,
tests/frontend-loading-contract.test.mjs 7/7.
@Apolloccrypt
Apolloccrypt deleted the fe/nav-rustig branch September 5, 2026 18:56
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