+
Usage
+
Paste this CSS into your project after the SLASHED import.
{`@import 'slashed/slashed.full.css';
diff --git a/configurator/src/components/panels/GenericTokenPanel.svelte b/configurator/src/components/panels/GenericTokenPanel.svelte
index 40d72fbc..6310a705 100644
--- a/configurator/src/components/panels/GenericTokenPanel.svelte
+++ b/configurator/src/components/panels/GenericTokenPanel.svelte
@@ -33,7 +33,7 @@
{domain} tokens
{#if modifiedCount > 0}
-
{modifiedCount} modified
+
{modifiedCount} modified
{/if}
@@ -42,11 +42,11 @@
placeholder={`Search ${domain} tokens…`}
value={query}
oninput={(e) => { query = (e.target as HTMLInputElement).value; }}
- class="w-full bg-white/5 border border-white/10 rounded-lg px-3 py-1.5 text-[11px] text-slate-200 placeholder:text-slate-600 focus:outline-none focus:border-indigo-500"
+ class="w-full bg-black/5 dark:bg-white/5 border border-black/10 dark:border-white/10 rounded-lg px-3 py-1.5 text-[11px] text-slate-800 dark:text-slate-200 placeholder:text-slate-600 focus:outline-none focus:border-indigo-500"
/>
{#if filtered().length === 0}
- No tokens found
+ No tokens found
{:else}
{#each filtered().slice(0, 60) as t (t.name)}
@@ -58,7 +58,7 @@
/>
{/each}
{#if filtered().length > 60}
-
+
{filtered().length - 60} more — refine your search
{/if}
diff --git a/configurator/src/components/panels/HomePanel.svelte b/configurator/src/components/panels/HomePanel.svelte
index 3a695283..48218a06 100644
--- a/configurator/src/components/panels/HomePanel.svelte
+++ b/configurator/src/components/panels/HomePanel.svelte
@@ -68,13 +68,13 @@
-
- SLASHED Studio
+
+ SLASHED Studio
-
+
Configure every SLASHED design token visually. Changes appear live in the preview.
{#if overridesCount > 0}
- You have {overridesCount} active override{overridesCount !== 1 ? "s" : ""}.
+ You have {overridesCount} active override{overridesCount !== 1 ? "s" : ""}.
{:else}
Pick any domain below to get started.
{/if}
@@ -82,7 +82,7 @@
{#if overridesCount > 0}
@@ -98,19 +98,19 @@
{@const DomainIcon = d.icon}
@@ -155,28 +155,28 @@
bind:value={newName}
placeholder="Save current as…"
onkeydown={(e) => { if (e.key === "Enter") handleQuickSave(); }}
- class="flex-1 min-w-0 bg-white/5 border border-white/10 rounded-lg px-2 py-1.5 text-[11px] text-slate-200 placeholder:text-slate-600 focus:outline-none focus:border-indigo-500"
+ class="flex-1 min-w-0 bg-black/5 dark:bg-white/5 border border-black/10 dark:border-white/10 rounded-lg px-2 py-1.5 text-[11px] text-slate-800 dark:text-slate-200 placeholder:text-slate-600 focus:outline-none focus:border-indigo-500"
/>
{#if savedThemes.length === 0}
- No saved themes yet. Configure tokens, then save.
+ No saved themes yet. Configure tokens, then save.
{:else}
{#each savedThemes.slice(0, 6) as theme (theme.id)}
onApplyTheme(theme.overrides)}
- class="flex items-start gap-2 p-3 rounded-xl bg-white/4 border border-white/8 hover:bg-white/8 hover:border-white/14 transition-all cursor-pointer text-left group"
+ class="flex items-start gap-2 p-3 rounded-xl bg-black/4 dark:bg-white/4 border border-black/8 dark:border-white/8 hover:bg-black/8 dark:hover:bg-white/8 hover:border-black/14 dark:hover:border-white/14 transition-all cursor-pointer text-left group"
>
{theme.icon}
-
{theme.name}
+
{theme.name}
{theme.blurb}
diff --git a/configurator/src/components/panels/LayoutPanel.svelte b/configurator/src/components/panels/LayoutPanel.svelte
index 89b8f2c7..8eddc9f3 100644
--- a/configurator/src/components/panels/LayoutPanel.svelte
+++ b/configurator/src/components/panels/LayoutPanel.svelte
@@ -90,7 +90,7 @@
-
Wide
+
Wide
-
+
{#each [
{ label: "narrow", value: containerNarrow, max: containerWide, unit: "rem" },
{ label: "prose", value: containerProse, max: containerWide * 1.5, unit: "ch" },
@@ -136,8 +136,8 @@
{ label: "wide", value: containerWide, max: containerWide, unit: "rem" },
] as row (row.label)}
-
{row.label}
-
+
{row.label}
+
-
+
@@ -186,7 +186,7 @@
{/if}
-
+
@@ -208,7 +208,7 @@
/>
-
Named sizes (grid-min-xs → 2xl)
+
Named sizes (grid-min-xs → 2xl)
{#each [
{ label: "xs", token: "--sf-grid-min-xs", def: 10 },
@@ -227,21 +227,21 @@
{/each}
-
-
Preview at 360px panel width
+
+
Preview at 360px panel width
{#each Array.from({ length: 8 }) as _, i (i)}
-
col
+
col
{/each}
{/if}
-
+
@@ -256,7 +256,7 @@
{#if showHeaderHeight}
-
+
{#each [1, 2, 3] as i (i)}
-
+
{/each}
-
{headerMobile}rem mobile · {headerDesktop}rem desktop
+
{headerMobile}rem mobile · {headerDesktop}rem desktop
{/if}
-
+
@@ -304,10 +304,10 @@
{showStickyOffset ? "▲" : "▼"}
{#if showStickyOffset}
- Offsets position:sticky elements below a fixed header. Defaults to header heights.
+ Offsets position:sticky elements below a fixed header. Defaults to header heights.
-
Desktop
+
Desktop
-
+
@@ -354,26 +354,26 @@
onChange={(v) => onSet("--sf-sidebar-width", `${v}rem`)}
onReset={() => onReset("--sf-sidebar-width")}
/>
-
+
- {sidebarWidth}rem
+ {sidebarWidth}rem
-
+
{/if}
-
+
{ showBento = !showBento; }}
aria-expanded={showBento}
- class="w-full flex items-center justify-between text-[10px] font-semibold text-slate-400 hover:text-slate-200 transition-colors cursor-pointer py-1"
+ class="w-full flex items-center justify-between text-[10px] font-semibold text-slate-600 dark:text-slate-400 hover:text-slate-800 dark:hover:text-slate-200 transition-colors cursor-pointer py-1"
>
Bento grid
{showBento ? "▲" : "▼"}
@@ -393,7 +393,7 @@
{/if}
-
+
@@ -401,7 +401,7 @@
onclick={() => { showMore = !showMore; }}
aria-expanded={showMore}
aria-controls="more-layout-primitives"
- class="w-full flex items-center justify-between text-[10px] font-semibold text-slate-400 hover:text-slate-200 transition-colors cursor-pointer py-1"
+ class="w-full flex items-center justify-between text-[10px] font-semibold text-slate-600 dark:text-slate-400 hover:text-slate-800 dark:hover:text-slate-200 transition-colors cursor-pointer py-1"
>
More layout primitives
{showMore ? "▲" : "▼"}
@@ -441,21 +441,21 @@
{ label: "Justify", token: "--sf-cluster-justify", def: "flex-start", opts: ["flex-start","center","flex-end","space-between","space-around"] },
] as row (row.token)}
- {row.label}
+ {row.label}
{#if row.token in overrides}
- onReset(row.token)} class="text-[8px] text-slate-500 hover:text-rose-400 cursor-pointer shrink-0">reset
+ onReset(row.token)} class="text-[8px] text-slate-500 hover:text-rose-600 dark:hover:text-rose-400 cursor-pointer shrink-0">reset
{/if}
{/each}
@@ -472,7 +472,7 @@
onReset={() => onReset("--sf-equal-min-col")}
/>
-
Named column counts (equal-min-col-2/3/4/6)
+
Named column counts (equal-min-col-2/3/4/6)
{#each [
{ label: "2-col", token: "--sf-equal-min-col-2", def: 28 },
@@ -506,15 +506,15 @@
Frame ratio
- --sf-frame-ratio — aspect ratio for .sf-frame
+ --sf-frame-ratio — aspect ratio for .sf-frame
{#each FRAME_PRESETS as r (r)}
r === "16 / 9" ? onReset("--sf-frame-ratio") : onSet("--sf-frame-ratio", r)}
class={`py-1 rounded-lg text-[9px] border transition-all cursor-pointer font-mono ${
frameRatio.replace(/\s/g, "") === r.replace(/\s/g, "")
- ? "bg-indigo-500/15 border-indigo-500/40 text-indigo-200"
- : "border-white/8 text-slate-400 hover:bg-white/5 hover:text-slate-200"
+ ? "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"
}`}
>{r}
{/each}
@@ -524,21 +524,21 @@
Reel
- --sf-reel-item-width — width of each item in a horizontal .sf-reel
+ --sf-reel-item-width — width of each item in a horizontal .sf-reel
{#each REEL_PRESETS as p (p.value)}
p.value === "max-content" ? onReset("--sf-reel-item-width") : onSet("--sf-reel-item-width", p.value)}
class={`py-1 rounded-lg text-[9px] border transition-all cursor-pointer ${
reelItemWidth === p.value
- ? "bg-indigo-500/15 border-indigo-500/40 text-indigo-200"
- : "border-white/8 text-slate-400 hover:bg-white/5 hover:text-slate-200"
+ ? "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"
}`}
>{p.label}
{/each}
- Height
+ Height
{#if "--sf-reel-height" in overrides}
- onReset("--sf-reel-height")} class="text-[8px] text-slate-500 hover:text-rose-400 cursor-pointer shrink-0">reset
+ onReset("--sf-reel-height")} class="text-[8px] text-slate-500 hover:text-rose-600 dark:hover:text-rose-400 cursor-pointer shrink-0">reset
{/if}
@@ -613,7 +613,7 @@
{/if}
-
+
@@ -627,7 +627,7 @@
{#if showNamedRatios}
-
Override the built-in named aspect ratios used by .sf-ratio-* utilities.
+
Override the built-in named aspect ratios used by .sf-ratio-* utilities.
{#each [
{ label: "Square", token: "--sf-ratio-square", def: "1" },
{ label: "Portrait", token: "--sf-ratio-portrait", def: "3 / 4" },
@@ -638,7 +638,7 @@
{ label: "Golden", token: "--sf-ratio-golden", def: "1.618 / 1" },
] as r (r.token)}
- {r.label}
+ {r.label}
{#if r.token in overrides}
- onReset(r.token)} class="text-[8px] text-slate-500 hover:text-rose-400 cursor-pointer shrink-0">reset
+ onReset(r.token)} class="text-[8px] text-slate-500 hover:text-rose-600 dark:hover:text-rose-400 cursor-pointer shrink-0">reset
{/if}
{/each}
@@ -658,7 +658,7 @@
{/if}
-
+
@@ -672,27 +672,27 @@
{#if showBgLayer}
-
Default values for the .sf-bg full-bleed background utility.
+
Default values for the .sf-bg full-bleed background utility.
{#each [
{ label: "Fit", token: "--sf-bg-fit", placeholder: "cover", opts: ["cover","contain","fill","none","scale-down"] },
{ label: "Position", token: "--sf-bg-position", placeholder: "50% 50%", opts: ["50% 50%","top","bottom","left","right","center"] },
] as row (row.token)}
- {row.label}
+ {row.label}
{#if row.token in overrides}
- onReset(row.token)} class="text-[8px] text-slate-500 hover:text-rose-400 cursor-pointer shrink-0">reset
+ onReset(row.token)} class="text-[8px] text-slate-500 hover:text-rose-600 dark:hover:text-rose-400 cursor-pointer shrink-0">reset
{/if}
{/each}
diff --git a/configurator/src/components/panels/MacrosPanel.svelte b/configurator/src/components/panels/MacrosPanel.svelte
index 62203918..577a59a0 100644
--- a/configurator/src/components/panels/MacrosPanel.svelte
+++ b/configurator/src/components/panels/MacrosPanel.svelte
@@ -70,11 +70,11 @@
-
- Tokens for the framework's utility macros — .sf-prose,
- .sf-flow, .sf-line-clamp,
- .sf-aspect, .sf-scroll-shadow
- and .sf-scrim.
+
+ Tokens for the framework's utility macros — .sf-prose,
+ .sf-flow, .sf-line-clamp,
+ .sf-aspect, .sf-scroll-shadow
+ and .sf-scrim.
@@ -98,7 +98,7 @@
currentRaw={overrides["--sf-flow-space"]}
onRawSet={(v) => onSet("--sf-flow-space", v)}
/>
-
+
{#each [0, 1, 2] as i (i)}
{/each}
@@ -106,7 +106,7 @@
{/if}
-
+
@@ -126,9 +126,9 @@
onChange={(v) => onSet("--sf-line-clamp", String(v))}
onReset={() => onReset("--sf-line-clamp")}
/>
-
+
SLASHED is a token-first CSS framework. This sample paragraph repeats so you
@@ -140,7 +140,7 @@
{/if}
-
+
@@ -153,29 +153,29 @@
{showAspect ? "▲" : "▼"}
{#if showAspect}
- --sf-aspect — default ratio for .sf-aspect / .sf-frame
+ --sf-aspect — default ratio for .sf-aspect / .sf-frame
{#each ASPECT_PRESETS as p (p.value)}
p.value === "16 / 9" ? onReset("--sf-aspect") : onSet("--sf-aspect", p.value)}
class={`py-1.5 rounded-lg text-[10px] border transition-all cursor-pointer font-mono ${
aspectVal.replace(/\s/g, "") === p.value.replace(/\s/g, "")
- ? "bg-indigo-500/15 border-indigo-500/40 text-indigo-200"
- : "border-white/8 text-slate-400 hover:bg-white/5 hover:text-slate-200"
+ ? "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"
}`}
>{p.label}
{/each}
-
+
{/if}
-
+
@@ -198,7 +198,7 @@
currentRaw={overrides["--sf-scroll-shadow-size"]}
onRawSet={(v) => onSet("--sf-scroll-shadow-size", v)}
/>
-
+
-
+
@@ -220,9 +220,9 @@
{showScrim ? "▲" : "▼"}
{#if showScrim}
- --sf-scrim-color / --sf-scrim-direction — darkening gradient for .sf-scrim
+ --sf-scrim-color / --sf-scrim-direction — darkening gradient for .sf-scrim
-
Color
+
Color
{#if "--sf-scrim-color" in overrides}
-
onReset("--sf-scrim-color")} class="text-[8px] text-slate-500 hover:text-rose-400 cursor-pointer shrink-0">reset
+
onReset("--sf-scrim-color")} class="text-[8px] text-slate-500 hover:text-rose-600 dark:hover:text-rose-400 cursor-pointer shrink-0">reset
{/if}
-
Direction
+
Direction
{#each SCRIM_DIRECTIONS as d (d.value)}
d.value === "to top" ? onReset("--sf-scrim-direction") : onSet("--sf-scrim-direction", d.value)}
class={`py-1 rounded-lg text-[10px] border transition-all cursor-pointer ${
scrimDir === d.value
- ? "bg-indigo-500/15 border-indigo-500/40 text-indigo-200"
- : "border-white/8 text-slate-400 hover:bg-white/5 hover:text-slate-200"
+ ? "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"
}`}
>{d.label}
{/each}
-
-
Text shadow
+
Text shadow
{#if "--sf-scrim-text-shadow" in overrides}
-
onReset("--sf-scrim-text-shadow")} class="text-[8px] text-slate-500 hover:text-rose-400 cursor-pointer shrink-0">reset
+
onReset("--sf-scrim-text-shadow")} class="text-[8px] text-slate-500 hover:text-rose-600 dark:hover:text-rose-400 cursor-pointer shrink-0">reset
{/if}
{/if}
-
+
@@ -289,7 +289,7 @@
{showProse ? "▲" : "▼"}
{#if showProse}
- Spacing tokens for .sf-prose. Defaults track the space scale; sliders write rem overrides.
+ Spacing tokens for .sf-prose. Defaults track the space scale; sliders write rem overrides.
{#each PROSE_SPACE as t (t.token)}
onSet("--sf-prose-media-radius", v)}
/>
-
Marker color
+
Marker color
{#if "--sf-prose-marker-color" in overrides}
-
onReset("--sf-prose-marker-color")} class="text-[8px] text-slate-500 hover:text-rose-400 cursor-pointer shrink-0">reset
+
onReset("--sf-prose-marker-color")} class="text-[8px] text-slate-500 hover:text-rose-600 dark:hover:text-rose-400 cursor-pointer shrink-0">reset
{/if}
@@ -344,11 +344,11 @@
{showContentIntrinsic ? "▲" : "▼"}
{#if showContentIntrinsic}
-
- --sf-content-intrinsic-size — placeholder size used with content-visibility: auto to improve scroll performance on tall pages.
+
+ --sf-content-intrinsic-size — placeholder size used with content-visibility: auto to improve scroll performance on tall pages.
-
Intrinsic size
+
Intrinsic size
{#if "--sf-content-intrinsic-size" in overrides}
-
onReset("--sf-content-intrinsic-size")} class="text-[8px] text-slate-500 hover:text-rose-400 cursor-pointer shrink-0">reset
+
onReset("--sf-content-intrinsic-size")} class="text-[8px] text-slate-500 hover:text-rose-600 dark:hover:text-rose-400 cursor-pointer shrink-0">reset
{/if}
{/if}
-
+
- All tokens tab — edit every prose, scrim and surface token directly.
+ All tokens tab — edit every prose, scrim and surface token directly.
diff --git a/configurator/src/components/panels/MiscPanel.svelte b/configurator/src/components/panels/MiscPanel.svelte
index e8fc861a..faa73823 100644
--- a/configurator/src/components/panels/MiscPanel.svelte
+++ b/configurator/src/components/panels/MiscPanel.svelte
@@ -88,19 +88,19 @@
onReset={() => onReset("--sf-touch-target")}
/>
-
+
{touchTarget}px
-
Minimum interactive area — ensures accessibility on touch devices.
+
Minimum interactive area — ensures accessibility on touch devices.
{/if}
-
+
@@ -119,8 +119,8 @@
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-200"
- : "border-white/8 text-slate-400 hover:bg-white/5 hover:text-slate-200"
+ ? "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}
@@ -130,7 +130,7 @@
{/if}
-
+
@@ -150,22 +150,22 @@
onChange={(v) => onSet("--sf-z-base", String(v))}
onReset={() => onReset("--sf-z-base")}
/>
-
+
{#each Z_INDEX_STEPS as z (z.token)}
{@const isOverridden = z.token in overrides}
-
-
{z.label}
-
{z.note}
+
+
{z.label}
+
{z.note}
{z.token.replace("--sf-z-", "")}
{/each}
-
Override individual z-index tokens in the "All tokens" tab.
+
Override individual z-index tokens in the "All tokens" tab.
{/if}
-
+
@@ -180,7 +180,7 @@
{#if showTextSelection}
-
Selection background
+
Selection background
-
Selection text color
+
Selection text color
-
+
Select this text to preview the selection color.
@@ -214,7 +214,7 @@
{/if}
-
+
@@ -234,8 +234,8 @@
onclick={() => style === "solid" ? onReset("--sf-focus-ring-style") : onSet("--sf-focus-ring-style", style)}
class={`flex-1 py-2 rounded-lg text-[10px] border transition-all cursor-pointer capitalize ${
current === style
- ? "bg-indigo-500/15 border-indigo-500/40 text-indigo-200"
- : "border-white/8 text-slate-400 hover:bg-white/5 hover:text-slate-200"
+ ? "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"
}`}
>
{style}
@@ -243,9 +243,9 @@
{/each}
-
+
Focus ring · {focusRingStyle}
@@ -254,7 +254,7 @@
{/if}
-
+
@@ -267,7 +267,7 @@
{showComponentSizes ? "▲" : "▼"}
{#if showComponentSizes}
-
+
Controls the height / tap-target of all sized components (buttons, inputs, badges).
@@ -283,7 +283,7 @@
{/each}
-
+
{#each SIZE_TOKENS as s (s.token)}
{@const val = getSizeValue(s)}
@@ -291,14 +291,14 @@
class="w-full bg-indigo-500/30 border border-indigo-500/30 rounded flex items-center justify-center"
style={`height: ${Math.min(val * 14, 64)}px`}
>
-
{s.label}
+
{s.label}
{/each}
{/if}
-
+
@@ -312,7 +312,7 @@
{#if showCaretLinks}
-
Caret color
+
Caret color
- Underline thickness
+ Underline thickness
{#if "--sf-link-underline-thickness" in overrides}
- onReset("--sf-link-underline-thickness")} class="text-[9px] text-slate-500 hover:text-rose-400 cursor-pointer opacity-100 sm:opacity-0 sm:group-hover:opacity-100 sm:group-focus-within:opacity-100 focus:opacity-100">reset
+ onReset("--sf-link-underline-thickness")} class="text-[9px] text-slate-500 hover:text-rose-600 dark:hover:text-rose-400 cursor-pointer opacity-100 sm:opacity-0 sm:group-hover:opacity-100 sm:group-focus-within:opacity-100 focus:opacity-100">reset
{/if}
@@ -342,26 +342,26 @@
onclick={() => val === "auto" ? onReset("--sf-link-underline-thickness") : onSet("--sf-link-underline-thickness", val)}
class={`flex-1 py-1.5 rounded-lg text-[10px] border transition-all cursor-pointer ${
underlineThickness === val
- ? "bg-indigo-500/15 border-indigo-500/40 text-indigo-200"
- : "border-white/8 text-slate-400 hover:bg-white/5 hover:text-slate-200"
+ ? "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"
}`}
>{label}
{/each}
-
+
-
+
A e.preventDefault()}
- class="text-indigo-300"
+ class="text-indigo-700 dark:text-indigo-300"
style={`text-decoration:underline;text-underline-offset:${underlineOffset}em;text-decoration-thickness:${underlineThickness}`}
>sample link with the current underline settings.
@@ -369,7 +369,7 @@
{/if}
-
+
@@ -382,8 +382,8 @@
{showIconSizes ? "▲" : "▼"}
{#if showIconSizes}
-
- Icon scale tokens used by .sf-icon-* utilities. Values are in em units relative to surrounding text.
+
+ Icon scale tokens used by .sf-icon-* utilities. Values are in em units relative to surrounding text.
{#each [
@@ -412,7 +412,7 @@
{/if}
-
+
@@ -425,24 +425,24 @@
{showObjectFit ? "▲" : "▼"}
{#if showObjectFit}
- Default values for .sf-media images and replaced elements.
+ Default values for .sf-media images and replaced elements.
-
Fit
+
Fit
{#each ["cover","contain","fill","none"] as v (v)}
{@const cur = overrides["--sf-object-fit"] ?? "cover"}
v === "cover" ? onReset("--sf-object-fit") : onSet("--sf-object-fit", v)}
- class={`flex-1 py-1.5 rounded-lg text-[9px] border transition-all cursor-pointer ${cur === v ? "bg-indigo-500/15 border-indigo-500/40 text-indigo-200" : "border-white/8 text-slate-400 hover:bg-white/5"}`}
+ class={`flex-1 py-1.5 rounded-lg text-[9px] border transition-all cursor-pointer ${cur === v ? "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"}`}
>{v}
{/each}
{#if "--sf-object-fit" in overrides}
-
onReset("--sf-object-fit")} class="text-[8px] text-slate-500 hover:text-rose-400 cursor-pointer">reset
+
onReset("--sf-object-fit")} class="text-[8px] text-slate-500 hover:text-rose-600 dark:hover:text-rose-400 cursor-pointer">reset
{/if}
- Position
+ Position
{#if "--sf-object-position" in overrides}
- onReset("--sf-object-position")} class="text-[8px] text-slate-500 hover:text-rose-400 cursor-pointer shrink-0">reset
+ onReset("--sf-object-position")} class="text-[8px] text-slate-500 hover:text-rose-600 dark:hover:text-rose-400 cursor-pointer shrink-0">reset
{/if}
{/if}
-
+
@@ -473,8 +473,8 @@
{showSafeArea ? "▲" : "▼"}
{#if showSafeArea}
-
- Default to the device's env(safe-area-inset-*). Override only if you need fixed padding regardless of notch/home-indicator.
+
+ Default to the device's env(safe-area-inset-*). Override only if you need fixed padding regardless of notch/home-indicator.
{#each [
@@ -484,7 +484,7 @@
{ label: "Left", token: "--sf-safe-left", placeholder: "env(safe-area-inset-left, 0px)" },
] as row (row.token)}
- {row.label}
+ {row.label}
{#if row.token in overrides}
- onReset(row.token)} class="text-[8px] text-slate-500 hover:text-rose-400 cursor-pointer shrink-0">reset
+ onReset(row.token)} class="text-[8px] text-slate-500 hover:text-rose-600 dark:hover:text-rose-400 cursor-pointer shrink-0">reset
{/if}
{/each}
@@ -504,7 +504,7 @@
{/if}
-
+
@@ -517,15 +517,15 @@
{showPrint ? "▲" : "▼"}
{#if showPrint}
- Applied inside @media print via optional/print.css.
+ Applied inside @media print via optional/print.css.
-
Page size
+
Page size
{#each ["a4","letter","legal","a3"] as v (v)}
{@const cur = overrides["--sf-print-page-size"] ?? "a4"}
v === "a4" ? onReset("--sf-print-page-size") : onSet("--sf-print-page-size", v)}
- class={`flex-1 py-1.5 rounded-lg text-[9px] border transition-all cursor-pointer ${cur === v ? "bg-indigo-500/15 border-indigo-500/40 text-indigo-200" : "border-white/8 text-slate-400 hover:bg-white/5"}`}
+ class={`flex-1 py-1.5 rounded-lg text-[9px] border transition-all cursor-pointer ${cur === v ? "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"}`}
>{v}
{/each}
@@ -535,7 +535,7 @@
{ label: "Page margin", token: "--sf-print-page-margin", placeholder: "2cm" },
] as row (row.token)}
- {row.label}
+ {row.label}
{#if row.token in overrides}
- onReset(row.token)} class="text-[8px] text-slate-500 hover:text-rose-400 cursor-pointer shrink-0">reset
+ onReset(row.token)} class="text-[8px] text-slate-500 hover:text-rose-600 dark:hover:text-rose-400 cursor-pointer shrink-0">reset
{/if}
{/each}
{/if}
-
+
@@ -567,7 +567,7 @@
{showStateFlags ? "▲" : "▼"}
{#if showStateFlags}
-
+
Boolean CSS custom property flags (0 = off, 1 = on). Components toggle these to activate state-specific styles.
Overriding globally forces all matching elements into that state — useful for testing and demos.
@@ -585,20 +585,20 @@
aria-label={f.label}
aria-pressed={cur === "1"}
onclick={() => cur === "1" ? onReset(f.token) : onSet(f.token, "1")}
- class={`w-8 h-4 rounded-full transition-colors relative cursor-pointer shrink-0 ${cur === "1" ? "bg-indigo-600" : "bg-white/10"}`}
+ class={`w-8 h-4 rounded-full transition-colors relative cursor-pointer shrink-0 ${cur === "1" ? "bg-indigo-600" : "bg-black/10 dark:bg-white/10"}`}
>
- {f.label}
- {f.note}
+ {f.label}
+ {f.note}
{#if f.token in overrides}
- onReset(f.token)} class="text-[8px] text-slate-500 hover:text-rose-400 cursor-pointer shrink-0">reset
+ onReset(f.token)} class="text-[8px] text-slate-500 hover:text-rose-600 dark:hover:text-rose-400 cursor-pointer shrink-0">reset
{/if}
{/each}
- Required marker
+ Required marker
{#if "--sf-field-required-marker" in overrides}
- onReset("--sf-field-required-marker")} class="text-[8px] text-slate-500 hover:text-rose-400 cursor-pointer shrink-0">reset
+ onReset("--sf-field-required-marker")} class="text-[8px] text-slate-500 hover:text-rose-600 dark:hover:text-rose-400 cursor-pointer shrink-0">reset
{/if}
{/if}
-
+
-
+
Selection colors, caret, links, focus ring, borders and sizes all render in the
- live preview (try the Components template).
- Scroll behavior applies to the page
+ live preview (try the Components template).
+ Scroll behavior applies to the page
itself and can't be shown in the static canvas.
diff --git a/configurator/src/components/panels/MotionPanel.svelte b/configurator/src/components/panels/MotionPanel.svelte
index b25dff18..172b6820 100644
--- a/configurator/src/components/panels/MotionPanel.svelte
+++ b/configurator/src/components/panels/MotionPanel.svelte
@@ -94,9 +94,9 @@
-
+
-
Disable motion
+
Disable motion
Respects prefers-reduced-motion
-
Motion is disabled — duration & easing have no effect while scale is 0.
+
Motion is disabled — duration & easing have no effect while scale is 0.
{/if}
@@ -125,7 +125,7 @@
easing/duration edit can be previewed immediately. -->
Live preview
-
+
{ demoEase = (e.target as HTMLSelectElement).value; }}
- class="flex-1 min-w-0 bg-white/5 border border-white/10 rounded-lg px-2 py-1.5 text-[10px] text-slate-200 focus:outline-none focus:border-indigo-500 cursor-pointer"
+ class="flex-1 min-w-0 bg-black/5 dark:bg-white/5 border border-black/10 dark:border-white/10 rounded-lg px-2 py-1.5 text-[10px] text-slate-800 dark:text-slate-200 focus:outline-none focus:border-indigo-500 cursor-pointer"
>
{#each EASINGS as e (e.token)}
@@ -147,7 +147,7 @@
value={demoDuration}
aria-label="Preview duration"
onchange={(e) => { demoDuration = (e.target as HTMLSelectElement).value; }}
- class="flex-1 min-w-0 bg-white/5 border border-white/10 rounded-lg px-2 py-1.5 text-[10px] text-slate-200 focus:outline-none focus:border-indigo-500 cursor-pointer"
+ class="flex-1 min-w-0 bg-black/5 dark:bg-white/5 border border-black/10 dark:border-white/10 rounded-lg px-2 py-1.5 text-[10px] text-slate-800 dark:text-slate-200 focus:outline-none focus:border-indigo-500 cursor-pointer"
>
{#each DURATIONS as d (d.token)}
@@ -156,13 +156,13 @@
{animating ? "…" : "▶ Play"}
-
+
@@ -175,9 +175,9 @@
{showDurations ? "▲" : "▼"}
{#if showDurations}
-
+
Named duration tokens. Drag to set an absolute ms value, overriding the global scale.
- {#if motionDisabled} (No effect while motion is disabled.){/if}
+ {#if motionDisabled} (No effect while motion is disabled.){/if}
{#each DURATIONS as d (d.token)}
{@const computed = getDuration(d.token, d.base)}
@@ -194,12 +194,12 @@
/>
{/each}
-
+
{#each DURATIONS as d (d.label)}
{@const actual = getDuration(d.token, d.base)}
{d.label}
-
+
{actual}ms
@@ -209,7 +209,7 @@
{/if}
-
+
@@ -222,16 +222,16 @@
{showEasing ? "▲" : "▼"}
{#if showEasing}
-
+
Edit the named easing tokens. Accepts any CSS timing function —
- cubic-bezier(…) or
- linear(…). Use “Live preview” above to feel a curve.
+ cubic-bezier(…) or
+ linear(…). Use “Live preview” above to feel a curve.
{#each EASINGS as e (e.token)}
{@const isOverridden = e.token in overrides}
-
{e.label}
+
{e.label}
{ demoEase = e.token; playDemo(); }}
- class="text-[8px] text-slate-500 hover:text-indigo-400 cursor-pointer"
+ class="text-[8px] text-slate-500 hover:text-indigo-600 dark:hover:text-indigo-400 cursor-pointer"
>preview
{#if isOverridden}
- onReset(e.token)} class="text-[8px] text-slate-500 hover:text-rose-400 cursor-pointer">reset
+ onReset(e.token)} class="text-[8px] text-slate-500 hover:text-rose-600 dark:hover:text-rose-400 cursor-pointer">reset
{/if}
@@ -258,7 +258,7 @@
const v = (ev.target as HTMLInputElement).value.trim();
v ? onSet(e.token, v) : onReset(e.token);
}}
- class="w-full bg-white/5 border border-white/10 rounded px-1.5 py-1 text-[9px] font-mono text-slate-300 placeholder:text-slate-600 focus:outline-none focus:border-indigo-500"
+ class="w-full bg-black/5 dark:bg-white/5 border border-black/10 dark:border-white/10 rounded px-1.5 py-1 text-[9px] font-mono text-slate-700 dark:text-slate-300 placeholder:text-slate-600 focus:outline-none focus:border-indigo-500"
/>
@@ -268,7 +268,7 @@
{/if}
-
+
@@ -281,14 +281,14 @@
{showStagger ? "▲" : "▼"}
{#if showStagger}
-
+
One slider sets all five stagger delays (delay-1 through delay-5 are multiples of this base).
- Stagger base
+ Stagger base
{#if STAGGER_TOKENS.some(t => t in overrides)}
- reset
+ reset
{/if}
-
+
{#each [1,2,3,4,5] as n (n)}
{@const delayMs = Math.round(staggerBase * n)}
-
–{n}
-
+
–{n}
+
{delayMs}ms
@@ -314,14 +314,14 @@
{/if}
-
+
{ showAdvanced = !showAdvanced; }}
aria-expanded={showAdvanced}
- class="w-full flex items-center justify-between text-slate-600 hover:text-slate-400 transition-colors cursor-pointer"
+ class="w-full flex items-center justify-between text-slate-400 dark:text-slate-600 hover:text-slate-600 dark:hover:text-slate-400 transition-colors cursor-pointer"
>
Advanced
{showAdvanced ? "▲" : "▼"}
@@ -349,16 +349,16 @@
-
+
Default animation-range for scroll-driven animations using the
- .sf-scroll-timeline utility.
+ .sf-scroll-timeline utility.
{#each [
{ label: "Range start", token: "--sf-scroll-timeline-range-start", placeholder: "entry 0%" },
{ label: "Range end", token: "--sf-scroll-timeline-range-end", placeholder: "cover 30%" },
] as r (r.token)}
-
{r.label}
+
{r.label}
{#if r.token in overrides}
-
onReset(r.token)} class="text-[8px] text-slate-500 hover:text-rose-400 cursor-pointer shrink-0">reset
+
onReset(r.token)} class="text-[8px] text-slate-500 hover:text-rose-600 dark:hover:text-rose-400 cursor-pointer shrink-0">reset
{/if}
{/each}
diff --git a/configurator/src/components/panels/ShadowsPanel.svelte b/configurator/src/components/panels/ShadowsPanel.svelte
index 145d62bf..f6110ad1 100644
--- a/configurator/src/components/panels/ShadowsPanel.svelte
+++ b/configurator/src/components/panels/ShadowsPanel.svelte
@@ -27,19 +27,19 @@
-
+
{#each SHADOW_STEPS as step, i (step)}
{/each}
-
+
onReset("--sf-shadow-lightness")}
/>
-
+
@@ -64,7 +64,7 @@
{#if showShadowColor}
-
Shadow color
+
Shadow color
onSet("--sf-shadow-color", v)}
onReset={() => onReset("--sf-shadow-color")}
/>
- Warm, cool or brand-tinted shadows. Default uses neutral hue.
+ Warm, cool or brand-tinted shadows. Default uses neutral hue.
{/if}
-
+
@@ -92,9 +92,9 @@
{#if showGlow}
-
+
-
Shadow glow
+
Shadow glow
Ambient glow on elevated surfaces
-
Glow color
+
Glow color
onSet("--sf-shadow-glow-color", v)}
onReset={() => onReset("--sf-shadow-glow-color")}
/>
- Which brand color radiates the glow. Defaults to primary.
+ Which brand color radiates the glow. Defaults to primary.
{/if}
-
+
{#if glowDisabled}
-
Glow disabled.
+
Glow disabled.
{/if}
{/if}
-
+
{ showAdvanced = !showAdvanced; }}
aria-expanded={showAdvanced}
- class="w-full flex items-center justify-between text-slate-600 hover:text-slate-400 transition-colors cursor-pointer"
+ class="w-full flex items-center justify-between text-slate-400 dark:text-slate-600 hover:text-slate-600 dark:hover:text-slate-400 transition-colors cursor-pointer"
>
Advanced
{showAdvanced ? "▲" : "▼"}
diff --git a/configurator/src/components/panels/SpacingPanel.svelte b/configurator/src/components/panels/SpacingPanel.svelte
index 1f988581..e9d716a6 100644
--- a/configurator/src/components/panels/SpacingPanel.svelte
+++ b/configurator/src/components/panels/SpacingPanel.svelte
@@ -51,7 +51,7 @@
Space scale preview
-
+
{#each SPACE_STEPS as step, i (step)}
{@const midBase = (baseMin + baseMax) / 2}
{@const ratio = (ratioMin + ratioMax) / 2}
@@ -60,7 +60,7 @@
{@const scaled = rawRem * spaceScale}
{@const barWidth = Math.min(scaled * 28, 240)}
-
{step}
+
{step}
{scaled.toFixed(2)}rem
@@ -68,7 +68,7 @@
-
+
@@ -81,7 +81,7 @@
{showLayoutGap ? "▲" : "▼"}
{#if showLayoutGap}
-
+
The two most-referenced spacing tokens. Referenced by every layout primitive (cluster, grid, stack, bento…).
-
+
@@ -130,7 +130,7 @@
{showModularScale ? "▲" : "▼"}
{#if showModularScale}
-
+
Utopia-style fluid scale — viewport width, base unit and ratio per endpoint.
The viewport range is shared with the type scale.
@@ -165,14 +165,14 @@
{/if}
-
+
{ showAdvanced = !showAdvanced; }}
aria-expanded={showAdvanced}
- class="w-full flex items-center justify-between text-slate-600 hover:text-slate-400 transition-colors cursor-pointer"
+ class="w-full flex items-center justify-between text-slate-400 dark:text-slate-600 hover:text-slate-600 dark:hover:text-slate-400 transition-colors cursor-pointer"
>
Advanced
{showAdvanced ? "▲" : "▼"}
diff --git a/configurator/src/components/panels/ThemesPanel.svelte b/configurator/src/components/panels/ThemesPanel.svelte
index 5c95a3aa..d06e78c0 100644
--- a/configurator/src/components/panels/ThemesPanel.svelte
+++ b/configurator/src/components/panels/ThemesPanel.svelte
@@ -36,32 +36,32 @@
Themes
-
+
Save your current configuration as a named theme, then re-apply it any time.
Themes are stored in this browser.
-
-
Save current
+
+
Save current
{ if (e.key === "Enter") handleSave(); }}
- class="flex-1 min-w-0 bg-white/5 border border-white/10 rounded-lg px-2 py-1.5 text-[11px] text-slate-200 placeholder:text-slate-600 focus:outline-none focus:border-indigo-500"
+ class="flex-1 min-w-0 bg-black/5 dark:bg-white/5 border border-black/10 dark:border-white/10 rounded-lg px-2 py-1.5 text-[11px] text-slate-800 dark:text-slate-200 placeholder:text-slate-600 focus:outline-none focus:border-indigo-500"
/>
Save
-
+
{#if overridesCount === 0}
No active overrides yet — adjust some tokens first.
{:else}
@@ -73,9 +73,9 @@
{#if themes.length === 0}
-
+
No saved themes yet.
-
Configure your tokens, then save them above.
+
Configure your tokens, then save them above.
{:else}
{#each themes as theme (theme.id)}
@@ -83,15 +83,15 @@
{theme.icon}
-
+
{theme.name}
{#if activeId === theme.id}
-
+
{/if}
{tokenCount} override{tokenCount === 1 ? "" : "s"}
@@ -99,12 +99,12 @@
onApplyTheme(theme.overrides)}
- class="px-3 py-1.5 rounded-lg text-[11px] font-bold bg-indigo-600/20 border border-indigo-500/30 text-indigo-300 hover:bg-indigo-600/30 transition-all cursor-pointer"
+ class="px-3 py-1.5 rounded-lg text-[11px] font-bold bg-indigo-600/20 border border-indigo-500/30 text-indigo-700 dark:text-indigo-300 hover:bg-indigo-600/30 transition-all cursor-pointer"
>Apply
handleDelete(theme.id)}
- class="p-1.5 rounded-lg border border-white/8 text-slate-500 hover:text-rose-400 hover:border-rose-500/30 transition-all cursor-pointer opacity-100 sm:opacity-0 sm:group-hover:opacity-100 sm:group-focus-within:opacity-100 focus:opacity-100"
+ class="p-1.5 rounded-lg border border-black/8 dark:border-white/8 text-slate-500 hover:text-rose-600 dark:hover:text-rose-400 hover:border-rose-500/30 transition-all cursor-pointer opacity-100 sm:opacity-0 sm:group-hover:opacity-100 sm:group-focus-within:opacity-100 focus:opacity-100"
>
@@ -112,15 +112,15 @@
{#if tokenCount > 0}
{#each Object.entries(theme.overrides).slice(0, 5) as [k, v] (k)}
-
+
{#if k.includes("color") || k.includes("source")}
-
+
{/if}
-
{k.replace("--sf-", "")}
+
{k.replace("--sf-", "")}
{/each}
{#if tokenCount > 5}
-
+{tokenCount - 5} more
+
+{tokenCount - 5} more
{/if}
{/if}
@@ -133,7 +133,7 @@
{#if overridesCount > 0}
Reset all overrides
{/if}
diff --git a/configurator/src/components/panels/TypographyPanel.svelte b/configurator/src/components/panels/TypographyPanel.svelte
index 4ac839e6..bd4f5097 100644
--- a/configurator/src/components/panels/TypographyPanel.svelte
+++ b/configurator/src/components/panels/TypographyPanel.svelte
@@ -265,14 +265,14 @@
{ label: "Mono", token: "--sf-font-mono", current: currentMonoFont, opts: MONO_STACKS },
] as f (f.token)}
- {f.label}
+ {f.label}
{#if f.token in overrides}
- onReset(f.token)} class="text-[9px] text-slate-500 hover:text-rose-400 cursor-pointer shrink-0">reset
+ onReset(f.token)} class="text-[9px] text-slate-500 hover:text-rose-600 dark:hover:text-rose-400 cursor-pointer shrink-0">reset
{/if}
{/each}
-
-
Google Font
+
+
Google Font
@@ -318,11 +318,11 @@
placeholder="…or type any Google font name"
oninput={(e) => { googleFontChoice = (e.target as HTMLInputElement).value; }}
value={googleFontChoice}
- class="w-full bg-white/5 border border-white/10 rounded-lg px-2 py-1.5 text-[11px] text-slate-200 placeholder:text-slate-600 focus:outline-none focus:border-indigo-500"
+ class="w-full bg-black/5 dark:bg-white/5 border border-black/10 dark:border-white/10 rounded-lg px-2 py-1.5 text-[11px] text-slate-800 dark:text-slate-200 placeholder:text-slate-600 focus:outline-none focus:border-indigo-500"
/>
applyCustomFont(googleFontChoice, customFontTarget)}
- class="w-full py-1.5 rounded-lg bg-indigo-600/20 border border-indigo-500/30 text-indigo-300 text-[11px] font-bold cursor-pointer hover:bg-indigo-600/30 transition-all"
+ class="w-full py-1.5 rounded-lg bg-indigo-600/20 border border-indigo-500/30 text-indigo-700 dark:text-indigo-300 text-[11px] font-bold cursor-pointer hover:bg-indigo-600/30 transition-all"
>
Load & apply to {customFontTarget}
@@ -330,7 +330,7 @@
{/if}
-
+
@@ -345,13 +345,13 @@
{#if showPerType}
-
+
{#each TYPE_LEVELS as lvl (lvl.id)}
{@const isOv = (lvl.size in overrides) || (lvl.lh in overrides) || (lvl.wt in overrides) || (!!lvl.ls && lvl.ls in overrides)}
{ activeLevelId = lvl.id; }}
class={`relative flex-1 py-1 rounded-md text-[10px] font-bold transition-all cursor-pointer ${
- activeLevelId === lvl.id ? "bg-white/12 text-white" : "text-slate-500 hover:text-slate-300"
+ activeLevelId === lvl.id ? "bg-black/12 dark:bg-white/12 text-slate-900 dark:text-white" : "text-slate-500 hover:text-slate-700 dark:hover:text-slate-300"
}`}
>
{lvl.label}
@@ -361,9 +361,9 @@
-
+
The quick brown fox
@@ -371,11 +371,11 @@
-
Size
+
Size