Skip to content

feat(background): film strip background, frosted glass, hero under the navbar - #44

Merged
kris1027 merged 25 commits into
mainfrom
feat/film-strip-background
Oct 11, 2026
Merged

kris1027 merged 25 commits into
mainfrom
feat/film-strip-background

Conversation

@kris1027

Copy link
Copy Markdown
Owner

What

This PR swaps the old ambient background for the film strip scene from the v2 Claude Design file: three coiled film strips and rising bokeh over a teal gradient, drawn in hand written WebGL2. It also makes controls, panels and overlays frosted glass, so the scene shows through them, blurred. On title pages, the backdrop image now reaches up behind the navbar.

Why

You asked for the new background design and for glass surfaces that let it show through. The decisions are recorded in three plans:

Changes

Film strip background

  • New lib/film-scene/ module (budget, layout, palette, matrices, shaders, scene, clock, run, messages, worker, start, continuity). It replaces lib/backdrop/ and adds no new dependency (no three.js).
  • Each frame takes two WebGL2 programs and strips + 1 draw calls. The film frame is drawn procedurally in the fragment shader, and the bokeh motion runs in the vertex shader.
  • PageBackground (Server Component) paints the gradient and vignette as CSS, so they are the first paint and the fallback. PageBackgroundCanvas imports the scene only when the browser is idle.
  • The scene runs in a worker where OffscreenCanvas supports WebGL2, and on the main thread otherwise. Worker messages are validated with Zod.
  • Budgets: the pixel ratio is capped at 1.5. Below 768px or with a coarse pointer, it draws 2 strips and 35 bokeh, otherwise 3 strips and 70 bokeh.
  • Reduced motion draws one still frame. A hidden tab pauses the loop and resumes without a jump. Without WebGL2, or after a context loss, only the CSS layers remain, with nothing thrown.
  • Every fragment is clamped so text-muted-foreground keeps 4.5:1. The bokeh uses MAX blending, so overlapping discs cannot add up past that cap.
  • The design's cursor parallax is gone: the camera is fixed and no pointer listener is added.
  • The intro plays once per tab. The clock time is kept in sessionStorage (bestats:film-scene), so a reload or a rebuild across the breakpoint carries on without the fade. The /showcase still frame never touches that key.
  • The gradient was later retuned: brighter along the top, with a vignette that also deepens the bottom half.

Frosted glass

  • New --frost-* opacity tokens and --blur-control/panel/overlay tokens. The glass-plate* utilities now blend the plate colour with transparency and set backdrop-filter, with the -webkit- prefix included.
  • New glass-plate-float for the popover, quick search and toasts. New plate-solid for slots over poster artwork, missing poster tiles and skeletons, which stay opaque.
  • Selected surfaces keep an opaque plate. The glass sheen drops from 18% to 12% white.
  • Reduced transparency, higher contrast, and no backdrop-filter support all bring back the opaque plates with no blur.
  • New app/glass-contrast.test.ts composites each plate over its worst background and fails on any text token below 4.5:1.

Title hero and chrome

  • On title pages, the backdrop extends up behind the navbar (--navbar-height). A backdrop-scrim keeps the bar readable, and the navbar gets relative z-20.
  • The hero fades into the page with a CSS mask instead of --background gradients, so it dissolves into the film background rather than ending in a black band. The skeleton matches the new geometry.
  • The footer loses the hairline above the icon key.

Docs and showcase

  • /showcase gets a Page background section (token swatches and the still frame) and an updated plate rule section showing frosted, solid and fallback surfaces.
  • components/AGENTS.md rewrites the background, plate and blur rules and ends the personal cyan exception for the background.

How to test / verify

  • Run pnpm typecheck && pnpm lint && pnpm test && pnpm build.
  • Check that rg -i "lib/backdrop|ambient-(canvas|background)|backdrop-wave|backdrop-dust" finds nothing outside prompts/.
  • In pnpm dev, open /shows. The gradient paints first, then the strips and bokeh fade in. Moving the mouse changes nothing.
  • Reload the same tab: the strips appear with no fade, in a later pose. A new tab fades in once.
  • Emulate prefers-reduced-motion: reduce: the scene freezes. Hide the tab and come back: it resumes without a jump.
  • Disable WebGL and reload: only the gradient and vignette show, with no console errors.
  • Check the navbar tabs, search field, buttons and panels: they show the scene blurred behind them. Poster badges and round buttons look as they did before.
  • Emulate prefers-reduced-transparency: reduce, then prefers-contrast: more: the surfaces turn solid.
  • Open a movie or show: the backdrop sits behind the navbar, the bar text is readable, and the image fades into the film background.
  • Open /showcase and look at the Page background and plate rule sections.

Risk & rollout

No migrations, no environment values, no flags, and no new runtime dependency. Every change is client side visuals or CSS.

  • Performance: the scene and worker are separate chunks loaded after idle. The plan measured about 7.5 KB and 5.7 KB gzipped. Blur cost is limited by the nesting rule and by the solid slots on poster grids.
  • Layout: backdrop-filter creates a containing block and a stacking context, which could affect fixed children inside plated elements. Base UI portals its popups.
  • Rollback: revert the PR.

Notes for reviewers

  • Look closely at lib/film-scene/scene.ts. gl.clear respects depthMask, and the bokeh pass leaves it off. Without turning it back on before each clear, strips flickered and a redrawn still frame lost them. scene.test.ts guards this.
  • The frost values in globals.css (control 65%, panel 60%, sheet 75%, float 90%, score 65%) come from glass-contrast.test.ts. Please don't lower them without rerunning it.
  • prompts/ambient-background.md is kept as history, with a pointer to the new plan.

🤖 Generated with Claude Code

kris1027 and others added 15 commits October 11, 2026 14:27
Coiled film strips and rising bokeh from the v2 Claude Design file, drawn
in a worker where OffscreenCanvas allows and on the main thread otherwise,
with the muted text contrast cap, reduced motion and hidden tab pause.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
PageBackground renders the teal gradient and vignette as the first paint
and starts the film strip scene once the browser is idle. The point wave
background, its tokens and its tests are removed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The camera is fixed, its view is computed once, and no pointer listener
or worker message remains, on any device.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A stored scene time read and saved through sessionStorage, validated and
silent on every storage failure, and a clock that starts from a given time
and reports it once a second while running and once when it stops.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The worker forwards the clock's reports, the page keeps the latest in
sessionStorage, and a later full load or a rebuild across the breakpoint
carries on from it without the fade. The showcase still frame leaves the
stored time alone.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The footer now sits straight on the page background with no border or
background, and its test fails if either appears anywhere inside it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kris1027 kris1027 added accessibility Keyboard access, screen reader support, contrast, and accessibility docs Documentation additions and updates feature New user-facing functionality test Automated tests and test infrastructure performance Load time, responsiveness, and resource usage improvements ui Layouts, styling, and shared interface components labels Oct 11, 2026
@kris1027 kris1027 self-assigned this Oct 11, 2026
@github-project-automation github-project-automation Bot moved this to Backlog in BeStats Oct 11, 2026
@vercel

vercel Bot commented Oct 11, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
bestats Ready Ready Preview Oct 11, 2026 1:28pm UTC

kris1027 and others added 4 commits October 11, 2026 15:16
…startFilmScene

Each thread's start is its own function, fed the scene's start state and a
set of events bound to the run that made them, so startFilmScene keeps only
the page's own state: the canvas, the clock time, the budget and the run.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Terminating the worker on a rebuild dropped its clock's final report, so the
new scene started from the page's copy, up to a second of real time old. The
page now posts a rebuild with a fresh canvas instead, and the worker starts
the new scene from the time its old clock reported as it stopped.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nvas

The stored time, the fade on a first load only, the unfaded rebuild and the
showcase still frame staying out of storage are now checked end to end
through PageBackgroundCanvas, not only piece by piece.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…lain the frame length

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
kris1027 and others added 6 commits October 11, 2026 15:25
lib/color/contrast.ts now holds the channel parsers, relative luminance and
the contrast ratio, so the glass contrast guard no longer depends on the
WebGL background module and its tests stop re-parsing colours by hand.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… page-*

backdrop- meant both the page background and a title's TMDB artwork. The
gradient, the vignette and their four tokens are now page-gradient,
page-vignette and --color-page-*, leaving backdrop to the artwork.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Each plate role now sets only its colour, frost and blur, and one shared
glass-plate-frost utility turns them into the see-through plate and its
blur. The compiled CSS for every role is unchanged in value.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The scrim's comment claimed the contrast glass-contrast.test.ts proves, but
nothing checked it. White artwork under the scrim's faintest shade across
the bar now has to stay at or below the page background's peak.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The token restated the navbar's height as a bare 6.5rem and 3.5rem. It is
now written from the rows, gap and padding, and the navbar test fails if
the bar's classes stop matching those parts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The film strip and continuity plans note the first frame fade, the rebuild
message, the tuned gradient and the page-* names. The title backdrop under
the navbar gets its own plan, and the footer plan records the dropped
hairline.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kris1027
kris1027 merged commit f4a73fd into main Oct 11, 2026
4 checks passed
@github-project-automation github-project-automation Bot moved this from In review to Done in BeStats Oct 11, 2026
@kris1027
kris1027 deleted the feat/film-strip-background branch October 11, 2026 13:33

This branch was successfully deployed

1 active deployment
Preview — dc0d61db Deployed Oct 11, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

accessibility Keyboard access, screen reader support, contrast, and accessibility docs Documentation additions and updates feature New user-facing functionality performance Load time, responsiveness, and resource usage improvements test Automated tests and test infrastructure ui Layouts, styling, and shared interface components

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

1 participant