From 1a14ac050e3acf4823ab094c4e4d482930f94995 Mon Sep 17 00:00:00 2001 From: Randall Stillwell Date: Wed, 3 Jun 2026 21:35:23 -0500 Subject: [PATCH] fix(design-system): glass-panel rules now actually apply backdrop-filter Production check after #97 promoted: the .glass-panel and .glass-panel-strong utilities deployed without ANY backdrop-filter declaration, making the "glass" surfaces functionally indistinguishable from solid color cards. Root cause (two compounding Lightning CSS quirks in Next.js 16): 1) Lightning CSS silently strips `backdrop-filter: blur(var(--X)) saturate(var(--Y))` from the compiled output when the values use `var()` chains inside the blur()/saturate() function args. Tailwind's own .backdrop-blur-* utilities survive because they wrap the *entire* `blur(...)` expression in a single CSS var; we were nesting var() inside the function call which the optimizer doesn't understand. Same root cause emptied .mobile-nav-backdrop in production earlier. 2) When both `backdrop-filter` and `-webkit-backdrop-filter` are written with identical values, Lightning CSS de-duplicates them and (oddly) keeps only the `-webkit-` form. Modern Chrome/Edge/Firefox ignore the webkit prefix, so the blur never applies. Fix: - Use LITERAL values: `blur(12px) saturate(180%)` instead of `blur(var(...)) saturate(var(...))`. The --glass-blur-* tokens remain in :root for JSX consumers (inline-style backdropFilter is unaffected by Lightning CSS). - Write only the unprefixed `backdrop-filter`; Lightning CSS's autoprefixer adds `-webkit-backdrop-filter` based on browserslist (ios_saf <18 still needs it). Verified by `npm run build`: .glass-panel { ... -webkit-backdrop-filter: blur(12px) saturate(180%); backdrop-filter: blur(12px) saturate(180%); ... } Both forms now present in the compiled CSS, in the order modern dedupers prefer (prefixed first, standard second). No behavior change for JSX consumers or for any other CSS class that already worked. Just gets the two utility classes from #97 actually doing their job. Co-authored-by: Cursor --- styles/globals.css | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/styles/globals.css b/styles/globals.css index 51c1b72..4f6a67f 100644 --- a/styles/globals.css +++ b/styles/globals.css @@ -439,18 +439,29 @@ body { from Tailwind (the class itself doesn't set border-radius because consumers vary between rounded-xl and rounded-2xl). ============================================================ */ +/* IMPORTANT — backdrop-filter quirks with Next.js 16's Lightning CSS: + 1) `backdrop-filter: blur(var(--X)) saturate(var(--Y))` is silently + stripped from the compiled CSS (same root cause that emptied + `.mobile-nav-backdrop` in production). Use LITERAL values. + 2) Writing both `backdrop-filter` and `-webkit-backdrop-filter` with + identical values triggers Lightning CSS's de-dup; it keeps only the + `-webkit-` form which Chrome/Edge/Firefox ignore. Write only the + unprefixed `backdrop-filter` and let Lightning CSS's autoprefixer + add the vendor prefix per browserslist (it does for ios_saf <18). + If the blur radii ever need to change, change them here AND in the + matching --glass-blur-* tokens (kept for `` JSX consumers + which set `backdropFilter` via inline style — those are unaffected by + the Lightning CSS optimizer). */ .glass-panel { background-color: var(--glass-surface-mid); - backdrop-filter: blur(var(--glass-blur-mid)) saturate(var(--glass-saturate)); - -webkit-backdrop-filter: blur(var(--glass-blur-mid)) saturate(var(--glass-saturate)); + backdrop-filter: blur(12px) saturate(180%); border: 1px solid var(--rim-light-outer); box-shadow: var(--elevation-ambient), inset 0 1px 0 var(--rim-light-inner); } .glass-panel-strong { background-color: var(--glass-surface-high); - backdrop-filter: blur(var(--glass-blur-high)) saturate(var(--glass-saturate)); - -webkit-backdrop-filter: blur(var(--glass-blur-high)) saturate(var(--glass-saturate)); + backdrop-filter: blur(20px) saturate(180%); border: 1px solid var(--rim-light-outer); box-shadow: var(--elevation-pronounced), inset 0 1px 0 var(--rim-light-inner); }