Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion configurator/src/components/DomainPanel.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@
{:else if domain === "shadows"}
<ShadowsPanel {overrides} {onSet} {onReset} />
{:else if domain === "motion"}
<MotionPanel {overrides} {onSet} {onReset} {onBulkChange} />
<MotionPanel {overrides} {onSet} {onReset} />
{:else if domain === "effects"}
<EffectsPanel {overrides} {onSet} {onReset} />
{:else if domain === "macros"}
Expand Down
13 changes: 9 additions & 4 deletions configurator/src/components/panels/ColorsPanel.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,9 @@
{ name: "--sf-gradient-tertiary", label: "Tertiary", group: "brand", kind: "angle", space: "oklch", angle: 135, dir: "right",
stop1: "var(--sf-color-tertiary)", stop2: "oklch(from var(--sf-color-tertiary) calc(l - 0.08) c h)",
formula: "linear-gradient(in oklch 135deg, var(--sf-color-tertiary), oklch(from var(--sf-color-tertiary) calc(l - 0.08) c h))" },
{ name: "--sf-gradient-action", label: "Action", group: "brand", kind: "angle", space: "oklch", angle: 135, dir: "right",
stop1: "var(--sf-color-action)", stop2: "oklch(from var(--sf-color-action) calc(l - 0.08) c h)",
formula: "linear-gradient(in oklch 135deg, var(--sf-color-action), oklch(from var(--sf-color-action) calc(l - 0.08) c h))" },
{ name: "--sf-gradient-brand", label: "Brand", group: "brand", kind: "angle", space: "oklch", angle: 135, dir: "right",
stop1: "var(--sf-color-primary)", stop2: "oklch(from var(--sf-color-primary) l c calc(h + 30))",
formula: "linear-gradient(in oklch 135deg, var(--sf-color-primary), oklch(from var(--sf-color-primary) l c calc(h + 30)))" },
Expand Down Expand Up @@ -108,7 +111,7 @@
{ name: "--sf-color-info-source-light", label: "Info", side: "light", default: "oklch(0.48 0.18 235)", colorKey: "info" },
{ name: "--sf-color-info-source-dark", label: "Info", side: "dark", default: "oklch(0.71 0.162 235)", colorKey: "info" },
{ name: "--sf-color-danger-source-light", label: "Danger", side: "light", default: "oklch(0.48 0.22 12)", colorKey: "danger" },
{ name: "--sf-color-danger-source-dark", label: "Danger", side: "dark", default: "oklch(0.73 0.198 12)", colorKey: "danger" },
{ name: "--sf-color-danger-source-dark", label: "Danger", side: "dark", default: "oklch(0.71 0.198 12)", colorKey: "danger" },
];

// Baked per-step ramp curve — mirrors core/tokens.css: how far each step
Expand Down Expand Up @@ -178,11 +181,13 @@
const PALETTE_COLOR_KEYS = [...BRAND_COLOR_KEYS, "base"];

// Status families (success/warning/info/danger) have NO numeric 50-950
// ramp in the framework — only these four derived values actually exist
// (core/tokens.css: resolved color + -subtle/-muted alpha washes + a
// light-dark() -strong shift). Mirror that here instead of faking a ramp.
// ramp in the framework — only these five derived values actually exist
// (core/tokens.css: resolved color + -tint/-subtle/-muted alpha washes at
// 0.05/0.12/0.3 + a light-dark() -strong shift). Mirror that here instead of
// faking a ramp.
const STATUS_VARIANTS: { label: string; expr: (key: string) => string }[] = [
{ label: "Base", expr: (k) => `var(--sf-color-${k})` },
{ label: "Tint", expr: (k) => `var(--sf-color-${k}-tint)` },
{ label: "Subtle", expr: (k) => `var(--sf-color-${k}-subtle)` },
{ label: "Muted", expr: (k) => `var(--sf-color-${k}-muted)` },
{ label: "Strong", expr: (k) => `var(--sf-color-${k}-strong)` },
Expand Down
27 changes: 0 additions & 27 deletions configurator/src/components/panels/MiscPanel.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,6 @@
{ label: "Tooltip", token: "--sf-z-tooltip", note: "Inline tooltips", def: 1060 },
];

const SCROLL_BEHAVIORS = [
{ label: "Smooth", value: "smooth" },
{ label: "Auto", value: "auto" },
];

const SIZE_TOKENS = [
{ label: "XS", token: "--sf-size-xs", default: 1.5 },
{ label: "S", token: "--sf-size-s", default: 2 },
Expand All @@ -43,14 +38,12 @@
}

let touchTarget = $derived(parseNum(overrides["--sf-touch-target"], 44, "px"));
let scrollBehavior = $derived(overrides["--sf-scroll-behavior"] ?? "smooth");
let zBaseOffset = $derived(parseNum(overrides["--sf-z-base"], 0));
let caretColor = $derived(overrides["--sf-caret-color"] ?? "");
let underlineOffset = $derived(parseNum(overrides["--sf-link-underline-offset"]?.replace("em",""), 0.15));
let underlineThickness = $derived(overrides["--sf-link-underline-thickness"] ?? "auto");

let showTouchTarget = $state(false);
let showScrollBehavior = $state(false);
let showZIndex = $state(false);
let showTextSelection = $state(false);
let showComponentSizes = $state(false);
Expand Down Expand Up @@ -93,26 +86,6 @@

<div class="h-px bg-black/6 dark:bg-white/6"></div>

<!-- SCROLL BEHAVIOR -->
<Section title="Scroll behavior" bind:open={showScrollBehavior}>
<div class="flex gap-2">
{#each SCROLL_BEHAVIORS as b (b.value)}
<button
onclick={() => b.value === "smooth" ? onReset("--sf-scroll-behavior") : onSet("--sf-scroll-behavior", b.value)}
class={`flex-1 py-2 rounded-lg text-[10px] border transition-all cursor-pointer ${
scrollBehavior === b.value
? "bg-indigo-500/15 border-indigo-500/40 text-indigo-800 dark:text-indigo-200"
: "border-black/8 dark:border-white/8 text-slate-600 dark:text-slate-400 hover:bg-black/5 dark:hover:bg-white/5 hover:text-slate-800 dark:hover:text-slate-200"
}`}
>
{b.label}
</button>
{/each}
</div>
</Section>

<div class="h-px bg-black/6 dark:bg-white/6"></div>

<!-- Z-INDEX -->
<Section title="Z-index layers" bind:open={showZIndex}>
<SliderRow
Expand Down
49 changes: 26 additions & 23 deletions configurator/src/components/panels/MotionPanel.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,10 @@
// variant support), so it's derived from the chrome theme directly.
let optionBg = $derived(themeState.value === 'dark' ? '#16161e' : '#ffffff');

let { overrides, onSet, onReset, onBulkChange }: {
let { overrides, onSet, onReset }: {
overrides: Record<string, string>;
onSet: (name: string, value: string) => void;
onReset: (name: string) => void;
onBulkChange: (patch: Record<string, string | null>) => void;
} = $props();

const DURATIONS = [
Expand All @@ -36,18 +35,25 @@
{ 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" },
];

const STAGGER_TOKENS = ["--sf-animation-delay-1","--sf-animation-delay-2","--sf-animation-delay-3","--sf-animation-delay-4","--sf-animation-delay-5"];
// The five fixed per-index stagger delay tokens were removed when
// .sf-stagger landed; the whole sequence is now one knob, and
// core/motion.css multiplies it by the child index and --sf-motion-scale.
const STAGGER_TOKEN = "--sf-stagger-step";
const STAGGER_DEFAULT_MS = 75;

const knobs = KNOBS_BY_DOMAIN["motion"] ?? [];

let scale = $derived((() => { const v = parseFloat(overrides["--sf-motion-scale"] ?? "1"); return isFinite(v) ? v : 1; })());
let motionDisabled = $derived(overrides["--sf-motion-scale"] === "0");
let themeTransition = $derived((() => { const v = parseFloat(overrides["--sf-theme-transition-duration"]?.replace("ms","") ?? String(300 * scale)); return isFinite(v) ? v : Math.round(300 * scale); })());
let staggerBase = $derived.by(() => {
const raw = overrides[STAGGER_TOKENS[0]];
if (raw) return parseFloat(raw.replace("ms",""));
return 75 * scale;
// The stored knob is the un-scaled step; the preview below shows the
// effective delay, so --sf-motion-scale is applied for display only.
let staggerStep = $derived.by(() => {
const raw = overrides[STAGGER_TOKEN];
const v = raw ? parseFloat(raw.replace("ms", "")) : NaN;
return isFinite(v) ? v : STAGGER_DEFAULT_MS;
});
let staggerOverridden = $derived(STAGGER_TOKEN in overrides);

// Animation demo — user picks which easing + duration to feel.
let animating = $state(false);
Expand Down Expand Up @@ -85,16 +91,12 @@
}, dur);
}

function setStaggerBase(baseMs: number) {
const patch: Record<string, string> = {};
for (let i = 1; i <= 5; i++) patch[`--sf-animation-delay-${i}`] = `${Math.round(baseMs * i)}ms`;
onBulkChange(patch);
function setStaggerStep(ms: number) {
onSet(STAGGER_TOKEN, `${Math.round(ms)}ms`);
}

function resetStagger() {
const patch: Record<string, null> = {};
for (let i = 1; i <= 5; i++) patch[`--sf-animation-delay-${i}`] = null;
onBulkChange(patch);
onReset(STAGGER_TOKEN);
}
</script>

Expand Down Expand Up @@ -252,27 +254,28 @@
<!-- STAGGER -->
<Section title="Stagger" bind:open={showStagger}>
<p class="text-[10px] text-slate-400 dark:text-slate-600 leading-relaxed">
One slider sets all five stagger delays (delay-1 through delay-5 are multiples of this base).
Put <code class="text-slate-600 dark:text-slate-400">.sf-stagger</code> on a parent and each child's
entrance is delayed by its index × this step × the motion scale. One knob retunes the whole sequence.
</p>
<div class="group">
<div class="flex items-center justify-between mb-1.5">
<span class="text-[11px] font-semibold text-slate-800 dark:text-slate-200">Stagger base</span>
{#if STAGGER_TOKENS.some(t => t in overrides)}
<span class="text-[11px] font-semibold text-slate-800 dark:text-slate-200">Stagger step</span>
{#if staggerOverridden}
<button onclick={resetStagger} class="text-[9px] text-slate-500 hover:text-rose-600 dark:hover:text-rose-400 cursor-pointer ">reset</button>
{/if}
</div>
<SliderRow
label="" value={Math.round(staggerBase)} min={0} max={200} step={5} unit="ms"
help="Base unit for --sf-animation-delay-1 through -5"
overridden={STAGGER_TOKENS.some(t => t in overrides)}
onChange={(v) => setStaggerBase(v)}
label="" value={Math.round(staggerStep)} min={0} max={200} step={5} unit="ms"
help="--sf-stagger-step — per-child delay unit for .sf-stagger"
overridden={staggerOverridden}
onChange={(v) => setStaggerStep(v)}
onReset={resetStagger}
/>
</div>
<div class="bg-black/4 dark:bg-white/4 rounded-xl border border-black/8 dark:border-white/8 p-3 space-y-1">
{#each [1,2,3,4,5] as n (n)}
{@const delayMs = Math.round(staggerBase * n)}
{@const maxStaggerDelay = Math.round(staggerBase * 5)}
{@const delayMs = Math.round(staggerStep * scale * n)}
{@const maxStaggerDelay = Math.round(staggerStep * scale * 5)}
<div class="flex items-center gap-2">
<span class="text-[9px] font-mono text-slate-400 dark:text-slate-600 w-6">–{n}</span>
<div class="flex-1 h-1.5 bg-black/8 dark:bg-white/8 rounded-full">
Expand Down
4 changes: 2 additions & 2 deletions configurator/src/lib/preview/sections.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export function color(): string {
9,
...GRADS.map((g) => specimen(`--sf-gradient-${g}`, `<div class="pv-grad pv-grad--${g}"></div>`)),
);
const surfaceTokens = tokensMatching("--sf-color-surface", "--sf-color-canvas", "--sf-color-inset", "--sf-color-raised")
const surfaceTokens = tokensMatching("--sf-color-surface", "--sf-color-inset", "--sf-color-raised")
.map((t) => t.name);
const surfaces = grid(
10,
Expand Down Expand Up @@ -118,7 +118,7 @@ export function type(): string {
const leading = well(`<div class="sf-cluster sf-cluster--m">
<p style="line-height:var(--sf-leading-tight);max-inline-size:12rem">Tight leading — --sf-leading-tight. Two or three lines of copy to show the rhythm.</p>
<p style="line-height:var(--sf-leading-normal);max-inline-size:12rem">Normal leading — --sf-leading-normal. Two or three lines of copy to show the rhythm.</p>
<p style="line-height:var(--sf-leading-loose);max-inline-size:12rem">Loose leading — --sf-leading-loose. Two or three lines of copy to show the rhythm.</p>
<p style="line-height:var(--sf-leading-relaxed);max-inline-size:12rem">Relaxed leading — --sf-leading-relaxed. Two or three lines of copy to show the rhythm.</p>
</div>`);
return page(
"Typography",
Expand Down
14 changes: 7 additions & 7 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.