refactor(website): make the nav bar one translucent CSS surface - #1088
Merged
Conversation
The bar switched between a transparent hero surface and a solid white one. That switch cost a `useNavSurface` hook, an 8px sentinel div, an IntersectionObserver, a `getBoundingClientRect` seed, a `data-surface` attribute, a `HERO_ROUTES` list and 12 tests — all of which are deleted here. It also had a defect that could not be fixed from inside the mechanism. `atTop` initialised to `false`, so the server rendered `data-surface="solid"`: the server cannot know scroll position and effects do not run during SSR, so the browser painted a white bar over the yellow hero for the duration of hydration (~250ms against `next dev`) before flipping to transparent. Seeding `true` only moves the same flash to already-scrolled loads. One CSS surface replaces it: a 72% white over `saturate(180%) blur(14px)`. It reads over the hero's yellow and over white content alike, so there is nothing to switch — and, because it has no hydration-dependent state at all, there is no flash to fix. Measured at 1440px against `next dev`, sampling rendered pixels rather than estimating the blend: - over the yellow hero the bar resolves to rgb(255, 232, 184); the nav links (`--color-text-secondary`, rgb(70, 70, 70)) sit at 7.86:1 on it, so they stay grey rather than moving to navy - over white content (scrolled `/`, `/docs`, `/pricing`) the bar resolves to a clean rgb(255, 255, 255) behind the hairline; links 9.44:1 - over the dark reliability band it resolves to rgb(193, 198, 204); links 5.49:1, still past 4.5:1 - the blur samples live content: a hard section edge behind the bar renders as a ~56px ramp through it, and the sampled bar colour changes with scroll Two fallbacks are required because a translucent bar that fails to blur is an unreadable smear: `@supports not (backdrop-filter: ...)` and `prefers-reduced-transparency: reduce` both fall back to the opaque surface. The `-webkit-` prefix is deliberately NOT hand-written — Lightning CSS emits it, and hand-writing it makes Lightning collapse the pair to the prefixed property alone, which Chromium does not implement at all. That silently disables the blur, and the @supports fallback cannot catch it. See the comment in chrome.css. `e2e/nav-surface.spec.ts` is rewritten to assert the single surface — alpha strictly between 0 and 1, a non-`none` backdrop-filter, no box-shadow, and an identical computed surface before and after a scroll. That last case is the guard for the whole simplification and was proved non-vacuous by mutation. The `/docs` condensation (`data-route`, the flat 58px, the demoted CTA) is untouched, and its test is kept. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Contributor
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Replaces the navbar's JavaScript-driven transparent/solid switch with one translucent CSS surface, and deletes the machinery it needed.
Why
The bar switched between transparent (on
HERO_ROUTESat scroll 0) and solid. That cost auseNavSurfacehook, an 8px sentinel div, an IntersectionObserver, agetBoundingClientRectseed, adata-surfaceattribute, aHERO_ROUTESlist, and 9 tests.It also had a defect that could not be fixed, only moved.
atTopinitialises tofalse, so the server rendersdata-surface="solid"— the server cannot know scroll position, and effects do not run during SSR. The browser painted a white bar over the yellow hero for the duration of hydration (~250ms againstnext dev), then flipped and faded over 200ms. Initialisingtrueinstead merely relocates the flash to already-scrolled loads (#hashdeep links, back-navigation with scroll restoration).A single translucent blurred bar reads correctly over the yellow hero and over white content, needs no JavaScript, and has no hydration-dependent state — so it cannot flash at all.
Measured, not estimated
Sampled rendered pixels at 1440px (modal colour of a 7×7 patch via Playwright + sharp):
rgb(255, 232, 184)/docs,/pricing, scrolledrgb(255, 255, 255)rgb(193, 198, 204)Link contrast never drops below 5.49:1, so the links stay on
--color-text-secondary— no navy switch was needed.The blur genuinely samples live content. A hard section edge parked behind the bar renders as a smooth ~56px ramp through it (
rgb(250,229,185)at y=20 →rgb(186,193,204)at y=76) against a 1px step in the source. Pure alpha would show a step, not a ramp.0.72alpha and14pxblur were kept: raising alpha drains the yellow character, lowering it pushes the dark-band case toward 4.5:1.A silent failure worth knowing about
Hand-writing both
backdrop-filterand-webkit-backdrop-filterbreaks the blur. Lightning CSS (Next's pipeline) collapses the pair and emits only the prefixed one, and Chromium does not implement-webkit-backdrop-filterat all —CSS.supports('-webkit-backdrop-filter','blur(1px)')isfalse. Computedbackdrop-filterwasnoneand the bar was a flat alpha smear.The
@supportsfallback cannot catch this, because Chrome does support the unprefixed property that guard asks about.Writing only the standard property makes Lightning emit both, and expand the
@supportscondition itself. Recorded as a "do not hand-prefix" comment inchrome.css.Deleted
useNavSurface.ts·useNavSurface.spec.tsx· the sentinel div ·data-surface·HERO_ROUTES·.nav-scroll-sentinel· every[data-surface='transparent']rule · the now-dead transition and its reduced-motion override.The
/docscondensation is untouched —data-route, the flat 58px, and the demoted CTA are a separate mechanism.Two fallbacks guard the case where a translucent bar fails to blur and becomes an unreadable smear:
@supports not (backdrop-filter)andprefers-reduced-transparency: reduce, both falling back to opaque.Tests
e2e/nav-surface.spec.tsno longer asserts a switch that does not exist. It now pins the translucent background, a non-nonebackdrop filter, no box-shadow, and — the guard for the whole simplification — that the bar is byte-identical before and after scrolling. A regression reintroducing state breaks it. Proven non-vacuous against a scroll-driven repaint:The CTA marketing-fill-vs-docs-text-link test is kept — it covers the docs condensation.
Verification
nx buildexit 0 · unit 1455 passed (main's 1464 minus the 9 deleted, exactly) ·nx lint0 errors · 32 nav e2e specs pass on the rebased branch, including the strengthened hover traverse from #1087.One judgement call left open
The
Talk to UsCTA measures 1.54:1 against the bar over the hero. That is low — but the same button is 1.84:1 on plain white everywhere else on the site today, its ink is 10.73:1, and its boundary is carried by hue and its existing shadow. The bar softens an edge that was already sub-3:1 rather than creating a new failure, and retinting only here would make the nav CTA the one primary button on the site with different chrome.Left alone deliberately. Setting
border-color: var(--color-ink)on the nav CTA is a one-line change if preferred.🤖 Generated with Claude Code