training/dashboard: theme-aware text greys with discontinuous L step + fix hardcoded colors

Two changes:

1. Replaced the linear L→grey-L mapping with a STEP function at
   theme-L = 50%. clamp(0, (L - 50) × 1000, 1) gives 0 below 50%
   and 1 above 50% with a vanishingly small transition zone —
   effectively a step in pure CSS. Both landing values are inside
   each grey's safe-contrast band, so text stays readable at every
   slider position, with a clear visible "click" as the slider
   crosses 50%. The chroma tint stays linear (it doesn't threaten
   contrast).

2. Fixed text that wasn't responding to the theme because it had
   hardcoded color values:
   - .intro-title gradient (#fff → #8b949e) → var(--fg) → var(--fg-dim)
   - .chunk-cell text (rgba(255,255,255,0.85)) → var(--fg)
   - .scene .prose strong (#fff) → var(--fg)

JS now publishes --theme-l-num (unitless) alongside --theme-l (with
%), since calc() can't multiply a percentage by a unitless number
to produce a unitless step value.
This commit is contained in:
Max Gorog 2026-05-08 01:37:26 -05:00
parent 058970de76
commit 153860f1db
3 changed files with 41 additions and 23 deletions

View file

@ -19,33 +19,46 @@
--bg-elev: #0d1117;
--bg-elev2: #161b22;
/* Text & line greys derive from the theme's L/C/H so the
lightness and chroma sliders nudge them too. Each rule keeps
a fixed base lightness (matching the previous static grey)
plus a small bias scaled from how far the L slider moved
from 70% (its default), and a fractional chroma so the greys
pick up a hint of the theme hue at higher saturations.
Falls back to the static base values if --theme-l etc. aren't
set yet (initial load before JS runs). */
/* Text & line greys, theme-aware with a discontinuous jump.
A *linear* response of grey-L to theme-L is too easy to push
into unreadable territory at the slider extremes. So the L
dependence is a STEP at theme-L = 50%: clamp(0, (L - 50) ×
1000, 1) gives 0 below 50 and 1 above 50 with a vanishingly
small transition zone effectively a step function expressed
in pure CSS. Both step values are inside their grey's safe
contrast band. Chroma tint stays linear (high C more hue
in the greys) since chroma doesn't threaten contrast. Falls
back to mid-band values if --theme-l-num isn't set (briefly
during initial load before JS runs).
The step landing zones are calibrated so:
theme-L < 50 cooler / dimmer greys (suits darker accents)
theme-L 50 brighter / fuller greys (suits brighter accents)
and the difference is small enough not to wreck the design,
yet large enough that you SEE the click as you cross 50%. */
--l-step: clamp(0, calc((var(--theme-l-num, 70) - 50) * 1000), 1);
--c-tint: calc(var(--theme-c, 0.15) * 0.18);
--fg: oklch(
calc(93% + (var(--theme-l, 70%) - 70%) * 0.06)
calc(var(--theme-c, 0.15) * 0.10)
calc(89% + var(--l-step) * 5%)
var(--c-tint)
var(--theme-h, 250));
--fg-dim: oklch(
calc(63% + (var(--theme-l, 70%) - 70%) * 0.10)
calc(var(--theme-c, 0.15) * 0.15)
calc(56% + var(--l-step) * 9%)
calc(var(--c-tint) * 1.2)
var(--theme-h, 250));
--fg-mute: oklch(
calc(38% + (var(--theme-l, 70%) - 70%) * 0.07)
calc(var(--theme-c, 0.15) * 0.12)
calc(33% + var(--l-step) * 7%)
calc(var(--c-tint) * 0.9)
var(--theme-h, 250));
--line: oklch(
calc(18% + (var(--theme-l, 70%) - 70%) * 0.03)
calc(var(--theme-c, 0.15) * 0.06)
calc(16% + var(--l-step) * 4%)
calc(var(--c-tint) * 0.4)
var(--theme-h, 250));
--line-soft: oklch(
calc(22% + (var(--theme-l, 70%) - 70%) * 0.03)
calc(var(--theme-c, 0.15) * 0.08)
calc(20% + var(--l-step) * 4%)
calc(var(--c-tint) * 0.5)
var(--theme-h, 250));
--accent: #58a6ff;
--accent-soft: rgba(88, 166, 255, 0.15);
@ -530,7 +543,7 @@ html, body { overflow-anchor: none; }
.intro-title {
font-size: clamp(56px, 9vw, 168px); line-height: 0.95; font-weight: 700;
letter-spacing: -0.04em;
background: linear-gradient(180deg, #fff 0%, #8b949e 100%);
background: linear-gradient(180deg, var(--fg) 0%, var(--fg-dim) 100%);
-webkit-background-clip: text; background-clip: text; color: transparent;
}
@ -830,7 +843,7 @@ html, body { overflow-anchor: none; }
flex: 1; border-radius: 3px;
display: flex; align-items: center; justify-content: center;
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
font-size: clamp(11px, 0.95vw, 14px); color: rgba(255,255,255,0.85);
font-size: clamp(11px, 0.95vw, 14px); color: var(--fg);
}
.chunk-cell.clean { background: var(--phase-clean); }
.chunk-cell.armed { background: var(--phase-armed); }
@ -933,7 +946,7 @@ html, body { overflow-anchor: none; }
.scene .prose .lede { font-size: 24px; line-height: 1.4; font-weight: 500;
margin: 0 0 20px; }
.scene .prose p { margin: 0 0 14px; }
.scene .prose strong { color: #fff; font-weight: 600; }
.scene .prose strong { color: var(--fg); font-weight: 600; }
.scene .prose code {
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
font-size: 0.9em; color: var(--accent);

View file

@ -419,6 +419,11 @@
root.setProperty('--accent-soft',
`oklch(${primary.L}% ${primary.C.toFixed(3)} ${primary.H.toFixed(1)} / 0.15)`);
root.setProperty('--theme-l', `${state.L}%`);
// Unitless version for math in calc() (e.g. clamp ramps).
// var(--theme-l) is "70%" — usable directly inside oklch but
// not as a unitless input to calc steps; --theme-l-num is the
// raw number (e.g. 70).
root.setProperty('--theme-l-num', state.L);
root.setProperty('--theme-c', state.C);
root.setProperty('--theme-h', state.H);
root.setProperty('--anim-speed', state.animSpeed);

View file

@ -4,7 +4,7 @@
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>CIS490 — live</title>
<link rel="stylesheet" href="/static/dashboard.css?v=6d01a682">
<link rel="stylesheet" href="/static/dashboard.css?v=3d3cb368">
</head>
<body>
<!-- SVG filter defs for the lava-lamp goo effect. Width/height 0
@ -491,6 +491,6 @@
</article>
</div>
<script src="/static/dashboard.js?v=81f9539a"></script>
<script src="/static/dashboard.js?v=a00dc514"></script>
</body>
</html>