diff --git a/README.md b/README.md index b4be02e..2db47e0 100644 --- a/README.md +++ b/README.md @@ -1361,6 +1361,20 @@ Things I chose while building, with the reasoning: phase-1 suite (43 checks, including the real route mapping against a stub Ollama returning a monthly-limit body, a two-device sync round-trip, and the generation guards below). +61. **Glass that sits still doesn't blur.** The MacBook got hot just browsing: the ice scene animates + constantly, and every `backdrop-filter` surface re-runs its blur on every frame while the backdrop + moves — the whole card and rail area was re-blurring the scene ~60 times a second. Measured on + desktop (2x CPU throttle, software renderer): p50 frame 66ms with everything blurring, 33ms once + the static surfaces stopped, 17ms with the scene's animation also off — frames over 34ms dropped + from 128 to 14. The recipe is now split: **floating glass** (sheets, dropdowns, dialogs, the + sticky header/chrome/tab bar — the layers that can have content scroll underneath) keeps the + blur, now `backdrop-blur-lg` (which is what phones already used), while **static glass** (cards, + the rail, the gate, the study prompt card) keeps the exact same tint, rim, highlight and shadow + without the filter. A deterministic before/after pixel diff (scene motion frozen) shows the + change is a ~1% colour shift in the card backdrops — invisible in both themes. + `tests/bubble-perf.mjs` now reports the glass cost too, so a future blurred surface over the + scene shows up immediately. + 60. **There is exactly one loading screen.** The deck wizard had the good one — the app icon, the rotating stage message as the headline, the progress bar, the stage checklist, a tip and Cancel — while the homework flows showed a spinner with a title and the route loads a pulsing diff --git a/app/globals.css b/app/globals.css index 90a0b50..4357eb8 100644 --- a/app/globals.css +++ b/app/globals.css @@ -404,8 +404,20 @@ body:has(.overlay-open) :is(.app-header, .segmented, .tabbar) { @layer components { /* Frosted ice glass ------------------------------------------------------ - One shared recipe for the large surfaces only: translucent tint, 24px - blur, a bright rim, an inner top highlight and a blue-tinted shadow. + Two recipes now: + + - **Floating glass** (sheets, dropdowns, dialogs and the sticky chrome): + the tint below plus a backdrop blur — the blur is what reads as glass + when content scrolls underneath (the app header, the study chrome, the + tab bar, a sheet over the page). + - **Static glass** (cards, the rail, the gate, the study prompt card): the + exact same tint, rim, highlight and shadow, but **no** backdrop-filter. + On a smooth gradient the blur is invisible — and because the ice scene + animates, every blurred surface re-runs the filter on every frame. + Measured on desktop (2x throttle, software renderer): p50 frame time + 66ms while the static surfaces blurred the scene, 33ms without them, + 17ms with the scene's animation also stopped. + Nested glass (chips, inputs) never adds a second blur layer. Tailwind's backdrop utilities emit the -webkit- prefix too (hand-written backdrop-filter gets minified to -webkit- only, which paints nothing). */ @@ -416,11 +428,18 @@ body:has(.overlay-open) :is(.app-header, .segmented, .tabbar) { .tabbar, .glass, .gate-card { - @apply backdrop-blur-xl backdrop-saturate-[1.4]; background: var(--glass-bg); box-shadow: var(--glass-shadow), inset 0 1px 0 var(--glass-highlight); } +/* Only the layers that can have content behind them blur. */ +.glass-panel, +.study-chrome, +.tabbar, +.glass { + @apply backdrop-blur-lg backdrop-saturate-[1.4]; +} + .glass-panel { border: 1px solid var(--glass-border); border-radius: 1.5rem; @@ -460,28 +479,23 @@ body:has(.overlay-open) :is(.app-header, .segmented, .tabbar) { border-radius: 1.75rem; /* 28px */ } -/* Blur is expensive on phones: halve it, and thin out the ice scene. */ +/* Blur is expensive: halve it on phones (floating glass only). */ @media (max-width: 1023px) { .glass-panel, - .card, - .sidebar-surface, .study-chrome, .tabbar, - .glass, - .gate-card { - @apply backdrop-blur-lg; + .glass { + @apply backdrop-blur-md; } } -/* No backdrop-filter support: fall back to a more opaque surface. */ +/* No backdrop-filter support: fall back to a more opaque surface (floating + glass only — the static surfaces already look right without a filter). */ @supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) { .glass-panel, - .card, - .sidebar-surface, .study-chrome, .tabbar, - .glass, - .gate-card { + .glass { background: var(--glass-fallback); } } diff --git a/components/activities/shared.tsx b/components/activities/shared.tsx index 4185cd9..9c520fd 100644 --- a/components/activities/shared.tsx +++ b/components/activities/shared.tsx @@ -78,7 +78,7 @@ export function PromptCard({ }) { return (