mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-08 11:01:35 +00:00
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:
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();
|
||||
|
||||
|
||||
Reference in new issue
Block a user