theme 0.4s; the reveal band trails the front — no pre-beat center leak

Theme cross-fade drops to 0.4s across all four layers (token
transitions, toggle class window, WebGL lerp, settle re-derive). And
the inner cards showing through during the logo animation had a
precise cause: the reveal band was oriented as a LEADING edge —
(waveR − rr)/band + 1 puts the soft zone a full band AHEAD of the
front, so at radius zero the innermost 220px of cards sat partly
revealed before the beat even fired. The band now trails: a card stays
fully hidden until the front reaches it, then resolves over the band
behind it.

Claude-Session: https://claude.ai/code/session_01YBoaBEt3gynzTwogBFtn4B
This commit is contained in:
Tanish Manku committed 2026-09-11 00:03:06 -07:00
1 parent 7d14355875
commit f1e45ee521
4 files changed
+28 -25

No files matched your search

@@ -127,7 +127,7 @@ body {
/* Theme switch cross-fade. The tokens are registered as typed <color>
custom properties so the VARIABLES THEMSELVES can transition: when the
toggle stamps .theme-anim on <html>, the root transitions every token
over 0.75s and every consumer — utilities, borders, SVG fills,
over 0.4s and every consumer — utilities, borders, SVG fills,
color-mix() strings, and gradients (background-image is not animatable,
so element-level transitions could never cover the scrims and pools) —
re-resolves each animated frame. No per-element transitions exist, so
@@ -150,21 +150,21 @@ body {
html.theme-anim {
transition:
--bg 0.75s ease,
--bg-raised 0.75s ease,
--bg-sunken 0.75s ease,
--ink 0.75s ease,
--ink-strong 0.75s ease,
--muted 0.75s ease,
--faint 0.75s ease,
--line 0.75s ease,
--line-strong 0.75s ease,
--accent 0.75s ease,
--accent-ink 0.75s ease,
--invert-bg 0.75s ease,
--invert-fg 0.75s ease,
--danger 0.75s ease,
--scanline 0.75s ease;
--bg 0.4s ease,
--bg-raised 0.4s ease,
--bg-sunken 0.4s ease,
--ink 0.4s ease,
--ink-strong 0.4s ease,
--muted 0.4s ease,
--faint 0.4s ease,
--line 0.4s ease,
--line-strong 0.4s ease,
--accent 0.4s ease,
--accent-ink 0.4s ease,
--invert-bg 0.4s ease,
--invert-fg 0.4s ease,
--danger 0.4s ease,
--scanline 0.4s ease;
}
/* Signature interaction: label blocks invert on hover/active. */
@@ -799,10 +799,10 @@ function GalaxyScene({
clipBest.fill(-1);
// Theme cross-fade: WebGL colors chase their targets at ~0.75s settle,
// Theme cross-fade: WebGL colors chase their targets at ~0.4s settle,
// matching the DOM's theme transition.
const cs = colorState.current;
const themeK = 1 - Math.exp(-dt / 0.25);
const themeK = 1 - Math.exp(-dt / 0.13);
cs.bg.lerp(cs.tBg, themeK);
cs.frame.lerp(cs.tFrame, themeK);
cs.line.lerp(cs.tLine, themeK);
@@ -1160,7 +1160,11 @@ function GalaxyScene({
const band = Math.max(1, mv.waveBand);
if (rollT < 1) {
const waveR = waveEase * L.b * L.thetaExit;
waveK = clamp01((waveR - rr) / band + 1);
// The band TRAILS the front: a card stays fully hidden until the
// front reaches it, then resolves over the band behind it. (As a
// leading edge, the band covered the innermost cards at radius
// zero — partly-visible center cards before the beat.)
waveK = clamp01((waveR - rr) / band);
}
if (flareT > 0 && flareT < 1) {
const fr = (rr - flareEase * L.b * L.thetaExit) / band;
@@ -32,14 +32,13 @@ export default function ThemeToggle({ className }: { className?: string }) {
const toggle = () => {
const next: Theme = resolveCurrentTheme() === "dark" ? "light" : "dark";
const root = document.documentElement;
// Transient class: every themed property (colors, backgrounds, rules,
// SVG fills) cross-fades over 0.75s, then the blanket transition is
// lifted so per-frame color writes and hover states stay instant.
// Transient class: the theme tokens cross-fade over 0.4s (see the
// @property transitions in globals.css), then the class lifts.
root.classList.add("theme-anim");
window.clearTimeout(themeAnimTimer);
themeAnimTimer = window.setTimeout(
() => root.classList.remove("theme-anim"),
800,
450,
);
// Commit the transition property in its own style pass BEFORE the
// token flip — same-recalc changes can skip the transition entirely.
@@ -67,7 +67,7 @@ export function watchThemeColors(
onChange: (colors: ThemeColors) => void,
): () => void {
const derive = () => onChange(deriveThemeColors());
// Theme flips animate the tokens over 0.75s (see globals.css), so a
// Theme flips animate the tokens over 0.4s (see globals.css), so a
// derive at flip time reads MID-transition values. Deliver both: the
// immediate read (consumers that lerp start moving at once) and a final
// read after the fade settles so targets land on the true colors.
@@ -75,7 +75,7 @@ export function watchThemeColors(
const deriveTwice = () => {
derive();
clearTimeout(settleTimer);
settleTimer = setTimeout(derive, 850);
settleTimer = setTimeout(derive, 500);
};
derive();