From f1e45ee521248aeef4346b38842257e33afe362e Mon Sep 17 00:00:00 2001 From: Tanish Manku Date: Fri, 11 Sep 2026 00:03:06 -0700 Subject: [PATCH] =?UTF-8?q?theme=200.4s;=20the=20reveal=20band=20trails=20?= =?UTF-8?q?the=20front=20=E2=80=94=20no=20pre-beat=20center=20leak?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- .../artcraft-website-next/src/app/globals.css | 32 +++++++++---------- .../src/components/landing/hero-galaxy.tsx | 10 ++++-- .../src/components/theme-toggle.tsx | 7 ++-- .../src/lib/theme-colors.ts | 4 +-- 4 files changed, 28 insertions(+), 25 deletions(-) diff --git a/frontend/apps/artcraft-website-next/src/app/globals.css b/frontend/apps/artcraft-website-next/src/app/globals.css index 819f5e7a45..05ccd23213 100644 --- a/frontend/apps/artcraft-website-next/src/app/globals.css +++ b/frontend/apps/artcraft-website-next/src/app/globals.css @@ -127,7 +127,7 @@ body { /* Theme switch cross-fade. The tokens are registered as typed custom properties so the VARIABLES THEMSELVES can transition: when the toggle stamps .theme-anim on , 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. */ diff --git a/frontend/apps/artcraft-website-next/src/components/landing/hero-galaxy.tsx b/frontend/apps/artcraft-website-next/src/components/landing/hero-galaxy.tsx index 23869aaf6e..4b363116bb 100644 --- a/frontend/apps/artcraft-website-next/src/components/landing/hero-galaxy.tsx +++ b/frontend/apps/artcraft-website-next/src/components/landing/hero-galaxy.tsx @@ -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; diff --git a/frontend/apps/artcraft-website-next/src/components/theme-toggle.tsx b/frontend/apps/artcraft-website-next/src/components/theme-toggle.tsx index b347c568cc..cee46cea9f 100644 --- a/frontend/apps/artcraft-website-next/src/components/theme-toggle.tsx +++ b/frontend/apps/artcraft-website-next/src/components/theme-toggle.tsx @@ -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. diff --git a/frontend/apps/artcraft-website-next/src/lib/theme-colors.ts b/frontend/apps/artcraft-website-next/src/lib/theme-colors.ts index fdf6da0b7f..97ba50274b 100644 --- a/frontend/apps/artcraft-website-next/src/lib/theme-colors.ts +++ b/frontend/apps/artcraft-website-next/src/lib/theme-colors.ts @@ -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();