|
10 | 10 | // variant support), so it's derived from the chrome theme directly. |
11 | 11 | let optionBg = $derived(themeState.value === 'dark' ? '#16161e' : '#ffffff'); |
12 | 12 |
|
13 | | - let { overrides, onSet, onReset, onBulkChange }: { |
| 13 | + let { overrides, onSet, onReset }: { |
14 | 14 | overrides: Record<string, string>; |
15 | 15 | onSet: (name: string, value: string) => void; |
16 | 16 | onReset: (name: string) => void; |
17 | | - onBulkChange: (patch: Record<string, string | null>) => void; |
18 | 17 | } = $props(); |
19 | 18 |
|
20 | 19 | const DURATIONS = [ |
|
36 | 35 | { label: "Overshoot", token: "--sf-ease-overshoot", value: "linear(0, 0.6 30%, 1.08 55%, 0.98 75%, 1)", preview: "M0,40 C8,40 20,-10 40,0" }, |
37 | 36 | ]; |
38 | 37 |
|
39 | | - const STAGGER_TOKENS = ["--sf-animation-delay-1","--sf-animation-delay-2","--sf-animation-delay-3","--sf-animation-delay-4","--sf-animation-delay-5"]; |
| 38 | + // The five fixed per-index stagger delay tokens were removed when |
| 39 | + // .sf-stagger landed; the whole sequence is now one knob, and |
| 40 | + // core/motion.css multiplies it by the child index and --sf-motion-scale. |
| 41 | + const STAGGER_TOKEN = "--sf-stagger-step"; |
| 42 | + const STAGGER_DEFAULT_MS = 75; |
40 | 43 |
|
41 | 44 | const knobs = KNOBS_BY_DOMAIN["motion"] ?? []; |
42 | 45 |
|
43 | 46 | let scale = $derived((() => { const v = parseFloat(overrides["--sf-motion-scale"] ?? "1"); return isFinite(v) ? v : 1; })()); |
44 | 47 | let motionDisabled = $derived(overrides["--sf-motion-scale"] === "0"); |
45 | 48 | let themeTransition = $derived((() => { const v = parseFloat(overrides["--sf-theme-transition-duration"]?.replace("ms","") ?? String(300 * scale)); return isFinite(v) ? v : Math.round(300 * scale); })()); |
46 | | - let staggerBase = $derived.by(() => { |
47 | | - const raw = overrides[STAGGER_TOKENS[0]]; |
48 | | - if (raw) return parseFloat(raw.replace("ms","")); |
49 | | - return 75 * scale; |
| 49 | + // The stored knob is the un-scaled step; the preview below shows the |
| 50 | + // effective delay, so --sf-motion-scale is applied for display only. |
| 51 | + let staggerStep = $derived.by(() => { |
| 52 | + const raw = overrides[STAGGER_TOKEN]; |
| 53 | + const v = raw ? parseFloat(raw.replace("ms", "")) : NaN; |
| 54 | + return isFinite(v) ? v : STAGGER_DEFAULT_MS; |
50 | 55 | }); |
| 56 | + let staggerOverridden = $derived(STAGGER_TOKEN in overrides); |
51 | 57 |
|
52 | 58 | // Animation demo — user picks which easing + duration to feel. |
53 | 59 | let animating = $state(false); |
|
85 | 91 | }, dur); |
86 | 92 | } |
87 | 93 |
|
88 | | - function setStaggerBase(baseMs: number) { |
89 | | - const patch: Record<string, string> = {}; |
90 | | - for (let i = 1; i <= 5; i++) patch[`--sf-animation-delay-${i}`] = `${Math.round(baseMs * i)}ms`; |
91 | | - onBulkChange(patch); |
| 94 | + function setStaggerStep(ms: number) { |
| 95 | + onSet(STAGGER_TOKEN, `${Math.round(ms)}ms`); |
92 | 96 | } |
93 | 97 |
|
94 | 98 | function resetStagger() { |
95 | | - const patch: Record<string, null> = {}; |
96 | | - for (let i = 1; i <= 5; i++) patch[`--sf-animation-delay-${i}`] = null; |
97 | | - onBulkChange(patch); |
| 99 | + onReset(STAGGER_TOKEN); |
98 | 100 | } |
99 | 101 | </script> |
100 | 102 |
|
|
252 | 254 | <!-- STAGGER --> |
253 | 255 | <Section title="Stagger" bind:open={showStagger}> |
254 | 256 | <p class="text-[10px] text-slate-400 dark:text-slate-600 leading-relaxed"> |
255 | | - One slider sets all five stagger delays (delay-1 through delay-5 are multiples of this base). |
| 257 | + Put <code class="text-slate-600 dark:text-slate-400">.sf-stagger</code> on a parent and each child's |
| 258 | + entrance is delayed by its index × this step × the motion scale. One knob retunes the whole sequence. |
256 | 259 | </p> |
257 | 260 | <div class="group"> |
258 | 261 | <div class="flex items-center justify-between mb-1.5"> |
259 | | - <span class="text-[11px] font-semibold text-slate-800 dark:text-slate-200">Stagger base</span> |
260 | | - {#if STAGGER_TOKENS.some(t => t in overrides)} |
| 262 | + <span class="text-[11px] font-semibold text-slate-800 dark:text-slate-200">Stagger step</span> |
| 263 | + {#if staggerOverridden} |
261 | 264 | <button onclick={resetStagger} class="text-[9px] text-slate-500 hover:text-rose-600 dark:hover:text-rose-400 cursor-pointer ">reset</button> |
262 | 265 | {/if} |
263 | 266 | </div> |
264 | 267 | <SliderRow |
265 | | - label="" value={Math.round(staggerBase)} min={0} max={200} step={5} unit="ms" |
266 | | - help="Base unit for --sf-animation-delay-1 through -5" |
267 | | - overridden={STAGGER_TOKENS.some(t => t in overrides)} |
268 | | - onChange={(v) => setStaggerBase(v)} |
| 268 | + label="" value={Math.round(staggerStep)} min={0} max={200} step={5} unit="ms" |
| 269 | + help="--sf-stagger-step — per-child delay unit for .sf-stagger" |
| 270 | + overridden={staggerOverridden} |
| 271 | + onChange={(v) => setStaggerStep(v)} |
269 | 272 | onReset={resetStagger} |
270 | 273 | /> |
271 | 274 | </div> |
272 | 275 | <div class="bg-black/4 dark:bg-white/4 rounded-xl border border-black/8 dark:border-white/8 p-3 space-y-1"> |
273 | 276 | {#each [1,2,3,4,5] as n (n)} |
274 | | - {@const delayMs = Math.round(staggerBase * n)} |
275 | | - {@const maxStaggerDelay = Math.round(staggerBase * 5)} |
| 277 | + {@const delayMs = Math.round(staggerStep * scale * n)} |
| 278 | + {@const maxStaggerDelay = Math.round(staggerStep * scale * 5)} |
276 | 279 | <div class="flex items-center gap-2"> |
277 | 280 | <span class="text-[9px] font-mono text-slate-400 dark:text-slate-600 w-6">–{n}</span> |
278 | 281 | <div class="flex-1 h-1.5 bg-black/8 dark:bg-white/8 rounded-full"> |
|
0 commit comments