diff --git a/CHANGELOG.md b/CHANGELOG.md
index 19a4f927..e0f2c376 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,8 +7,14 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
## [0.7.19] - 2026-07-16
### Bug Fixes
+- **tokens:** `--sf-color-info-subtle` and `--sf-color-danger-subtle` were `0.10` alpha while `--sf-color-success-subtle` / `--sf-color-warning-subtle` were `0.12`, so a `danger`/`info` alert-box background rendered slightly fainter than a `success`/`warning` one at the same content. Unified all four status `-subtle` washes to `0.12` (the `-muted` `0.3` and `-tint` `0.05` rungs were already consistent).
+- **tokens:** the numeric brand ramps (`--sf-color-{primary,secondary,tertiary,action,neutral}-50…950`) no longer fold into a "U". The previous model generated each step with `color-mix()` toward the theme's `--sf-color-surface` (tints) / `--sf-color-text` (shades) — so when a chosen source colour fell outside the band between those two anchors (e.g. a brand darker than the text colour, or a project that lightened `--sf-color-neutral`), the shade half inverted and climbed back up: steps 600–950 came out *lighter* than 500, and 50 ≈ 950. The ramp is now generated in OKLCH by pulling the family colour's own lightness a fixed fraction toward an **absolute** target — `--sf-palette-tint-l` (tints) / `--sf-palette-shade-l` (shades) — clamped with `max()`/`min()` so a tint can never end darker than the base nor a shade lighter. The ladder is therefore monotonic light→dark for **any** source colour and any anchor configuration. Chroma is tapered toward the extremes to avoid oversaturation. For default-palette colours (which already sat inside the safe band) the output is visually unchanged; only out-of-band / moved-anchor cases are corrected. `base` is unchanged (it already used absolute lightness steps). New parametric test `tests/ramp-monotonic.spec.js` sweeps the lightness axis for all five families and asserts the ramp never folds and no step is transparent.
+- **configurator:** the Colors panel's "Palette ramp" section now edits the two lightness anchors (`--sf-palette-tint-l` / `--sf-palette-shade-l`) with two `0–1` sliders instead of the ten removed `--sf-palette-mix-*` percentage sliders; the Softer/Default/Punchy presets and the live palette preview were rewired to the absolute-anchor model so the preview matches the framework and stays monotonic.
- **layout:** `@container` breakpoints for `.sf-grid-cols-*`, `.sf-bento` and `.sf-alternate` now use `rem` instead of `em`. Inside a container query an `em` resolves against the query container's own font-size, which under the default fluid type scale is inherited from `body` (a `clamp()` over `100vw`, ~16–20px) — so the same fixed-width container rendered a different column count depending on the *viewport*: an 800px container showed `.sf-grid-cols-3` as 3 columns at a 500px viewport but 1 column at 1200px+. `rem` resolves against the fixed root (`html` stays 16px; fluid type is applied to `body`), restoring the documented "responds to its own container width, not the viewport" contract. Thresholds are unchanged numerically (30rem = 480px, 48rem = 768px) and still honour user zoom / root-font-size. Also corrects `docs/architecture.md`, which wrongly listed `.sf-grid` / `.sf-sidebar` as `@container` primitives (both are intrinsic and carry no container query).
+### Breaking Changes
+- **tokens:** removed the `--sf-palette-mix-50…950` ramp-shape knobs (color-mix percentages of the old model) and replaced them with two lightness-anchor knobs — `--sf-palette-tint-l` (default `0.97`) and `--sf-palette-shade-l` (default `0.1`) — which set how light the lightest / how dark the darkest generated palette step may reach. Per-step pull fraction and chroma taper are now baked into each family. If you overrode `--sf-palette-mix-*` to reshape the ramp, retune via the two new anchors instead (or override individual `--sf-color-{family}-{step}` resolved tokens).
+
## [0.7.18] - 2026-07-16
### Features
diff --git a/README.md b/README.md
index 08191626..cd6ec95e 100644
--- a/README.md
+++ b/README.md
@@ -4,7 +4,7 @@
A cascade-layer CSS framework you never compile.
- 754 design tokens, automatic dark mode, fluid type & spacing — no build step, no Node, no JavaScript.
+ 746 design tokens, automatic dark mode, fluid type & spacing — no build step, no Node, no JavaScript.
- Controls how much color is mixed into each palette step. Step 500 is always the source color.
+ Set how light the lightest step and how dark the darkest step reach; every
+ step in between is derived automatically and stays light→dark for any
+ colour. Step 500 is always the source colour.
@@ -874,35 +879,18 @@
{/each}
-
+
-
Tints (50–400) — mixed toward surface
+
Ramp anchors — absolute OKLCH lightness
- {#each MIX_STEPS.filter((s) => s.step === "tint") as s (s.name)}
+ {#each ANCHOR_KNOBS as k (k.name)}
onSet(s.name, `${v}%`)}
- onReset={() => onReset(s.name)}
- />
- {/each}
-
Values resolve from the loaded bundle. Component-scoped tokens
(--sf-btn-*, --sf-card-*, --sf-field-*) resolve against a
hidden probe of that component; a — means the token has no value outside its
@@ -4380,17 +4380,17 @@
Design tokens 7
--sf-color-action
--sf-color-action--active
--sf-color-action--hover
-
--sf-color-action-100
-
--sf-color-action-200
-
--sf-color-action-300
-
--sf-color-action-400
-
--sf-color-action-50
+
--sf-color-action-100
+
--sf-color-action-200
+
--sf-color-action-300
+
--sf-color-action-400
+
--sf-color-action-50
--sf-color-action-500
-
--sf-color-action-600
-
--sf-color-action-700
-
--sf-color-action-800
-
--sf-color-action-900
-
--sf-color-action-950
+
--sf-color-action-600
+
--sf-color-action-700
+
--sf-color-action-800
+
--sf-color-action-900
+
--sf-color-action-950
--sf-color-action-a10
--sf-color-action-a30
--sf-color-action-a5
@@ -4451,7 +4451,7 @@
Design tokens 7
--sf-color-danger-source-dark
--sf-color-danger-source-light
--sf-color-danger-strong
-
--sf-color-danger-subtle
+
--sf-color-danger-subtle
--sf-color-danger-tint
--sf-color-dim
--sf-color-heading
@@ -4460,7 +4460,7 @@
Design tokens 7
--sf-color-info-source-dark
--sf-color-info-source-light
--sf-color-info-strong
-
--sf-color-info-subtle
+
--sf-color-info-subtle
--sf-color-info-tint
--sf-color-inset
--sf-color-inverse
@@ -4475,17 +4475,17 @@
Design tokens 7
--sf-color-neutral
--sf-color-neutral--active
--sf-color-neutral--hover
-
--sf-color-neutral-100
-
--sf-color-neutral-200
-
--sf-color-neutral-300
-
--sf-color-neutral-400
-
--sf-color-neutral-50
+
--sf-color-neutral-100
+
--sf-color-neutral-200
+
--sf-color-neutral-300
+
--sf-color-neutral-400
+
--sf-color-neutral-50
--sf-color-neutral-500
-
--sf-color-neutral-600
-
--sf-color-neutral-700
-
--sf-color-neutral-800
-
--sf-color-neutral-900
-
--sf-color-neutral-950
+
--sf-color-neutral-600
+
--sf-color-neutral-700
+
--sf-color-neutral-800
+
--sf-color-neutral-900
+
--sf-color-neutral-950
--sf-color-neutral-a10
--sf-color-neutral-a30
--sf-color-neutral-a5
@@ -4506,17 +4506,17 @@
Design tokens 7
--sf-color-primary
--sf-color-primary--active
--sf-color-primary--hover
-
--sf-color-primary-100
-
--sf-color-primary-200
-
--sf-color-primary-300
-
--sf-color-primary-400
-
--sf-color-primary-50
+
--sf-color-primary-100
+
--sf-color-primary-200
+
--sf-color-primary-300
+
--sf-color-primary-400
+
--sf-color-primary-50
--sf-color-primary-500
-
--sf-color-primary-600
-
--sf-color-primary-700
-
--sf-color-primary-800
-
--sf-color-primary-900
-
--sf-color-primary-950
+
--sf-color-primary-600
+
--sf-color-primary-700
+
--sf-color-primary-800
+
--sf-color-primary-900
+
--sf-color-primary-950
--sf-color-primary-a10
--sf-color-primary-a30
--sf-color-primary-a5
@@ -4538,17 +4538,17 @@
Design tokens 7
--sf-color-secondary
--sf-color-secondary--active
--sf-color-secondary--hover
-
--sf-color-secondary-100
-
--sf-color-secondary-200
-
--sf-color-secondary-300
-
--sf-color-secondary-400
-
--sf-color-secondary-50
+
--sf-color-secondary-100
+
--sf-color-secondary-200
+
--sf-color-secondary-300
+
--sf-color-secondary-400
+
--sf-color-secondary-50
--sf-color-secondary-500
-
--sf-color-secondary-600
-
--sf-color-secondary-700
-
--sf-color-secondary-800
-
--sf-color-secondary-900
-
--sf-color-secondary-950
+
--sf-color-secondary-600
+
--sf-color-secondary-700
+
--sf-color-secondary-800
+
--sf-color-secondary-900
+
--sf-color-secondary-950
--sf-color-secondary-a10
--sf-color-secondary-a30
--sf-color-secondary-a5
@@ -4580,17 +4580,17 @@
Design tokens 7
--sf-color-tertiary
--sf-color-tertiary--active
--sf-color-tertiary--hover
-
--sf-color-tertiary-100
-
--sf-color-tertiary-200
-
--sf-color-tertiary-300
-
--sf-color-tertiary-400
-
--sf-color-tertiary-50
+
--sf-color-tertiary-100
+
--sf-color-tertiary-200
+
--sf-color-tertiary-300
+
--sf-color-tertiary-400
+
--sf-color-tertiary-50
--sf-color-tertiary-500
-
--sf-color-tertiary-600
-
--sf-color-tertiary-700
-
--sf-color-tertiary-800
-
--sf-color-tertiary-900
-
--sf-color-tertiary-950
+
--sf-color-tertiary-600
+
--sf-color-tertiary-700
+
--sf-color-tertiary-800
+
--sf-color-tertiary-900
+
--sf-color-tertiary-950
--sf-color-tertiary-a10
--sf-color-tertiary-a30
--sf-color-tertiary-a5
@@ -4810,16 +4810,8 @@
Design tokens 7
--sf-opacity-muted
--sf-optical-sizing
--sf-overlap-pull
-
--sf-palette-mix-100
-
--sf-palette-mix-200
-
--sf-palette-mix-300
-
--sf-palette-mix-400
-
--sf-palette-mix-50
-
--sf-palette-mix-600
-
--sf-palette-mix-700
-
--sf-palette-mix-800
-
--sf-palette-mix-900
-
--sf-palette-mix-950
+
--sf-palette-shade-l
+
--sf-palette-tint-l
--sf-print-base-size
--sf-print-page-margin
--sf-print-page-size
@@ -5029,7 +5021,7 @@
Design tokens 7
--sf-z-tooltip
-
+
diff --git a/dist/css-custom-data.json b/dist/css-custom-data.json
index 9859c871..3763c8aa 100644
--- a/dist/css-custom-data.json
+++ b/dist/css-custom-data.json
@@ -538,27 +538,27 @@
},
{
"name": "--sf-color-action-100",
- "description": "Very light action shade. Hover fills on white surfaces, badge backgrounds.\n\nDefault: `color-mix(in oklab, var(--sf-color-action) var(--sf-palette-mix-100), var(--sf-color-surface))`",
+ "description": "Very light action shade. Hover fills on white surfaces, badge backgrounds.\n\nDefault: `oklch(from var(--sf-color-action) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.88)) calc(c * 0.3) h)`",
"syntax": "*"
},
{
"name": "--sf-color-action-200",
- "description": "Light action shade. Subtle fills and outlined badge backgrounds.\n\nDefault: `color-mix(in oklab, var(--sf-color-action) var(--sf-palette-mix-200), var(--sf-color-surface))`",
+ "description": "Light action shade. Subtle fills and outlined badge backgrounds.\n\nDefault: `oklch(from var(--sf-color-action) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.72)) calc(c * 0.5) h)`",
"syntax": "*"
},
{
"name": "--sf-color-action-300",
- "description": "Light-mid action shade. Borders on light backgrounds, low-emphasis fills.\n\nDefault: `color-mix(in oklab, var(--sf-color-action) var(--sf-palette-mix-300), var(--sf-color-surface))`",
+ "description": "Light-mid action shade. Borders on light backgrounds, low-emphasis fills.\n\nDefault: `oklch(from var(--sf-color-action) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.52)) calc(c * 0.72) h)`",
"syntax": "*"
},
{
"name": "--sf-color-action-400",
- "description": "Mid-light action shade. Disabled state fills, decorative accents.\n\nDefault: `color-mix(in oklab, var(--sf-color-action) var(--sf-palette-mix-400), var(--sf-color-surface))`",
+ "description": "Mid-light action shade. Disabled state fills, decorative accents.\n\nDefault: `oklch(from var(--sf-color-action) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.3)) calc(c * 0.88) h)`",
"syntax": "*"
},
{
"name": "--sf-color-action-50",
- "description": "Lightest action tint. Background fill for subtle action-tinted surfaces.\n\nDefault: `color-mix(in oklab, var(--sf-color-action) var(--sf-palette-mix-50), var(--sf-color-surface))`",
+ "description": "Lightest action tint. Background fill for subtle action-tinted surfaces.\n\nDefault: `oklch(from var(--sf-color-action) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.95)) calc(c * 0.2) h)`",
"syntax": "*"
},
{
@@ -568,27 +568,27 @@
},
{
"name": "--sf-color-action-600",
- "description": "Mid-dark action shade. Text on light surfaces, icon fills.\n\nDefault: `color-mix(in oklab, var(--sf-color-action) var(--sf-palette-mix-600), var(--sf-color-text))`",
+ "description": "Mid-dark action shade. Text on light surfaces, icon fills.\n\nDefault: `oklch(from var(--sf-color-action) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.16)) calc(c * 0.96) h)`",
"syntax": "*"
},
{
"name": "--sf-color-action-700",
- "description": "Dark action shade. Strong text and icon fills on light backgrounds.\n\nDefault: `color-mix(in oklab, var(--sf-color-action) var(--sf-palette-mix-700), var(--sf-color-text))`",
+ "description": "Dark action shade. Strong text and icon fills on light backgrounds.\n\nDefault: `oklch(from var(--sf-color-action) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.36)) calc(c * 0.88) h)`",
"syntax": "*"
},
{
"name": "--sf-color-action-800",
- "description": "Very dark action shade. High-contrast text on white.\n\nDefault: `color-mix(in oklab, var(--sf-color-action) var(--sf-palette-mix-800), var(--sf-color-text))`",
+ "description": "Very dark action shade. High-contrast text on white.\n\nDefault: `oklch(from var(--sf-color-action) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.58)) calc(c * 0.72) h)`",
"syntax": "*"
},
{
"name": "--sf-color-action-900",
- "description": "Near-black action shade. Extreme contrast on white surfaces.\n\nDefault: `color-mix(in oklab, var(--sf-color-action) var(--sf-palette-mix-900), var(--sf-color-text))`",
+ "description": "Near-black action shade. Extreme contrast on white surfaces.\n\nDefault: `oklch(from var(--sf-color-action) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.78)) calc(c * 0.52) h)`",
"syntax": "*"
},
{
"name": "--sf-color-action-950",
- "description": "Darkest action tint. Near-black for dark-theme surface accents.\n\nDefault: `color-mix(in oklab, var(--sf-color-action) var(--sf-palette-mix-950), var(--sf-color-text))`",
+ "description": "Darkest action tint. Near-black for dark-theme surface accents.\n\nDefault: `oklch(from var(--sf-color-action) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.9)) calc(c * 0.38) h)`",
"syntax": "*"
},
{
@@ -893,7 +893,7 @@
},
{
"name": "--sf-color-danger-subtle",
- "description": "Light danger tint for error row backgrounds and alert container fills.\n\nDefault: `oklch(from var(--sf-color-danger) l c h / 0.1)`",
+ "description": "Light danger tint for error row backgrounds and alert container fills.\n\nDefault: `oklch(from var(--sf-color-danger) l c h / 0.12)`",
"syntax": "*"
},
{
@@ -938,7 +938,7 @@
},
{
"name": "--sf-color-info-subtle",
- "description": "Light info tint for informational note backgrounds.\n\nDefault: `oklch(from var(--sf-color-info) l c h / 0.1)`",
+ "description": "Light info tint for informational note backgrounds.\n\nDefault: `oklch(from var(--sf-color-info) l c h / 0.12)`",
"syntax": "*"
},
{
@@ -1013,27 +1013,27 @@
},
{
"name": "--sf-color-neutral-100",
- "description": "Very light neutral shade.\n\nDefault: `color-mix(in oklab, var(--sf-color-neutral) var(--sf-palette-mix-100), var(--sf-color-surface))`",
+ "description": "Very light neutral shade.\n\nDefault: `oklch(from var(--sf-color-neutral) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.88)) calc(c * 0.3) h)`",
"syntax": "*"
},
{
"name": "--sf-color-neutral-200",
- "description": "Light neutral shade at step 200 on the numeric scale. Subtle fills and outlined badge backgrounds.\n\nDefault: `color-mix(in oklab, var(--sf-color-neutral) var(--sf-palette-mix-200), var(--sf-color-surface))`",
+ "description": "Light neutral shade at step 200 on the numeric scale. Subtle fills and outlined badge backgrounds.\n\nDefault: `oklch(from var(--sf-color-neutral) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.72)) calc(c * 0.5) h)`",
"syntax": "*"
},
{
"name": "--sf-color-neutral-300",
- "description": "Light-mid neutral shade.\n\nDefault: `color-mix(in oklab, var(--sf-color-neutral) var(--sf-palette-mix-300), var(--sf-color-surface))`",
+ "description": "Light-mid neutral shade.\n\nDefault: `oklch(from var(--sf-color-neutral) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.52)) calc(c * 0.72) h)`",
"syntax": "*"
},
{
"name": "--sf-color-neutral-400",
- "description": "Mid-light neutral shade.\n\nDefault: `color-mix(in oklab, var(--sf-color-neutral) var(--sf-palette-mix-400), var(--sf-color-surface))`",
+ "description": "Mid-light neutral shade.\n\nDefault: `oklch(from var(--sf-color-neutral) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.3)) calc(c * 0.88) h)`",
"syntax": "*"
},
{
"name": "--sf-color-neutral-50",
- "description": "Lightest neutral tint.\n\nDefault: `color-mix(in oklab, var(--sf-color-neutral) var(--sf-palette-mix-50), var(--sf-color-surface))`",
+ "description": "Lightest neutral tint.\n\nDefault: `oklch(from var(--sf-color-neutral) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.95)) calc(c * 0.2) h)`",
"syntax": "*"
},
{
@@ -1043,27 +1043,27 @@
},
{
"name": "--sf-color-neutral-600",
- "description": "Mid-dark neutral shade.\n\nDefault: `color-mix(in oklab, var(--sf-color-neutral) var(--sf-palette-mix-600), var(--sf-color-text))`",
+ "description": "Mid-dark neutral shade.\n\nDefault: `oklch(from var(--sf-color-neutral) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.16)) calc(c * 0.96) h)`",
"syntax": "*"
},
{
"name": "--sf-color-neutral-700",
- "description": "Dark neutral shade.\n\nDefault: `color-mix(in oklab, var(--sf-color-neutral) var(--sf-palette-mix-700), var(--sf-color-text))`",
+ "description": "Dark neutral shade.\n\nDefault: `oklch(from var(--sf-color-neutral) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.36)) calc(c * 0.88) h)`",
"syntax": "*"
},
{
"name": "--sf-color-neutral-800",
- "description": "Very dark neutral shade.\n\nDefault: `color-mix(in oklab, var(--sf-color-neutral) var(--sf-palette-mix-800), var(--sf-color-text))`",
+ "description": "Very dark neutral shade.\n\nDefault: `oklch(from var(--sf-color-neutral) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.58)) calc(c * 0.72) h)`",
"syntax": "*"
},
{
"name": "--sf-color-neutral-900",
- "description": "Near-black neutral shade at step 900 on the numeric scale. Extreme contrast on light surfaces.\n\nDefault: `color-mix(in oklab, var(--sf-color-neutral) var(--sf-palette-mix-900), var(--sf-color-text))`",
+ "description": "Near-black neutral shade at step 900 on the numeric scale. Extreme contrast on light surfaces.\n\nDefault: `oklch(from var(--sf-color-neutral) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.78)) calc(c * 0.52) h)`",
"syntax": "*"
},
{
"name": "--sf-color-neutral-950",
- "description": "Darkest neutral tint.\n\nDefault: `color-mix(in oklab, var(--sf-color-neutral) var(--sf-palette-mix-950), var(--sf-color-text))`",
+ "description": "Darkest neutral tint.\n\nDefault: `oklch(from var(--sf-color-neutral) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.9)) calc(c * 0.38) h)`",
"syntax": "*"
},
{
@@ -1168,27 +1168,27 @@
},
{
"name": "--sf-color-primary-100",
- "description": "Very light primary shade.\n\nDefault: `color-mix(in oklab, var(--sf-color-primary) var(--sf-palette-mix-100), var(--sf-color-surface))`",
+ "description": "Very light primary shade.\n\nDefault: `oklch(from var(--sf-color-primary) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.88)) calc(c * 0.3) h)`",
"syntax": "*"
},
{
"name": "--sf-color-primary-200",
- "description": "Light primary shade at step 200 on the numeric scale. Subtle fills and outlined badge backgrounds.\n\nDefault: `color-mix(in oklab, var(--sf-color-primary) var(--sf-palette-mix-200), var(--sf-color-surface))`",
+ "description": "Light primary shade at step 200 on the numeric scale. Subtle fills and outlined badge backgrounds.\n\nDefault: `oklch(from var(--sf-color-primary) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.72)) calc(c * 0.5) h)`",
"syntax": "*"
},
{
"name": "--sf-color-primary-300",
- "description": "Light-mid primary shade.\n\nDefault: `color-mix(in oklab, var(--sf-color-primary) var(--sf-palette-mix-300), var(--sf-color-surface))`",
+ "description": "Light-mid primary shade.\n\nDefault: `oklch(from var(--sf-color-primary) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.52)) calc(c * 0.72) h)`",
"syntax": "*"
},
{
"name": "--sf-color-primary-400",
- "description": "Mid-light primary shade.\n\nDefault: `color-mix(in oklab, var(--sf-color-primary) var(--sf-palette-mix-400), var(--sf-color-surface))`",
+ "description": "Mid-light primary shade.\n\nDefault: `oklch(from var(--sf-color-primary) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.3)) calc(c * 0.88) h)`",
"syntax": "*"
},
{
"name": "--sf-color-primary-50",
- "description": "Lightest primary tint.\n\nDefault: `color-mix(in oklab, var(--sf-color-primary) var(--sf-palette-mix-50), var(--sf-color-surface))`",
+ "description": "Lightest primary tint.\n\nDefault: `oklch(from var(--sf-color-primary) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.95)) calc(c * 0.2) h)`",
"syntax": "*"
},
{
@@ -1198,27 +1198,27 @@
},
{
"name": "--sf-color-primary-600",
- "description": "Mid-dark primary shade.\n\nDefault: `color-mix(in oklab, var(--sf-color-primary) var(--sf-palette-mix-600), var(--sf-color-text))`",
+ "description": "Mid-dark primary shade.\n\nDefault: `oklch(from var(--sf-color-primary) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.16)) calc(c * 0.96) h)`",
"syntax": "*"
},
{
"name": "--sf-color-primary-700",
- "description": "Dark primary shade.\n\nDefault: `color-mix(in oklab, var(--sf-color-primary) var(--sf-palette-mix-700), var(--sf-color-text))`",
+ "description": "Dark primary shade.\n\nDefault: `oklch(from var(--sf-color-primary) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.36)) calc(c * 0.88) h)`",
"syntax": "*"
},
{
"name": "--sf-color-primary-800",
- "description": "Very dark primary shade.\n\nDefault: `color-mix(in oklab, var(--sf-color-primary) var(--sf-palette-mix-800), var(--sf-color-text))`",
+ "description": "Very dark primary shade.\n\nDefault: `oklch(from var(--sf-color-primary) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.58)) calc(c * 0.72) h)`",
"syntax": "*"
},
{
"name": "--sf-color-primary-900",
- "description": "Near-black primary shade at step 900 on the numeric scale. Extreme contrast on light surfaces.\n\nDefault: `color-mix(in oklab, var(--sf-color-primary) var(--sf-palette-mix-900), var(--sf-color-text))`",
+ "description": "Near-black primary shade at step 900 on the numeric scale. Extreme contrast on light surfaces.\n\nDefault: `oklch(from var(--sf-color-primary) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.78)) calc(c * 0.52) h)`",
"syntax": "*"
},
{
"name": "--sf-color-primary-950",
- "description": "Darkest primary tint.\n\nDefault: `color-mix(in oklab, var(--sf-color-primary) var(--sf-palette-mix-950), var(--sf-color-text))`",
+ "description": "Darkest primary tint.\n\nDefault: `oklch(from var(--sf-color-primary) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.9)) calc(c * 0.38) h)`",
"syntax": "*"
},
{
@@ -1328,27 +1328,27 @@
},
{
"name": "--sf-color-secondary-100",
- "description": "Very light secondary shade.\n\nDefault: `color-mix(in oklab, var(--sf-color-secondary) var(--sf-palette-mix-100), var(--sf-color-surface))`",
+ "description": "Very light secondary shade.\n\nDefault: `oklch(from var(--sf-color-secondary) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.88)) calc(c * 0.3) h)`",
"syntax": "*"
},
{
"name": "--sf-color-secondary-200",
- "description": "Light secondary shade at step 200 on the numeric scale. Subtle fills and outlined badge backgrounds.\n\nDefault: `color-mix(in oklab, var(--sf-color-secondary) var(--sf-palette-mix-200), var(--sf-color-surface))`",
+ "description": "Light secondary shade at step 200 on the numeric scale. Subtle fills and outlined badge backgrounds.\n\nDefault: `oklch(from var(--sf-color-secondary) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.72)) calc(c * 0.5) h)`",
"syntax": "*"
},
{
"name": "--sf-color-secondary-300",
- "description": "Light-mid secondary shade.\n\nDefault: `color-mix(in oklab, var(--sf-color-secondary) var(--sf-palette-mix-300), var(--sf-color-surface))`",
+ "description": "Light-mid secondary shade.\n\nDefault: `oklch(from var(--sf-color-secondary) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.52)) calc(c * 0.72) h)`",
"syntax": "*"
},
{
"name": "--sf-color-secondary-400",
- "description": "Mid-light secondary shade.\n\nDefault: `color-mix(in oklab, var(--sf-color-secondary) var(--sf-palette-mix-400), var(--sf-color-surface))`",
+ "description": "Mid-light secondary shade.\n\nDefault: `oklch(from var(--sf-color-secondary) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.3)) calc(c * 0.88) h)`",
"syntax": "*"
},
{
"name": "--sf-color-secondary-50",
- "description": "Lightest secondary tint.\n\nDefault: `color-mix(in oklab, var(--sf-color-secondary) var(--sf-palette-mix-50), var(--sf-color-surface))`",
+ "description": "Lightest secondary tint.\n\nDefault: `oklch(from var(--sf-color-secondary) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.95)) calc(c * 0.2) h)`",
"syntax": "*"
},
{
@@ -1358,27 +1358,27 @@
},
{
"name": "--sf-color-secondary-600",
- "description": "Mid-dark secondary shade.\n\nDefault: `color-mix(in oklab, var(--sf-color-secondary) var(--sf-palette-mix-600), var(--sf-color-text))`",
+ "description": "Mid-dark secondary shade.\n\nDefault: `oklch(from var(--sf-color-secondary) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.16)) calc(c * 0.96) h)`",
"syntax": "*"
},
{
"name": "--sf-color-secondary-700",
- "description": "Dark secondary shade.\n\nDefault: `color-mix(in oklab, var(--sf-color-secondary) var(--sf-palette-mix-700), var(--sf-color-text))`",
+ "description": "Dark secondary shade.\n\nDefault: `oklch(from var(--sf-color-secondary) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.36)) calc(c * 0.88) h)`",
"syntax": "*"
},
{
"name": "--sf-color-secondary-800",
- "description": "Very dark secondary shade.\n\nDefault: `color-mix(in oklab, var(--sf-color-secondary) var(--sf-palette-mix-800), var(--sf-color-text))`",
+ "description": "Very dark secondary shade.\n\nDefault: `oklch(from var(--sf-color-secondary) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.58)) calc(c * 0.72) h)`",
"syntax": "*"
},
{
"name": "--sf-color-secondary-900",
- "description": "Near-black secondary shade at step 900 on the numeric scale. Extreme contrast on light surfaces.\n\nDefault: `color-mix(in oklab, var(--sf-color-secondary) var(--sf-palette-mix-900), var(--sf-color-text))`",
+ "description": "Near-black secondary shade at step 900 on the numeric scale. Extreme contrast on light surfaces.\n\nDefault: `oklch(from var(--sf-color-secondary) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.78)) calc(c * 0.52) h)`",
"syntax": "*"
},
{
"name": "--sf-color-secondary-950",
- "description": "Darkest secondary tint.\n\nDefault: `color-mix(in oklab, var(--sf-color-secondary) var(--sf-palette-mix-950), var(--sf-color-text))`",
+ "description": "Darkest secondary tint.\n\nDefault: `oklch(from var(--sf-color-secondary) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.9)) calc(c * 0.38) h)`",
"syntax": "*"
},
{
@@ -1538,27 +1538,27 @@
},
{
"name": "--sf-color-tertiary-100",
- "description": "Very light tertiary shade.\n\nDefault: `color-mix(in oklab, var(--sf-color-tertiary) var(--sf-palette-mix-100), var(--sf-color-surface))`",
+ "description": "Very light tertiary shade.\n\nDefault: `oklch(from var(--sf-color-tertiary) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.88)) calc(c * 0.3) h)`",
"syntax": "*"
},
{
"name": "--sf-color-tertiary-200",
- "description": "Light tertiary shade at step 200 on the numeric scale. Subtle fills and outlined badge backgrounds.\n\nDefault: `color-mix(in oklab, var(--sf-color-tertiary) var(--sf-palette-mix-200), var(--sf-color-surface))`",
+ "description": "Light tertiary shade at step 200 on the numeric scale. Subtle fills and outlined badge backgrounds.\n\nDefault: `oklch(from var(--sf-color-tertiary) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.72)) calc(c * 0.5) h)`",
"syntax": "*"
},
{
"name": "--sf-color-tertiary-300",
- "description": "Light-mid tertiary shade.\n\nDefault: `color-mix(in oklab, var(--sf-color-tertiary) var(--sf-palette-mix-300), var(--sf-color-surface))`",
+ "description": "Light-mid tertiary shade.\n\nDefault: `oklch(from var(--sf-color-tertiary) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.52)) calc(c * 0.72) h)`",
"syntax": "*"
},
{
"name": "--sf-color-tertiary-400",
- "description": "Mid-light tertiary shade.\n\nDefault: `color-mix(in oklab, var(--sf-color-tertiary) var(--sf-palette-mix-400), var(--sf-color-surface))`",
+ "description": "Mid-light tertiary shade.\n\nDefault: `oklch(from var(--sf-color-tertiary) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.3)) calc(c * 0.88) h)`",
"syntax": "*"
},
{
"name": "--sf-color-tertiary-50",
- "description": "Lightest tertiary tint.\n\nDefault: `color-mix(in oklab, var(--sf-color-tertiary) var(--sf-palette-mix-50), var(--sf-color-surface))`",
+ "description": "Lightest tertiary tint.\n\nDefault: `oklch(from var(--sf-color-tertiary) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.95)) calc(c * 0.2) h)`",
"syntax": "*"
},
{
@@ -1568,27 +1568,27 @@
},
{
"name": "--sf-color-tertiary-600",
- "description": "Mid-dark tertiary shade.\n\nDefault: `color-mix(in oklab, var(--sf-color-tertiary) var(--sf-palette-mix-600), var(--sf-color-text))`",
+ "description": "Mid-dark tertiary shade.\n\nDefault: `oklch(from var(--sf-color-tertiary) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.16)) calc(c * 0.96) h)`",
"syntax": "*"
},
{
"name": "--sf-color-tertiary-700",
- "description": "Dark tertiary shade.\n\nDefault: `color-mix(in oklab, var(--sf-color-tertiary) var(--sf-palette-mix-700), var(--sf-color-text))`",
+ "description": "Dark tertiary shade.\n\nDefault: `oklch(from var(--sf-color-tertiary) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.36)) calc(c * 0.88) h)`",
"syntax": "*"
},
{
"name": "--sf-color-tertiary-800",
- "description": "Very dark tertiary shade.\n\nDefault: `color-mix(in oklab, var(--sf-color-tertiary) var(--sf-palette-mix-800), var(--sf-color-text))`",
+ "description": "Very dark tertiary shade.\n\nDefault: `oklch(from var(--sf-color-tertiary) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.58)) calc(c * 0.72) h)`",
"syntax": "*"
},
{
"name": "--sf-color-tertiary-900",
- "description": "Near-black tertiary shade at step 900 on the numeric scale. Extreme contrast on light surfaces.\n\nDefault: `color-mix(in oklab, var(--sf-color-tertiary) var(--sf-palette-mix-900), var(--sf-color-text))`",
+ "description": "Near-black tertiary shade at step 900 on the numeric scale. Extreme contrast on light surfaces.\n\nDefault: `oklch(from var(--sf-color-tertiary) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.78)) calc(c * 0.52) h)`",
"syntax": "*"
},
{
"name": "--sf-color-tertiary-950",
- "description": "Darkest tertiary tint.\n\nDefault: `color-mix(in oklab, var(--sf-color-tertiary) var(--sf-palette-mix-950), var(--sf-color-text))`",
+ "description": "Darkest tertiary tint.\n\nDefault: `oklch(from var(--sf-color-tertiary) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.9)) calc(c * 0.38) h)`",
"syntax": "*"
},
{
@@ -2682,53 +2682,13 @@
"syntax": "*"
},
{
- "name": "--sf-palette-mix-100",
- "description": "Color-mix percentage for palette step 100. Controls blend depth toward the surface. Default 8%.\n\nDefault: `8%`\n\n(advanced token)",
+ "name": "--sf-palette-shade-l",
+ "description": "Absolute OKLCH lightness the shade steps (600-950) pull toward; sets how dark the darkest palette step can reach. Default 0.1.\n\nDefault: `0.1`\n\n(advanced token)",
"syntax": "*"
},
{
- "name": "--sf-palette-mix-200",
- "description": "Color-mix percentage for palette step 200. Controls blend depth toward the surface. Default 20%.\n\nDefault: `20%`\n\n(advanced token)",
- "syntax": "*"
- },
- {
- "name": "--sf-palette-mix-300",
- "description": "Color-mix percentage for palette step 300. Controls blend depth toward the surface. Default 40%.\n\nDefault: `40%`\n\n(advanced token)",
- "syntax": "*"
- },
- {
- "name": "--sf-palette-mix-400",
- "description": "Color-mix percentage for palette step 400 (near-mid tint). Controls blend depth. Default 65%.\n\nDefault: `65%`\n\n(advanced token)",
- "syntax": "*"
- },
- {
- "name": "--sf-palette-mix-50",
- "description": "Color-mix percentage for the lightest palette step (50). Controls how far step 50 blends toward the surface. Default 4%.\n\nDefault: `4%`\n\n(advanced token)",
- "syntax": "*"
- },
- {
- "name": "--sf-palette-mix-600",
- "description": "Color-mix percentage for palette step 600 (near-mid shade). Controls blend depth toward the text color. Default 82%.\n\nDefault: `82%`\n\n(advanced token)",
- "syntax": "*"
- },
- {
- "name": "--sf-palette-mix-700",
- "description": "Color-mix percentage for palette step 700. Controls blend depth toward the text color. Default 62%.\n\nDefault: `62%`\n\n(advanced token)",
- "syntax": "*"
- },
- {
- "name": "--sf-palette-mix-800",
- "description": "Color-mix percentage for palette step 800 (dark shade). Controls blend depth toward text. Default 38%.\n\nDefault: `38%`\n\n(advanced token)",
- "syntax": "*"
- },
- {
- "name": "--sf-palette-mix-900",
- "description": "Color-mix percentage for palette step 900 (very dark shade). Controls blend depth toward text. Default 18%.\n\nDefault: `18%`\n\n(advanced token)",
- "syntax": "*"
- },
- {
- "name": "--sf-palette-mix-950",
- "description": "Color-mix percentage for the darkest palette step (950). Controls how far step 950 blends toward the text color. Default 8%.\n\nDefault: `8%`\n\n(advanced token)",
+ "name": "--sf-palette-tint-l",
+ "description": "Absolute OKLCH lightness the tint steps (50-400) pull toward; sets how light the lightest palette step can reach. Default 0.97.\n\nDefault: `0.97`\n\n(advanced token)",
"syntax": "*"
},
{
diff --git a/docs/api-index.json b/docs/api-index.json
index 001980db..56b4f147 100644
--- a/docs/api-index.json
+++ b/docs/api-index.json
@@ -25,21 +25,21 @@
"optimal"
],
"counts": {
- "total": 1079,
+ "total": 1071,
"by_type": {
"class": 325,
- "token": 754
+ "token": 746
},
"by_tier": {
"INTERNAL": 1,
"PUBLIC": 1018,
- "PUBLIC-ADVANCED": 60
+ "PUBLIC-ADVANCED": 52
},
"by_role": {
"consumption": 489,
- "knob": 265
+ "knob": 257
},
- "tokens": 754,
+ "tokens": 746,
"classes": 325,
"sf_classes": 289,
"is_classes": 28,
@@ -48,7 +48,7 @@
"Accessibility": 8,
"Component tokens": 46,
"Components": 30,
- "Core tokens": 623,
+ "Core tokens": 615,
"Forms": 1,
"Layout primitives": 145,
"Layout tokens": 53,
@@ -2838,7 +2838,7 @@
"area": "core",
"group": "action",
"description": "Very light action shade. Hover fills on white surfaces, badge backgrounds.",
- "value": "color-mix(in oklab, var(--sf-color-action) var(--sf-palette-mix-100), var(--sf-color-surface))",
+ "value": "oklch(from var(--sf-color-action) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.88)) calc(c * 0.3) h)",
"aliasOf": null,
"registered": false,
"animatable": false,
@@ -2864,7 +2864,7 @@
"area": "core",
"group": "action",
"description": "Light action shade. Subtle fills and outlined badge backgrounds.",
- "value": "color-mix(in oklab, var(--sf-color-action) var(--sf-palette-mix-200), var(--sf-color-surface))",
+ "value": "oklch(from var(--sf-color-action) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.72)) calc(c * 0.5) h)",
"aliasOf": null,
"registered": false,
"animatable": false,
@@ -2890,7 +2890,7 @@
"area": "core",
"group": "action",
"description": "Light-mid action shade. Borders on light backgrounds, low-emphasis fills.",
- "value": "color-mix(in oklab, var(--sf-color-action) var(--sf-palette-mix-300), var(--sf-color-surface))",
+ "value": "oklch(from var(--sf-color-action) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.52)) calc(c * 0.72) h)",
"aliasOf": null,
"registered": false,
"animatable": false,
@@ -2916,7 +2916,7 @@
"area": "core",
"group": "action",
"description": "Mid-light action shade. Disabled state fills, decorative accents.",
- "value": "color-mix(in oklab, var(--sf-color-action) var(--sf-palette-mix-400), var(--sf-color-surface))",
+ "value": "oklch(from var(--sf-color-action) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.3)) calc(c * 0.88) h)",
"aliasOf": null,
"registered": false,
"animatable": false,
@@ -2942,7 +2942,7 @@
"area": "core",
"group": "action",
"description": "Lightest action tint. Background fill for subtle action-tinted surfaces.",
- "value": "color-mix(in oklab, var(--sf-color-action) var(--sf-palette-mix-50), var(--sf-color-surface))",
+ "value": "oklch(from var(--sf-color-action) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.95)) calc(c * 0.2) h)",
"aliasOf": null,
"registered": false,
"animatable": false,
@@ -2994,7 +2994,7 @@
"area": "core",
"group": "action",
"description": "Mid-dark action shade. Text on light surfaces, icon fills.",
- "value": "color-mix(in oklab, var(--sf-color-action) var(--sf-palette-mix-600), var(--sf-color-text))",
+ "value": "oklch(from var(--sf-color-action) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.16)) calc(c * 0.96) h)",
"aliasOf": null,
"registered": false,
"animatable": false,
@@ -3020,7 +3020,7 @@
"area": "core",
"group": "action",
"description": "Dark action shade. Strong text and icon fills on light backgrounds.",
- "value": "color-mix(in oklab, var(--sf-color-action) var(--sf-palette-mix-700), var(--sf-color-text))",
+ "value": "oklch(from var(--sf-color-action) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.36)) calc(c * 0.88) h)",
"aliasOf": null,
"registered": false,
"animatable": false,
@@ -3046,7 +3046,7 @@
"area": "core",
"group": "action",
"description": "Very dark action shade. High-contrast text on white.",
- "value": "color-mix(in oklab, var(--sf-color-action) var(--sf-palette-mix-800), var(--sf-color-text))",
+ "value": "oklch(from var(--sf-color-action) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.58)) calc(c * 0.72) h)",
"aliasOf": null,
"registered": false,
"animatable": false,
@@ -3072,7 +3072,7 @@
"area": "core",
"group": "action",
"description": "Near-black action shade. Extreme contrast on white surfaces.",
- "value": "color-mix(in oklab, var(--sf-color-action) var(--sf-palette-mix-900), var(--sf-color-text))",
+ "value": "oklch(from var(--sf-color-action) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.78)) calc(c * 0.52) h)",
"aliasOf": null,
"registered": false,
"animatable": false,
@@ -3098,7 +3098,7 @@
"area": "core",
"group": "action",
"description": "Darkest action tint. Near-black for dark-theme surface accents.",
- "value": "color-mix(in oklab, var(--sf-color-action) var(--sf-palette-mix-950), var(--sf-color-text))",
+ "value": "oklch(from var(--sf-color-action) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.9)) calc(c * 0.38) h)",
"aliasOf": null,
"registered": false,
"animatable": false,
@@ -4684,7 +4684,7 @@
"area": "core",
"group": "Status triplets",
"description": "Light danger tint for error row backgrounds and alert container fills.",
- "value": "oklch(from var(--sf-color-danger) l c h / 0.1)",
+ "value": "oklch(from var(--sf-color-danger) l c h / 0.12)",
"aliasOf": null,
"registered": false,
"animatable": false,
@@ -4918,7 +4918,7 @@
"area": "core",
"group": "Status triplets",
"description": "Light info tint for informational note backgrounds.",
- "value": "oklch(from var(--sf-color-info) l c h / 0.1)",
+ "value": "oklch(from var(--sf-color-info) l c h / 0.12)",
"aliasOf": null,
"registered": false,
"animatable": false,
@@ -5308,7 +5308,7 @@
"area": "core",
"group": "neutral",
"description": "Very light neutral shade.",
- "value": "color-mix(in oklab, var(--sf-color-neutral) var(--sf-palette-mix-100), var(--sf-color-surface))",
+ "value": "oklch(from var(--sf-color-neutral) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.88)) calc(c * 0.3) h)",
"aliasOf": null,
"registered": false,
"animatable": false,
@@ -5334,7 +5334,7 @@
"area": "core",
"group": "neutral",
"description": "Light neutral shade at step 200 on the numeric scale. Subtle fills and outlined badge backgrounds.",
- "value": "color-mix(in oklab, var(--sf-color-neutral) var(--sf-palette-mix-200), var(--sf-color-surface))",
+ "value": "oklch(from var(--sf-color-neutral) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.72)) calc(c * 0.5) h)",
"aliasOf": null,
"registered": false,
"animatable": false,
@@ -5360,7 +5360,7 @@
"area": "core",
"group": "neutral",
"description": "Light-mid neutral shade.",
- "value": "color-mix(in oklab, var(--sf-color-neutral) var(--sf-palette-mix-300), var(--sf-color-surface))",
+ "value": "oklch(from var(--sf-color-neutral) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.52)) calc(c * 0.72) h)",
"aliasOf": null,
"registered": false,
"animatable": false,
@@ -5386,7 +5386,7 @@
"area": "core",
"group": "neutral",
"description": "Mid-light neutral shade.",
- "value": "color-mix(in oklab, var(--sf-color-neutral) var(--sf-palette-mix-400), var(--sf-color-surface))",
+ "value": "oklch(from var(--sf-color-neutral) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.3)) calc(c * 0.88) h)",
"aliasOf": null,
"registered": false,
"animatable": false,
@@ -5412,7 +5412,7 @@
"area": "core",
"group": "neutral",
"description": "Lightest neutral tint.",
- "value": "color-mix(in oklab, var(--sf-color-neutral) var(--sf-palette-mix-50), var(--sf-color-surface))",
+ "value": "oklch(from var(--sf-color-neutral) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.95)) calc(c * 0.2) h)",
"aliasOf": null,
"registered": false,
"animatable": false,
@@ -5464,7 +5464,7 @@
"area": "core",
"group": "neutral",
"description": "Mid-dark neutral shade.",
- "value": "color-mix(in oklab, var(--sf-color-neutral) var(--sf-palette-mix-600), var(--sf-color-text))",
+ "value": "oklch(from var(--sf-color-neutral) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.16)) calc(c * 0.96) h)",
"aliasOf": null,
"registered": false,
"animatable": false,
@@ -5490,7 +5490,7 @@
"area": "core",
"group": "neutral",
"description": "Dark neutral shade.",
- "value": "color-mix(in oklab, var(--sf-color-neutral) var(--sf-palette-mix-700), var(--sf-color-text))",
+ "value": "oklch(from var(--sf-color-neutral) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.36)) calc(c * 0.88) h)",
"aliasOf": null,
"registered": false,
"animatable": false,
@@ -5516,7 +5516,7 @@
"area": "core",
"group": "neutral",
"description": "Very dark neutral shade.",
- "value": "color-mix(in oklab, var(--sf-color-neutral) var(--sf-palette-mix-800), var(--sf-color-text))",
+ "value": "oklch(from var(--sf-color-neutral) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.58)) calc(c * 0.72) h)",
"aliasOf": null,
"registered": false,
"animatable": false,
@@ -5542,7 +5542,7 @@
"area": "core",
"group": "neutral",
"description": "Near-black neutral shade at step 900 on the numeric scale. Extreme contrast on light surfaces.",
- "value": "color-mix(in oklab, var(--sf-color-neutral) var(--sf-palette-mix-900), var(--sf-color-text))",
+ "value": "oklch(from var(--sf-color-neutral) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.78)) calc(c * 0.52) h)",
"aliasOf": null,
"registered": false,
"animatable": false,
@@ -5568,7 +5568,7 @@
"area": "core",
"group": "neutral",
"description": "Darkest neutral tint.",
- "value": "color-mix(in oklab, var(--sf-color-neutral) var(--sf-palette-mix-950), var(--sf-color-text))",
+ "value": "oklch(from var(--sf-color-neutral) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.9)) calc(c * 0.38) h)",
"aliasOf": null,
"registered": false,
"animatable": false,
@@ -6114,7 +6114,7 @@
"area": "core",
"group": "primary",
"description": "Very light primary shade.",
- "value": "color-mix(in oklab, var(--sf-color-primary) var(--sf-palette-mix-100), var(--sf-color-surface))",
+ "value": "oklch(from var(--sf-color-primary) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.88)) calc(c * 0.3) h)",
"aliasOf": null,
"registered": false,
"animatable": false,
@@ -6140,7 +6140,7 @@
"area": "core",
"group": "primary",
"description": "Light primary shade at step 200 on the numeric scale. Subtle fills and outlined badge backgrounds.",
- "value": "color-mix(in oklab, var(--sf-color-primary) var(--sf-palette-mix-200), var(--sf-color-surface))",
+ "value": "oklch(from var(--sf-color-primary) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.72)) calc(c * 0.5) h)",
"aliasOf": null,
"registered": false,
"animatable": false,
@@ -6166,7 +6166,7 @@
"area": "core",
"group": "primary",
"description": "Light-mid primary shade.",
- "value": "color-mix(in oklab, var(--sf-color-primary) var(--sf-palette-mix-300), var(--sf-color-surface))",
+ "value": "oklch(from var(--sf-color-primary) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.52)) calc(c * 0.72) h)",
"aliasOf": null,
"registered": false,
"animatable": false,
@@ -6192,7 +6192,7 @@
"area": "core",
"group": "primary",
"description": "Mid-light primary shade.",
- "value": "color-mix(in oklab, var(--sf-color-primary) var(--sf-palette-mix-400), var(--sf-color-surface))",
+ "value": "oklch(from var(--sf-color-primary) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.3)) calc(c * 0.88) h)",
"aliasOf": null,
"registered": false,
"animatable": false,
@@ -6218,7 +6218,7 @@
"area": "core",
"group": "primary",
"description": "Lightest primary tint.",
- "value": "color-mix(in oklab, var(--sf-color-primary) var(--sf-palette-mix-50), var(--sf-color-surface))",
+ "value": "oklch(from var(--sf-color-primary) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.95)) calc(c * 0.2) h)",
"aliasOf": null,
"registered": false,
"animatable": false,
@@ -6270,7 +6270,7 @@
"area": "core",
"group": "primary",
"description": "Mid-dark primary shade.",
- "value": "color-mix(in oklab, var(--sf-color-primary) var(--sf-palette-mix-600), var(--sf-color-text))",
+ "value": "oklch(from var(--sf-color-primary) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.16)) calc(c * 0.96) h)",
"aliasOf": null,
"registered": false,
"animatable": false,
@@ -6296,7 +6296,7 @@
"area": "core",
"group": "primary",
"description": "Dark primary shade.",
- "value": "color-mix(in oklab, var(--sf-color-primary) var(--sf-palette-mix-700), var(--sf-color-text))",
+ "value": "oklch(from var(--sf-color-primary) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.36)) calc(c * 0.88) h)",
"aliasOf": null,
"registered": false,
"animatable": false,
@@ -6322,7 +6322,7 @@
"area": "core",
"group": "primary",
"description": "Very dark primary shade.",
- "value": "color-mix(in oklab, var(--sf-color-primary) var(--sf-palette-mix-800), var(--sf-color-text))",
+ "value": "oklch(from var(--sf-color-primary) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.58)) calc(c * 0.72) h)",
"aliasOf": null,
"registered": false,
"animatable": false,
@@ -6348,7 +6348,7 @@
"area": "core",
"group": "primary",
"description": "Near-black primary shade at step 900 on the numeric scale. Extreme contrast on light surfaces.",
- "value": "color-mix(in oklab, var(--sf-color-primary) var(--sf-palette-mix-900), var(--sf-color-text))",
+ "value": "oklch(from var(--sf-color-primary) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.78)) calc(c * 0.52) h)",
"aliasOf": null,
"registered": false,
"animatable": false,
@@ -6374,7 +6374,7 @@
"area": "core",
"group": "primary",
"description": "Darkest primary tint.",
- "value": "color-mix(in oklab, var(--sf-color-primary) var(--sf-palette-mix-950), var(--sf-color-text))",
+ "value": "oklch(from var(--sf-color-primary) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.9)) calc(c * 0.38) h)",
"aliasOf": null,
"registered": false,
"animatable": false,
@@ -6946,7 +6946,7 @@
"area": "core",
"group": "secondary",
"description": "Very light secondary shade.",
- "value": "color-mix(in oklab, var(--sf-color-secondary) var(--sf-palette-mix-100), var(--sf-color-surface))",
+ "value": "oklch(from var(--sf-color-secondary) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.88)) calc(c * 0.3) h)",
"aliasOf": null,
"registered": false,
"animatable": false,
@@ -6972,7 +6972,7 @@
"area": "core",
"group": "secondary",
"description": "Light secondary shade at step 200 on the numeric scale. Subtle fills and outlined badge backgrounds.",
- "value": "color-mix(in oklab, var(--sf-color-secondary) var(--sf-palette-mix-200), var(--sf-color-surface))",
+ "value": "oklch(from var(--sf-color-secondary) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.72)) calc(c * 0.5) h)",
"aliasOf": null,
"registered": false,
"animatable": false,
@@ -6998,7 +6998,7 @@
"area": "core",
"group": "secondary",
"description": "Light-mid secondary shade.",
- "value": "color-mix(in oklab, var(--sf-color-secondary) var(--sf-palette-mix-300), var(--sf-color-surface))",
+ "value": "oklch(from var(--sf-color-secondary) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.52)) calc(c * 0.72) h)",
"aliasOf": null,
"registered": false,
"animatable": false,
@@ -7024,7 +7024,7 @@
"area": "core",
"group": "secondary",
"description": "Mid-light secondary shade.",
- "value": "color-mix(in oklab, var(--sf-color-secondary) var(--sf-palette-mix-400), var(--sf-color-surface))",
+ "value": "oklch(from var(--sf-color-secondary) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.3)) calc(c * 0.88) h)",
"aliasOf": null,
"registered": false,
"animatable": false,
@@ -7050,7 +7050,7 @@
"area": "core",
"group": "secondary",
"description": "Lightest secondary tint.",
- "value": "color-mix(in oklab, var(--sf-color-secondary) var(--sf-palette-mix-50), var(--sf-color-surface))",
+ "value": "oklch(from var(--sf-color-secondary) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.95)) calc(c * 0.2) h)",
"aliasOf": null,
"registered": false,
"animatable": false,
@@ -7102,7 +7102,7 @@
"area": "core",
"group": "secondary",
"description": "Mid-dark secondary shade.",
- "value": "color-mix(in oklab, var(--sf-color-secondary) var(--sf-palette-mix-600), var(--sf-color-text))",
+ "value": "oklch(from var(--sf-color-secondary) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.16)) calc(c * 0.96) h)",
"aliasOf": null,
"registered": false,
"animatable": false,
@@ -7128,7 +7128,7 @@
"area": "core",
"group": "secondary",
"description": "Dark secondary shade.",
- "value": "color-mix(in oklab, var(--sf-color-secondary) var(--sf-palette-mix-700), var(--sf-color-text))",
+ "value": "oklch(from var(--sf-color-secondary) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.36)) calc(c * 0.88) h)",
"aliasOf": null,
"registered": false,
"animatable": false,
@@ -7154,7 +7154,7 @@
"area": "core",
"group": "secondary",
"description": "Very dark secondary shade.",
- "value": "color-mix(in oklab, var(--sf-color-secondary) var(--sf-palette-mix-800), var(--sf-color-text))",
+ "value": "oklch(from var(--sf-color-secondary) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.58)) calc(c * 0.72) h)",
"aliasOf": null,
"registered": false,
"animatable": false,
@@ -7180,7 +7180,7 @@
"area": "core",
"group": "secondary",
"description": "Near-black secondary shade at step 900 on the numeric scale. Extreme contrast on light surfaces.",
- "value": "color-mix(in oklab, var(--sf-color-secondary) var(--sf-palette-mix-900), var(--sf-color-text))",
+ "value": "oklch(from var(--sf-color-secondary) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.78)) calc(c * 0.52) h)",
"aliasOf": null,
"registered": false,
"animatable": false,
@@ -7206,7 +7206,7 @@
"area": "core",
"group": "secondary",
"description": "Darkest secondary tint.",
- "value": "color-mix(in oklab, var(--sf-color-secondary) var(--sf-palette-mix-950), var(--sf-color-text))",
+ "value": "oklch(from var(--sf-color-secondary) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.9)) calc(c * 0.38) h)",
"aliasOf": null,
"registered": false,
"animatable": false,
@@ -8038,7 +8038,7 @@
"area": "core",
"group": "tertiary",
"description": "Very light tertiary shade.",
- "value": "color-mix(in oklab, var(--sf-color-tertiary) var(--sf-palette-mix-100), var(--sf-color-surface))",
+ "value": "oklch(from var(--sf-color-tertiary) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.88)) calc(c * 0.3) h)",
"aliasOf": null,
"registered": false,
"animatable": false,
@@ -8064,7 +8064,7 @@
"area": "core",
"group": "tertiary",
"description": "Light tertiary shade at step 200 on the numeric scale. Subtle fills and outlined badge backgrounds.",
- "value": "color-mix(in oklab, var(--sf-color-tertiary) var(--sf-palette-mix-200), var(--sf-color-surface))",
+ "value": "oklch(from var(--sf-color-tertiary) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.72)) calc(c * 0.5) h)",
"aliasOf": null,
"registered": false,
"animatable": false,
@@ -8090,7 +8090,7 @@
"area": "core",
"group": "tertiary",
"description": "Light-mid tertiary shade.",
- "value": "color-mix(in oklab, var(--sf-color-tertiary) var(--sf-palette-mix-300), var(--sf-color-surface))",
+ "value": "oklch(from var(--sf-color-tertiary) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.52)) calc(c * 0.72) h)",
"aliasOf": null,
"registered": false,
"animatable": false,
@@ -8116,7 +8116,7 @@
"area": "core",
"group": "tertiary",
"description": "Mid-light tertiary shade.",
- "value": "color-mix(in oklab, var(--sf-color-tertiary) var(--sf-palette-mix-400), var(--sf-color-surface))",
+ "value": "oklch(from var(--sf-color-tertiary) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.3)) calc(c * 0.88) h)",
"aliasOf": null,
"registered": false,
"animatable": false,
@@ -8142,7 +8142,7 @@
"area": "core",
"group": "tertiary",
"description": "Lightest tertiary tint.",
- "value": "color-mix(in oklab, var(--sf-color-tertiary) var(--sf-palette-mix-50), var(--sf-color-surface))",
+ "value": "oklch(from var(--sf-color-tertiary) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.95)) calc(c * 0.2) h)",
"aliasOf": null,
"registered": false,
"animatable": false,
@@ -8194,7 +8194,7 @@
"area": "core",
"group": "tertiary",
"description": "Mid-dark tertiary shade.",
- "value": "color-mix(in oklab, var(--sf-color-tertiary) var(--sf-palette-mix-600), var(--sf-color-text))",
+ "value": "oklch(from var(--sf-color-tertiary) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.16)) calc(c * 0.96) h)",
"aliasOf": null,
"registered": false,
"animatable": false,
@@ -8220,7 +8220,7 @@
"area": "core",
"group": "tertiary",
"description": "Dark tertiary shade.",
- "value": "color-mix(in oklab, var(--sf-color-tertiary) var(--sf-palette-mix-700), var(--sf-color-text))",
+ "value": "oklch(from var(--sf-color-tertiary) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.36)) calc(c * 0.88) h)",
"aliasOf": null,
"registered": false,
"animatable": false,
@@ -8246,7 +8246,7 @@
"area": "core",
"group": "tertiary",
"description": "Very dark tertiary shade.",
- "value": "color-mix(in oklab, var(--sf-color-tertiary) var(--sf-palette-mix-800), var(--sf-color-text))",
+ "value": "oklch(from var(--sf-color-tertiary) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.58)) calc(c * 0.72) h)",
"aliasOf": null,
"registered": false,
"animatable": false,
@@ -8272,7 +8272,7 @@
"area": "core",
"group": "tertiary",
"description": "Near-black tertiary shade at step 900 on the numeric scale. Extreme contrast on light surfaces.",
- "value": "color-mix(in oklab, var(--sf-color-tertiary) var(--sf-palette-mix-900), var(--sf-color-text))",
+ "value": "oklch(from var(--sf-color-tertiary) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.78)) calc(c * 0.52) h)",
"aliasOf": null,
"registered": false,
"animatable": false,
@@ -8298,7 +8298,7 @@
"area": "core",
"group": "tertiary",
"description": "Darkest tertiary tint.",
- "value": "color-mix(in oklab, var(--sf-color-tertiary) var(--sf-palette-mix-950), var(--sf-color-text))",
+ "value": "oklch(from var(--sf-color-tertiary) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.9)) calc(c * 0.38) h)",
"aliasOf": null,
"registered": false,
"animatable": false,
@@ -14006,16 +14006,16 @@
]
},
{
- "name": "--sf-palette-mix-100",
+ "name": "--sf-palette-shade-l",
"type": "token",
"tier": "PUBLIC-ADVANCED",
"role": "knob",
"namespace": "palette",
"category": "Core tokens",
"area": "core",
- "group": "Palette shade mix percentages",
- "description": "Color-mix percentage for palette step 100. Controls blend depth toward the surface. Default 8%.",
- "value": "8%",
+ "group": "Palette ramp lightness anchors",
+ "description": "Absolute OKLCH lightness the shade steps (600-950) pull toward; sets how dark the darkest palette step can reach. Default 0.1.",
+ "value": "0.1",
"aliasOf": null,
"registered": false,
"animatable": false,
@@ -14032,224 +14032,16 @@
]
},
{
- "name": "--sf-palette-mix-200",
+ "name": "--sf-palette-tint-l",
"type": "token",
"tier": "PUBLIC-ADVANCED",
"role": "knob",
"namespace": "palette",
"category": "Core tokens",
"area": "core",
- "group": "Palette shade mix percentages",
- "description": "Color-mix percentage for palette step 200. Controls blend depth toward the surface. Default 20%.",
- "value": "20%",
- "aliasOf": null,
- "registered": false,
- "animatable": false,
- "syntax": null,
- "inherits": null,
- "optional": false,
- "layer": "slashed.tokens",
- "sourceFiles": [
- "core/tokens.css"
- ],
- "bundles": [
- "full",
- "optimal"
- ]
- },
- {
- "name": "--sf-palette-mix-300",
- "type": "token",
- "tier": "PUBLIC-ADVANCED",
- "role": "knob",
- "namespace": "palette",
- "category": "Core tokens",
- "area": "core",
- "group": "Palette shade mix percentages",
- "description": "Color-mix percentage for palette step 300. Controls blend depth toward the surface. Default 40%.",
- "value": "40%",
- "aliasOf": null,
- "registered": false,
- "animatable": false,
- "syntax": null,
- "inherits": null,
- "optional": false,
- "layer": "slashed.tokens",
- "sourceFiles": [
- "core/tokens.css"
- ],
- "bundles": [
- "full",
- "optimal"
- ]
- },
- {
- "name": "--sf-palette-mix-400",
- "type": "token",
- "tier": "PUBLIC-ADVANCED",
- "role": "knob",
- "namespace": "palette",
- "category": "Core tokens",
- "area": "core",
- "group": "Palette shade mix percentages",
- "description": "Color-mix percentage for palette step 400 (near-mid tint). Controls blend depth. Default 65%.",
- "value": "65%",
- "aliasOf": null,
- "registered": false,
- "animatable": false,
- "syntax": null,
- "inherits": null,
- "optional": false,
- "layer": "slashed.tokens",
- "sourceFiles": [
- "core/tokens.css"
- ],
- "bundles": [
- "full",
- "optimal"
- ]
- },
- {
- "name": "--sf-palette-mix-50",
- "type": "token",
- "tier": "PUBLIC-ADVANCED",
- "role": "knob",
- "namespace": "palette",
- "category": "Core tokens",
- "area": "core",
- "group": "Palette shade mix percentages",
- "description": "Color-mix percentage for the lightest palette step (50). Controls how far step 50 blends toward the surface. Default 4%.",
- "value": "4%",
- "aliasOf": null,
- "registered": false,
- "animatable": false,
- "syntax": null,
- "inherits": null,
- "optional": false,
- "layer": "slashed.tokens",
- "sourceFiles": [
- "core/tokens.css"
- ],
- "bundles": [
- "full",
- "optimal"
- ]
- },
- {
- "name": "--sf-palette-mix-600",
- "type": "token",
- "tier": "PUBLIC-ADVANCED",
- "role": "knob",
- "namespace": "palette",
- "category": "Core tokens",
- "area": "core",
- "group": "Palette shade mix percentages",
- "description": "Color-mix percentage for palette step 600 (near-mid shade). Controls blend depth toward the text color. Default 82%.",
- "value": "82%",
- "aliasOf": null,
- "registered": false,
- "animatable": false,
- "syntax": null,
- "inherits": null,
- "optional": false,
- "layer": "slashed.tokens",
- "sourceFiles": [
- "core/tokens.css"
- ],
- "bundles": [
- "full",
- "optimal"
- ]
- },
- {
- "name": "--sf-palette-mix-700",
- "type": "token",
- "tier": "PUBLIC-ADVANCED",
- "role": "knob",
- "namespace": "palette",
- "category": "Core tokens",
- "area": "core",
- "group": "Palette shade mix percentages",
- "description": "Color-mix percentage for palette step 700. Controls blend depth toward the text color. Default 62%.",
- "value": "62%",
- "aliasOf": null,
- "registered": false,
- "animatable": false,
- "syntax": null,
- "inherits": null,
- "optional": false,
- "layer": "slashed.tokens",
- "sourceFiles": [
- "core/tokens.css"
- ],
- "bundles": [
- "full",
- "optimal"
- ]
- },
- {
- "name": "--sf-palette-mix-800",
- "type": "token",
- "tier": "PUBLIC-ADVANCED",
- "role": "knob",
- "namespace": "palette",
- "category": "Core tokens",
- "area": "core",
- "group": "Palette shade mix percentages",
- "description": "Color-mix percentage for palette step 800 (dark shade). Controls blend depth toward text. Default 38%.",
- "value": "38%",
- "aliasOf": null,
- "registered": false,
- "animatable": false,
- "syntax": null,
- "inherits": null,
- "optional": false,
- "layer": "slashed.tokens",
- "sourceFiles": [
- "core/tokens.css"
- ],
- "bundles": [
- "full",
- "optimal"
- ]
- },
- {
- "name": "--sf-palette-mix-900",
- "type": "token",
- "tier": "PUBLIC-ADVANCED",
- "role": "knob",
- "namespace": "palette",
- "category": "Core tokens",
- "area": "core",
- "group": "Palette shade mix percentages",
- "description": "Color-mix percentage for palette step 900 (very dark shade). Controls blend depth toward text. Default 18%.",
- "value": "18%",
- "aliasOf": null,
- "registered": false,
- "animatable": false,
- "syntax": null,
- "inherits": null,
- "optional": false,
- "layer": "slashed.tokens",
- "sourceFiles": [
- "core/tokens.css"
- ],
- "bundles": [
- "full",
- "optimal"
- ]
- },
- {
- "name": "--sf-palette-mix-950",
- "type": "token",
- "tier": "PUBLIC-ADVANCED",
- "role": "knob",
- "namespace": "palette",
- "category": "Core tokens",
- "area": "core",
- "group": "Palette shade mix percentages",
- "description": "Color-mix percentage for the darkest palette step (950). Controls how far step 950 blends toward the text color. Default 8%.",
- "value": "8%",
+ "group": "Palette ramp lightness anchors",
+ "description": "Absolute OKLCH lightness the tint steps (50-400) pull toward; sets how light the lightest palette step can reach. Default 0.97.",
+ "value": "0.97",
"aliasOf": null,
"registered": false,
"animatable": false,
diff --git a/docs/api-index.md b/docs/api-index.md
index a87508b9..fa63420c 100644
--- a/docs/api-index.md
+++ b/docs/api-index.md
@@ -10,15 +10,15 @@ and a short description. The machine-readable companion (with all columns) is
[registry.json](registry.json); for the tier contract see
[architecture.md](architecture.md).
-**1079 elements** — 754 tokens, 325 classes.
+**1071 elements** — 746 tokens, 325 classes.
| Tier | Count | Meaning |
|---|---|---|
| PUBLIC | 1018 | Everyday surface. SemVer-stable. |
-| PUBLIC-ADVANCED | 60 | Same SemVer guarantee; niche/powerful. |
+| PUBLIC-ADVANCED | 52 | Same SemVer guarantee; niche/powerful. |
| INTERNAL | 1 | Implementation detail; may change without a major bump. |
-## Tokens (754)
+## Tokens (746)
### Component tokens (46)
@@ -71,7 +71,7 @@ and a short description. The machine-readable companion (with all columns) is
| `--sf-field-padding-inline` | PUBLIC | consumption | field | `var(--sf-space-s)` | Horizontal (inline) inner padding for form field inputs. Reserved for a future .sf-field class — declared but not yet consumed by any shipped rule. |
| `--sf-field-radius` | PUBLIC | consumption | field | `var(--sf-radius-m)` | Border radius for form field inputs. Defaults to --sf-radius-m; override to reshape all inputs at once. Reserved for a future .sf-field class — declared but not yet consumed by any shipped rule. |
-### Core tokens (623)
+### Core tokens (615)
| Token | Tier | Role | Namespace | Default | Description |
|---|---|---|---|---|---|
@@ -117,17 +117,17 @@ and a short description. The machine-readable companion (with all columns) is
| `--sf-color-action` | PUBLIC | consumption | color | `light-dark(var(--sf-color-action-source-light), var(--sf-color-action-source-dark, oklch(from var(--sf-color-action-source-light) clamp(0.65, calc(0.95 - l * 0.5), 0.88) calc(c * 0.9) h)))` | Primary interactive color. Applied to filled buttons, active indicators, and brand accent surfaces. |
| `--sf-color-action--active` | PUBLIC | consumption | color | `var(--sf-color-action-xdark)` | Action color at pressed/active brightness. |
| `--sf-color-action--hover` | PUBLIC | consumption | color | `var(--sf-color-action-darker)` | Action color at hover brightness. |
-| `--sf-color-action-100` | PUBLIC | consumption | color | `color-mix(in oklab, var(--sf-color-action) var(--sf-palette-mix-100), var(--sf-color-surface))` | Very light action shade. Hover fills on white surfaces, badge backgrounds. |
-| `--sf-color-action-200` | PUBLIC | consumption | color | `color-mix(in oklab, var(--sf-color-action) var(--sf-palette-mix-200), var(--sf-color-surface))` | Light action shade. Subtle fills and outlined badge backgrounds. |
-| `--sf-color-action-300` | PUBLIC | consumption | color | `color-mix(in oklab, var(--sf-color-action) var(--sf-palette-mix-300), var(--sf-color-surface))` | Light-mid action shade. Borders on light backgrounds, low-emphasis fills. |
-| `--sf-color-action-400` | PUBLIC | consumption | color | `color-mix(in oklab, var(--sf-color-action) var(--sf-palette-mix-400), var(--sf-color-surface))` | Mid-light action shade. Disabled state fills, decorative accents. |
-| `--sf-color-action-50` | PUBLIC | consumption | color | `color-mix(in oklab, var(--sf-color-action) var(--sf-palette-mix-50), var(--sf-color-surface))` | Lightest action tint. Background fill for subtle action-tinted surfaces. |
+| `--sf-color-action-100` | PUBLIC | consumption | color | `oklch(from var(--sf-color-action) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.88)) calc(c * 0.3) h)` | Very light action shade. Hover fills on white surfaces, badge backgrounds. |
+| `--sf-color-action-200` | PUBLIC | consumption | color | `oklch(from var(--sf-color-action) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.72)) calc(c * 0.5) h)` | Light action shade. Subtle fills and outlined badge backgrounds. |
+| `--sf-color-action-300` | PUBLIC | consumption | color | `oklch(from var(--sf-color-action) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.52)) calc(c * 0.72) h)` | Light-mid action shade. Borders on light backgrounds, low-emphasis fills. |
+| `--sf-color-action-400` | PUBLIC | consumption | color | `oklch(from var(--sf-color-action) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.3)) calc(c * 0.88) h)` | Mid-light action shade. Disabled state fills, decorative accents. |
+| `--sf-color-action-50` | PUBLIC | consumption | color | `oklch(from var(--sf-color-action) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.95)) calc(c * 0.2) h)` | Lightest action tint. Background fill for subtle action-tinted surfaces. |
| `--sf-color-action-500` | PUBLIC | consumption | color | `var(--sf-color-action)` | Mid action shade — the 'pure' hue. Accessible on both light and dark backgrounds. |
-| `--sf-color-action-600` | PUBLIC | consumption | color | `color-mix(in oklab, var(--sf-color-action) var(--sf-palette-mix-600), var(--sf-color-text))` | Mid-dark action shade. Text on light surfaces, icon fills. |
-| `--sf-color-action-700` | PUBLIC | consumption | color | `color-mix(in oklab, var(--sf-color-action) var(--sf-palette-mix-700), var(--sf-color-text))` | Dark action shade. Strong text and icon fills on light backgrounds. |
-| `--sf-color-action-800` | PUBLIC | consumption | color | `color-mix(in oklab, var(--sf-color-action) var(--sf-palette-mix-800), var(--sf-color-text))` | Very dark action shade. High-contrast text on white. |
-| `--sf-color-action-900` | PUBLIC | consumption | color | `color-mix(in oklab, var(--sf-color-action) var(--sf-palette-mix-900), var(--sf-color-text))` | Near-black action shade. Extreme contrast on white surfaces. |
-| `--sf-color-action-950` | PUBLIC | consumption | color | `color-mix(in oklab, var(--sf-color-action) var(--sf-palette-mix-950), var(--sf-color-text))` | Darkest action tint. Near-black for dark-theme surface accents. |
+| `--sf-color-action-600` | PUBLIC | consumption | color | `oklch(from var(--sf-color-action) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.16)) calc(c * 0.96) h)` | Mid-dark action shade. Text on light surfaces, icon fills. |
+| `--sf-color-action-700` | PUBLIC | consumption | color | `oklch(from var(--sf-color-action) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.36)) calc(c * 0.88) h)` | Dark action shade. Strong text and icon fills on light backgrounds. |
+| `--sf-color-action-800` | PUBLIC | consumption | color | `oklch(from var(--sf-color-action) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.58)) calc(c * 0.72) h)` | Very dark action shade. High-contrast text on white. |
+| `--sf-color-action-900` | PUBLIC | consumption | color | `oklch(from var(--sf-color-action) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.78)) calc(c * 0.52) h)` | Near-black action shade. Extreme contrast on white surfaces. |
+| `--sf-color-action-950` | PUBLIC | consumption | color | `oklch(from var(--sf-color-action) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.9)) calc(c * 0.38) h)` | Darkest action tint. Near-black for dark-theme surface accents. |
| `--sf-color-action-a10` | PUBLIC | consumption | color | `oklch(from var(--sf-color-action) l c h / 0.10)` | 10% opacity action tint. Subtle hover or selected-row background. |
| `--sf-color-action-a30` | PUBLIC | consumption | color | `oklch(from var(--sf-color-action) l c h / 0.30)` | 30% opacity action tint. |
| `--sf-color-action-a5` | PUBLIC | consumption | color | `oklch(from var(--sf-color-action) l c h / 0.05)` | 5% opacity action tint. Barely-there hover highlight. |
@@ -188,7 +188,7 @@ and a short description. The machine-readable companion (with all columns) is
| `--sf-color-danger-source-dark` | PUBLIC | knob | color | `oklch(0.71 0.198 12)` | Registered dark counterpart of --sf-color-danger. |
| `--sf-color-danger-source-light` | PUBLIC | knob | color | `oklch(0.48 0.22 12)` | OKLCH lightness source for the danger status color (covers destructive actions and form validation errors). |
| `--sf-color-danger-strong` | PUBLIC | consumption | color | `light-dark( oklch(from var(--sf-color-danger-source-light) calc(l - 0.1) c h), oklch(from var(--sf-color-danger) clamp(0.70, calc(l + 0.15), 1) c h) )` | High-contrast danger color for alert body text and icons. |
-| `--sf-color-danger-subtle` | PUBLIC | consumption | color | `oklch(from var(--sf-color-danger) l c h / 0.1)` | Light danger tint for error row backgrounds and alert container fills. |
+| `--sf-color-danger-subtle` | PUBLIC | consumption | color | `oklch(from var(--sf-color-danger) l c h / 0.12)` | Light danger tint for error row backgrounds and alert container fills. |
| `--sf-color-danger-tint` | PUBLIC | consumption | color | `oklch(from var(--sf-color-danger) l c h / 0.05)` | Near-transparent danger tint (5% alpha) for hover-state washes and the most subtle backgrounds. |
| `--sf-color-dim` | PUBLIC | knob | color | `oklch(0 0 0 / 0.5)` | Backdrop overlay color for modals, drawers, and lightboxes. |
| `--sf-color-heading` | PUBLIC | consumption | color | `light-dark( oklch(from var(--sf-color-neutral-source-light) clamp(0.05, calc(l - 0.4 - var(--sf-contrast-bias)), 0.35) c h), oklch(from var(--sf-color-neutral) clamp(0.70, calc(l + 0.25 + var(--sf-contrast-bias)), 1) c h) )` | Heading text color. Slightly stronger contrast than body text for visual hierarchy. |
@@ -197,7 +197,7 @@ and a short description. The machine-readable companion (with all columns) is
| `--sf-color-info-source-dark` | PUBLIC | knob | color | `oklch(0.71 0.162 235)` | Registered dark counterpart of --sf-color-info. |
| `--sf-color-info-source-light` | PUBLIC | knob | color | `oklch(0.48 0.18 235)` | OKLCH lightness source for the info status color. |
| `--sf-color-info-strong` | PUBLIC | consumption | color | `light-dark( oklch(from var(--sf-color-info-source-light) calc(l - 0.1) c h), oklch(from var(--sf-color-info) clamp(0.70, calc(l + 0.15), 1) c h) )` | High-contrast info color for hint body text. |
-| `--sf-color-info-subtle` | PUBLIC | consumption | color | `oklch(from var(--sf-color-info) l c h / 0.1)` | Light info tint for informational note backgrounds. |
+| `--sf-color-info-subtle` | PUBLIC | consumption | color | `oklch(from var(--sf-color-info) l c h / 0.12)` | Light info tint for informational note backgrounds. |
| `--sf-color-info-tint` | PUBLIC | consumption | color | `oklch(from var(--sf-color-info) l c h / 0.05)` | Near-transparent info tint (5% alpha) for hover-state washes and the most subtle backgrounds. |
| `--sf-color-inset` | PUBLIC | consumption | color | `oklch(from var(--sf-color-base) calc(l - 0.02) c h)` | Slightly inset surface for nested areas — inputs, code blocks, sidebar panels. |
| `--sf-color-inverse` | PUBLIC | consumption | color | `oklch(from var(--sf-color-base) calc(1 - l) c h)` | Inverted (dark-on-light / light-on-dark) surface for high-contrast banners or tooltips. |
@@ -212,17 +212,17 @@ and a short description. The machine-readable companion (with all columns) is
| `--sf-color-neutral` | PUBLIC | consumption | color | `light-dark(var(--sf-color-neutral-source-light), var(--sf-color-neutral-source-dark, oklch(from var(--sf-color-neutral-source-light) clamp(0.65, calc(0.95 - l * 0.5), 0.88) calc(c * 0.9) h)))` | Neutral interactive color for low-emphasis actions and tertiary buttons. |
| `--sf-color-neutral--active` | PUBLIC | consumption | color | `var(--sf-color-neutral-xdark)` | Neutral color at pressed/active brightness. |
| `--sf-color-neutral--hover` | PUBLIC | consumption | color | `var(--sf-color-neutral-darker)` | Neutral color at hover brightness. |
-| `--sf-color-neutral-100` | PUBLIC | consumption | color | `color-mix(in oklab, var(--sf-color-neutral) var(--sf-palette-mix-100), var(--sf-color-surface))` | Very light neutral shade. |
-| `--sf-color-neutral-200` | PUBLIC | consumption | color | `color-mix(in oklab, var(--sf-color-neutral) var(--sf-palette-mix-200), var(--sf-color-surface))` | Light neutral shade at step 200 on the numeric scale. Subtle fills and outlined badge backgrounds. |
-| `--sf-color-neutral-300` | PUBLIC | consumption | color | `color-mix(in oklab, var(--sf-color-neutral) var(--sf-palette-mix-300), var(--sf-color-surface))` | Light-mid neutral shade. |
-| `--sf-color-neutral-400` | PUBLIC | consumption | color | `color-mix(in oklab, var(--sf-color-neutral) var(--sf-palette-mix-400), var(--sf-color-surface))` | Mid-light neutral shade. |
-| `--sf-color-neutral-50` | PUBLIC | consumption | color | `color-mix(in oklab, var(--sf-color-neutral) var(--sf-palette-mix-50), var(--sf-color-surface))` | Lightest neutral tint. |
+| `--sf-color-neutral-100` | PUBLIC | consumption | color | `oklch(from var(--sf-color-neutral) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.88)) calc(c * 0.3) h)` | Very light neutral shade. |
+| `--sf-color-neutral-200` | PUBLIC | consumption | color | `oklch(from var(--sf-color-neutral) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.72)) calc(c * 0.5) h)` | Light neutral shade at step 200 on the numeric scale. Subtle fills and outlined badge backgrounds. |
+| `--sf-color-neutral-300` | PUBLIC | consumption | color | `oklch(from var(--sf-color-neutral) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.52)) calc(c * 0.72) h)` | Light-mid neutral shade. |
+| `--sf-color-neutral-400` | PUBLIC | consumption | color | `oklch(from var(--sf-color-neutral) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.3)) calc(c * 0.88) h)` | Mid-light neutral shade. |
+| `--sf-color-neutral-50` | PUBLIC | consumption | color | `oklch(from var(--sf-color-neutral) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.95)) calc(c * 0.2) h)` | Lightest neutral tint. |
| `--sf-color-neutral-500` | PUBLIC | consumption | color | `var(--sf-color-neutral)` | Mid neutral shade — the 'pure' hue. |
-| `--sf-color-neutral-600` | PUBLIC | consumption | color | `color-mix(in oklab, var(--sf-color-neutral) var(--sf-palette-mix-600), var(--sf-color-text))` | Mid-dark neutral shade. |
-| `--sf-color-neutral-700` | PUBLIC | consumption | color | `color-mix(in oklab, var(--sf-color-neutral) var(--sf-palette-mix-700), var(--sf-color-text))` | Dark neutral shade. |
-| `--sf-color-neutral-800` | PUBLIC | consumption | color | `color-mix(in oklab, var(--sf-color-neutral) var(--sf-palette-mix-800), var(--sf-color-text))` | Very dark neutral shade. |
-| `--sf-color-neutral-900` | PUBLIC | consumption | color | `color-mix(in oklab, var(--sf-color-neutral) var(--sf-palette-mix-900), var(--sf-color-text))` | Near-black neutral shade at step 900 on the numeric scale. Extreme contrast on light surfaces. |
-| `--sf-color-neutral-950` | PUBLIC | consumption | color | `color-mix(in oklab, var(--sf-color-neutral) var(--sf-palette-mix-950), var(--sf-color-text))` | Darkest neutral tint. |
+| `--sf-color-neutral-600` | PUBLIC | consumption | color | `oklch(from var(--sf-color-neutral) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.16)) calc(c * 0.96) h)` | Mid-dark neutral shade. |
+| `--sf-color-neutral-700` | PUBLIC | consumption | color | `oklch(from var(--sf-color-neutral) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.36)) calc(c * 0.88) h)` | Dark neutral shade. |
+| `--sf-color-neutral-800` | PUBLIC | consumption | color | `oklch(from var(--sf-color-neutral) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.58)) calc(c * 0.72) h)` | Very dark neutral shade. |
+| `--sf-color-neutral-900` | PUBLIC | consumption | color | `oklch(from var(--sf-color-neutral) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.78)) calc(c * 0.52) h)` | Near-black neutral shade at step 900 on the numeric scale. Extreme contrast on light surfaces. |
+| `--sf-color-neutral-950` | PUBLIC | consumption | color | `oklch(from var(--sf-color-neutral) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.9)) calc(c * 0.38) h)` | Darkest neutral tint. |
| `--sf-color-neutral-a10` | PUBLIC | consumption | color | `oklch(from var(--sf-color-neutral) l c h / 0.10)` | 10% opacity neutral tint. |
| `--sf-color-neutral-a30` | PUBLIC | consumption | color | `oklch(from var(--sf-color-neutral) l c h / 0.30)` | 30% opacity neutral tint. |
| `--sf-color-neutral-a5` | PUBLIC | consumption | color | `oklch(from var(--sf-color-neutral) l c h / 0.05)` | 5% opacity neutral tint. |
@@ -243,17 +243,17 @@ and a short description. The machine-readable companion (with all columns) is
| `--sf-color-primary` | PUBLIC | consumption | color | `light-dark(var(--sf-color-primary-source-light), var(--sf-color-primary-source-dark, oklch(from var(--sf-color-primary-source-light) clamp(0.65, calc(0.95 - l * 0.5), 0.88) calc(c * 0.9) h)))` | Primary brand palette color. Independent from --sf-color-action by default; override either source token to couple or separate brand and action roles. |
| `--sf-color-primary--active` | PUBLIC | consumption | color | `var(--sf-color-primary-xdark)` | Primary color at pressed/active brightness. |
| `--sf-color-primary--hover` | PUBLIC | consumption | color | `var(--sf-color-primary-darker)` | Primary color at hover brightness. |
-| `--sf-color-primary-100` | PUBLIC | consumption | color | `color-mix(in oklab, var(--sf-color-primary) var(--sf-palette-mix-100), var(--sf-color-surface))` | Very light primary shade. |
-| `--sf-color-primary-200` | PUBLIC | consumption | color | `color-mix(in oklab, var(--sf-color-primary) var(--sf-palette-mix-200), var(--sf-color-surface))` | Light primary shade at step 200 on the numeric scale. Subtle fills and outlined badge backgrounds. |
-| `--sf-color-primary-300` | PUBLIC | consumption | color | `color-mix(in oklab, var(--sf-color-primary) var(--sf-palette-mix-300), var(--sf-color-surface))` | Light-mid primary shade. |
-| `--sf-color-primary-400` | PUBLIC | consumption | color | `color-mix(in oklab, var(--sf-color-primary) var(--sf-palette-mix-400), var(--sf-color-surface))` | Mid-light primary shade. |
-| `--sf-color-primary-50` | PUBLIC | consumption | color | `color-mix(in oklab, var(--sf-color-primary) var(--sf-palette-mix-50), var(--sf-color-surface))` | Lightest primary tint. |
+| `--sf-color-primary-100` | PUBLIC | consumption | color | `oklch(from var(--sf-color-primary) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.88)) calc(c * 0.3) h)` | Very light primary shade. |
+| `--sf-color-primary-200` | PUBLIC | consumption | color | `oklch(from var(--sf-color-primary) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.72)) calc(c * 0.5) h)` | Light primary shade at step 200 on the numeric scale. Subtle fills and outlined badge backgrounds. |
+| `--sf-color-primary-300` | PUBLIC | consumption | color | `oklch(from var(--sf-color-primary) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.52)) calc(c * 0.72) h)` | Light-mid primary shade. |
+| `--sf-color-primary-400` | PUBLIC | consumption | color | `oklch(from var(--sf-color-primary) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.3)) calc(c * 0.88) h)` | Mid-light primary shade. |
+| `--sf-color-primary-50` | PUBLIC | consumption | color | `oklch(from var(--sf-color-primary) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.95)) calc(c * 0.2) h)` | Lightest primary tint. |
| `--sf-color-primary-500` | PUBLIC | consumption | color | `var(--sf-color-primary)` | Mid primary shade — the 'pure' hue. |
-| `--sf-color-primary-600` | PUBLIC | consumption | color | `color-mix(in oklab, var(--sf-color-primary) var(--sf-palette-mix-600), var(--sf-color-text))` | Mid-dark primary shade. |
-| `--sf-color-primary-700` | PUBLIC | consumption | color | `color-mix(in oklab, var(--sf-color-primary) var(--sf-palette-mix-700), var(--sf-color-text))` | Dark primary shade. |
-| `--sf-color-primary-800` | PUBLIC | consumption | color | `color-mix(in oklab, var(--sf-color-primary) var(--sf-palette-mix-800), var(--sf-color-text))` | Very dark primary shade. |
-| `--sf-color-primary-900` | PUBLIC | consumption | color | `color-mix(in oklab, var(--sf-color-primary) var(--sf-palette-mix-900), var(--sf-color-text))` | Near-black primary shade at step 900 on the numeric scale. Extreme contrast on light surfaces. |
-| `--sf-color-primary-950` | PUBLIC | consumption | color | `color-mix(in oklab, var(--sf-color-primary) var(--sf-palette-mix-950), var(--sf-color-text))` | Darkest primary tint. |
+| `--sf-color-primary-600` | PUBLIC | consumption | color | `oklch(from var(--sf-color-primary) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.16)) calc(c * 0.96) h)` | Mid-dark primary shade. |
+| `--sf-color-primary-700` | PUBLIC | consumption | color | `oklch(from var(--sf-color-primary) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.36)) calc(c * 0.88) h)` | Dark primary shade. |
+| `--sf-color-primary-800` | PUBLIC | consumption | color | `oklch(from var(--sf-color-primary) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.58)) calc(c * 0.72) h)` | Very dark primary shade. |
+| `--sf-color-primary-900` | PUBLIC | consumption | color | `oklch(from var(--sf-color-primary) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.78)) calc(c * 0.52) h)` | Near-black primary shade at step 900 on the numeric scale. Extreme contrast on light surfaces. |
+| `--sf-color-primary-950` | PUBLIC | consumption | color | `oklch(from var(--sf-color-primary) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.9)) calc(c * 0.38) h)` | Darkest primary tint. |
| `--sf-color-primary-a10` | PUBLIC | consumption | color | `oklch(from var(--sf-color-primary) l c h / 0.10)` | 10% opacity primary tint. |
| `--sf-color-primary-a30` | PUBLIC | consumption | color | `oklch(from var(--sf-color-primary) l c h / 0.30)` | 30% opacity primary tint. |
| `--sf-color-primary-a5` | PUBLIC | consumption | color | `oklch(from var(--sf-color-primary) l c h / 0.05)` | 5% opacity primary tint. |
@@ -275,17 +275,17 @@ and a short description. The machine-readable companion (with all columns) is
| `--sf-color-secondary` | PUBLIC | consumption | color | `light-dark(var(--sf-color-secondary-source-light), var(--sf-color-secondary-source-dark, oklch(from var(--sf-color-secondary-source-light) clamp(0.65, calc(0.95 - l * 0.5), 0.88) calc(c * 0.9) h)))` | Raw secondary palette token for the secondary brand color. |
| `--sf-color-secondary--active` | PUBLIC | consumption | color | `var(--sf-color-secondary-xdark)` | Secondary color at pressed/active brightness. |
| `--sf-color-secondary--hover` | PUBLIC | consumption | color | `var(--sf-color-secondary-darker)` | Secondary color at hover brightness. |
-| `--sf-color-secondary-100` | PUBLIC | consumption | color | `color-mix(in oklab, var(--sf-color-secondary) var(--sf-palette-mix-100), var(--sf-color-surface))` | Very light secondary shade. |
-| `--sf-color-secondary-200` | PUBLIC | consumption | color | `color-mix(in oklab, var(--sf-color-secondary) var(--sf-palette-mix-200), var(--sf-color-surface))` | Light secondary shade at step 200 on the numeric scale. Subtle fills and outlined badge backgrounds. |
-| `--sf-color-secondary-300` | PUBLIC | consumption | color | `color-mix(in oklab, var(--sf-color-secondary) var(--sf-palette-mix-300), var(--sf-color-surface))` | Light-mid secondary shade. |
-| `--sf-color-secondary-400` | PUBLIC | consumption | color | `color-mix(in oklab, var(--sf-color-secondary) var(--sf-palette-mix-400), var(--sf-color-surface))` | Mid-light secondary shade. |
-| `--sf-color-secondary-50` | PUBLIC | consumption | color | `color-mix(in oklab, var(--sf-color-secondary) var(--sf-palette-mix-50), var(--sf-color-surface))` | Lightest secondary tint. |
+| `--sf-color-secondary-100` | PUBLIC | consumption | color | `oklch(from var(--sf-color-secondary) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.88)) calc(c * 0.3) h)` | Very light secondary shade. |
+| `--sf-color-secondary-200` | PUBLIC | consumption | color | `oklch(from var(--sf-color-secondary) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.72)) calc(c * 0.5) h)` | Light secondary shade at step 200 on the numeric scale. Subtle fills and outlined badge backgrounds. |
+| `--sf-color-secondary-300` | PUBLIC | consumption | color | `oklch(from var(--sf-color-secondary) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.52)) calc(c * 0.72) h)` | Light-mid secondary shade. |
+| `--sf-color-secondary-400` | PUBLIC | consumption | color | `oklch(from var(--sf-color-secondary) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.3)) calc(c * 0.88) h)` | Mid-light secondary shade. |
+| `--sf-color-secondary-50` | PUBLIC | consumption | color | `oklch(from var(--sf-color-secondary) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.95)) calc(c * 0.2) h)` | Lightest secondary tint. |
| `--sf-color-secondary-500` | PUBLIC | consumption | color | `var(--sf-color-secondary)` | Mid secondary shade — the 'pure' hue. |
-| `--sf-color-secondary-600` | PUBLIC | consumption | color | `color-mix(in oklab, var(--sf-color-secondary) var(--sf-palette-mix-600), var(--sf-color-text))` | Mid-dark secondary shade. |
-| `--sf-color-secondary-700` | PUBLIC | consumption | color | `color-mix(in oklab, var(--sf-color-secondary) var(--sf-palette-mix-700), var(--sf-color-text))` | Dark secondary shade. |
-| `--sf-color-secondary-800` | PUBLIC | consumption | color | `color-mix(in oklab, var(--sf-color-secondary) var(--sf-palette-mix-800), var(--sf-color-text))` | Very dark secondary shade. |
-| `--sf-color-secondary-900` | PUBLIC | consumption | color | `color-mix(in oklab, var(--sf-color-secondary) var(--sf-palette-mix-900), var(--sf-color-text))` | Near-black secondary shade at step 900 on the numeric scale. Extreme contrast on light surfaces. |
-| `--sf-color-secondary-950` | PUBLIC | consumption | color | `color-mix(in oklab, var(--sf-color-secondary) var(--sf-palette-mix-950), var(--sf-color-text))` | Darkest secondary tint. |
+| `--sf-color-secondary-600` | PUBLIC | consumption | color | `oklch(from var(--sf-color-secondary) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.16)) calc(c * 0.96) h)` | Mid-dark secondary shade. |
+| `--sf-color-secondary-700` | PUBLIC | consumption | color | `oklch(from var(--sf-color-secondary) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.36)) calc(c * 0.88) h)` | Dark secondary shade. |
+| `--sf-color-secondary-800` | PUBLIC | consumption | color | `oklch(from var(--sf-color-secondary) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.58)) calc(c * 0.72) h)` | Very dark secondary shade. |
+| `--sf-color-secondary-900` | PUBLIC | consumption | color | `oklch(from var(--sf-color-secondary) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.78)) calc(c * 0.52) h)` | Near-black secondary shade at step 900 on the numeric scale. Extreme contrast on light surfaces. |
+| `--sf-color-secondary-950` | PUBLIC | consumption | color | `oklch(from var(--sf-color-secondary) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.9)) calc(c * 0.38) h)` | Darkest secondary tint. |
| `--sf-color-secondary-a10` | PUBLIC | consumption | color | `oklch(from var(--sf-color-secondary) l c h / 0.10)` | 10% opacity secondary tint. |
| `--sf-color-secondary-a30` | PUBLIC | consumption | color | `oklch(from var(--sf-color-secondary) l c h / 0.30)` | 30% opacity secondary tint. |
| `--sf-color-secondary-a5` | PUBLIC | consumption | color | `oklch(from var(--sf-color-secondary) l c h / 0.05)` | 5% opacity secondary tint. |
@@ -317,17 +317,17 @@ and a short description. The machine-readable companion (with all columns) is
| `--sf-color-tertiary` | PUBLIC | consumption | color | `light-dark(var(--sf-color-tertiary-source-light), var(--sf-color-tertiary-source-dark, oklch(from var(--sf-color-tertiary-source-light) clamp(0.65, calc(0.95 - l * 0.5), 0.88) calc(c * 0.9) h)))` | Raw tertiary palette token for the third brand color. |
| `--sf-color-tertiary--active` | PUBLIC | consumption | color | `var(--sf-color-tertiary-xdark)` | Tertiary color at pressed/active brightness. |
| `--sf-color-tertiary--hover` | PUBLIC | consumption | color | `var(--sf-color-tertiary-darker)` | Tertiary color at hover brightness. |
-| `--sf-color-tertiary-100` | PUBLIC | consumption | color | `color-mix(in oklab, var(--sf-color-tertiary) var(--sf-palette-mix-100), var(--sf-color-surface))` | Very light tertiary shade. |
-| `--sf-color-tertiary-200` | PUBLIC | consumption | color | `color-mix(in oklab, var(--sf-color-tertiary) var(--sf-palette-mix-200), var(--sf-color-surface))` | Light tertiary shade at step 200 on the numeric scale. Subtle fills and outlined badge backgrounds. |
-| `--sf-color-tertiary-300` | PUBLIC | consumption | color | `color-mix(in oklab, var(--sf-color-tertiary) var(--sf-palette-mix-300), var(--sf-color-surface))` | Light-mid tertiary shade. |
-| `--sf-color-tertiary-400` | PUBLIC | consumption | color | `color-mix(in oklab, var(--sf-color-tertiary) var(--sf-palette-mix-400), var(--sf-color-surface))` | Mid-light tertiary shade. |
-| `--sf-color-tertiary-50` | PUBLIC | consumption | color | `color-mix(in oklab, var(--sf-color-tertiary) var(--sf-palette-mix-50), var(--sf-color-surface))` | Lightest tertiary tint. |
+| `--sf-color-tertiary-100` | PUBLIC | consumption | color | `oklch(from var(--sf-color-tertiary) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.88)) calc(c * 0.3) h)` | Very light tertiary shade. |
+| `--sf-color-tertiary-200` | PUBLIC | consumption | color | `oklch(from var(--sf-color-tertiary) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.72)) calc(c * 0.5) h)` | Light tertiary shade at step 200 on the numeric scale. Subtle fills and outlined badge backgrounds. |
+| `--sf-color-tertiary-300` | PUBLIC | consumption | color | `oklch(from var(--sf-color-tertiary) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.52)) calc(c * 0.72) h)` | Light-mid tertiary shade. |
+| `--sf-color-tertiary-400` | PUBLIC | consumption | color | `oklch(from var(--sf-color-tertiary) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.3)) calc(c * 0.88) h)` | Mid-light tertiary shade. |
+| `--sf-color-tertiary-50` | PUBLIC | consumption | color | `oklch(from var(--sf-color-tertiary) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.95)) calc(c * 0.2) h)` | Lightest tertiary tint. |
| `--sf-color-tertiary-500` | PUBLIC | consumption | color | `var(--sf-color-tertiary)` | Mid tertiary shade — the 'pure' hue. |
-| `--sf-color-tertiary-600` | PUBLIC | consumption | color | `color-mix(in oklab, var(--sf-color-tertiary) var(--sf-palette-mix-600), var(--sf-color-text))` | Mid-dark tertiary shade. |
-| `--sf-color-tertiary-700` | PUBLIC | consumption | color | `color-mix(in oklab, var(--sf-color-tertiary) var(--sf-palette-mix-700), var(--sf-color-text))` | Dark tertiary shade. |
-| `--sf-color-tertiary-800` | PUBLIC | consumption | color | `color-mix(in oklab, var(--sf-color-tertiary) var(--sf-palette-mix-800), var(--sf-color-text))` | Very dark tertiary shade. |
-| `--sf-color-tertiary-900` | PUBLIC | consumption | color | `color-mix(in oklab, var(--sf-color-tertiary) var(--sf-palette-mix-900), var(--sf-color-text))` | Near-black tertiary shade at step 900 on the numeric scale. Extreme contrast on light surfaces. |
-| `--sf-color-tertiary-950` | PUBLIC | consumption | color | `color-mix(in oklab, var(--sf-color-tertiary) var(--sf-palette-mix-950), var(--sf-color-text))` | Darkest tertiary tint. |
+| `--sf-color-tertiary-600` | PUBLIC | consumption | color | `oklch(from var(--sf-color-tertiary) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.16)) calc(c * 0.96) h)` | Mid-dark tertiary shade. |
+| `--sf-color-tertiary-700` | PUBLIC | consumption | color | `oklch(from var(--sf-color-tertiary) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.36)) calc(c * 0.88) h)` | Dark tertiary shade. |
+| `--sf-color-tertiary-800` | PUBLIC | consumption | color | `oklch(from var(--sf-color-tertiary) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.58)) calc(c * 0.72) h)` | Very dark tertiary shade. |
+| `--sf-color-tertiary-900` | PUBLIC | consumption | color | `oklch(from var(--sf-color-tertiary) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.78)) calc(c * 0.52) h)` | Near-black tertiary shade at step 900 on the numeric scale. Extreme contrast on light surfaces. |
+| `--sf-color-tertiary-950` | PUBLIC | consumption | color | `oklch(from var(--sf-color-tertiary) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.9)) calc(c * 0.38) h)` | Darkest tertiary tint. |
| `--sf-color-tertiary-a10` | PUBLIC | consumption | color | `oklch(from var(--sf-color-tertiary) l c h / 0.10)` | 10% opacity tertiary tint. |
| `--sf-color-tertiary-a30` | PUBLIC | consumption | color | `oklch(from var(--sf-color-tertiary) l c h / 0.30)` | 30% opacity tertiary tint. |
| `--sf-color-tertiary-a5` | PUBLIC | consumption | color | `oklch(from var(--sf-color-tertiary) l c h / 0.05)` | 5% opacity tertiary tint. |
@@ -518,16 +518,8 @@ and a short description. The machine-readable companion (with all columns) is
| `--sf-opacity-disabled` | PUBLIC | knob | opacity | `0.45` | Opacity for disabled UI elements (typically 0.4–0.5). |
| `--sf-opacity-muted` | PUBLIC | knob | opacity | `0.5` | General de-emphasis opacity for secondary content, ghost elements, or non-primary layers. Default 0.5. |
| `--sf-optical-sizing` | PUBLIC-ADVANCED | knob | optical | `auto` | CSS font-optical-sizing value. 'auto' lets the browser optimize letterforms for the rendered size. Set 'none' to disable optical sizing. |
-| `--sf-palette-mix-100` | PUBLIC-ADVANCED | knob | palette | `8%` | Color-mix percentage for palette step 100. Controls blend depth toward the surface. Default 8%. |
-| `--sf-palette-mix-200` | PUBLIC-ADVANCED | knob | palette | `20%` | Color-mix percentage for palette step 200. Controls blend depth toward the surface. Default 20%. |
-| `--sf-palette-mix-300` | PUBLIC-ADVANCED | knob | palette | `40%` | Color-mix percentage for palette step 300. Controls blend depth toward the surface. Default 40%. |
-| `--sf-palette-mix-400` | PUBLIC-ADVANCED | knob | palette | `65%` | Color-mix percentage for palette step 400 (near-mid tint). Controls blend depth. Default 65%. |
-| `--sf-palette-mix-50` | PUBLIC-ADVANCED | knob | palette | `4%` | Color-mix percentage for the lightest palette step (50). Controls how far step 50 blends toward the surface. Default 4%. |
-| `--sf-palette-mix-600` | PUBLIC-ADVANCED | knob | palette | `82%` | Color-mix percentage for palette step 600 (near-mid shade). Controls blend depth toward the text color. Default 82%. |
-| `--sf-palette-mix-700` | PUBLIC-ADVANCED | knob | palette | `62%` | Color-mix percentage for palette step 700. Controls blend depth toward the text color. Default 62%. |
-| `--sf-palette-mix-800` | PUBLIC-ADVANCED | knob | palette | `38%` | Color-mix percentage for palette step 800 (dark shade). Controls blend depth toward text. Default 38%. |
-| `--sf-palette-mix-900` | PUBLIC-ADVANCED | knob | palette | `18%` | Color-mix percentage for palette step 900 (very dark shade). Controls blend depth toward text. Default 18%. |
-| `--sf-palette-mix-950` | PUBLIC-ADVANCED | knob | palette | `8%` | Color-mix percentage for the darkest palette step (950). Controls how far step 950 blends toward the text color. Default 8%. |
+| `--sf-palette-shade-l` | PUBLIC-ADVANCED | knob | palette | `0.1` | Absolute OKLCH lightness the shade steps (600-950) pull toward; sets how dark the darkest palette step can reach. Default 0.1. |
+| `--sf-palette-tint-l` | PUBLIC-ADVANCED | knob | palette | `0.97` | Absolute OKLCH lightness the tint steps (50-400) pull toward; sets how light the lightest palette step can reach. Default 0.97. |
| `--sf-print-base-size` | PUBLIC-ADVANCED | knob | print | `11pt` | Base font size for @media print stylesheets. Default 11pt. Override on :root to adjust printed body text size. |
| `--sf-print-page-margin` | PUBLIC-ADVANCED | knob | print | `2cm` | Page margin for printed output. Default 2cm. Maps to @page margin. |
| `--sf-print-page-size` | PUBLIC-ADVANCED | knob | print | `a4` | Target paper size for printing. Default 'a4'. Maps to @page size. Common values: a4, letter, legal. |
diff --git a/docs/llm-guide.md b/docs/llm-guide.md
index 18e89e55..6dc7d005 100644
--- a/docs/llm-guide.md
+++ b/docs/llm-guide.md
@@ -1,6 +1,6 @@
# Slashed Framework — LLM Reference Guide
-> Version: **0.7.19** · Tokens: **754** · Prefix: `--sf-`
+> Version: **0.7.19** · Tokens: **746** · Prefix: `--sf-`
---
@@ -407,21 +407,13 @@ Examples: `--sf-color-danger-subtle`, `--sf-color-warning-strong`.
/* Alpha variants */
--sf-color-{family}-a5 / -a10 / -a30 / -a50 / -a80
-/* PUBLIC-ADVANCED: ramp shape for the five mid-tone brand families */
---sf-palette-mix-50: 4%; /* How far step 50 blends toward surface */
---sf-palette-mix-100: 8%; /* … */
---sf-palette-mix-200: 20%;
---sf-palette-mix-300: 40%;
---sf-palette-mix-400: 65%;
-/* step 500 is the source color itself; no separate mix knob exists */
---sf-palette-mix-600: 82%;
---sf-palette-mix-700: 62%;
---sf-palette-mix-800: 38%;
---sf-palette-mix-900: 18%;
---sf-palette-mix-950: 8%; /* deepest step */
+/* PUBLIC-ADVANCED: ramp lightness anchors for the five mid-tone families */
+--sf-palette-tint-l: 0.97; /* absolute OKLCH L the tint steps (50–400) reach toward */
+--sf-palette-shade-l: 0.1; /* absolute OKLCH L the shade steps (600–950) reach toward */
+/* step 500 is the source color itself; per-step pull + chroma taper are baked in */
```
-All six families honour the 50=Lightest / 950=Darkest contract. The five mid-tone brand families (`primary`, `secondary`, `tertiary`, `action`, `neutral`) are generated with `color-mix` anchored to `--sf-color-surface` and `--sf-color-text` as the two poles. **`base` uses fixed OKLCH lightness steps** (0.97 at step 50 → 0.06 at step 950) preserving the source hue and chroma. The `--sf-palette-mix-*` knobs do not affect `base`. Because it is an absolute scale, `base` deliberately has **no relative aliases** (`-lighter` / `-darker` / `-xlight` / `-superlight` / …) — those exist only on the five mid-tone brand families, where the source sits in the middle of the ramp. For mode-adaptive surface adjustments on base-coloured elements use `--sf-color-inset` / `--sf-color-raised` / `--sf-color-bg` or the `--sf-color-base--hover` / `--sf-color-base--active` tokens (which use relative oklch offsets and adapt to both modes automatically).
+All six families honour the 50=Lightest / 950=Darkest contract. The five mid-tone brand families (`primary`, `secondary`, `tertiary`, `action`, `neutral`) are generated in OKLCH by pulling the family colour's own lightness a fixed fraction toward an **absolute** target — `--sf-palette-tint-l` for tints (50–400), `--sf-palette-shade-l` for shades (600–950) — clamped (`max()`/`min()`) so a tint can never end darker than the base nor a shade lighter. Because the poles are absolute (not the theme's `--sf-color-surface` / `--sf-color-text`), the ramp stays monotonic light→dark for **any** source colour and any anchor configuration — it cannot fold into a "U". **`base` uses fixed OKLCH lightness steps** (0.97 at step 50 → 0.06 at step 950) preserving the source hue and chroma; it does not read the `--sf-palette-*-l` knobs. Because it is an absolute scale, `base` deliberately has **no relative aliases** (`-lighter` / `-darker` / `-xlight` / `-superlight` / …) — those exist only on the five mid-tone brand families, where the source sits in the middle of the ramp. For mode-adaptive surface adjustments on base-coloured elements use `--sf-color-inset` / `--sf-color-raised` / `--sf-color-bg` or the `--sf-color-base--hover` / `--sf-color-base--active` tokens (which use relative oklch offsets and adapt to both modes automatically).
Status families (success/warning/info/danger) have **no numeric scale** — use their triplets.
@@ -1310,15 +1302,13 @@ Example — if your primary brand color has L ≈ 0.6 and you prefer white text:
Each step up the type scale subtracts `step-index × taper` from that step's default line-height.
-### 11.5 Palette mix percentages
+### 11.5 Palette ramp lightness anchors
```css
-/* Shape of the 50–950 numeric palette ramp */
---sf-palette-mix-50: 4% /* how far step 50 blends toward surface */
---sf-palette-mix-100: 8%
---sf-palette-mix-200: 20%
---sf-palette-mix-300: 40%
-/* … similarly for all steps */
+/* Absolute OKLCH lightness poles the 50–950 numeric ramp reaches toward */
+--sf-palette-tint-l: 0.97 /* how light the lightest step (50) can reach */
+--sf-palette-shade-l: 0.1 /* how dark the darkest step (950) can reach */
+/* per-step pull fraction + chroma taper are baked into each family */
```
### 11.6 Scroll and mask
diff --git a/docs/registry.json b/docs/registry.json
index 63394ad6..44f78c39 100644
--- a/docs/registry.json
+++ b/docs/registry.json
@@ -19,7 +19,7 @@
"optional/utilities.css"
],
"counts": {
- "tokens": 754,
+ "tokens": 746,
"sf_classes": 289,
"is_classes": 28
}
@@ -562,16 +562,8 @@
"--sf-opacity-muted",
"--sf-optical-sizing",
"--sf-overlap-pull",
- "--sf-palette-mix-100",
- "--sf-palette-mix-200",
- "--sf-palette-mix-300",
- "--sf-palette-mix-400",
- "--sf-palette-mix-50",
- "--sf-palette-mix-600",
- "--sf-palette-mix-700",
- "--sf-palette-mix-800",
- "--sf-palette-mix-900",
- "--sf-palette-mix-950",
+ "--sf-palette-shade-l",
+ "--sf-palette-tint-l",
"--sf-print-base-size",
"--sf-print-page-margin",
"--sf-print-page-size",
diff --git a/docs/token-annotations.json b/docs/token-annotations.json
index ba6aef20..e2ae2770 100644
--- a/docs/token-annotations.json
+++ b/docs/token-annotations.json
@@ -804,16 +804,8 @@
"--sf-density": "Compact ↔ comfortable dial for interactive control geometry (the --sf-size-* rung ladder). Default 1; below 1 packs controls tighter (dashboards, data tables), above 1 loosens them. A deliberate design/user choice, not a viewport response — orthogonal to --sf-space-scale (whitespace) and --sf-section-scale (section rhythm). The --sf-touch-target accessibility floor is independent, so a small value can't shrink native controls below the WCAG target. Like the other global multipliers it is a :root dial (the size ladder is computed at :root and inherits, so nested overrides don't retroactively rescale it).",
"--sf-text-scale": "Global type-size multiplier. Scales the entire fluid type scale. 1.08 gives noticeably larger body text without breaking layout.",
"--sf-text-display-scale": "Multiplier applied only to the display/hero type scale (--sf-text-display-*). Tune headline impact independently of body text.",
- "--sf-palette-mix-50": "Color-mix percentage for the lightest palette step (50). Controls how far step 50 blends toward the surface. Default 4%.",
- "--sf-palette-mix-100": "Color-mix percentage for palette step 100. Controls blend depth toward the surface. Default 8%.",
- "--sf-palette-mix-200": "Color-mix percentage for palette step 200. Controls blend depth toward the surface. Default 20%.",
- "--sf-palette-mix-300": "Color-mix percentage for palette step 300. Controls blend depth toward the surface. Default 40%.",
- "--sf-palette-mix-400": "Color-mix percentage for palette step 400 (near-mid tint). Controls blend depth. Default 65%.",
- "--sf-palette-mix-600": "Color-mix percentage for palette step 600 (near-mid shade). Controls blend depth toward the text color. Default 82%.",
- "--sf-palette-mix-700": "Color-mix percentage for palette step 700. Controls blend depth toward the text color. Default 62%.",
- "--sf-palette-mix-800": "Color-mix percentage for palette step 800 (dark shade). Controls blend depth toward text. Default 38%.",
- "--sf-palette-mix-900": "Color-mix percentage for palette step 900 (very dark shade). Controls blend depth toward text. Default 18%.",
- "--sf-palette-mix-950": "Color-mix percentage for the darkest palette step (950). Controls how far step 950 blends toward the text color. Default 8%.",
+ "--sf-palette-tint-l": "Absolute OKLCH lightness the tint steps (50-400) pull toward; sets how light the lightest palette step can reach. Default 0.97.",
+ "--sf-palette-shade-l": "Absolute OKLCH lightness the shade steps (600-950) pull toward; sets how dark the darkest palette step can reach. Default 0.1.",
"--sf-print-base-size": "Base font size for @media print stylesheets. Default 11pt. Override on :root to adjust printed body text size.",
"--sf-print-page-margin": "Page margin for printed output. Default 2cm. Maps to @page margin.",
"--sf-print-page-size": "Target paper size for printing. Default 'a4'. Maps to @page size. Common values: a4, letter, legal.",
diff --git a/docs/token-index.json b/docs/token-index.json
index 360cae07..498f8ff4 100644
--- a/docs/token-index.json
+++ b/docs/token-index.json
@@ -8,14 +8,14 @@
"optional/tokens.components.css"
],
"counts": {
- "tokens": 754,
+ "tokens": 746,
"by_tier": {
"PUBLIC": 693,
- "PUBLIC-ADVANCED": 60,
+ "PUBLIC-ADVANCED": 52,
"INTERNAL": 1
},
"by_role": {
- "knob": 265,
+ "knob": 257,
"consumption": 489
}
}
@@ -883,7 +883,7 @@
"files": [
"core/tokens.css"
],
- "value": "color-mix(in oklab, var(--sf-color-action) var(--sf-palette-mix-100), var(--sf-color-surface))"
+ "value": "oklch(from var(--sf-color-action) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.88)) calc(c * 0.3) h)"
},
"--sf-color-action-200": {
"tier": "PUBLIC",
@@ -891,7 +891,7 @@
"files": [
"core/tokens.css"
],
- "value": "color-mix(in oklab, var(--sf-color-action) var(--sf-palette-mix-200), var(--sf-color-surface))"
+ "value": "oklch(from var(--sf-color-action) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.72)) calc(c * 0.5) h)"
},
"--sf-color-action-300": {
"tier": "PUBLIC",
@@ -899,7 +899,7 @@
"files": [
"core/tokens.css"
],
- "value": "color-mix(in oklab, var(--sf-color-action) var(--sf-palette-mix-300), var(--sf-color-surface))"
+ "value": "oklch(from var(--sf-color-action) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.52)) calc(c * 0.72) h)"
},
"--sf-color-action-400": {
"tier": "PUBLIC",
@@ -907,7 +907,7 @@
"files": [
"core/tokens.css"
],
- "value": "color-mix(in oklab, var(--sf-color-action) var(--sf-palette-mix-400), var(--sf-color-surface))"
+ "value": "oklch(from var(--sf-color-action) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.3)) calc(c * 0.88) h)"
},
"--sf-color-action-50": {
"tier": "PUBLIC",
@@ -915,7 +915,7 @@
"files": [
"core/tokens.css"
],
- "value": "color-mix(in oklab, var(--sf-color-action) var(--sf-palette-mix-50), var(--sf-color-surface))"
+ "value": "oklch(from var(--sf-color-action) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.95)) calc(c * 0.2) h)"
},
"--sf-color-action-500": {
"tier": "PUBLIC",
@@ -931,7 +931,7 @@
"files": [
"core/tokens.css"
],
- "value": "color-mix(in oklab, var(--sf-color-action) var(--sf-palette-mix-600), var(--sf-color-text))"
+ "value": "oklch(from var(--sf-color-action) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.16)) calc(c * 0.96) h)"
},
"--sf-color-action-700": {
"tier": "PUBLIC",
@@ -939,7 +939,7 @@
"files": [
"core/tokens.css"
],
- "value": "color-mix(in oklab, var(--sf-color-action) var(--sf-palette-mix-700), var(--sf-color-text))"
+ "value": "oklch(from var(--sf-color-action) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.36)) calc(c * 0.88) h)"
},
"--sf-color-action-800": {
"tier": "PUBLIC",
@@ -947,7 +947,7 @@
"files": [
"core/tokens.css"
],
- "value": "color-mix(in oklab, var(--sf-color-action) var(--sf-palette-mix-800), var(--sf-color-text))"
+ "value": "oklch(from var(--sf-color-action) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.58)) calc(c * 0.72) h)"
},
"--sf-color-action-900": {
"tier": "PUBLIC",
@@ -955,7 +955,7 @@
"files": [
"core/tokens.css"
],
- "value": "color-mix(in oklab, var(--sf-color-action) var(--sf-palette-mix-900), var(--sf-color-text))"
+ "value": "oklch(from var(--sf-color-action) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.78)) calc(c * 0.52) h)"
},
"--sf-color-action-950": {
"tier": "PUBLIC",
@@ -963,7 +963,7 @@
"files": [
"core/tokens.css"
],
- "value": "color-mix(in oklab, var(--sf-color-action) var(--sf-palette-mix-950), var(--sf-color-text))"
+ "value": "oklch(from var(--sf-color-action) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.9)) calc(c * 0.38) h)"
},
"--sf-color-action-a10": {
"tier": "PUBLIC",
@@ -1451,7 +1451,7 @@
"files": [
"core/tokens.css"
],
- "value": "oklch(from var(--sf-color-danger) l c h / 0.1)"
+ "value": "oklch(from var(--sf-color-danger) l c h / 0.12)"
},
"--sf-color-danger-tint": {
"tier": "PUBLIC",
@@ -1523,7 +1523,7 @@
"files": [
"core/tokens.css"
],
- "value": "oklch(from var(--sf-color-info) l c h / 0.1)"
+ "value": "oklch(from var(--sf-color-info) l c h / 0.12)"
},
"--sf-color-info-tint": {
"tier": "PUBLIC",
@@ -1643,7 +1643,7 @@
"files": [
"core/tokens.css"
],
- "value": "color-mix(in oklab, var(--sf-color-neutral) var(--sf-palette-mix-100), var(--sf-color-surface))"
+ "value": "oklch(from var(--sf-color-neutral) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.88)) calc(c * 0.3) h)"
},
"--sf-color-neutral-200": {
"tier": "PUBLIC",
@@ -1651,7 +1651,7 @@
"files": [
"core/tokens.css"
],
- "value": "color-mix(in oklab, var(--sf-color-neutral) var(--sf-palette-mix-200), var(--sf-color-surface))"
+ "value": "oklch(from var(--sf-color-neutral) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.72)) calc(c * 0.5) h)"
},
"--sf-color-neutral-300": {
"tier": "PUBLIC",
@@ -1659,7 +1659,7 @@
"files": [
"core/tokens.css"
],
- "value": "color-mix(in oklab, var(--sf-color-neutral) var(--sf-palette-mix-300), var(--sf-color-surface))"
+ "value": "oklch(from var(--sf-color-neutral) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.52)) calc(c * 0.72) h)"
},
"--sf-color-neutral-400": {
"tier": "PUBLIC",
@@ -1667,7 +1667,7 @@
"files": [
"core/tokens.css"
],
- "value": "color-mix(in oklab, var(--sf-color-neutral) var(--sf-palette-mix-400), var(--sf-color-surface))"
+ "value": "oklch(from var(--sf-color-neutral) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.3)) calc(c * 0.88) h)"
},
"--sf-color-neutral-50": {
"tier": "PUBLIC",
@@ -1675,7 +1675,7 @@
"files": [
"core/tokens.css"
],
- "value": "color-mix(in oklab, var(--sf-color-neutral) var(--sf-palette-mix-50), var(--sf-color-surface))"
+ "value": "oklch(from var(--sf-color-neutral) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.95)) calc(c * 0.2) h)"
},
"--sf-color-neutral-500": {
"tier": "PUBLIC",
@@ -1691,7 +1691,7 @@
"files": [
"core/tokens.css"
],
- "value": "color-mix(in oklab, var(--sf-color-neutral) var(--sf-palette-mix-600), var(--sf-color-text))"
+ "value": "oklch(from var(--sf-color-neutral) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.16)) calc(c * 0.96) h)"
},
"--sf-color-neutral-700": {
"tier": "PUBLIC",
@@ -1699,7 +1699,7 @@
"files": [
"core/tokens.css"
],
- "value": "color-mix(in oklab, var(--sf-color-neutral) var(--sf-palette-mix-700), var(--sf-color-text))"
+ "value": "oklch(from var(--sf-color-neutral) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.36)) calc(c * 0.88) h)"
},
"--sf-color-neutral-800": {
"tier": "PUBLIC",
@@ -1707,7 +1707,7 @@
"files": [
"core/tokens.css"
],
- "value": "color-mix(in oklab, var(--sf-color-neutral) var(--sf-palette-mix-800), var(--sf-color-text))"
+ "value": "oklch(from var(--sf-color-neutral) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.58)) calc(c * 0.72) h)"
},
"--sf-color-neutral-900": {
"tier": "PUBLIC",
@@ -1715,7 +1715,7 @@
"files": [
"core/tokens.css"
],
- "value": "color-mix(in oklab, var(--sf-color-neutral) var(--sf-palette-mix-900), var(--sf-color-text))"
+ "value": "oklch(from var(--sf-color-neutral) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.78)) calc(c * 0.52) h)"
},
"--sf-color-neutral-950": {
"tier": "PUBLIC",
@@ -1723,7 +1723,7 @@
"files": [
"core/tokens.css"
],
- "value": "color-mix(in oklab, var(--sf-color-neutral) var(--sf-palette-mix-950), var(--sf-color-text))"
+ "value": "oklch(from var(--sf-color-neutral) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.9)) calc(c * 0.38) h)"
},
"--sf-color-neutral-a10": {
"tier": "PUBLIC",
@@ -1891,7 +1891,7 @@
"files": [
"core/tokens.css"
],
- "value": "color-mix(in oklab, var(--sf-color-primary) var(--sf-palette-mix-100), var(--sf-color-surface))"
+ "value": "oklch(from var(--sf-color-primary) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.88)) calc(c * 0.3) h)"
},
"--sf-color-primary-200": {
"tier": "PUBLIC",
@@ -1899,7 +1899,7 @@
"files": [
"core/tokens.css"
],
- "value": "color-mix(in oklab, var(--sf-color-primary) var(--sf-palette-mix-200), var(--sf-color-surface))"
+ "value": "oklch(from var(--sf-color-primary) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.72)) calc(c * 0.5) h)"
},
"--sf-color-primary-300": {
"tier": "PUBLIC",
@@ -1907,7 +1907,7 @@
"files": [
"core/tokens.css"
],
- "value": "color-mix(in oklab, var(--sf-color-primary) var(--sf-palette-mix-300), var(--sf-color-surface))"
+ "value": "oklch(from var(--sf-color-primary) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.52)) calc(c * 0.72) h)"
},
"--sf-color-primary-400": {
"tier": "PUBLIC",
@@ -1915,7 +1915,7 @@
"files": [
"core/tokens.css"
],
- "value": "color-mix(in oklab, var(--sf-color-primary) var(--sf-palette-mix-400), var(--sf-color-surface))"
+ "value": "oklch(from var(--sf-color-primary) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.3)) calc(c * 0.88) h)"
},
"--sf-color-primary-50": {
"tier": "PUBLIC",
@@ -1923,7 +1923,7 @@
"files": [
"core/tokens.css"
],
- "value": "color-mix(in oklab, var(--sf-color-primary) var(--sf-palette-mix-50), var(--sf-color-surface))"
+ "value": "oklch(from var(--sf-color-primary) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.95)) calc(c * 0.2) h)"
},
"--sf-color-primary-500": {
"tier": "PUBLIC",
@@ -1939,7 +1939,7 @@
"files": [
"core/tokens.css"
],
- "value": "color-mix(in oklab, var(--sf-color-primary) var(--sf-palette-mix-600), var(--sf-color-text))"
+ "value": "oklch(from var(--sf-color-primary) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.16)) calc(c * 0.96) h)"
},
"--sf-color-primary-700": {
"tier": "PUBLIC",
@@ -1947,7 +1947,7 @@
"files": [
"core/tokens.css"
],
- "value": "color-mix(in oklab, var(--sf-color-primary) var(--sf-palette-mix-700), var(--sf-color-text))"
+ "value": "oklch(from var(--sf-color-primary) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.36)) calc(c * 0.88) h)"
},
"--sf-color-primary-800": {
"tier": "PUBLIC",
@@ -1955,7 +1955,7 @@
"files": [
"core/tokens.css"
],
- "value": "color-mix(in oklab, var(--sf-color-primary) var(--sf-palette-mix-800), var(--sf-color-text))"
+ "value": "oklch(from var(--sf-color-primary) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.58)) calc(c * 0.72) h)"
},
"--sf-color-primary-900": {
"tier": "PUBLIC",
@@ -1963,7 +1963,7 @@
"files": [
"core/tokens.css"
],
- "value": "color-mix(in oklab, var(--sf-color-primary) var(--sf-palette-mix-900), var(--sf-color-text))"
+ "value": "oklch(from var(--sf-color-primary) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.78)) calc(c * 0.52) h)"
},
"--sf-color-primary-950": {
"tier": "PUBLIC",
@@ -1971,7 +1971,7 @@
"files": [
"core/tokens.css"
],
- "value": "color-mix(in oklab, var(--sf-color-primary) var(--sf-palette-mix-950), var(--sf-color-text))"
+ "value": "oklch(from var(--sf-color-primary) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.9)) calc(c * 0.38) h)"
},
"--sf-color-primary-a10": {
"tier": "PUBLIC",
@@ -2147,7 +2147,7 @@
"files": [
"core/tokens.css"
],
- "value": "color-mix(in oklab, var(--sf-color-secondary) var(--sf-palette-mix-100), var(--sf-color-surface))"
+ "value": "oklch(from var(--sf-color-secondary) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.88)) calc(c * 0.3) h)"
},
"--sf-color-secondary-200": {
"tier": "PUBLIC",
@@ -2155,7 +2155,7 @@
"files": [
"core/tokens.css"
],
- "value": "color-mix(in oklab, var(--sf-color-secondary) var(--sf-palette-mix-200), var(--sf-color-surface))"
+ "value": "oklch(from var(--sf-color-secondary) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.72)) calc(c * 0.5) h)"
},
"--sf-color-secondary-300": {
"tier": "PUBLIC",
@@ -2163,7 +2163,7 @@
"files": [
"core/tokens.css"
],
- "value": "color-mix(in oklab, var(--sf-color-secondary) var(--sf-palette-mix-300), var(--sf-color-surface))"
+ "value": "oklch(from var(--sf-color-secondary) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.52)) calc(c * 0.72) h)"
},
"--sf-color-secondary-400": {
"tier": "PUBLIC",
@@ -2171,7 +2171,7 @@
"files": [
"core/tokens.css"
],
- "value": "color-mix(in oklab, var(--sf-color-secondary) var(--sf-palette-mix-400), var(--sf-color-surface))"
+ "value": "oklch(from var(--sf-color-secondary) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.3)) calc(c * 0.88) h)"
},
"--sf-color-secondary-50": {
"tier": "PUBLIC",
@@ -2179,7 +2179,7 @@
"files": [
"core/tokens.css"
],
- "value": "color-mix(in oklab, var(--sf-color-secondary) var(--sf-palette-mix-50), var(--sf-color-surface))"
+ "value": "oklch(from var(--sf-color-secondary) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.95)) calc(c * 0.2) h)"
},
"--sf-color-secondary-500": {
"tier": "PUBLIC",
@@ -2195,7 +2195,7 @@
"files": [
"core/tokens.css"
],
- "value": "color-mix(in oklab, var(--sf-color-secondary) var(--sf-palette-mix-600), var(--sf-color-text))"
+ "value": "oklch(from var(--sf-color-secondary) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.16)) calc(c * 0.96) h)"
},
"--sf-color-secondary-700": {
"tier": "PUBLIC",
@@ -2203,7 +2203,7 @@
"files": [
"core/tokens.css"
],
- "value": "color-mix(in oklab, var(--sf-color-secondary) var(--sf-palette-mix-700), var(--sf-color-text))"
+ "value": "oklch(from var(--sf-color-secondary) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.36)) calc(c * 0.88) h)"
},
"--sf-color-secondary-800": {
"tier": "PUBLIC",
@@ -2211,7 +2211,7 @@
"files": [
"core/tokens.css"
],
- "value": "color-mix(in oklab, var(--sf-color-secondary) var(--sf-palette-mix-800), var(--sf-color-text))"
+ "value": "oklch(from var(--sf-color-secondary) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.58)) calc(c * 0.72) h)"
},
"--sf-color-secondary-900": {
"tier": "PUBLIC",
@@ -2219,7 +2219,7 @@
"files": [
"core/tokens.css"
],
- "value": "color-mix(in oklab, var(--sf-color-secondary) var(--sf-palette-mix-900), var(--sf-color-text))"
+ "value": "oklch(from var(--sf-color-secondary) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.78)) calc(c * 0.52) h)"
},
"--sf-color-secondary-950": {
"tier": "PUBLIC",
@@ -2227,7 +2227,7 @@
"files": [
"core/tokens.css"
],
- "value": "color-mix(in oklab, var(--sf-color-secondary) var(--sf-palette-mix-950), var(--sf-color-text))"
+ "value": "oklch(from var(--sf-color-secondary) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.9)) calc(c * 0.38) h)"
},
"--sf-color-secondary-a10": {
"tier": "PUBLIC",
@@ -2483,7 +2483,7 @@
"files": [
"core/tokens.css"
],
- "value": "color-mix(in oklab, var(--sf-color-tertiary) var(--sf-palette-mix-100), var(--sf-color-surface))"
+ "value": "oklch(from var(--sf-color-tertiary) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.88)) calc(c * 0.3) h)"
},
"--sf-color-tertiary-200": {
"tier": "PUBLIC",
@@ -2491,7 +2491,7 @@
"files": [
"core/tokens.css"
],
- "value": "color-mix(in oklab, var(--sf-color-tertiary) var(--sf-palette-mix-200), var(--sf-color-surface))"
+ "value": "oklch(from var(--sf-color-tertiary) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.72)) calc(c * 0.5) h)"
},
"--sf-color-tertiary-300": {
"tier": "PUBLIC",
@@ -2499,7 +2499,7 @@
"files": [
"core/tokens.css"
],
- "value": "color-mix(in oklab, var(--sf-color-tertiary) var(--sf-palette-mix-300), var(--sf-color-surface))"
+ "value": "oklch(from var(--sf-color-tertiary) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.52)) calc(c * 0.72) h)"
},
"--sf-color-tertiary-400": {
"tier": "PUBLIC",
@@ -2507,7 +2507,7 @@
"files": [
"core/tokens.css"
],
- "value": "color-mix(in oklab, var(--sf-color-tertiary) var(--sf-palette-mix-400), var(--sf-color-surface))"
+ "value": "oklch(from var(--sf-color-tertiary) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.3)) calc(c * 0.88) h)"
},
"--sf-color-tertiary-50": {
"tier": "PUBLIC",
@@ -2515,7 +2515,7 @@
"files": [
"core/tokens.css"
],
- "value": "color-mix(in oklab, var(--sf-color-tertiary) var(--sf-palette-mix-50), var(--sf-color-surface))"
+ "value": "oklch(from var(--sf-color-tertiary) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.95)) calc(c * 0.2) h)"
},
"--sf-color-tertiary-500": {
"tier": "PUBLIC",
@@ -2531,7 +2531,7 @@
"files": [
"core/tokens.css"
],
- "value": "color-mix(in oklab, var(--sf-color-tertiary) var(--sf-palette-mix-600), var(--sf-color-text))"
+ "value": "oklch(from var(--sf-color-tertiary) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.16)) calc(c * 0.96) h)"
},
"--sf-color-tertiary-700": {
"tier": "PUBLIC",
@@ -2539,7 +2539,7 @@
"files": [
"core/tokens.css"
],
- "value": "color-mix(in oklab, var(--sf-color-tertiary) var(--sf-palette-mix-700), var(--sf-color-text))"
+ "value": "oklch(from var(--sf-color-tertiary) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.36)) calc(c * 0.88) h)"
},
"--sf-color-tertiary-800": {
"tier": "PUBLIC",
@@ -2547,7 +2547,7 @@
"files": [
"core/tokens.css"
],
- "value": "color-mix(in oklab, var(--sf-color-tertiary) var(--sf-palette-mix-800), var(--sf-color-text))"
+ "value": "oklch(from var(--sf-color-tertiary) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.58)) calc(c * 0.72) h)"
},
"--sf-color-tertiary-900": {
"tier": "PUBLIC",
@@ -2555,7 +2555,7 @@
"files": [
"core/tokens.css"
],
- "value": "color-mix(in oklab, var(--sf-color-tertiary) var(--sf-palette-mix-900), var(--sf-color-text))"
+ "value": "oklch(from var(--sf-color-tertiary) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.78)) calc(c * 0.52) h)"
},
"--sf-color-tertiary-950": {
"tier": "PUBLIC",
@@ -2563,7 +2563,7 @@
"files": [
"core/tokens.css"
],
- "value": "color-mix(in oklab, var(--sf-color-tertiary) var(--sf-palette-mix-950), var(--sf-color-text))"
+ "value": "oklch(from var(--sf-color-tertiary) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.9)) calc(c * 0.38) h)"
},
"--sf-color-tertiary-a10": {
"tier": "PUBLIC",
@@ -4317,85 +4317,21 @@
],
"value": "var(--sf-space-xl)"
},
- "--sf-palette-mix-100": {
+ "--sf-palette-shade-l": {
"tier": "PUBLIC-ADVANCED",
"role": "knob",
"files": [
"core/tokens.css"
],
- "value": "8%"
+ "value": "0.1"
},
- "--sf-palette-mix-200": {
+ "--sf-palette-tint-l": {
"tier": "PUBLIC-ADVANCED",
"role": "knob",
"files": [
"core/tokens.css"
],
- "value": "20%"
- },
- "--sf-palette-mix-300": {
- "tier": "PUBLIC-ADVANCED",
- "role": "knob",
- "files": [
- "core/tokens.css"
- ],
- "value": "40%"
- },
- "--sf-palette-mix-400": {
- "tier": "PUBLIC-ADVANCED",
- "role": "knob",
- "files": [
- "core/tokens.css"
- ],
- "value": "65%"
- },
- "--sf-palette-mix-50": {
- "tier": "PUBLIC-ADVANCED",
- "role": "knob",
- "files": [
- "core/tokens.css"
- ],
- "value": "4%"
- },
- "--sf-palette-mix-600": {
- "tier": "PUBLIC-ADVANCED",
- "role": "knob",
- "files": [
- "core/tokens.css"
- ],
- "value": "82%"
- },
- "--sf-palette-mix-700": {
- "tier": "PUBLIC-ADVANCED",
- "role": "knob",
- "files": [
- "core/tokens.css"
- ],
- "value": "62%"
- },
- "--sf-palette-mix-800": {
- "tier": "PUBLIC-ADVANCED",
- "role": "knob",
- "files": [
- "core/tokens.css"
- ],
- "value": "38%"
- },
- "--sf-palette-mix-900": {
- "tier": "PUBLIC-ADVANCED",
- "role": "knob",
- "files": [
- "core/tokens.css"
- ],
- "value": "18%"
- },
- "--sf-palette-mix-950": {
- "tier": "PUBLIC-ADVANCED",
- "role": "knob",
- "files": [
- "core/tokens.css"
- ],
- "value": "8%"
+ "value": "0.97"
},
"--sf-print-base-size": {
"tier": "PUBLIC-ADVANCED",
diff --git a/docs/token-index.md b/docs/token-index.md
index d00e8582..42533b76 100644
--- a/docs/token-index.md
+++ b/docs/token-index.md
@@ -8,12 +8,12 @@ A cross-reference of every `--sf-*` custom property by **source file** and
for the flat name list see [registry.json](registry.json); for the tier
contract and naming rules see [architecture.md](architecture.md).
-**754 tokens** (deduplicated by name across the 4 token source files).
+**746 tokens** (deduplicated by name across the 4 token source files).
| Tier | Count | Meaning |
|---|---|---|
| PUBLIC | 693 | Everyday knobs. SemVer-stable. |
-| PUBLIC-ADVANCED | 60 | Same SemVer guarantee; niche/powerful. |
+| PUBLIC-ADVANCED | 52 | Same SemVer guarantee; niche/powerful. |
| INTERNAL | 1 | Implementation detail; may change without a major bump. |
Every token also carries a **role** — an orthogonal, SemVer-neutral hint about
@@ -22,7 +22,7 @@ declared value (a value that references `var(--sf-…)` is a derived output):
| Role | Count | Meaning |
|---|---|---|
-| knob | 265 | Input you **set** to configure the system (a literal primitive: length, number, colour literal, keyword, font stack, easing curve …). |
+| knob | 257 | Input you **set** to configure the system (a literal primitive: length, number, colour literal, keyword, font stack, easing curve …). |
| consumption | 489 | Ready-to-use output you **read**; derived from other tokens via `var(--sf-…)` (incl. `light-dark()`/`oklch(from …)`/`color-mix()`). |
## INTERNAL tokens
@@ -53,16 +53,8 @@ declared value (a value that references `var(--sf-…)` is a derived output):
- `--sf-mask-scrim-start`
- `--sf-motion-scale`
- `--sf-optical-sizing`
-- `--sf-palette-mix-100`
-- `--sf-palette-mix-200`
-- `--sf-palette-mix-300`
-- `--sf-palette-mix-400`
-- `--sf-palette-mix-50`
-- `--sf-palette-mix-600`
-- `--sf-palette-mix-700`
-- `--sf-palette-mix-800`
-- `--sf-palette-mix-900`
-- `--sf-palette-mix-950`
+- `--sf-palette-shade-l`
+- `--sf-palette-tint-l`
- `--sf-print-base-size`
- `--sf-print-page-margin`
- `--sf-print-page-size`
@@ -203,17 +195,17 @@ declared value (a value that references `var(--sf-…)` is a derived output):
| `--sf-color-action` | PUBLIC | consumption | Core | `light-dark(var(--sf-color-action-source-light), var(--sf-color-action-source-dark, oklch(from var(--sf-color-action-source-light) clamp(0.65, calc(0.95 - l * 0.5), 0.88) calc(c * 0.9) h)))` |
| `--sf-color-action--active` | PUBLIC | consumption | Core | `var(--sf-color-action-xdark)` |
| `--sf-color-action--hover` | PUBLIC | consumption | Core | `var(--sf-color-action-darker)` |
-| `--sf-color-action-100` | PUBLIC | consumption | Core | `color-mix(in oklab, var(--sf-color-action) var(--sf-palette-mix-100), var(--sf-color-surface))` |
-| `--sf-color-action-200` | PUBLIC | consumption | Core | `color-mix(in oklab, var(--sf-color-action) var(--sf-palette-mix-200), var(--sf-color-surface))` |
-| `--sf-color-action-300` | PUBLIC | consumption | Core | `color-mix(in oklab, var(--sf-color-action) var(--sf-palette-mix-300), var(--sf-color-surface))` |
-| `--sf-color-action-400` | PUBLIC | consumption | Core | `color-mix(in oklab, var(--sf-color-action) var(--sf-palette-mix-400), var(--sf-color-surface))` |
-| `--sf-color-action-50` | PUBLIC | consumption | Core | `color-mix(in oklab, var(--sf-color-action) var(--sf-palette-mix-50), var(--sf-color-surface))` |
+| `--sf-color-action-100` | PUBLIC | consumption | Core | `oklch(from var(--sf-color-action) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.88)) calc(c * 0.3) h)` |
+| `--sf-color-action-200` | PUBLIC | consumption | Core | `oklch(from var(--sf-color-action) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.72)) calc(c * 0.5) h)` |
+| `--sf-color-action-300` | PUBLIC | consumption | Core | `oklch(from var(--sf-color-action) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.52)) calc(c * 0.72) h)` |
+| `--sf-color-action-400` | PUBLIC | consumption | Core | `oklch(from var(--sf-color-action) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.3)) calc(c * 0.88) h)` |
+| `--sf-color-action-50` | PUBLIC | consumption | Core | `oklch(from var(--sf-color-action) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.95)) calc(c * 0.2) h)` |
| `--sf-color-action-500` | PUBLIC | consumption | Core | `var(--sf-color-action)` |
-| `--sf-color-action-600` | PUBLIC | consumption | Core | `color-mix(in oklab, var(--sf-color-action) var(--sf-palette-mix-600), var(--sf-color-text))` |
-| `--sf-color-action-700` | PUBLIC | consumption | Core | `color-mix(in oklab, var(--sf-color-action) var(--sf-palette-mix-700), var(--sf-color-text))` |
-| `--sf-color-action-800` | PUBLIC | consumption | Core | `color-mix(in oklab, var(--sf-color-action) var(--sf-palette-mix-800), var(--sf-color-text))` |
-| `--sf-color-action-900` | PUBLIC | consumption | Core | `color-mix(in oklab, var(--sf-color-action) var(--sf-palette-mix-900), var(--sf-color-text))` |
-| `--sf-color-action-950` | PUBLIC | consumption | Core | `color-mix(in oklab, var(--sf-color-action) var(--sf-palette-mix-950), var(--sf-color-text))` |
+| `--sf-color-action-600` | PUBLIC | consumption | Core | `oklch(from var(--sf-color-action) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.16)) calc(c * 0.96) h)` |
+| `--sf-color-action-700` | PUBLIC | consumption | Core | `oklch(from var(--sf-color-action) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.36)) calc(c * 0.88) h)` |
+| `--sf-color-action-800` | PUBLIC | consumption | Core | `oklch(from var(--sf-color-action) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.58)) calc(c * 0.72) h)` |
+| `--sf-color-action-900` | PUBLIC | consumption | Core | `oklch(from var(--sf-color-action) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.78)) calc(c * 0.52) h)` |
+| `--sf-color-action-950` | PUBLIC | consumption | Core | `oklch(from var(--sf-color-action) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.9)) calc(c * 0.38) h)` |
| `--sf-color-action-a10` | PUBLIC | consumption | Core | `oklch(from var(--sf-color-action) l c h / 0.10)` |
| `--sf-color-action-a30` | PUBLIC | consumption | Core | `oklch(from var(--sf-color-action) l c h / 0.30)` |
| `--sf-color-action-a5` | PUBLIC | consumption | Core | `oklch(from var(--sf-color-action) l c h / 0.05)` |
@@ -274,7 +266,7 @@ declared value (a value that references `var(--sf-…)` is a derived output):
| `--sf-color-danger-source-dark` | PUBLIC | knob | Core | `oklch(0.71 0.198 12) (registered)` |
| `--sf-color-danger-source-light` | PUBLIC | knob | Core | `oklch(0.48 0.22 12)` |
| `--sf-color-danger-strong` | PUBLIC | consumption | Core | `light-dark( oklch(from var(--sf-color-danger-source-light) calc(l - 0.1) c h), oklch(from var(--sf-color-danger) clamp(0.70, calc(l + 0.15), 1) c h) )` |
-| `--sf-color-danger-subtle` | PUBLIC | consumption | Core | `oklch(from var(--sf-color-danger) l c h / 0.1)` |
+| `--sf-color-danger-subtle` | PUBLIC | consumption | Core | `oklch(from var(--sf-color-danger) l c h / 0.12)` |
| `--sf-color-danger-tint` | PUBLIC | consumption | Core | `oklch(from var(--sf-color-danger) l c h / 0.05)` |
| `--sf-color-dim` | PUBLIC | knob | Core | `oklch(0 0 0 / 0.5)` |
| `--sf-color-heading` | PUBLIC | consumption | Core | `light-dark( oklch(from var(--sf-color-neutral-source-light) clamp(0.05, calc(l - 0.4 - var(--sf-contrast-bias)), 0.35) c h), oklch(from var(--sf-color-neutral) clamp(0.70, calc(l + 0.25 + var(--sf-contrast-bias)), 1) c h) )` |
@@ -283,7 +275,7 @@ declared value (a value that references `var(--sf-…)` is a derived output):
| `--sf-color-info-source-dark` | PUBLIC | knob | Core | `oklch(0.71 0.162 235) (registered)` |
| `--sf-color-info-source-light` | PUBLIC | knob | Core | `oklch(0.48 0.18 235)` |
| `--sf-color-info-strong` | PUBLIC | consumption | Core | `light-dark( oklch(from var(--sf-color-info-source-light) calc(l - 0.1) c h), oklch(from var(--sf-color-info) clamp(0.70, calc(l + 0.15), 1) c h) )` |
-| `--sf-color-info-subtle` | PUBLIC | consumption | Core | `oklch(from var(--sf-color-info) l c h / 0.1)` |
+| `--sf-color-info-subtle` | PUBLIC | consumption | Core | `oklch(from var(--sf-color-info) l c h / 0.12)` |
| `--sf-color-info-tint` | PUBLIC | consumption | Core | `oklch(from var(--sf-color-info) l c h / 0.05)` |
| `--sf-color-inset` | PUBLIC | consumption | Core | `oklch(from var(--sf-color-base) calc(l - 0.02) c h)` |
| `--sf-color-inverse` | PUBLIC | consumption | Core | `oklch(from var(--sf-color-base) calc(1 - l) c h)` |
@@ -298,17 +290,17 @@ declared value (a value that references `var(--sf-…)` is a derived output):
| `--sf-color-neutral` | PUBLIC | consumption | Core | `light-dark(var(--sf-color-neutral-source-light), var(--sf-color-neutral-source-dark, oklch(from var(--sf-color-neutral-source-light) clamp(0.65, calc(0.95 - l * 0.5), 0.88) calc(c * 0.9) h)))` |
| `--sf-color-neutral--active` | PUBLIC | consumption | Core | `var(--sf-color-neutral-xdark)` |
| `--sf-color-neutral--hover` | PUBLIC | consumption | Core | `var(--sf-color-neutral-darker)` |
-| `--sf-color-neutral-100` | PUBLIC | consumption | Core | `color-mix(in oklab, var(--sf-color-neutral) var(--sf-palette-mix-100), var(--sf-color-surface))` |
-| `--sf-color-neutral-200` | PUBLIC | consumption | Core | `color-mix(in oklab, var(--sf-color-neutral) var(--sf-palette-mix-200), var(--sf-color-surface))` |
-| `--sf-color-neutral-300` | PUBLIC | consumption | Core | `color-mix(in oklab, var(--sf-color-neutral) var(--sf-palette-mix-300), var(--sf-color-surface))` |
-| `--sf-color-neutral-400` | PUBLIC | consumption | Core | `color-mix(in oklab, var(--sf-color-neutral) var(--sf-palette-mix-400), var(--sf-color-surface))` |
-| `--sf-color-neutral-50` | PUBLIC | consumption | Core | `color-mix(in oklab, var(--sf-color-neutral) var(--sf-palette-mix-50), var(--sf-color-surface))` |
+| `--sf-color-neutral-100` | PUBLIC | consumption | Core | `oklch(from var(--sf-color-neutral) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.88)) calc(c * 0.3) h)` |
+| `--sf-color-neutral-200` | PUBLIC | consumption | Core | `oklch(from var(--sf-color-neutral) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.72)) calc(c * 0.5) h)` |
+| `--sf-color-neutral-300` | PUBLIC | consumption | Core | `oklch(from var(--sf-color-neutral) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.52)) calc(c * 0.72) h)` |
+| `--sf-color-neutral-400` | PUBLIC | consumption | Core | `oklch(from var(--sf-color-neutral) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.3)) calc(c * 0.88) h)` |
+| `--sf-color-neutral-50` | PUBLIC | consumption | Core | `oklch(from var(--sf-color-neutral) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.95)) calc(c * 0.2) h)` |
| `--sf-color-neutral-500` | PUBLIC | consumption | Core | `var(--sf-color-neutral)` |
-| `--sf-color-neutral-600` | PUBLIC | consumption | Core | `color-mix(in oklab, var(--sf-color-neutral) var(--sf-palette-mix-600), var(--sf-color-text))` |
-| `--sf-color-neutral-700` | PUBLIC | consumption | Core | `color-mix(in oklab, var(--sf-color-neutral) var(--sf-palette-mix-700), var(--sf-color-text))` |
-| `--sf-color-neutral-800` | PUBLIC | consumption | Core | `color-mix(in oklab, var(--sf-color-neutral) var(--sf-palette-mix-800), var(--sf-color-text))` |
-| `--sf-color-neutral-900` | PUBLIC | consumption | Core | `color-mix(in oklab, var(--sf-color-neutral) var(--sf-palette-mix-900), var(--sf-color-text))` |
-| `--sf-color-neutral-950` | PUBLIC | consumption | Core | `color-mix(in oklab, var(--sf-color-neutral) var(--sf-palette-mix-950), var(--sf-color-text))` |
+| `--sf-color-neutral-600` | PUBLIC | consumption | Core | `oklch(from var(--sf-color-neutral) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.16)) calc(c * 0.96) h)` |
+| `--sf-color-neutral-700` | PUBLIC | consumption | Core | `oklch(from var(--sf-color-neutral) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.36)) calc(c * 0.88) h)` |
+| `--sf-color-neutral-800` | PUBLIC | consumption | Core | `oklch(from var(--sf-color-neutral) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.58)) calc(c * 0.72) h)` |
+| `--sf-color-neutral-900` | PUBLIC | consumption | Core | `oklch(from var(--sf-color-neutral) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.78)) calc(c * 0.52) h)` |
+| `--sf-color-neutral-950` | PUBLIC | consumption | Core | `oklch(from var(--sf-color-neutral) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.9)) calc(c * 0.38) h)` |
| `--sf-color-neutral-a10` | PUBLIC | consumption | Core | `oklch(from var(--sf-color-neutral) l c h / 0.10)` |
| `--sf-color-neutral-a30` | PUBLIC | consumption | Core | `oklch(from var(--sf-color-neutral) l c h / 0.30)` |
| `--sf-color-neutral-a5` | PUBLIC | consumption | Core | `oklch(from var(--sf-color-neutral) l c h / 0.05)` |
@@ -329,17 +321,17 @@ declared value (a value that references `var(--sf-…)` is a derived output):
| `--sf-color-primary` | PUBLIC | consumption | Core | `light-dark(var(--sf-color-primary-source-light), var(--sf-color-primary-source-dark, oklch(from var(--sf-color-primary-source-light) clamp(0.65, calc(0.95 - l * 0.5), 0.88) calc(c * 0.9) h)))` |
| `--sf-color-primary--active` | PUBLIC | consumption | Core | `var(--sf-color-primary-xdark)` |
| `--sf-color-primary--hover` | PUBLIC | consumption | Core | `var(--sf-color-primary-darker)` |
-| `--sf-color-primary-100` | PUBLIC | consumption | Core | `color-mix(in oklab, var(--sf-color-primary) var(--sf-palette-mix-100), var(--sf-color-surface))` |
-| `--sf-color-primary-200` | PUBLIC | consumption | Core | `color-mix(in oklab, var(--sf-color-primary) var(--sf-palette-mix-200), var(--sf-color-surface))` |
-| `--sf-color-primary-300` | PUBLIC | consumption | Core | `color-mix(in oklab, var(--sf-color-primary) var(--sf-palette-mix-300), var(--sf-color-surface))` |
-| `--sf-color-primary-400` | PUBLIC | consumption | Core | `color-mix(in oklab, var(--sf-color-primary) var(--sf-palette-mix-400), var(--sf-color-surface))` |
-| `--sf-color-primary-50` | PUBLIC | consumption | Core | `color-mix(in oklab, var(--sf-color-primary) var(--sf-palette-mix-50), var(--sf-color-surface))` |
+| `--sf-color-primary-100` | PUBLIC | consumption | Core | `oklch(from var(--sf-color-primary) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.88)) calc(c * 0.3) h)` |
+| `--sf-color-primary-200` | PUBLIC | consumption | Core | `oklch(from var(--sf-color-primary) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.72)) calc(c * 0.5) h)` |
+| `--sf-color-primary-300` | PUBLIC | consumption | Core | `oklch(from var(--sf-color-primary) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.52)) calc(c * 0.72) h)` |
+| `--sf-color-primary-400` | PUBLIC | consumption | Core | `oklch(from var(--sf-color-primary) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.3)) calc(c * 0.88) h)` |
+| `--sf-color-primary-50` | PUBLIC | consumption | Core | `oklch(from var(--sf-color-primary) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.95)) calc(c * 0.2) h)` |
| `--sf-color-primary-500` | PUBLIC | consumption | Core | `var(--sf-color-primary)` |
-| `--sf-color-primary-600` | PUBLIC | consumption | Core | `color-mix(in oklab, var(--sf-color-primary) var(--sf-palette-mix-600), var(--sf-color-text))` |
-| `--sf-color-primary-700` | PUBLIC | consumption | Core | `color-mix(in oklab, var(--sf-color-primary) var(--sf-palette-mix-700), var(--sf-color-text))` |
-| `--sf-color-primary-800` | PUBLIC | consumption | Core | `color-mix(in oklab, var(--sf-color-primary) var(--sf-palette-mix-800), var(--sf-color-text))` |
-| `--sf-color-primary-900` | PUBLIC | consumption | Core | `color-mix(in oklab, var(--sf-color-primary) var(--sf-palette-mix-900), var(--sf-color-text))` |
-| `--sf-color-primary-950` | PUBLIC | consumption | Core | `color-mix(in oklab, var(--sf-color-primary) var(--sf-palette-mix-950), var(--sf-color-text))` |
+| `--sf-color-primary-600` | PUBLIC | consumption | Core | `oklch(from var(--sf-color-primary) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.16)) calc(c * 0.96) h)` |
+| `--sf-color-primary-700` | PUBLIC | consumption | Core | `oklch(from var(--sf-color-primary) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.36)) calc(c * 0.88) h)` |
+| `--sf-color-primary-800` | PUBLIC | consumption | Core | `oklch(from var(--sf-color-primary) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.58)) calc(c * 0.72) h)` |
+| `--sf-color-primary-900` | PUBLIC | consumption | Core | `oklch(from var(--sf-color-primary) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.78)) calc(c * 0.52) h)` |
+| `--sf-color-primary-950` | PUBLIC | consumption | Core | `oklch(from var(--sf-color-primary) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.9)) calc(c * 0.38) h)` |
| `--sf-color-primary-a10` | PUBLIC | consumption | Core | `oklch(from var(--sf-color-primary) l c h / 0.10)` |
| `--sf-color-primary-a30` | PUBLIC | consumption | Core | `oklch(from var(--sf-color-primary) l c h / 0.30)` |
| `--sf-color-primary-a5` | PUBLIC | consumption | Core | `oklch(from var(--sf-color-primary) l c h / 0.05)` |
@@ -361,17 +353,17 @@ declared value (a value that references `var(--sf-…)` is a derived output):
| `--sf-color-secondary` | PUBLIC | consumption | Core | `light-dark(var(--sf-color-secondary-source-light), var(--sf-color-secondary-source-dark, oklch(from var(--sf-color-secondary-source-light) clamp(0.65, calc(0.95 - l * 0.5), 0.88) calc(c * 0.9) h)))` |
| `--sf-color-secondary--active` | PUBLIC | consumption | Core | `var(--sf-color-secondary-xdark)` |
| `--sf-color-secondary--hover` | PUBLIC | consumption | Core | `var(--sf-color-secondary-darker)` |
-| `--sf-color-secondary-100` | PUBLIC | consumption | Core | `color-mix(in oklab, var(--sf-color-secondary) var(--sf-palette-mix-100), var(--sf-color-surface))` |
-| `--sf-color-secondary-200` | PUBLIC | consumption | Core | `color-mix(in oklab, var(--sf-color-secondary) var(--sf-palette-mix-200), var(--sf-color-surface))` |
-| `--sf-color-secondary-300` | PUBLIC | consumption | Core | `color-mix(in oklab, var(--sf-color-secondary) var(--sf-palette-mix-300), var(--sf-color-surface))` |
-| `--sf-color-secondary-400` | PUBLIC | consumption | Core | `color-mix(in oklab, var(--sf-color-secondary) var(--sf-palette-mix-400), var(--sf-color-surface))` |
-| `--sf-color-secondary-50` | PUBLIC | consumption | Core | `color-mix(in oklab, var(--sf-color-secondary) var(--sf-palette-mix-50), var(--sf-color-surface))` |
+| `--sf-color-secondary-100` | PUBLIC | consumption | Core | `oklch(from var(--sf-color-secondary) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.88)) calc(c * 0.3) h)` |
+| `--sf-color-secondary-200` | PUBLIC | consumption | Core | `oklch(from var(--sf-color-secondary) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.72)) calc(c * 0.5) h)` |
+| `--sf-color-secondary-300` | PUBLIC | consumption | Core | `oklch(from var(--sf-color-secondary) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.52)) calc(c * 0.72) h)` |
+| `--sf-color-secondary-400` | PUBLIC | consumption | Core | `oklch(from var(--sf-color-secondary) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.3)) calc(c * 0.88) h)` |
+| `--sf-color-secondary-50` | PUBLIC | consumption | Core | `oklch(from var(--sf-color-secondary) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.95)) calc(c * 0.2) h)` |
| `--sf-color-secondary-500` | PUBLIC | consumption | Core | `var(--sf-color-secondary)` |
-| `--sf-color-secondary-600` | PUBLIC | consumption | Core | `color-mix(in oklab, var(--sf-color-secondary) var(--sf-palette-mix-600), var(--sf-color-text))` |
-| `--sf-color-secondary-700` | PUBLIC | consumption | Core | `color-mix(in oklab, var(--sf-color-secondary) var(--sf-palette-mix-700), var(--sf-color-text))` |
-| `--sf-color-secondary-800` | PUBLIC | consumption | Core | `color-mix(in oklab, var(--sf-color-secondary) var(--sf-palette-mix-800), var(--sf-color-text))` |
-| `--sf-color-secondary-900` | PUBLIC | consumption | Core | `color-mix(in oklab, var(--sf-color-secondary) var(--sf-palette-mix-900), var(--sf-color-text))` |
-| `--sf-color-secondary-950` | PUBLIC | consumption | Core | `color-mix(in oklab, var(--sf-color-secondary) var(--sf-palette-mix-950), var(--sf-color-text))` |
+| `--sf-color-secondary-600` | PUBLIC | consumption | Core | `oklch(from var(--sf-color-secondary) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.16)) calc(c * 0.96) h)` |
+| `--sf-color-secondary-700` | PUBLIC | consumption | Core | `oklch(from var(--sf-color-secondary) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.36)) calc(c * 0.88) h)` |
+| `--sf-color-secondary-800` | PUBLIC | consumption | Core | `oklch(from var(--sf-color-secondary) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.58)) calc(c * 0.72) h)` |
+| `--sf-color-secondary-900` | PUBLIC | consumption | Core | `oklch(from var(--sf-color-secondary) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.78)) calc(c * 0.52) h)` |
+| `--sf-color-secondary-950` | PUBLIC | consumption | Core | `oklch(from var(--sf-color-secondary) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.9)) calc(c * 0.38) h)` |
| `--sf-color-secondary-a10` | PUBLIC | consumption | Core | `oklch(from var(--sf-color-secondary) l c h / 0.10)` |
| `--sf-color-secondary-a30` | PUBLIC | consumption | Core | `oklch(from var(--sf-color-secondary) l c h / 0.30)` |
| `--sf-color-secondary-a5` | PUBLIC | consumption | Core | `oklch(from var(--sf-color-secondary) l c h / 0.05)` |
@@ -403,17 +395,17 @@ declared value (a value that references `var(--sf-…)` is a derived output):
| `--sf-color-tertiary` | PUBLIC | consumption | Core | `light-dark(var(--sf-color-tertiary-source-light), var(--sf-color-tertiary-source-dark, oklch(from var(--sf-color-tertiary-source-light) clamp(0.65, calc(0.95 - l * 0.5), 0.88) calc(c * 0.9) h)))` |
| `--sf-color-tertiary--active` | PUBLIC | consumption | Core | `var(--sf-color-tertiary-xdark)` |
| `--sf-color-tertiary--hover` | PUBLIC | consumption | Core | `var(--sf-color-tertiary-darker)` |
-| `--sf-color-tertiary-100` | PUBLIC | consumption | Core | `color-mix(in oklab, var(--sf-color-tertiary) var(--sf-palette-mix-100), var(--sf-color-surface))` |
-| `--sf-color-tertiary-200` | PUBLIC | consumption | Core | `color-mix(in oklab, var(--sf-color-tertiary) var(--sf-palette-mix-200), var(--sf-color-surface))` |
-| `--sf-color-tertiary-300` | PUBLIC | consumption | Core | `color-mix(in oklab, var(--sf-color-tertiary) var(--sf-palette-mix-300), var(--sf-color-surface))` |
-| `--sf-color-tertiary-400` | PUBLIC | consumption | Core | `color-mix(in oklab, var(--sf-color-tertiary) var(--sf-palette-mix-400), var(--sf-color-surface))` |
-| `--sf-color-tertiary-50` | PUBLIC | consumption | Core | `color-mix(in oklab, var(--sf-color-tertiary) var(--sf-palette-mix-50), var(--sf-color-surface))` |
+| `--sf-color-tertiary-100` | PUBLIC | consumption | Core | `oklch(from var(--sf-color-tertiary) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.88)) calc(c * 0.3) h)` |
+| `--sf-color-tertiary-200` | PUBLIC | consumption | Core | `oklch(from var(--sf-color-tertiary) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.72)) calc(c * 0.5) h)` |
+| `--sf-color-tertiary-300` | PUBLIC | consumption | Core | `oklch(from var(--sf-color-tertiary) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.52)) calc(c * 0.72) h)` |
+| `--sf-color-tertiary-400` | PUBLIC | consumption | Core | `oklch(from var(--sf-color-tertiary) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.3)) calc(c * 0.88) h)` |
+| `--sf-color-tertiary-50` | PUBLIC | consumption | Core | `oklch(from var(--sf-color-tertiary) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.95)) calc(c * 0.2) h)` |
| `--sf-color-tertiary-500` | PUBLIC | consumption | Core | `var(--sf-color-tertiary)` |
-| `--sf-color-tertiary-600` | PUBLIC | consumption | Core | `color-mix(in oklab, var(--sf-color-tertiary) var(--sf-palette-mix-600), var(--sf-color-text))` |
-| `--sf-color-tertiary-700` | PUBLIC | consumption | Core | `color-mix(in oklab, var(--sf-color-tertiary) var(--sf-palette-mix-700), var(--sf-color-text))` |
-| `--sf-color-tertiary-800` | PUBLIC | consumption | Core | `color-mix(in oklab, var(--sf-color-tertiary) var(--sf-palette-mix-800), var(--sf-color-text))` |
-| `--sf-color-tertiary-900` | PUBLIC | consumption | Core | `color-mix(in oklab, var(--sf-color-tertiary) var(--sf-palette-mix-900), var(--sf-color-text))` |
-| `--sf-color-tertiary-950` | PUBLIC | consumption | Core | `color-mix(in oklab, var(--sf-color-tertiary) var(--sf-palette-mix-950), var(--sf-color-text))` |
+| `--sf-color-tertiary-600` | PUBLIC | consumption | Core | `oklch(from var(--sf-color-tertiary) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.16)) calc(c * 0.96) h)` |
+| `--sf-color-tertiary-700` | PUBLIC | consumption | Core | `oklch(from var(--sf-color-tertiary) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.36)) calc(c * 0.88) h)` |
+| `--sf-color-tertiary-800` | PUBLIC | consumption | Core | `oklch(from var(--sf-color-tertiary) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.58)) calc(c * 0.72) h)` |
+| `--sf-color-tertiary-900` | PUBLIC | consumption | Core | `oklch(from var(--sf-color-tertiary) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.78)) calc(c * 0.52) h)` |
+| `--sf-color-tertiary-950` | PUBLIC | consumption | Core | `oklch(from var(--sf-color-tertiary) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.9)) calc(c * 0.38) h)` |
| `--sf-color-tertiary-a10` | PUBLIC | consumption | Core | `oklch(from var(--sf-color-tertiary) l c h / 0.10)` |
| `--sf-color-tertiary-a30` | PUBLIC | consumption | Core | `oklch(from var(--sf-color-tertiary) l c h / 0.30)` |
| `--sf-color-tertiary-a5` | PUBLIC | consumption | Core | `oklch(from var(--sf-color-tertiary) l c h / 0.05)` |
@@ -633,16 +625,8 @@ declared value (a value that references `var(--sf-…)` is a derived output):
| `--sf-opacity-muted` | PUBLIC | knob | Core | `0.5` |
| `--sf-optical-sizing` | PUBLIC-ADVANCED | knob | Core | `auto` |
| `--sf-overlap-pull` | PUBLIC | consumption | Macros | `var(--sf-space-xl)` |
-| `--sf-palette-mix-100` | PUBLIC-ADVANCED | knob | Core | `8%` |
-| `--sf-palette-mix-200` | PUBLIC-ADVANCED | knob | Core | `20%` |
-| `--sf-palette-mix-300` | PUBLIC-ADVANCED | knob | Core | `40%` |
-| `--sf-palette-mix-400` | PUBLIC-ADVANCED | knob | Core | `65%` |
-| `--sf-palette-mix-50` | PUBLIC-ADVANCED | knob | Core | `4%` |
-| `--sf-palette-mix-600` | PUBLIC-ADVANCED | knob | Core | `82%` |
-| `--sf-palette-mix-700` | PUBLIC-ADVANCED | knob | Core | `62%` |
-| `--sf-palette-mix-800` | PUBLIC-ADVANCED | knob | Core | `38%` |
-| `--sf-palette-mix-900` | PUBLIC-ADVANCED | knob | Core | `18%` |
-| `--sf-palette-mix-950` | PUBLIC-ADVANCED | knob | Core | `8%` |
+| `--sf-palette-shade-l` | PUBLIC-ADVANCED | knob | Core | `0.1` |
+| `--sf-palette-tint-l` | PUBLIC-ADVANCED | knob | Core | `0.97` |
| `--sf-print-base-size` | PUBLIC-ADVANCED | knob | Core | `11pt` |
| `--sf-print-page-margin` | PUBLIC-ADVANCED | knob | Core | `2cm` |
| `--sf-print-page-size` | PUBLIC-ADVANCED | knob | Core | `a4` |
diff --git a/docs/tokens.md b/docs/tokens.md
index 938d4f73..4662992c 100644
--- a/docs/tokens.md
+++ b/docs/tokens.md
@@ -3,7 +3,7 @@
> **Generated** from source by `scripts/gen-token-reference.js` —
> run `npm run docs:tokens` to refresh. Do not edit by hand.
-**754 tokens.** Every `--sf-*` custom property and its default value, grouped by source file.
+**746 tokens.** Every `--sf-*` custom property and its default value, grouped by source file.
If a token is defined in multiple files, it is listed once per section — so this
count can be higher than `docs/registry.json` (which deduplicates by name). See
[architecture.md](architecture.md) for the PUBLIC / PUBLIC-ADVANCED / INTERNAL
@@ -13,7 +13,7 @@ rebrand workflow.
## Core tokens (`core/tokens.css`)
-623 tokens.
+615 tokens.
| Token | Default |
|---|---|
@@ -59,17 +59,17 @@ rebrand workflow.
| `--sf-color-action` | `light-dark(var(--sf-color-action-source-light), var(--sf-color-action-source-dark, oklch(from var(--sf-color-action-source-light) clamp(0.65, calc(0.95 - l * 0.5), 0.88) calc(c * 0.9) h)))` |
| `--sf-color-action--active` | `var(--sf-color-action-xdark)` |
| `--sf-color-action--hover` | `var(--sf-color-action-darker)` |
-| `--sf-color-action-100` | `color-mix(in oklab, var(--sf-color-action) var(--sf-palette-mix-100), var(--sf-color-surface))` |
-| `--sf-color-action-200` | `color-mix(in oklab, var(--sf-color-action) var(--sf-palette-mix-200), var(--sf-color-surface))` |
-| `--sf-color-action-300` | `color-mix(in oklab, var(--sf-color-action) var(--sf-palette-mix-300), var(--sf-color-surface))` |
-| `--sf-color-action-400` | `color-mix(in oklab, var(--sf-color-action) var(--sf-palette-mix-400), var(--sf-color-surface))` |
-| `--sf-color-action-50` | `color-mix(in oklab, var(--sf-color-action) var(--sf-palette-mix-50), var(--sf-color-surface))` |
+| `--sf-color-action-100` | `oklch(from var(--sf-color-action) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.88)) calc(c * 0.3) h)` |
+| `--sf-color-action-200` | `oklch(from var(--sf-color-action) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.72)) calc(c * 0.5) h)` |
+| `--sf-color-action-300` | `oklch(from var(--sf-color-action) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.52)) calc(c * 0.72) h)` |
+| `--sf-color-action-400` | `oklch(from var(--sf-color-action) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.3)) calc(c * 0.88) h)` |
+| `--sf-color-action-50` | `oklch(from var(--sf-color-action) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.95)) calc(c * 0.2) h)` |
| `--sf-color-action-500` | `var(--sf-color-action)` |
-| `--sf-color-action-600` | `color-mix(in oklab, var(--sf-color-action) var(--sf-palette-mix-600), var(--sf-color-text))` |
-| `--sf-color-action-700` | `color-mix(in oklab, var(--sf-color-action) var(--sf-palette-mix-700), var(--sf-color-text))` |
-| `--sf-color-action-800` | `color-mix(in oklab, var(--sf-color-action) var(--sf-palette-mix-800), var(--sf-color-text))` |
-| `--sf-color-action-900` | `color-mix(in oklab, var(--sf-color-action) var(--sf-palette-mix-900), var(--sf-color-text))` |
-| `--sf-color-action-950` | `color-mix(in oklab, var(--sf-color-action) var(--sf-palette-mix-950), var(--sf-color-text))` |
+| `--sf-color-action-600` | `oklch(from var(--sf-color-action) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.16)) calc(c * 0.96) h)` |
+| `--sf-color-action-700` | `oklch(from var(--sf-color-action) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.36)) calc(c * 0.88) h)` |
+| `--sf-color-action-800` | `oklch(from var(--sf-color-action) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.58)) calc(c * 0.72) h)` |
+| `--sf-color-action-900` | `oklch(from var(--sf-color-action) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.78)) calc(c * 0.52) h)` |
+| `--sf-color-action-950` | `oklch(from var(--sf-color-action) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.9)) calc(c * 0.38) h)` |
| `--sf-color-action-a10` | `oklch(from var(--sf-color-action) l c h / 0.10)` |
| `--sf-color-action-a30` | `oklch(from var(--sf-color-action) l c h / 0.30)` |
| `--sf-color-action-a5` | `oklch(from var(--sf-color-action) l c h / 0.05)` |
@@ -130,7 +130,7 @@ rebrand workflow.
| `--sf-color-danger-source-dark` | `oklch(0.71 0.198 12) *(registered)*` |
| `--sf-color-danger-source-light` | `oklch(0.48 0.22 12)` |
| `--sf-color-danger-strong` | `light-dark( oklch(from var(--sf-color-danger-source-light) calc(l - 0.1) c h), oklch(from var(--sf-color-danger) clamp(0.70, calc(l + 0.15), 1) c h) )` |
-| `--sf-color-danger-subtle` | `oklch(from var(--sf-color-danger) l c h / 0.1)` |
+| `--sf-color-danger-subtle` | `oklch(from var(--sf-color-danger) l c h / 0.12)` |
| `--sf-color-danger-tint` | `oklch(from var(--sf-color-danger) l c h / 0.05)` |
| `--sf-color-dim` | `oklch(0 0 0 / 0.5)` |
| `--sf-color-heading` | `light-dark( oklch(from var(--sf-color-neutral-source-light) clamp(0.05, calc(l - 0.4 - var(--sf-contrast-bias)), 0.35) c h), oklch(from var(--sf-color-neutral) clamp(0.70, calc(l + 0.25 + var(--sf-contrast-bias)), 1) c h) )` |
@@ -139,7 +139,7 @@ rebrand workflow.
| `--sf-color-info-source-dark` | `oklch(0.71 0.162 235) *(registered)*` |
| `--sf-color-info-source-light` | `oklch(0.48 0.18 235)` |
| `--sf-color-info-strong` | `light-dark( oklch(from var(--sf-color-info-source-light) calc(l - 0.1) c h), oklch(from var(--sf-color-info) clamp(0.70, calc(l + 0.15), 1) c h) )` |
-| `--sf-color-info-subtle` | `oklch(from var(--sf-color-info) l c h / 0.1)` |
+| `--sf-color-info-subtle` | `oklch(from var(--sf-color-info) l c h / 0.12)` |
| `--sf-color-info-tint` | `oklch(from var(--sf-color-info) l c h / 0.05)` |
| `--sf-color-inset` | `oklch(from var(--sf-color-base) calc(l - 0.02) c h)` |
| `--sf-color-inverse` | `oklch(from var(--sf-color-base) calc(1 - l) c h)` |
@@ -154,17 +154,17 @@ rebrand workflow.
| `--sf-color-neutral` | `light-dark(var(--sf-color-neutral-source-light), var(--sf-color-neutral-source-dark, oklch(from var(--sf-color-neutral-source-light) clamp(0.65, calc(0.95 - l * 0.5), 0.88) calc(c * 0.9) h)))` |
| `--sf-color-neutral--active` | `var(--sf-color-neutral-xdark)` |
| `--sf-color-neutral--hover` | `var(--sf-color-neutral-darker)` |
-| `--sf-color-neutral-100` | `color-mix(in oklab, var(--sf-color-neutral) var(--sf-palette-mix-100), var(--sf-color-surface))` |
-| `--sf-color-neutral-200` | `color-mix(in oklab, var(--sf-color-neutral) var(--sf-palette-mix-200), var(--sf-color-surface))` |
-| `--sf-color-neutral-300` | `color-mix(in oklab, var(--sf-color-neutral) var(--sf-palette-mix-300), var(--sf-color-surface))` |
-| `--sf-color-neutral-400` | `color-mix(in oklab, var(--sf-color-neutral) var(--sf-palette-mix-400), var(--sf-color-surface))` |
-| `--sf-color-neutral-50` | `color-mix(in oklab, var(--sf-color-neutral) var(--sf-palette-mix-50), var(--sf-color-surface))` |
+| `--sf-color-neutral-100` | `oklch(from var(--sf-color-neutral) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.88)) calc(c * 0.3) h)` |
+| `--sf-color-neutral-200` | `oklch(from var(--sf-color-neutral) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.72)) calc(c * 0.5) h)` |
+| `--sf-color-neutral-300` | `oklch(from var(--sf-color-neutral) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.52)) calc(c * 0.72) h)` |
+| `--sf-color-neutral-400` | `oklch(from var(--sf-color-neutral) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.3)) calc(c * 0.88) h)` |
+| `--sf-color-neutral-50` | `oklch(from var(--sf-color-neutral) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.95)) calc(c * 0.2) h)` |
| `--sf-color-neutral-500` | `var(--sf-color-neutral)` |
-| `--sf-color-neutral-600` | `color-mix(in oklab, var(--sf-color-neutral) var(--sf-palette-mix-600), var(--sf-color-text))` |
-| `--sf-color-neutral-700` | `color-mix(in oklab, var(--sf-color-neutral) var(--sf-palette-mix-700), var(--sf-color-text))` |
-| `--sf-color-neutral-800` | `color-mix(in oklab, var(--sf-color-neutral) var(--sf-palette-mix-800), var(--sf-color-text))` |
-| `--sf-color-neutral-900` | `color-mix(in oklab, var(--sf-color-neutral) var(--sf-palette-mix-900), var(--sf-color-text))` |
-| `--sf-color-neutral-950` | `color-mix(in oklab, var(--sf-color-neutral) var(--sf-palette-mix-950), var(--sf-color-text))` |
+| `--sf-color-neutral-600` | `oklch(from var(--sf-color-neutral) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.16)) calc(c * 0.96) h)` |
+| `--sf-color-neutral-700` | `oklch(from var(--sf-color-neutral) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.36)) calc(c * 0.88) h)` |
+| `--sf-color-neutral-800` | `oklch(from var(--sf-color-neutral) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.58)) calc(c * 0.72) h)` |
+| `--sf-color-neutral-900` | `oklch(from var(--sf-color-neutral) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.78)) calc(c * 0.52) h)` |
+| `--sf-color-neutral-950` | `oklch(from var(--sf-color-neutral) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.9)) calc(c * 0.38) h)` |
| `--sf-color-neutral-a10` | `oklch(from var(--sf-color-neutral) l c h / 0.10)` |
| `--sf-color-neutral-a30` | `oklch(from var(--sf-color-neutral) l c h / 0.30)` |
| `--sf-color-neutral-a5` | `oklch(from var(--sf-color-neutral) l c h / 0.05)` |
@@ -185,17 +185,17 @@ rebrand workflow.
| `--sf-color-primary` | `light-dark(var(--sf-color-primary-source-light), var(--sf-color-primary-source-dark, oklch(from var(--sf-color-primary-source-light) clamp(0.65, calc(0.95 - l * 0.5), 0.88) calc(c * 0.9) h)))` |
| `--sf-color-primary--active` | `var(--sf-color-primary-xdark)` |
| `--sf-color-primary--hover` | `var(--sf-color-primary-darker)` |
-| `--sf-color-primary-100` | `color-mix(in oklab, var(--sf-color-primary) var(--sf-palette-mix-100), var(--sf-color-surface))` |
-| `--sf-color-primary-200` | `color-mix(in oklab, var(--sf-color-primary) var(--sf-palette-mix-200), var(--sf-color-surface))` |
-| `--sf-color-primary-300` | `color-mix(in oklab, var(--sf-color-primary) var(--sf-palette-mix-300), var(--sf-color-surface))` |
-| `--sf-color-primary-400` | `color-mix(in oklab, var(--sf-color-primary) var(--sf-palette-mix-400), var(--sf-color-surface))` |
-| `--sf-color-primary-50` | `color-mix(in oklab, var(--sf-color-primary) var(--sf-palette-mix-50), var(--sf-color-surface))` |
+| `--sf-color-primary-100` | `oklch(from var(--sf-color-primary) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.88)) calc(c * 0.3) h)` |
+| `--sf-color-primary-200` | `oklch(from var(--sf-color-primary) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.72)) calc(c * 0.5) h)` |
+| `--sf-color-primary-300` | `oklch(from var(--sf-color-primary) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.52)) calc(c * 0.72) h)` |
+| `--sf-color-primary-400` | `oklch(from var(--sf-color-primary) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.3)) calc(c * 0.88) h)` |
+| `--sf-color-primary-50` | `oklch(from var(--sf-color-primary) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.95)) calc(c * 0.2) h)` |
| `--sf-color-primary-500` | `var(--sf-color-primary)` |
-| `--sf-color-primary-600` | `color-mix(in oklab, var(--sf-color-primary) var(--sf-palette-mix-600), var(--sf-color-text))` |
-| `--sf-color-primary-700` | `color-mix(in oklab, var(--sf-color-primary) var(--sf-palette-mix-700), var(--sf-color-text))` |
-| `--sf-color-primary-800` | `color-mix(in oklab, var(--sf-color-primary) var(--sf-palette-mix-800), var(--sf-color-text))` |
-| `--sf-color-primary-900` | `color-mix(in oklab, var(--sf-color-primary) var(--sf-palette-mix-900), var(--sf-color-text))` |
-| `--sf-color-primary-950` | `color-mix(in oklab, var(--sf-color-primary) var(--sf-palette-mix-950), var(--sf-color-text))` |
+| `--sf-color-primary-600` | `oklch(from var(--sf-color-primary) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.16)) calc(c * 0.96) h)` |
+| `--sf-color-primary-700` | `oklch(from var(--sf-color-primary) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.36)) calc(c * 0.88) h)` |
+| `--sf-color-primary-800` | `oklch(from var(--sf-color-primary) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.58)) calc(c * 0.72) h)` |
+| `--sf-color-primary-900` | `oklch(from var(--sf-color-primary) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.78)) calc(c * 0.52) h)` |
+| `--sf-color-primary-950` | `oklch(from var(--sf-color-primary) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.9)) calc(c * 0.38) h)` |
| `--sf-color-primary-a10` | `oklch(from var(--sf-color-primary) l c h / 0.10)` |
| `--sf-color-primary-a30` | `oklch(from var(--sf-color-primary) l c h / 0.30)` |
| `--sf-color-primary-a5` | `oklch(from var(--sf-color-primary) l c h / 0.05)` |
@@ -217,17 +217,17 @@ rebrand workflow.
| `--sf-color-secondary` | `light-dark(var(--sf-color-secondary-source-light), var(--sf-color-secondary-source-dark, oklch(from var(--sf-color-secondary-source-light) clamp(0.65, calc(0.95 - l * 0.5), 0.88) calc(c * 0.9) h)))` |
| `--sf-color-secondary--active` | `var(--sf-color-secondary-xdark)` |
| `--sf-color-secondary--hover` | `var(--sf-color-secondary-darker)` |
-| `--sf-color-secondary-100` | `color-mix(in oklab, var(--sf-color-secondary) var(--sf-palette-mix-100), var(--sf-color-surface))` |
-| `--sf-color-secondary-200` | `color-mix(in oklab, var(--sf-color-secondary) var(--sf-palette-mix-200), var(--sf-color-surface))` |
-| `--sf-color-secondary-300` | `color-mix(in oklab, var(--sf-color-secondary) var(--sf-palette-mix-300), var(--sf-color-surface))` |
-| `--sf-color-secondary-400` | `color-mix(in oklab, var(--sf-color-secondary) var(--sf-palette-mix-400), var(--sf-color-surface))` |
-| `--sf-color-secondary-50` | `color-mix(in oklab, var(--sf-color-secondary) var(--sf-palette-mix-50), var(--sf-color-surface))` |
+| `--sf-color-secondary-100` | `oklch(from var(--sf-color-secondary) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.88)) calc(c * 0.3) h)` |
+| `--sf-color-secondary-200` | `oklch(from var(--sf-color-secondary) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.72)) calc(c * 0.5) h)` |
+| `--sf-color-secondary-300` | `oklch(from var(--sf-color-secondary) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.52)) calc(c * 0.72) h)` |
+| `--sf-color-secondary-400` | `oklch(from var(--sf-color-secondary) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.3)) calc(c * 0.88) h)` |
+| `--sf-color-secondary-50` | `oklch(from var(--sf-color-secondary) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.95)) calc(c * 0.2) h)` |
| `--sf-color-secondary-500` | `var(--sf-color-secondary)` |
-| `--sf-color-secondary-600` | `color-mix(in oklab, var(--sf-color-secondary) var(--sf-palette-mix-600), var(--sf-color-text))` |
-| `--sf-color-secondary-700` | `color-mix(in oklab, var(--sf-color-secondary) var(--sf-palette-mix-700), var(--sf-color-text))` |
-| `--sf-color-secondary-800` | `color-mix(in oklab, var(--sf-color-secondary) var(--sf-palette-mix-800), var(--sf-color-text))` |
-| `--sf-color-secondary-900` | `color-mix(in oklab, var(--sf-color-secondary) var(--sf-palette-mix-900), var(--sf-color-text))` |
-| `--sf-color-secondary-950` | `color-mix(in oklab, var(--sf-color-secondary) var(--sf-palette-mix-950), var(--sf-color-text))` |
+| `--sf-color-secondary-600` | `oklch(from var(--sf-color-secondary) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.16)) calc(c * 0.96) h)` |
+| `--sf-color-secondary-700` | `oklch(from var(--sf-color-secondary) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.36)) calc(c * 0.88) h)` |
+| `--sf-color-secondary-800` | `oklch(from var(--sf-color-secondary) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.58)) calc(c * 0.72) h)` |
+| `--sf-color-secondary-900` | `oklch(from var(--sf-color-secondary) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.78)) calc(c * 0.52) h)` |
+| `--sf-color-secondary-950` | `oklch(from var(--sf-color-secondary) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.9)) calc(c * 0.38) h)` |
| `--sf-color-secondary-a10` | `oklch(from var(--sf-color-secondary) l c h / 0.10)` |
| `--sf-color-secondary-a30` | `oklch(from var(--sf-color-secondary) l c h / 0.30)` |
| `--sf-color-secondary-a5` | `oklch(from var(--sf-color-secondary) l c h / 0.05)` |
@@ -259,17 +259,17 @@ rebrand workflow.
| `--sf-color-tertiary` | `light-dark(var(--sf-color-tertiary-source-light), var(--sf-color-tertiary-source-dark, oklch(from var(--sf-color-tertiary-source-light) clamp(0.65, calc(0.95 - l * 0.5), 0.88) calc(c * 0.9) h)))` |
| `--sf-color-tertiary--active` | `var(--sf-color-tertiary-xdark)` |
| `--sf-color-tertiary--hover` | `var(--sf-color-tertiary-darker)` |
-| `--sf-color-tertiary-100` | `color-mix(in oklab, var(--sf-color-tertiary) var(--sf-palette-mix-100), var(--sf-color-surface))` |
-| `--sf-color-tertiary-200` | `color-mix(in oklab, var(--sf-color-tertiary) var(--sf-palette-mix-200), var(--sf-color-surface))` |
-| `--sf-color-tertiary-300` | `color-mix(in oklab, var(--sf-color-tertiary) var(--sf-palette-mix-300), var(--sf-color-surface))` |
-| `--sf-color-tertiary-400` | `color-mix(in oklab, var(--sf-color-tertiary) var(--sf-palette-mix-400), var(--sf-color-surface))` |
-| `--sf-color-tertiary-50` | `color-mix(in oklab, var(--sf-color-tertiary) var(--sf-palette-mix-50), var(--sf-color-surface))` |
+| `--sf-color-tertiary-100` | `oklch(from var(--sf-color-tertiary) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.88)) calc(c * 0.3) h)` |
+| `--sf-color-tertiary-200` | `oklch(from var(--sf-color-tertiary) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.72)) calc(c * 0.5) h)` |
+| `--sf-color-tertiary-300` | `oklch(from var(--sf-color-tertiary) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.52)) calc(c * 0.72) h)` |
+| `--sf-color-tertiary-400` | `oklch(from var(--sf-color-tertiary) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.3)) calc(c * 0.88) h)` |
+| `--sf-color-tertiary-50` | `oklch(from var(--sf-color-tertiary) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.95)) calc(c * 0.2) h)` |
| `--sf-color-tertiary-500` | `var(--sf-color-tertiary)` |
-| `--sf-color-tertiary-600` | `color-mix(in oklab, var(--sf-color-tertiary) var(--sf-palette-mix-600), var(--sf-color-text))` |
-| `--sf-color-tertiary-700` | `color-mix(in oklab, var(--sf-color-tertiary) var(--sf-palette-mix-700), var(--sf-color-text))` |
-| `--sf-color-tertiary-800` | `color-mix(in oklab, var(--sf-color-tertiary) var(--sf-palette-mix-800), var(--sf-color-text))` |
-| `--sf-color-tertiary-900` | `color-mix(in oklab, var(--sf-color-tertiary) var(--sf-palette-mix-900), var(--sf-color-text))` |
-| `--sf-color-tertiary-950` | `color-mix(in oklab, var(--sf-color-tertiary) var(--sf-palette-mix-950), var(--sf-color-text))` |
+| `--sf-color-tertiary-600` | `oklch(from var(--sf-color-tertiary) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.16)) calc(c * 0.96) h)` |
+| `--sf-color-tertiary-700` | `oklch(from var(--sf-color-tertiary) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.36)) calc(c * 0.88) h)` |
+| `--sf-color-tertiary-800` | `oklch(from var(--sf-color-tertiary) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.58)) calc(c * 0.72) h)` |
+| `--sf-color-tertiary-900` | `oklch(from var(--sf-color-tertiary) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.78)) calc(c * 0.52) h)` |
+| `--sf-color-tertiary-950` | `oklch(from var(--sf-color-tertiary) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.9)) calc(c * 0.38) h)` |
| `--sf-color-tertiary-a10` | `oklch(from var(--sf-color-tertiary) l c h / 0.10)` |
| `--sf-color-tertiary-a30` | `oklch(from var(--sf-color-tertiary) l c h / 0.30)` |
| `--sf-color-tertiary-a5` | `oklch(from var(--sf-color-tertiary) l c h / 0.05)` |
@@ -460,16 +460,8 @@ rebrand workflow.
| `--sf-opacity-disabled` | `0.45` |
| `--sf-opacity-muted` | `0.5` |
| `--sf-optical-sizing` | `auto` |
-| `--sf-palette-mix-100` | `8%` |
-| `--sf-palette-mix-200` | `20%` |
-| `--sf-palette-mix-300` | `40%` |
-| `--sf-palette-mix-400` | `65%` |
-| `--sf-palette-mix-50` | `4%` |
-| `--sf-palette-mix-600` | `82%` |
-| `--sf-palette-mix-700` | `62%` |
-| `--sf-palette-mix-800` | `38%` |
-| `--sf-palette-mix-900` | `18%` |
-| `--sf-palette-mix-950` | `8%` |
+| `--sf-palette-shade-l` | `0.1` |
+| `--sf-palette-tint-l` | `0.97` |
| `--sf-print-base-size` | `11pt` |
| `--sf-print-page-margin` | `2cm` |
| `--sf-print-page-size` | `a4` |
diff --git a/scripts/token-tiers.js b/scripts/token-tiers.js
index 954e4215..06f9265a 100644
--- a/scripts/token-tiers.js
+++ b/scripts/token-tiers.js
@@ -77,11 +77,12 @@ const ADVANCED = new Set([
const STEP = '(?:2xs|xs|s|m|l|xl|2xl|3xl|4xl)';
const ADVANCED_PATTERNS = [
new RegExp(`^--sf-(?:space|text)-${STEP}-to-${STEP}$`),
- // · Palette ramp-shape knobs (core/tokens.css):
- // `--sf-palette-mix-{step}` — color-mix() percentages reshaping the
- // entire numeric palette ladder at once. Niche/powerful, documented
+ // · Palette ramp lightness anchors (core/tokens.css):
+ // `--sf-palette-tint-l` / `--sf-palette-shade-l` — absolute OKLCH
+ // lightness targets the tint/shade steps pull toward, reshaping the
+ // whole numeric palette ladder at once. Niche/powerful, documented
// as PUBLIC-ADVANCED in the core palette section.
- /^--sf-palette-mix-\d+$/,
+ /^--sf-palette-(?:tint|shade)-l$/,
];
/**
diff --git a/tests/ramp-monotonic.spec.js b/tests/ramp-monotonic.spec.js
new file mode 100644
index 00000000..46c1ffe7
--- /dev/null
+++ b/tests/ramp-monotonic.spec.js
@@ -0,0 +1,117 @@
+// @ts-check
+// Proves the numeric brand ramp (--sf-color-{family}-50…950) never FOLDS —
+// its luminance is monotonically non-increasing from step 50 to 950 for ANY
+// source colour the user picks, at any lightness of that colour.
+//
+// This is the guarantee the absolute-anchor ramp model buys us: each step
+// pulls the family colour's own OKLCH lightness a fixed fraction toward an
+// ABSOLUTE target (--sf-palette-tint-l / --sf-palette-shade-l), clamped so a
+// tint can never end darker than the base nor a shade lighter. The ladder
+// therefore stays light→dark regardless of where the source sits relative to
+// the theme's text/surface tokens. The previous color-mix()-toward-text/surface
+// model folded into a "U" when the source fell outside the anchor band.
+//
+// fixture.html loads the full bundle (palette tokens are core, gated behind the
+// relative-colour @supports block Chromium/Firefox/WebKit satisfy).
+import { test, expect } from '@playwright/test';
+import path from 'node:path';
+import { pathToFileURL } from 'node:url';
+
+const FIXTURE = pathToFileURL(path.join(import.meta.dirname, 'fixture.html')).href;
+
+// Distinct hue + chroma per family so the sweep exercises genuinely different
+// source colours (not the same ramp maths five times). Monotonicity is a
+// function of lightness, but varying hue/chroma guards against any hue-specific
+// gamut-mapping surprise.
+const FAMILIES = [
+ { name: 'primary', hue: 264, chroma: 0.16 },
+ { name: 'secondary', hue: 200, chroma: 0.09 },
+ { name: 'tertiary', hue: 320, chroma: 0.18 },
+ { name: 'action', hue: 150, chroma: 0.14 },
+ { name: 'neutral', hue: 30, chroma: 0.03 },
+];
+const STEPS = [50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 950];
+
+// Source lightnesses spanning the whole axis, including values OUTSIDE the
+// default anchor band (0.06 is darker than the text anchor; 0.98 is lighter
+// than the surface anchor) — exactly where the old model folded.
+const SOURCE_LS = [0.06, 0.22, 0.5, 0.78, 0.98];
+
+// Self-contained for page.evaluate: for one family, sweep several source
+// lightnesses and return, per sweep, the {step, lum, alpha} of every step.
+function sweepFamily([family, steps, sourceLs, theme, chroma, hue]) {
+ const root = document.documentElement;
+ root.setAttribute('data-theme', theme);
+ // Sweep the theme's own source token: source-light under light, source-dark
+ // under dark — dark exercises the same clamp path with different source
+ // colours, so the guarantee is symmetric across both modes.
+ const prop = `--sf-color-${family}-source-${theme}`;
+ const cv = document.createElement('canvas'); cv.width = cv.height = 1;
+ const ctx = cv.getContext('2d', { willReadFrequently: true });
+ const lin = v => { v /= 255; return v <= 0.03928 ? v / 12.92 : ((v + 0.055) / 1.055) ** 2.4; };
+ const el = document.createElement('div'); document.body.appendChild(el);
+
+ const sweeps = sourceLs.map(L => {
+ root.style.setProperty(prop, `oklch(${L} ${chroma} ${hue})`);
+ const rows = steps.map(step => {
+ el.style.backgroundColor = `var(--sf-color-${family}-${step})`;
+ const c = getComputedStyle(el).backgroundColor;
+ ctx.clearRect(0, 0, 1, 1); ctx.fillStyle = c; ctx.fillRect(0, 0, 1, 1);
+ const [r, g, b, a] = ctx.getImageData(0, 0, 1, 1).data;
+ return { step, lum: 0.2126 * lin(r) + 0.7152 * lin(g) + 0.0722 * lin(b), alpha: a };
+ });
+ return { L, rows };
+ });
+
+ el.remove();
+ root.style.removeProperty(prop);
+ return sweeps;
+}
+
+test.describe('Brand ramp is monotonic for any source colour', () => {
+ for (const { name: family, hue, chroma } of FAMILIES) {
+ for (const theme of ['light', 'dark']) {
+ test(`${family} ramp never folds across the lightness axis (${theme})`, async ({ page }) => {
+ await page.goto(FIXTURE);
+ const sweeps = await page.evaluate(sweepFamily, [family, STEPS, SOURCE_LS, theme, chroma, hue]);
+
+ const EPS = 0.006; // absorbs 8-bit rounding; clamped extremes go flat (equal), a fold increases
+ for (const { L, rows } of sweeps) {
+ for (const { step, alpha } of rows) {
+ expect(alpha, `${family}-${step} (${theme}, source L=${L}) should be opaque`).toBe(255);
+ }
+ for (let i = 0; i < rows.length - 1; i++) {
+ expect(
+ rows[i + 1].lum,
+ `${family} (${theme}, source L=${L}): step ${rows[i + 1].step} must not be lighter than ${rows[i].step}`
+ ).toBeLessThanOrEqual(rows[i].lum + EPS);
+ }
+ }
+ });
+ }
+ }
+
+ test('default palette keeps light-to-dark endpoints (50 light, 950 dark)', async ({ page }) => {
+ await page.goto(FIXTURE);
+ for (const { name: family } of FAMILIES) {
+ const ends = await page.evaluate(([fam, steps]) => {
+ document.documentElement.setAttribute('data-theme', 'light');
+ const cv = document.createElement('canvas'); cv.width = cv.height = 1;
+ const ctx = cv.getContext('2d', { willReadFrequently: true });
+ const lin = v => { v /= 255; return v <= 0.03928 ? v / 12.92 : ((v + 0.055) / 1.055) ** 2.4; };
+ const el = document.createElement('div'); document.body.appendChild(el);
+ const read = step => {
+ el.style.backgroundColor = `var(--sf-color-${fam}-${step})`;
+ ctx.clearRect(0, 0, 1, 1); ctx.fillStyle = getComputedStyle(el).backgroundColor; ctx.fillRect(0, 0, 1, 1);
+ const [r, g, b] = ctx.getImageData(0, 0, 1, 1).data;
+ return 0.2126 * lin(r) + 0.7152 * lin(g) + 0.0722 * lin(b);
+ };
+ const res = { light: read(steps[0]), dark: read(steps[steps.length - 1]) };
+ el.remove();
+ return res;
+ }, [family, STEPS]);
+ expect(ends.light, `${family}-50 should be light`).toBeGreaterThan(0.6);
+ expect(ends.dark, `${family}-950 should be dark`).toBeLessThan(0.15);
+ }
+ });
+});
diff --git a/tests/token-api.snapshot.json b/tests/token-api.snapshot.json
index 64fd35fd..5c3f49a0 100644
--- a/tests/token-api.snapshot.json
+++ b/tests/token-api.snapshot.json
@@ -536,16 +536,8 @@
"--sf-opacity-muted",
"--sf-optical-sizing",
"--sf-overlap-pull",
- "--sf-palette-mix-100",
- "--sf-palette-mix-200",
- "--sf-palette-mix-300",
- "--sf-palette-mix-400",
- "--sf-palette-mix-50",
- "--sf-palette-mix-600",
- "--sf-palette-mix-700",
- "--sf-palette-mix-800",
- "--sf-palette-mix-900",
- "--sf-palette-mix-950",
+ "--sf-palette-shade-l",
+ "--sf-palette-tint-l",
"--sf-print-base-size",
"--sf-print-page-margin",
"--sf-print-page-size",
diff --git a/token-registry.json b/token-registry.json
index 2899b694..5e23826f 100644
--- a/token-registry.json
+++ b/token-registry.json
@@ -1,7 +1,7 @@
{
"_meta": {
"generatedBy": "scripts/gen-token-registry.js",
- "nextId": 798
+ "nextId": 800
},
"tokens": [
{
@@ -1993,43 +1993,53 @@
},
{
"id": 489,
- "name": "--sf-palette-mix-100"
+ "name": "--sf-palette-mix-100",
+ "removed": true
},
{
"id": 490,
- "name": "--sf-palette-mix-200"
+ "name": "--sf-palette-mix-200",
+ "removed": true
},
{
"id": 491,
- "name": "--sf-palette-mix-300"
+ "name": "--sf-palette-mix-300",
+ "removed": true
},
{
"id": 492,
- "name": "--sf-palette-mix-400"
+ "name": "--sf-palette-mix-400",
+ "removed": true
},
{
"id": 493,
- "name": "--sf-palette-mix-50"
+ "name": "--sf-palette-mix-50",
+ "removed": true
},
{
"id": 494,
- "name": "--sf-palette-mix-600"
+ "name": "--sf-palette-mix-600",
+ "removed": true
},
{
"id": 495,
- "name": "--sf-palette-mix-700"
+ "name": "--sf-palette-mix-700",
+ "removed": true
},
{
"id": 496,
- "name": "--sf-palette-mix-800"
+ "name": "--sf-palette-mix-800",
+ "removed": true
},
{
"id": 497,
- "name": "--sf-palette-mix-900"
+ "name": "--sf-palette-mix-900",
+ "removed": true
},
{
"id": 498,
- "name": "--sf-palette-mix-950"
+ "name": "--sf-palette-mix-950",
+ "removed": true
},
{
"id": 499,
@@ -3239,6 +3249,14 @@
{
"id": 797,
"name": "--sf-density"
+ },
+ {
+ "id": 798,
+ "name": "--sf-palette-shade-l"
+ },
+ {
+ "id": 799,
+ "name": "--sf-palette-tint-l"
}
]
}
diff --git a/user-manual/colors.md b/user-manual/colors.md
index e3461607..17f83820 100644
--- a/user-manual/colors.md
+++ b/user-manual/colors.md
@@ -150,9 +150,17 @@ background:
Use these (not the numeric 50–950 ramp) when the element sits on top of a
background you don't control — e.g. a colored badge that might appear on a
-light or dark card. The numeric ramp is baked against a specific surface/text
-color and will look wrong if the background changes underneath it; the alpha
-variants stay correct anywhere.
+light or dark card. The numeric ramp gives **solid** colors, so it can't blend
+into an unknown background; the alpha variants stay correct anywhere.
+
+> **How the numeric ramp is generated.** Each step pulls the family color's own
+> OKLCH lightness a fixed fraction toward an **absolute** target —
+> `--sf-palette-tint-l` (`0.97`) for tints 50–400, `--sf-palette-shade-l`
+> (`0.1`) for shades 600–950 — clamped so a tint is never darker than the base
+> and a shade never lighter. The ramp is therefore **monotonic light→dark for
+> any source color** you pick, and does not fold even if your brand color is
+> very dark/light or you move the neutral/base anchors. (`base` is a separate
+> absolute grey ladder; status colors have no numeric ramp.)
## Status colors
diff --git a/user-manual/system-kolorow.md b/user-manual/system-kolorow.md
new file mode 100644
index 00000000..40c7ee84
--- /dev/null
+++ b/user-manual/system-kolorow.md
@@ -0,0 +1,452 @@
+# System kolorów SLASHED — pełna instrukcja użytkownika
+
+Ten przewodnik tłumaczy **kiedy, na jakim elemencie i jakiego narzędzia użyć**,
+żeby pokolorować stronę na SLASHED — na konkretnym przykładzie landing page'a
+(typu „WordPress sites that load faster, convert better”). Jeśli gubisz się w
+tym, czym różni się `surface` od `color-on-*` od `data-theme` — to jest
+dokument dla Ciebie.
+
+> Źródło prawdy: `core/tokens.css` i `core/themes.css`. Jeśli cokolwiek tutaj
+> kłóci się z kodem — wygrywa kod. Krótszy słownik tokenów: [`colors.md`](colors.md).
+
+---
+
+## 1. Model mentalny — trzy warstwy
+
+Cały system to **trzy warstwy**. Zawsze pracujesz na najwyższej, do której się da
+zejść — im wyżej, tym mniej decyzji podejmujesz ręcznie.
+
+```
+WARSTWA 3 Powierzchnie .sf-surface--primary, .sf-btn--action, .sf-card
+ (klasy) → „pomaluj cały blok, tekst dobierz sam”
+ ▲
+WARSTWA 2 Tokeny semantyczne --sf-color-text, --sf-color-bg, --sf-color-border,
+ (role) --sf-color-link, --sf-color-heading
+ ▲ → „to jest tekst / tło / obramowanie” (nie kolor!)
+ ▲
+WARSTWA 1 Kolory źródłowe --sf-color-primary, --sf-color-action, …
+ (marka) → ustawiasz RAZ przy brandingu, potem nie ruszasz
+```
+
+Reguła nadrzędna: **im wyżej, tym lepiej.**
+
+1. Da się użyć gotowej **powierzchni/komponentu**? Użyj (`.sf-surface--*`, `.sf-btn`, `.sf-card`).
+2. Nie da się? Sięgnij po **token semantyczny** (`--sf-color-text`, `--sf-color-bg`…).
+3. Prawie nigdy nie odwołuj się bezpośrednio do **koloru źródłowego** (`--sf-color-primary`)
+ w treści strony — to knob do brandingu, nie do malowania akapitów.
+
+Dlaczego to działa: warstwa 2 i 3 **same przełączają się** w tryb ciemny, na hover,
+pod `prefers-contrast` itd. Jeśli wpiszesz gdzieś `color: #1a1a1a`, tracisz to
+wszystko i musisz pisać własne reguły `[data-theme="dark"]`. Nie rób tego.
+
+---
+
+## 2. Trzy sposoby nakładania koloru — drzewko decyzyjne
+
+To najważniejsza tabela w całym dokumencie. Zanim pomalujesz cokolwiek, zadaj
+sobie pytanie: **czym jest ten element?**
+
+| Chcę… | Użyj | Dlaczego |
+|---|---|---|
+| pojedynczy **kolorowy kafelek/płytę** (jeden brandowy blok, karta CTA), a tekst w środku ma sam się skontrastować | klasa powierzchni `.sf-surface--primary` (`--action`, `--inverse`, `--success`…) | ustawia tło + auto-dobiera kolor tekstu, linków, obramowań, focus-ringu |
+| **całą sekcję w innym trybie** (jasna strona, ale ciemny pas z kartami, formularzem, przyciskami) | atrybut `data-theme="dark"` na `` | przełącza *cały* zestaw tokenów trybu — tła, tekst, karty, inputy w środku adaptują się jak w prawdziwym dark mode |
+| **treść na neutralnym tle strony** (akapit, nagłówek, obramowanie, link) | token semantyczny `--sf-color-text`, `--sf-color-heading`, `--sf-color-border`, `--sf-color-link` | to są *role*, nie kolory — same śledzą motyw |
+| **przycisk / link / akcent interaktywny** | `.sf-btn` (+ wariant) albo `--sf-color-action` | interaktywność ma swój osobny kolor (patrz §3) |
+| **tekst leżący wprost na jednolitym kolorze marki** (napis na pomarańczowym) | `--sf-color-text--on-primary` (`--on-action`, `--on-danger`…) | auto-dobiera czerń albo biel pod ten konkretny kolor |
+| **delikatny barwny wash** (tło alertu, podświetlony wiersz) | wariant `-subtle` / `-tint` albo rampa numeryczna (`-100`, `-200`) | patrz §6 |
+
+### Kluczowa różnica: `.sf-surface--*` vs `data-theme` vs `--sf-color-*-on`
+
+To jest dokładnie to, co Cię myli — rozbijmy na czynniki pierwsze:
+
+- **`.sf-surface--primary`** = *„weź kolor marki i zrób z niego jednolitą płytę”.*
+ Tło = `--sf-color-primary`, a framework policzy, czy tekst ma być czarny czy
+ biały (`--sf-color-text--on-primary`) i przepisze to na WSZYSTKIE tokeny
+ potomków (`--sf-color-text`, `--sf-color-heading`, `--sf-color-link`,
+ `--sf-color-border`, focus-ring, karetka). Efekt: cokolwiek włożysz do środka,
+ będzie czytelne. Używasz do **jednej kolorowej rzeczy**: baner CTA, wyróżniona
+ karta cennika, badge.
+
+- **`data-theme="dark"`** (lub `="light"`) = *„ta sekcja żyje w innym trybie”.*
+ Nie maluje jednym kolorem marki — przełącza cały motyw: `--sf-color-bg` robi się
+ ciemne, `--sf-color-text` jasne, a karty (`.sf-card`), inputy i przyciski w
+ środku **adaptują się same**, tak jak zrobiłyby to w dark mode całej strony.
+ Używasz do **całego pasa/sekcji** z wieloma elementami (ciemna sekcja z trzema
+ kartami cennika i przyciskami).
+
+- **`--sf-color-text--on-*`** = *„sam kolor tekstu na znanym tle marki”.*
+ Nie tło, nie płyta — tylko wartość koloru. Sięgasz po nią, gdy malujesz coś
+ ręcznie (np. własny badge) i potrzebujesz czcionki, która skontrastuje się z
+ konkretnym `--sf-color-primary`.
+
+Zapamiętaj skrótowo:
+**jeden kolorowy klocek → `.sf-surface--*`;
+cały ciemny pas → `data-theme`;
+sam kolor napisu na kolorze → `--sf-color-text--on-*`.**
+
+---
+
+## 3. `primary` vs `action` — rozróżnienie, które psuje najwięcej stron
+
+Framework rozdziela **kolor marki** od **koloru interakcji** na dwa osobne knoby.
+Na naszym przykładzie oba są pomarańczowe — ale to nie znaczy, że to jedno i to samo.
+
+| Token | Rola | Na przykładowej stronie |
+|---|---|---|
+| `--sf-color-primary` | tożsamość marki | akcent w nagłówku hero („**actually** win business”), wyróżniona płyta, znaczniki dekoracyjne |
+| `--sf-color-action` | **domyślny kolor interaktywny** | każdy przycisk CTA, link, focus-ring, kontrolka formularza |
+
+**Najczęstszy błąd:** malowanie przycisków przez `--sf-color-primary`. Przyciski
+i linki domyślnie biorą `--sf-color-action`. Trzymaj je rozdzielnie — dzięki temu
+możesz zmienić kolor wszystkich CTA bez ruszania koloru logo i odwrotnie.
+
+Do stanów interakcji nie kombinuj z wartościami — są gotowe aliasy:
+
+```css
+--sf-color-action /* stan spoczynkowy */
+--sf-color-action--hover /* = -600, hover */
+--sf-color-action--active /* = -800, wciśnięcie */
+```
+
+(ten sam wzorzec dla `primary`, `secondary`, `tertiary`, `neutral`).
+
+---
+
+## 4. Sekcja po sekcji — jak zbudować przykładową stronę
+
+Landing z załącznika to naprzemienne pasy **jasny → ciemny → jasny → ciemny**,
+z jednym pomarańczowym akcentem. Poniżej każdy pas rozpisany na właściwe narzędzia.
+
+### 4.1. Hero (jasny pas) — „WordPress sites that load faster…”
+
+Tło strony jest domyślne (nie ustawiasz go — to `--sf-color-bg`). Malujesz tylko treść.
+
+```html
+
+
+
+ WordPress sites that load faster, convert better, and
+ actually win business
+
` **nie dostaje żadnej klasy koloru** — bierze `--sf-color-heading`
+ automatycznie z reguł bazowych. Nie pisz `color:` ręcznie.
+- Pomarańczowy akcent w nagłówku: `.sf-text-gradient` (gradient marki na tekście)
+ albo, jeśli chcesz płaski kolor, ``.
+- Podtytuł „ściszony”: klasa rozmiaru + token `--sf-color-text--secondary`
+ (np. własna klasa: `.lead { color: var(--sf-color-text--secondary); }`).
+- Przycisk CTA: `.sf-btn .sf-btn--action`. Tło, kolor napisu (`--on-action`),
+ hover i focus-ring — wszystko gotowe.
+- Pływająca karta/widget po prawej: `.sf-card` — dostaje `--sf-color-surface` jako
+ tło i `--sf-color-border` jako obramowanie, bez dotykania kolorów.
+
+### 4.2. Ciemny pas — „Three ways to work together” (trzy karty cennika)
+
+To jest **cała ciemna sekcja z wieloma elementami** → `data-theme="dark"`, nie
+`.sf-surface`.
+
+```html
+
+
+
Three ways to work together
+
+ …
+ …
+ …
+
+
+
+```
+
+Dlaczego `data-theme="dark"`, a nie `.sf-surface--inverse`:
+- `data-theme="dark"` przełącza **cały motyw sekcji** — `--sf-color-bg` robi się
+ ciemne, a karty `.sf-card` w środku **same** biorą ciemne `--sf-color-surface`,
+ jasny tekst, właściwe obramowania. Dokładnie to widać na makiecie.
+- `.sf-surface--inverse` dałoby jedną jednolitą ciemną płytę, ale karty w środku
+ nie „wskoczyłyby” w tryb ciemny tak naturalnie — inverse jest do pojedynczego
+ klocka, nie do pasa z zagnieżdżonymi komponentami.
+- `.sf-full-bleed` rozciąga tło na całą szerokość ekranu, a `.sf-container`
+ trzyma treść w kolumnie.
+
+Wyróżniona (środkowa, pomarańczowa) karta — to **jeden kolorowy klocek**, więc tu
+już wchodzi powierzchnia:
+
+```html
+…
+```
+
+`.sf-surface--primary` na karcie = pomarańczowe tło + auto-kontrast napisów w środku.
+
+### 4.3. Jasny pas — tabela porównawcza „Agency results. Freelancer access.”
+
+Wracamy na neutralne tło. Wszystko na tokenach semantycznych:
+
+```css
+.compare { border: var(--sf-border-width-1) solid var(--sf-color-border); }
+.compare thead { background: var(--sf-color-inset); } /* delikatnie wciśnięty nagłówek */
+.compare td, .compare th { border-block: var(--sf-border-width-1) solid var(--sf-color-border--subtle); }
+.compare .highlight { color: var(--sf-color-action); } /* wyróżniona kolumna */
+```
+
+- Linie tabeli: `--sf-color-border` (mocniejsze) i `--sf-color-border--subtle` (cieńsze).
+- Tło paska nagłówka: `--sf-color-inset` (płyta lekko cofnięta względem `--sf-color-bg`).
+- Nigdzie nie podajesz konkretnego szarego — te tokeny same się dostroją w dark mode.
+
+### 4.4. Ciemny pas — opinie „4.9 across 50+ projects”
+
+Znów `data-theme="dark"` na sekcji + `.sf-card` na cytatach. Gwiazdki oceny mogą
+użyć `--sf-color-warning` (klasyczny „złoty” akcent ocen), logotypy klientów —
+`--sf-color-text--secondary`, żeby były obecne, ale nie krzyczały.
+
+### 4.5. Jasny pas — proces „A 5-phase process” (stepper)
+
+Aktywny krok = interakcja/stan → `--sf-color-action` i stan `.sf-is-active`:
+
+```css
+.step { color: var(--sf-color-text--secondary); } /* krok nieaktywny */
+.step.sf-is-active { color: var(--sf-color-action); } /* krok bieżący */
+.step__dot { background: var(--sf-color-border); }
+.step.sf-is-active .step__dot { background: var(--sf-color-action); }
+```
+
+`.sf-is-*` to klasy stanu (patrz [`docs/states.md`](../docs/states.md)) — sygnalizują
+stan JS-em, a kolory zostają w tokenach.
+
+### 4.6. Ciemny pas CTA — „Ready to build something that actually performs”
+
+`data-theme="dark"` + przycisk `.sf-btn--action`. Ponieważ to sekcja w trybie
+ciemnym, `--sf-color-action` automatycznie użyje swojego wariantu dla ciemnego tła —
+nic nie robisz ręcznie.
+
+### 4.7. Jasny pas — cennik „Your site doesn't stop working after launch day”
+
+Trzy karty, środkowa wyróżniona na pomarańczowo (dokładnie wzorzec z §4.2):
+
+```html
+
+ Core
+ Commerce
+ Custom
+
+```
+
+Alternatywa dla wyróżnienia „ramką zamiast wypełnienia” — użyj rampy albo obramowania marki:
+
+```css
+.plan--featured {
+ border-color: var(--sf-color-primary);
+ background: var(--sf-color-primary-superlight); /* ledwie widoczny ciepły wash */
+}
+```
+
+Kiedy `.sf-surface--primary` (pełny pomarańcz), a kiedy `-superlight` (delikatny wash)?
+Pełna płyta = maksymalne wyróżnienie; wash = subtelny akcent bez krzyku. Wybór estetyczny.
+
+### 4.8. Ciemny pas — „I don't take on every project” (dwie kolumny z listami)
+
+`data-theme="dark"` + znaczniki list w kolorze marki:
+
+```html
+
…
+```
+
+Gdybyś chciał listę „robię / nie robię” w barwach statusów: `--sf-color-success`
+i `--sf-color-danger` na ikonkach.
+
+### 4.9. Jasny pas — FAQ „Questions I get asked a lot”
+
+Akordeon = wiersze rozdzielone `--sf-color-border`, pytanie w `--sf-color-heading`,
+odpowiedź w `--sf-color-text`. Ikona +/- w `--sf-color-action`.
+
+### 4.10. Ciemny pas — formularz kontaktowy „Let's talk about your project”
+
+`data-theme="dark"` na sekcji i… nic więcej przy kolorach inputów. Kontrolki z
+`optional/forms.css` **same** wezmą ciemne tło pola, jasny tekst, obramowanie i
+focus-ring w kolorze `--sf-color-action`. To jest cała nagroda za trzymanie się
+tokenów semantycznych — formularz „po prostu działa” w ciemnym pasie.
+
+> Uwaga na `color-scheme`: `data-theme="dark"` ustawia je za Ciebie, więc natywne
+> elementy (scrollbary, autouzupełnianie, kalendarz w ``) też będą
+> ciemne. Jeśli kiedyś zrobisz własny tryb bez `data-theme`, pamiętaj ustawić
+> `color-scheme` ręcznie.
+
+### 4.11. Stopka (ciemna)
+
+Najciemniejszy pas. Dwie opcje:
+- `data-theme="dark"` — jeśli stopka ma linki, inputy newslettera itp. (adaptują się).
+- `.sf-surface--inverse` albo `.sf-surface--secondary` — jeśli to zwarta, jednolita
+ płyta bez interaktywnych komponentów wymagających pełnego motywu.
+
+---
+
+## 5. Tokeny semantyczne — pełna ściąga „na co którego”
+
+To są role, których używasz na neutralnym tle strony (poza kolorowymi płytami):
+
+| Token | Na czym |
+|---|---|
+| `--sf-color-text` | tekst akapitów, treść |
+| `--sf-color-text--secondary` | podtytuły, metadane, „ściszony” tekst |
+| `--sf-color-heading` | nagłówki (h1–h6 biorą go automatycznie) |
+| `--sf-color-link` | linki (śledzi hue `action`, ale z bezpiecznym kontrastem) |
+| `--sf-color-border` | obramowania, dividery |
+| `--sf-color-border--subtle` / `--strong` | cieńsze / mocniejsze linie |
+| `--sf-color-bg` | tło strony (rzadko ustawiasz — jest domyślne) |
+| `--sf-color-surface` | tło kart/kontenerów (`.sf-card` używa go sam) |
+| `--sf-color-inset` | płyta cofnięta (nagłówek tabeli, pole kodu) |
+| `--sf-color-raised` | płyta wyniesiona (uniesiona karta, popover) |
+| `--sf-color-inverse` | odwrócone tło (ciemne w jasnym trybie) |
+
+Wszystkie **same przełączają się** w dark mode i pod `data-theme`. Nigdy nie
+podajesz pod nie konkretnego hexa.
+
+---
+
+## 6. Rampy, alfa i statusy — kiedy które
+
+### Rampy numeryczne 50 → 950 (marka)
+
+`primary`, `secondary`, `tertiary`, `action`, `neutral`, `base` mają drabinę
+`-50` (ledwie widoczny tint) → `-500` (sam kolor) → `-950` (prawie czarny).
+Aliasy do tych, po które naprawdę sięgasz:
+
+```
+--sf-color-primary-superlight (= -50) subtelne tło / wash
+--sf-color-primary-xlight (= -200) tło pigułki/badge
+--sf-color-primary-lighter (= -400) obramowania na kolorowym komponencie
+--sf-color-primary-darker (= -600) hover, tekst-na-tincie
+--sf-color-primary-xdark (= -800) stan wciśnięty
+```
+
+**Jak liczona jest rampa.** Każdy krok przyciąga jasność OKLCH koloru marki o
+ustalony ułamek ku **absolutnej** kotwicy — `--sf-palette-tint-l` (`0.97`) dla
+tintów 50–400, `--sf-palette-shade-l` (`0.1`) dla cieni 600–950 — z klamrą, więc
+tint nigdy nie jest ciemniejszy od bazy, a cień nigdy jaśniejszy. Dzięki temu
+rampa jest **monotoniczna (jasno→ciemno) dla dowolnego koloru źródłowego** i
+**nie składa się w „U"** — nawet gdy Twój brand jest bardzo ciemny/jasny albo
+przesuniesz kotwice `neutral`/`base`. (`base` to osobna, absolutna drabina
+szarości; statusy nie mają rampy numerycznej.)
+
+Kroki dają **jednolite** (nieprzezroczyste) kolory, więc nie wtapiają się w
+nieznane tło — do tego służą warianty alfa (niżej).
+
+### Warianty alfa (przezroczyste)
+
+Gdy element leży na **nieznanym** tle (badge, który może trafić na jasną albo
+ciemną kartę) — użyj wariantów alfa zamiast rampy numerycznej:
+
+| Token | Krycie | Alias |
+|---|---|---|
+| `-a5` | 5% | `-tint` |
+| `-a10` | 10% | `-subtle` |
+| `-a30` | 30% | `-muted` |
+| `-a50` | 50% | — |
+
+Alfa przepuszcza tło pod spodem (przezroczystość), więc jest poprawna na każdym
+tle; rampa numeryczna daje kolory jednolite, które nie reagują na to, co jest
+pod nimi.
+
+### Statusy: `success`, `warning`, `info`, `danger`
+
+Nie mają pełnej rampy — mają zestaw celowy:
+
+| Token | Na czym |
+|---|---|
+| `--sf-color-{status}` | ikony, drobne akcenty, obramowania |
+| `--sf-color-{status}-strong` | jednolite tło z większym kontrastem, hover na przycisku statusu |
+| `--sf-color-{status}-subtle` | tinted tło kontenera (np. pudełko alertu) |
+| `--sf-color-{status}-muted` (~30%) | mocniejszy wash, np. tło badge |
+| `--sf-color-{status}-tint` (~5%) | najlżejszy wash |
+
+Przykład — alert błędu:
+
+```css
+.alert--danger {
+ background: var(--sf-color-danger-subtle);
+ border-color: var(--sf-color-danger);
+ color: var(--sf-color-danger-strong);
+}
+```
+
+---
+
+## 7. Tekst wprost na kolorze — `--sf-color-text--on-*`
+
+Ilekroć napis leży bezpośrednio na jednolitym kolorze marki/statusu, użyj
+pasującego `--sf-color-text--on-*` zamiast wpisywać czerń/biel. Framework
+sam wybierze wariant dający lepszy kontrast pod ten konkretny kolor:
+
+```css
+.badge--primary {
+ background: var(--sf-color-primary);
+ color: var(--sf-color-text--on-primary);
+}
+```
+
+Dostępne: `--on-primary`, `--on-secondary`, `--on-tertiary`, `--on-action`,
+`--on-neutral`, `--on-success`, `--on-warning`, `--on-info`, `--on-danger`,
+`--on-inverse`.
+
+**Uwaga na „pas niejednoznaczny” (L ≈ 0.52–0.67).** Jeśli Twój kolor marki
+wpada w środek jasności, binarny wybór czerń/biel może nie wyrobić 4.5:1.
+Wtedy albo przesuń próg dla tej powierzchni:
+
+```css
+.sf-surface--primary { --sf-contrast-threshold: 0.55; }
+```
+
+…albo przypnij kolor tekstu ręcznie:
+
+```css
+.sf-surface--primary { --sf-color-text--on-primary: oklch(0.15 0 0); }
+```
+
+Te nadpisania trzymaj w warstwie `slashed.overrides`, żeby przetrwały aktualizacje.
+
+---
+
+## 8. Najczęstsze błędy (i jak je naprawić)
+
+1. **Malowanie przycisku przez `--sf-color-primary`.** → Przyciski to `--sf-color-action`
+ (albo `.sf-btn--action`). `primary` to marka, nie interakcja (§3).
+2. **Wpisywanie `#hex` / `rgb()` w treści strony.** → Tracisz dark mode i hover.
+ Zejdź na tokeny semantyczne (§5).
+3. **Pisanie własnych reguł `[data-theme="dark"] { color: … }`.** → Niepotrzebne.
+ Tokeny semantyczne robią to za Ciebie.
+4. **Użycie `.sf-surface--inverse` na całym ciemnym pasie z kartami i formularzem.**
+ → To zadanie dla `data-theme="dark"`; `.sf-surface--*` jest do pojedynczego klocka (§2).
+5. **Rampa numeryczna (`-200`) na elemencie pływającym nad nieznanym tłem.**
+ → Użyj wariantu alfa (`-subtle` / `-tint`), bo krok rampy to kolor jednolity i nie wtapia się w nieznane tło (§6).
+6. **Ręczne dobieranie czerni/bieli na kolorowym tle.** → `--sf-color-text--on-*` (§7).
+
+---
+
+## 9. Ściąga „potrzebuję…”
+
+| Potrzebuję… | Sięgam po |
+|---|---|
+| przycisk / link / focus-ring | `.sf-btn--action` lub `--sf-color-action` (+ `--hover`) |
+| kolorowy hero/marketingowy blok | `--sf-color-primary` / `.sf-text-gradient` |
+| cały ciemny pas z kartami i formularzem | `data-theme="dark"` na `` |
+| pojedynczy kolorowy klocek (wyróżniona karta, baner) | `.sf-surface--primary` (`--action`, `--inverse`…) |
+| tekst, nagłówki, obramowania na tle strony | `--sf-color-text`, `--sf-color-heading`, `--sf-color-border` |
+| tła kart/paneli | `--sf-color-surface`, `--sf-color-inset`, `--sf-color-raised` |
+| feedback sukces/ostrzeżenie/info/błąd | token statusu (`-subtle` tło, `-strong` hover) |
+| tekst wprost na jednolitym kolorze | `--sf-color-text--on-*` |
+| tint marki nad ZNANYM tłem | rampa numeryczna (`-100`…`-400` / `-superlight`…) |
+| tint marki nad NIEZNANYM tłem | wariant alfa (`-tint` / `-subtle` / `-muted`) |
+
+---
+
+## Zobacz też
+
+- [`colors.md`](colors.md) — zwięzły słownik 10 kolorów źródłowych i tokenów
+- [`../docs/theming.md`](../docs/theming.md) — rebranding w 6 tokenach + silnik fluid
+- [`../docs/tokens.md`](../docs/tokens.md) — pełny wykaz tokenów
+- [`../docs/states.md`](../docs/states.md) — klasy stanu `.sf-is-*`