feat(glass): variable-drive the glass system + dev GlassTuner (Phase 0/0b)
Foundation for the app-wide glass-consistency epic: - index.css: every look-driving value (blur/saturate/dark-brightness/panel-card- menu opacity/border/inset/edge/scrim/ambient + mosaic) is now a CSS custom property defaulting to the historical hard-coded value — one-place tunable, no visual change until a value is bumped. - GlassTuner.tsx: dev-only (localhost-gated) always-on corner panel that live- drives those vars, with treatment presets (Adaptive/Gradient+/Edge+scrim), per-scheme palette editor, muted thumbnail-mosaic toggle, localStorage persist, and Copy-CSS export. Deleted once the tuned finals are baked in. - MosaicBackdrop.tsx: opt-in muted (blur+darken+desaturate) mosaic of on-screen thumbnails so dark glass has real content to refract.
This commit is contained in:
parent
59aa727715
commit
22cce807a0
4 changed files with 520 additions and 20 deletions
|
|
@ -4,6 +4,29 @@
|
|||
|
||||
:root {
|
||||
--font-scale: 1.06;
|
||||
|
||||
/* ===== Glass tunables — every value that materially drives the look lives here so it can be
|
||||
tuned in ONE place (and live-driven by the dev GlassTuner during UAT). Defaults below equal
|
||||
the historical hard-coded values, so nothing changes until a value is bumped. When the user
|
||||
signs off on tuned values, bake the finals right here. ===== */
|
||||
--glass-blur: 30px; /* backdrop blur radius (2026 sweet spot is ~8–16px; 30 is heavy) */
|
||||
--glass-saturate: 1.8; /* backdrop saturation */
|
||||
--glass-dark-bright: 1.4; /* extra brightness lift applied to .glass/.glass-menu in dark mode only */
|
||||
--glass-surface-alpha: 78%; /* .glass panel translucency (higher = more solid = "adaptive") */
|
||||
--glass-card-alpha: 84%; /* .glass-card translucency */
|
||||
--glass-menu-alpha: 92%; /* .glass-menu translucency */
|
||||
--glass-border-alpha: 78%; /* .glass border opacity */
|
||||
--glass-inset: 16%; /* top specular highlight strength (#fff N%) */
|
||||
--glass-edge: 0%; /* extra edge-light mixed into the TOP border (#fff N%) — 0 = off */
|
||||
--glass-scrim: 0%; /* under-content scrim: darkens surfaces toward --bg for text contrast — 0 = off */
|
||||
--ambient: 1; /* multiplier on the ambient accent backdrop pools */
|
||||
|
||||
/* Optional muted thumbnail-mosaic backdrop (off unless <MosaicBackdrop/> mounts). Conservative
|
||||
defaults: heavy blur + low saturation + darkened so it reads as subtle texture, never a rainbow. */
|
||||
--mosaic-blur: 64px;
|
||||
--mosaic-sat: 0.7;
|
||||
--mosaic-bright: 0.5;
|
||||
--mosaic-opacity: 0.5;
|
||||
}
|
||||
|
||||
/* Make native controls (date picker, scrollbars) follow the theme. */
|
||||
|
|
@ -26,9 +49,9 @@ body {
|
|||
subtle but a touch richer (three soft pools, incl. a bottom glow) now that the nav,
|
||||
header and dialogs are all frosted glass and refract it. */
|
||||
background:
|
||||
radial-gradient(1100px 620px at 10% -10%, color-mix(in srgb, var(--accent) 20%, transparent), transparent 60%),
|
||||
radial-gradient(1000px 720px at 115% 10%, color-mix(in srgb, var(--accent) 14%, transparent), transparent 55%),
|
||||
radial-gradient(900px 640px at 50% 118%, color-mix(in srgb, var(--accent) 11%, transparent), transparent 60%),
|
||||
radial-gradient(1100px 620px at 10% -10%, color-mix(in srgb, var(--accent) calc(20% * var(--ambient)), transparent), transparent 60%),
|
||||
radial-gradient(1000px 720px at 115% 10%, color-mix(in srgb, var(--accent) calc(14% * var(--ambient)), transparent), transparent 55%),
|
||||
radial-gradient(900px 640px at 50% 118%, color-mix(in srgb, var(--accent) calc(11% * var(--ambient)), transparent), transparent 60%),
|
||||
var(--bg);
|
||||
background-attachment: fixed;
|
||||
}
|
||||
|
|
@ -37,34 +60,44 @@ body {
|
|||
.glass {
|
||||
/* Frosted overlay surface: translucent enough that the blurred backdrop shows
|
||||
through (the glassy look), opaque enough that the background isn't distracting.
|
||||
The strong blur turns whatever is behind into soft colour, not a sharp image. */
|
||||
background: color-mix(in srgb, var(--surface) 78%, transparent);
|
||||
backdrop-filter: blur(30px) saturate(1.8);
|
||||
-webkit-backdrop-filter: blur(30px) saturate(1.8);
|
||||
border: 1px solid color-mix(in srgb, var(--border) 78%, transparent);
|
||||
The strong blur turns whatever is behind into soft colour, not a sharp image.
|
||||
Layer 1 (top) = the optional scrim; layer 2 = the translucent surface tint. */
|
||||
background:
|
||||
linear-gradient(color-mix(in srgb, var(--bg) var(--glass-scrim), transparent), color-mix(in srgb, var(--bg) var(--glass-scrim), transparent)),
|
||||
color-mix(in srgb, var(--surface) var(--glass-surface-alpha), transparent);
|
||||
backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturate));
|
||||
-webkit-backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturate));
|
||||
border: 1px solid color-mix(in srgb, var(--border) var(--glass-border-alpha), transparent);
|
||||
border-top-color: color-mix(in srgb, #fff var(--glass-edge), color-mix(in srgb, var(--border) var(--glass-border-alpha), transparent));
|
||||
box-shadow:
|
||||
inset 0 1px 0 color-mix(in srgb, #fff 16%, transparent),
|
||||
inset 0 1px 0 color-mix(in srgb, #fff var(--glass-inset), transparent),
|
||||
0 18px 44px -16px rgba(0, 0, 0, 0.6);
|
||||
}
|
||||
.glass-card {
|
||||
/* No backdrop-filter here: cards render in bulk (feed grid) over a solid background
|
||||
where blur adds almost nothing visually but is GPU-expensive and triggers reflow.
|
||||
Translucency + border + depth keep the look; blur is reserved for .glass overlays. */
|
||||
background: color-mix(in srgb, var(--card) 84%, transparent);
|
||||
background:
|
||||
linear-gradient(color-mix(in srgb, var(--bg) var(--glass-scrim), transparent), color-mix(in srgb, var(--bg) var(--glass-scrim), transparent)),
|
||||
color-mix(in srgb, var(--card) var(--glass-card-alpha), transparent);
|
||||
border: 1px solid color-mix(in srgb, var(--border) 65%, transparent);
|
||||
border-top-color: color-mix(in srgb, #fff var(--glass-edge), color-mix(in srgb, var(--border) 65%, transparent));
|
||||
box-shadow:
|
||||
inset 0 1px 0 color-mix(in srgb, #fff 8%, transparent),
|
||||
inset 0 1px 0 color-mix(in srgb, #fff calc(var(--glass-inset) / 2), transparent),
|
||||
0 8px 22px -14px rgba(0, 0, 0, 0.45);
|
||||
}
|
||||
.glass-menu {
|
||||
/* Floating menus/popovers hover over undimmed content (no backdrop scrim like dialogs),
|
||||
so they must be near-opaque to stay readable — only a hint of translucency + the blur. */
|
||||
background: color-mix(in srgb, var(--surface) 92%, transparent);
|
||||
backdrop-filter: blur(30px) saturate(1.8);
|
||||
-webkit-backdrop-filter: blur(30px) saturate(1.8);
|
||||
background:
|
||||
linear-gradient(color-mix(in srgb, var(--bg) var(--glass-scrim), transparent), color-mix(in srgb, var(--bg) var(--glass-scrim), transparent)),
|
||||
color-mix(in srgb, var(--surface) var(--glass-menu-alpha), transparent);
|
||||
backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturate));
|
||||
-webkit-backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturate));
|
||||
border: 1px solid color-mix(in srgb, var(--border) 80%, transparent);
|
||||
border-top-color: color-mix(in srgb, #fff var(--glass-edge), color-mix(in srgb, var(--border) 80%, transparent));
|
||||
box-shadow:
|
||||
inset 0 1px 0 color-mix(in srgb, #fff 14%, transparent),
|
||||
inset 0 1px 0 color-mix(in srgb, #fff calc(var(--glass-inset) - 2%), transparent),
|
||||
0 18px 44px -16px rgba(0, 0, 0, 0.6);
|
||||
}
|
||||
.glass-hover:hover {
|
||||
|
|
@ -80,9 +113,9 @@ body {
|
|||
(The full "videos behind glass everywhere" effect is Phase 2 / end-of-project polish.) */
|
||||
html[data-theme="dark"] body {
|
||||
background:
|
||||
radial-gradient(1100px 620px at 10% -10%, color-mix(in srgb, var(--accent) 34%, transparent), transparent 60%),
|
||||
radial-gradient(1000px 720px at 115% 10%, color-mix(in srgb, var(--accent) 24%, transparent), transparent 55%),
|
||||
radial-gradient(900px 640px at 50% 118%, color-mix(in srgb, var(--accent) 18%, transparent), transparent 60%),
|
||||
radial-gradient(1100px 620px at 10% -10%, color-mix(in srgb, var(--accent) calc(34% * var(--ambient)), transparent), transparent 60%),
|
||||
radial-gradient(1000px 720px at 115% 10%, color-mix(in srgb, var(--accent) calc(24% * var(--ambient)), transparent), transparent 55%),
|
||||
radial-gradient(900px 640px at 50% 118%, color-mix(in srgb, var(--accent) calc(18% * var(--ambient)), transparent), transparent 60%),
|
||||
var(--bg);
|
||||
background-attachment: fixed;
|
||||
}
|
||||
|
|
@ -90,8 +123,8 @@ html[data-theme="dark"] body {
|
|||
the frosted sheen shows even over dark UI (over colourful content it just looks richer). */
|
||||
html[data-theme="dark"] .glass,
|
||||
html[data-theme="dark"] .glass-menu {
|
||||
backdrop-filter: blur(30px) saturate(1.7) brightness(1.4);
|
||||
-webkit-backdrop-filter: blur(30px) saturate(1.7) brightness(1.4);
|
||||
backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturate)) brightness(var(--glass-dark-bright));
|
||||
-webkit-backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturate)) brightness(var(--glass-dark-bright));
|
||||
}
|
||||
/* Toasts surface bottom-left (by the nav's notification bell), off the conventional
|
||||
top-right; a brighter rim draws the eye there. Most needed in dark mode, where the
|
||||
|
|
@ -100,6 +133,37 @@ html[data-theme="dark"] .toast-card {
|
|||
border-color: color-mix(in srgb, #fff 50%, transparent);
|
||||
}
|
||||
|
||||
/* ===== Optional muted thumbnail-mosaic backdrop (mounted by <MosaicBackdrop/>) =====
|
||||
Gives dark glass real content to refract. Kept conservative: heavy blur + low saturation +
|
||||
darkened, so it reads as a soft texture behind everything, never a colourful collage. When it
|
||||
is present, the ambient accent pools on <body> are dimmed so the two don't fight. */
|
||||
.mosaic-backdrop {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: -1;
|
||||
overflow: hidden;
|
||||
pointer-events: none;
|
||||
}
|
||||
.mosaic-backdrop .mosaic-tiles {
|
||||
position: absolute;
|
||||
inset: -10%;
|
||||
display: grid;
|
||||
gap: 0;
|
||||
filter: blur(var(--mosaic-blur)) saturate(var(--mosaic-sat)) brightness(var(--mosaic-bright));
|
||||
opacity: var(--mosaic-opacity);
|
||||
}
|
||||
.mosaic-backdrop .mosaic-tiles > span {
|
||||
display: block;
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
}
|
||||
html[data-mosaic="1"] body {
|
||||
background: var(--bg);
|
||||
}
|
||||
html[data-perf="1"] .mosaic-backdrop {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Performance mode (Settings → Appearance) drops the expensive blur + soft shadows. */
|
||||
html[data-perf="1"] .glass,
|
||||
html[data-perf="1"] .glass-menu,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue