From 489a2048b2b66fce67b6473ba052fd89a3e732a7 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 20 Jul 2026 08:35:50 +0000 Subject: [PATCH 1/6] docs: add API audit checklist for token/class classification Add a manual-review checklist grouping the full public API surface (747 tokens + 323 classes) into three tiers: foundation, useful-optional, and niche. Large uniform families are grouped; per-element decisions (classes, component variants, state hooks) are listed individually. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01CAsUv13o2SAi7Tnsx93EtV --- docs/api-audit-checklist.md | 336 ++++++++++++++++++++++++++++++++++++ 1 file changed, 336 insertions(+) create mode 100644 docs/api-audit-checklist.md diff --git a/docs/api-audit-checklist.md b/docs/api-audit-checklist.md new file mode 100644 index 00000000..cf8fc00c --- /dev/null +++ b/docs/api-audit-checklist.md @@ -0,0 +1,336 @@ +# SLASHED — checklista audytu API (tokeny + klasy) + +> Robocza checklista do ręcznego przeglądu powierzchni API frameworka. +> Źródło: `docs/api-index.json` (1070 elementów — 747 tokenów + 323 klasy). +> Podział na 3 grupy: **fundament** / **przydatne-opcjonalne** / **niszowe**. + +## Jak odznaczać + +Każda pozycja ma checkbox. Proponowana konwencja (dopisz kod po pozycji): + +- `- [x]` **bez dopisku** → **ZOSTAJE** (świadomie zatwierdzone) +- `- [x] … — USUŃ` → kandydat do usunięcia +- `- [x] … — OGRANICZ` → zostaje, ale okrojone (mniej wariantów / do `full` / PUBLIC-ADVANCED) +- `- [ ]` → jeszcze nierozpatrzone + +Duże jednorodne rodziny (np. 27-stopniowe palety, aliasy typografii) są zgrupowane +w jeden checkbox — nie ma sensu klikać 162 razy. Tam, gdzie decyzja jest per-element +(klasy, komponenty, warianty), pozycje są rozpisane pojedynczo. + +Legenda kolumny „bundle": `opt` = jest w `slashed.optimal`, `full` = tylko w `slashed.full` (framework już traktuje jako opt-in). + +--- + +# GRUPA 1 — Fundament (bezdyskusyjny) + +Bez tego framework nie istnieje. Domyślnie wszystko `ZOSTAJE`; checkbox = potwierdzenie. + +## 1A. Kolory — palety (grupowo) + +- [ ] `--sf-primary-*` — rampa primary (27 stopni) `opt` +- [ ] `--sf-secondary-*` — rampa secondary (27) `opt` +- [ ] `--sf-tertiary-*` — rampa tertiary (27) `opt` +- [ ] `--sf-action-*` — rampa action (27) `opt` +- [ ] `--sf-neutral-*` — rampa neutral (27) `opt` +- [ ] `--sf-base-*` — rampa base (27) `opt` + +## 1B. Kolory — semantyka źródłowa (grupowo) + +- [ ] BRAND COLORS source (`*-source-light`, animatable) — 6 tok. `opt` +- [ ] DARK SOURCE TOKENS (`*-dark`, animatable) — 21 tok. `opt` +- [ ] STATUS COLORS source (`success/warning/danger/info`-source) — 4 tok. `opt` +- [ ] Status triplets (bg/fg/border dla 4 statusów) — 16 tok. `opt` +- [ ] Text-on-color (`--sf-on-*`) — 12 tok. `opt` +- [ ] Resolved color tokens + Color scheme — 2 tok. `opt` + +## 1C. Powierzchnie, tekst, obramowania + +- [ ] Surfaces (`--sf-color-surface*`) — 5 tok. `opt` +- [ ] Text (`--sf-color-text*`) — 7 tok. `opt` +- [ ] Borders (`--sf-color-border*`) — 4 tok. `opt` +- [ ] Border widths (`--sf-border-width-*`) — 6 tok. `opt` +- [ ] Border shorthands — 3 tok. `opt` +- [ ] Border alpha + Border style — 3 tok. `opt` +- [ ] Divider (`--sf-divider-*`) — 4 tok. `opt` + +## 1D. Typografia (grupowo) + +- [ ] Font families (`--sf-font-*`) — 10 tok. (3 PUBLIC-ADVANCED) `opt` +- [ ] Font sizes (`--sf-text-2xs … 4xl` skala) — 12 tok. `opt` +- [ ] Font weights (`--sf-font-weight-*`) — 11 tok. `opt` +- [ ] Line heights & letter spacing — 14 tok. `opt` +- [ ] TYPOGRAPHY ALIASES (`--sf-text-*` role) — 36 tok. `opt` +- [ ] Per-size typography sub-properties (`--sf-text-{size}-*`) — 36 tok. `opt` +- [ ] Heading line-length constraints (`--sf-heading-measure-*`) — 6 tok. `opt` + +## 1E. Skala / silnik rozmiarów (grupowo) + +- [ ] FLUID SCALE ENGINE — 13 tok. (12 PUBLIC-ADVANCED) `opt` +- [ ] Output token @property registrations (rejestracje space/radius/font/motion) — 56 tok. `opt` +- [ ] Odstępy `--sf-space-*` (skala 2xs…4xl + none/px/base) — `opt` +- [ ] Promienie `--sf-radius-*` (skala + full/pill/outer) — `opt` +- [ ] Semantic gap tokens (`--sf-content-gap` itp.) — 3 tok. `opt` +- [ ] Section padding — 1 tok. `opt` +- [ ] UI sizes — 5 tok. `opt` +- [ ] Icon sizes (`--sf-icon-size-*`) — 6 tok. `opt` +- [ ] Containers (`--sf-container-*`) — 5 tok. `opt` + +## 1F. Stany interaktywne, focus, z-index + +- [ ] Interactive states (kolory hover/active) — 6 tok. `opt` +- [ ] Focus / form colors — 3 tok. (1 PUBLIC-ADVANCED) `opt` +- [ ] Links + Link alpha (`--sf-link-*`) — 8 tok. `opt` +- [ ] Selection / Selection & backdrop / mark — 9 tok. `opt` +- [ ] Z-index (`--sf-z-*`) — 10 tok. `opt` +- [ ] Scrollbar — 2 tok. `opt` + +## 1G. Klasy fundamentu + +- [ ] `.sf-sr-only` `opt` +- [ ] `.sf-sr-only-focusable` `opt` +- [ ] `.skip-link` `opt` +- [ ] `.no-motion` `opt` +- [ ] `.sf-focus-parent` `opt` +- [ ] `.sf-focus-shadow` `opt` +- [ ] `.sf-clickable-parent` `opt` +- [ ] `.sf-clickable-parent__overlay` `opt` +- [ ] `.sf-theme-dark` `opt` +- [ ] `.sf-theme-light` `opt` +- [ ] `.sf-theme-transition` `opt` + +--- + +# GRUPA 2 — Przydatne, ale opcjonalne + +Realna wartość; rozważ okrojenie liczby wariantów lub przeniesienie do `full`. + +## 2A. Tokeny layoutu i makr (grupowo) + +- [ ] Layout tokens (gap/kolumny prymitywów) — 53 tok. `opt` +- [ ] Macro tokens (prose/scrim/surface presets) — 32 tok. `opt` + +## 2B. Tokeny ruchu i efektów (grupowo) + +- [ ] Motion & easing — 15 tok. `opt` +- [ ] Transition shorthands — 10 tok. `opt` +- [ ] Animation presets — 23 tok. `opt` +- [ ] Gradients — 10 tok. `opt` +- [ ] Shadow ramp / strength / glow — 4 tok. `opt` +- [ ] Blur & opacity + Opacity — 4 tok. `opt` +- [ ] Aspect ratios — 7 tok. `opt` +- [ ] Object fit / position — 2 tok. `opt` + +## 2C. Layout primitives — klasy (per rodzina) + +- [ ] `.sf-stack` (10) `opt` +- [ ] `.sf-cluster` (11) `opt` +- [ ] `.sf-grid` (9) `opt` +- [ ] `.sf-grid-flex` (8) `opt` +- [ ] `.sf-grid-cols-*` (1,1-2,1-3,2,2-1,3,3-1,4,6 — 9 klas) `opt` +- [ ] `.sf-section` (9) `opt` +- [ ] `.sf-section-group` (1) `opt` +- [ ] `.sf-frame` (8) `opt` +- [ ] `.sf-icon` (8) `opt` +- [ ] `.sf-divider` (7) `opt` +- [ ] `.sf-divide` (2) `opt` +- [ ] `.sf-gap` (7) `opt` +- [ ] `.sf-cover` (6) `opt` +- [ ] `.sf-container` (5) `opt` +- [ ] `.sf-equal` (5) `opt` +- [ ] `.sf-header` (5) `opt` +- [ ] `.sf-sidebar` (4) `opt` +- [ ] `.sf-switcher` (3) `opt` +- [ ] `.sf-center` (2) `opt` +- [ ] `.sf-box` (1) `opt` +- [ ] `.sf-cq` / `.sf-fluid-cq` (container queries — 2) `opt` + +## 2D. Macro classes — per element + +- [ ] `.sf-surface` + warianty (`--action/danger/info/inverse/neutral/primary/secondary/success/tertiary/warning`, `-bg`) — 11 `opt` +- [ ] `.sf-prose` `opt` +- [ ] `.sf-not-prose` `opt` +- [ ] `.sf-flow` `opt` +- [ ] `.sf-truncate` `opt` +- [ ] `.sf-line-clamp-2` `opt` +- [ ] `.sf-line-clamp-3` `opt` +- [ ] `.sf-line-clamp-N` `opt` +- [ ] `.sf-tabular-nums` `opt` +- [ ] `.sf-text-protect` `opt` +- [ ] `.sf-aspect` `opt` +- [ ] `.sf-equal-height` `opt` +- [ ] `.sf-content-auto` `opt` +- [ ] `.sf-link--reverse` `opt` +- [ ] `.sf-link--subtle` `opt` +- [ ] `.sf-link-external` `opt` + +## 2E. State classes (`.sf-is-*`) — per element + +- [ ] `.sf-is-active` `opt` +- [ ] `.sf-is-selected` `opt` +- [ ] `.sf-is-current` `opt` +- [ ] `.sf-is-disabled` `opt` +- [ ] `.sf-is-loading` `opt` +- [ ] `.sf-is-open` `opt` +- [ ] `.sf-is-collapsed` `opt` +- [ ] `.sf-is-expanded` `opt` +- [ ] `.sf-is-hidden` `opt` +- [ ] `.sf-is-visible` `opt` +- [ ] `.sf-is-invisible` `opt` +- [ ] `.sf-is-invalid` `opt` +- [ ] `.sf-is-valid` `opt` +- [ ] `.sf-is-error` `opt` +- [ ] `.sf-is-success` `opt` +- [ ] `.sf-is-warning` `opt` +- [ ] `.sf-is-info` `opt` +- [ ] `.sf-is-danger` `opt` +- [ ] `.sf-is-pending` `opt` +- [ ] `.sf-is-busy` `opt` +- [ ] `.sf-is-readonly` `opt` +- [ ] `.sf-is-pressed` `opt` +- [ ] `.sf-is-highlighted` `opt` +- [ ] `.sf-is-empty` `opt` +- [ ] `.sf-is-skeleton` `opt` + +## 2F. Motion classes — per element + +- [ ] `.sf-fade-in` `opt` +- [ ] `.sf-fade-out` `opt` +- [ ] `.sf-scale-up` `opt` +- [ ] `.sf-scale-down` `opt` +- [ ] `.sf-slide-in-up` `opt` +- [ ] `.sf-slide-in-down` `opt` +- [ ] `.sf-slide-in-left` `opt` +- [ ] `.sf-slide-in-right` `opt` +- [ ] `.sf-entrance--*` (fade/fade-up/down/left/right/scale-up — 6) `opt` +- [ ] `.sf-exit--*` (fade/fade-up/down/left/right/scale-down — 6) `opt` + +## 2G. Text-size utilities (klasy) — grupowo + +- [ ] `.sf-text-2xs … .sf-text-4xl` (9 klas) `full` +- [ ] `.sf-h1 … .sf-h6` (heading helpers — 6) `full` + +--- + +# GRUPA 3 — Niszowe / zbędne w ~90% projektów + +Framework już trzyma większość poza bundlem `optimal`. + +## 3A. Komponenty — tokeny (grupowo) + +- [ ] BUTTON TOKENS — 29 tok. (4 PUBLIC-ADVANCED; dużo per-rung knobów) `full` +- [ ] CARD TOKENS — 14 tok. `full` +- [ ] FIELD TOKENS — 3 tok. `full` + +## 3B. Komponenty — klasy `.sf-btn` (per wariant) + +- [ ] `.sf-btn` (baza) `full` +- [ ] `.sf-btn--primary` `full` +- [ ] `.sf-btn--secondary` `full` +- [ ] `.sf-btn--tertiary` `full` +- [ ] `.sf-btn--action` `full` +- [ ] `.sf-btn--neutral` `full` +- [ ] `.sf-btn--base` `full` +- [ ] `.sf-btn--success` `full` +- [ ] `.sf-btn--warning` `full` +- [ ] `.sf-btn--danger` `full` +- [ ] `.sf-btn--info` `full` +- [ ] `.sf-btn--outline` `full` +- [ ] `.sf-btn--soft` `full` +- [ ] `.sf-btn--gradient` `full` +- [ ] `.sf-btn--xs` `full` +- [ ] `.sf-btn--s` `full` +- [ ] `.sf-btn--l` `full` +- [ ] `.sf-btn--xl` `full` +- [ ] `.sf-btn--block` `full` +- [ ] `.sf-btn--block-cq` `full` + +## 3C. Komponenty — klasy `.sf-card` (per element) + +- [ ] `.sf-card` (baza) `full` +- [ ] `.sf-card--bordered` `full` +- [ ] `.sf-card--elevated` `full` +- [ ] `.sf-card--interactive` `full` +- [ ] `.sf-card__header` `full` +- [ ] `.sf-card__body` `full` +- [ ] `.sf-card__footer` `full` +- [ ] `.sf-card__title` `full` +- [ ] `.sf-card__media` `full` +- [ ] `.sf-card__avatar` `full` + +## 3D. Formularze + +- [ ] `.sf-live-validate` (forms) `opt` + +## 3E. Layout — prymitywy egzotyczne + +- [ ] `.sf-bento` + `--2/--3/--6/--row-compact` (6) `opt` +- [ ] `.sf-bento-featured` / `-full` / `-tall` / `-wide` (4) `opt` +- [ ] `.sf-alternate` (zigzag) `opt` +- [ ] `.sf-imposter` (3) `opt` +- [ ] `.sf-pancake` `opt` +- [ ] `.sf-reel` `opt` +- [ ] `.sf-breakout` `opt` +- [ ] `.sf-content-grid` `opt` +- [ ] `.sf-full-bleed` `opt` +- [ ] `.sf-subgrid` / `.sf-subgrid-rows` (2) `opt` +- [ ] `.sf-overlay` `opt` +- [ ] `.sf-bg-layer` `opt` + +## 3F. Utilities niszowe + +- [ ] `.sf-hover-float` `full` +- [ ] `.sf-hover-grow` `full` +- [ ] `.sf-hover-shrink` `full` +- [ ] `.sf-hover-sink` `full` +- [ ] `.sf-hover-slide-start` `full` +- [ ] `.sf-hover-slide-end` `full` +- [ ] `.sf-sticky` + `--s/--m/--l` (4) `full` +- [ ] `.sf-marker--primary/secondary/tertiary/action` (4) `full` +- [ ] `.sf-list-none` `full` +- [ ] `.sf-selection--alt` `full` + +## 3G. Makra niszowe + +- [ ] `.sf-scrim` + `--top/--bottom/--full/__content` (5) `opt` +- [ ] `.sf-overflow-fade` + `--top/bottom/left/right/block/inline` (7) `opt` +- [ ] `.sf-scroll-shadow` `opt` +- [ ] `.sf-scroll-snap` `opt` +- [ ] `.sf-overlap` / `--down` / `-host` (3) `opt` +- [ ] `.sf-drop-shadow-xs/s/m/l/xl` (5) `opt` +- [ ] `.sf-text-gradient` `opt` +- [ ] `.sf-no-tap-highlight` `opt` + +## 3H. Motion niszowe + +- [ ] `.sf-color-pulse` `opt` +- [ ] `.sf-stagger` `opt` + +## 3I. Tokeny zaawansowane / wewnętrzne (PUBLIC-ADVANCED) + +- [ ] LumLocker (`--sf-lumlocker`, `-dark`) — 2 tok. `opt` +- [ ] Mask scrim (`--sf-mask-scrim-start/end`) — 2 tok. `opt` +- [ ] Palette ramp lightness anchors (`--sf-palette-shade-l/tint-l`) — 2 tok. `opt` +- [ ] INTERACTION STATE FLAGS (`--sf-is-active/current/open/pressed`) — 4 tok. `opt` +- [ ] Scale multipliers (`--sf-*-scale`) — 6 tok. `opt` +- [ ] Density (`--sf-density`) — 1 tok. `opt` +- [ ] Shadow glow color — 1 tok. `opt` +- [ ] Scroll-driven animation range — 4 tok. `opt` + +## 3J. Druk (print) + +- [ ] Print tokens (`--sf-print-base-size/page-margin/page-size`) — 3 tok. `opt` +- [ ] `.no-print` `opt` +- [ ] `.print-only` `opt` +- [ ] `.print-color-exact` `opt` +- [ ] `.print-no-color` `opt` + +--- + +## Podsumowanie liczbowe + +| Grupa | Tokeny | Klasy | +|---|---:|---:| +| 1 — Fundament | ~455 | 11 | +| 2 — Przydatne-opcjonalne | ~150 | ~235 | +| 3 — Niszowe | ~140 | ~77 | +| **Razem** | **747** | **323** | From 395843f0dd04217bbb8314effa904229beda8adb Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 20 Jul 2026 18:24:03 +0000 Subject: [PATCH 2/6] refactor(tokens)!: rename --sf-color-text--secondary to --sf-color-text--subtle The name '--secondary' collided conceptually with the brand palette role (--sf-color-secondary) and with its own --sf-color-text--on-secondary, making it read as 'text in the secondary brand colour' when it actually means de-emphasised, lower-emphasis text. Rename to --subtle, which pairs cleanly with the existing --muted tier and carries no brand-role ambiguity. BREAKING CHANGE: the PUBLIC token --sf-color-text--secondary is renamed to --sf-color-text--subtle. Its value, derivation and role are unchanged. Stable-id ledger retires id 314 (removed) and assigns id 801 to the new name. Covers source (core/tokens.css, core/themes.css, core/macros.css), the regenerated docs/registry/configurator artifacts, hand-written docs, the user manual, the demo + landing page, configurator preview sources, and the test baselines/snapshots. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01CAsUv13o2SAi7Tnsx93EtV --- .../src/components/panels/WcagPanel.svelte | 2 +- .../src/data/api-index.generated.json | 6 ++-- .../src/data/token-registry.generated.json | 9 ++++-- configurator/src/lib/preview/sections.ts | 2 +- configurator/src/lib/preview/skin.ts | 4 +-- core/macros.css | 2 +- core/themes.css | 4 +-- core/tokens.css | 4 +-- demo/index.html | 32 +++++++++---------- dist/css-custom-data.json | 4 +-- docs/api-audit-checklist.md | 6 ++-- docs/api-index.json | 4 +-- docs/api-index.md | 2 +- docs/llm-guide.md | 2 +- docs/macros.md | 2 +- docs/registry.json | 2 +- docs/theming.md | 2 +- docs/token-annotations.json | 2 +- docs/token-index.json | 2 +- docs/token-index.md | 2 +- docs/tokens.md | 2 +- index.html | 12 +++---- tests/baseline-modern.json | 4 +-- tests/tier1-p8-modern.spec.js | 2 +- tests/token-api.snapshot.json | 2 +- token-registry.json | 9 ++++-- user-manual/system-kolorow.md | 10 +++--- 27 files changed, 73 insertions(+), 63 deletions(-) diff --git a/configurator/src/components/panels/WcagPanel.svelte b/configurator/src/components/panels/WcagPanel.svelte index cab1ea2c..19205bb4 100644 --- a/configurator/src/components/panels/WcagPanel.svelte +++ b/configurator/src/components/panels/WcagPanel.svelte @@ -25,7 +25,7 @@ const TEXT_OPTS: ColorOpt[] = [ { label: "Text", expr: "var(--sf-color-text)" }, - { label: "Text secondary", expr: "var(--sf-color-text--secondary)" }, + { label: "Text secondary", expr: "var(--sf-color-text--subtle)" }, { label: "Text muted", expr: "var(--sf-color-text--muted)" }, { label: "White", expr: "#ffffff" }, { label: "Black", expr: "#000000" }, diff --git a/configurator/src/data/api-index.generated.json b/configurator/src/data/api-index.generated.json index dad4b318..13e9e2fb 100644 --- a/configurator/src/data/api-index.generated.json +++ b/configurator/src/data/api-index.generated.json @@ -2,7 +2,7 @@ "_sync": { "generatedBy": "configurator/scripts/sync-api.mjs", "source": "docs/api-index.json", - "tokensHash": "2d7e174d5d83", + "tokensHash": "554ed4dfb519", "bundles": [ "full", "optimal" @@ -7331,14 +7331,14 @@ ] }, { - "name": "--sf-color-text--secondary", + "name": "--sf-color-text--subtle", "tier": "PUBLIC", "role": "consumption", "namespace": "color", "category": "Core tokens", "group": "Text", "description": "Semantic text colors for every content role. All tokens adapt to light and dark mode automatically.", - "note": "De-emphasised secondary text (captions, labels, supporting copy).", + "note": "De-emphasised, lower-emphasis text (captions, labels, supporting copy). Second tier below --sf-color-text; sits between it and --sf-color-text--muted.", "value": "light-dark( oklch(from var(--sf-color-neutral-source-light) clamp(0.15, calc(l - 0.25 - var(--sf-contrast-bias)), 0.45) c h), oklch(from var(--sf-color-neutral) clamp(0.55, calc(l + 0.1 + var(--sf-contrast-bias)), 0.90) c h) )", "aliasOf": null, "registered": false, diff --git a/configurator/src/data/token-registry.generated.json b/configurator/src/data/token-registry.generated.json index adff6376..8d03fd73 100644 --- a/configurator/src/data/token-registry.generated.json +++ b/configurator/src/data/token-registry.generated.json @@ -1,7 +1,7 @@ { "_meta": { "generatedBy": "scripts/gen-token-registry.js", - "nextId": 801 + "nextId": 802 }, "tokens": [ { @@ -1289,7 +1289,8 @@ }, { "id": 314, - "name": "--sf-color-text--secondary" + "name": "--sf-color-text--secondary", + "removed": true }, { "id": 315, @@ -3261,6 +3262,10 @@ { "id": 800, "name": "--sf-lumlocker-dark" + }, + { + "id": 801, + "name": "--sf-color-text--subtle" } ] } diff --git a/configurator/src/lib/preview/sections.ts b/configurator/src/lib/preview/sections.ts index d332b067..799665ac 100644 --- a/configurator/src/lib/preview/sections.ts +++ b/configurator/src/lib/preview/sections.ts @@ -49,7 +49,7 @@ export function color(): string { const textRoles = well(`
Heading — --sf-color-heading Body text — --sf-color-text - Secondary — --sf-color-text--secondary + Secondary — --sf-color-text--subtle Muted — --sf-color-text--muted Link — --sf-color-link
`); diff --git a/configurator/src/lib/preview/skin.ts b/configurator/src/lib/preview/skin.ts index f451ce0f..59ffecb5 100644 --- a/configurator/src/lib/preview/skin.ts +++ b/configurator/src/lib/preview/skin.ts @@ -24,9 +24,9 @@ export function previewSkinCSS(): string { return ` /* Text roles (framework ships no text utilities — these mirror semantic tokens) */ .pv-eyebrow{font-size:var(--sf-text-xs);font-weight:var(--sf-font-weight-heading);text-transform:uppercase;letter-spacing:0.08em;color:var(--sf-color-text--muted);} - .pv-lead{font-size:var(--sf-text-l);color:var(--sf-color-text--secondary);} + .pv-lead{font-size:var(--sf-text-l);color:var(--sf-color-text--subtle);} .pv-muted{color:var(--sf-color-text--muted);} - .pv-secondary{color:var(--sf-color-text--secondary);} + .pv-secondary{color:var(--sf-color-text--subtle);} .pv-accent{color:var(--sf-color-primary-600);} .pv-on-primary{color:var(--sf-color-text--on-primary);} .pv-center-text{text-align:center;} diff --git a/core/macros.css b/core/macros.css index 68b0aeaa..93f6ec60 100644 --- a/core/macros.css +++ b/core/macros.css @@ -276,7 +276,7 @@ --sf-color-link: oklch(from var(--sf-surface-contrast) l calc(c + 0.08) h); --sf-color-link--hover: var(--sf-surface-contrast); --sf-color-link--underline: oklch(from var(--sf-surface-contrast) l c h / 0.5); - --sf-color-text--secondary: oklch(from var(--sf-surface-contrast) l c h / 0.70); + --sf-color-text--subtle: oklch(from var(--sf-surface-contrast) l c h / 0.70); --sf-color-text--placeholder: oklch(from var(--sf-surface-contrast) l c h / 0.45); --sf-color-text--disabled: oklch(from var(--sf-surface-contrast) l c h / 0.30); --sf-color-border: oklch(from var(--sf-surface-contrast) l c h / 0.20); diff --git a/core/themes.css b/core/themes.css index a80c30ad..f72ef782 100644 --- a/core/themes.css +++ b/core/themes.css @@ -118,7 +118,7 @@ --sf-color-danger: var(--sf-color-danger-source-dark); --sf-color-text: oklch(from var(--sf-color-neutral) clamp(0.70, calc(l + 0.25 + var(--sf-contrast-bias)), 1) c h); - --sf-color-text--secondary: oklch(from var(--sf-color-neutral) clamp(0.55, calc(l + 0.1 + var(--sf-contrast-bias)), 0.90) c h); + --sf-color-text--subtle: oklch(from var(--sf-color-neutral) clamp(0.55, calc(l + 0.1 + var(--sf-contrast-bias)), 0.90) c h); --sf-color-text--placeholder: oklch(from var(--sf-color-neutral) clamp(0.35, calc(l - 0.1), 0.65) c h); --sf-color-text--disabled: oklch(from var(--sf-color-neutral) clamp(0.25, calc(l - 0.2), 0.55) c h); --sf-color-text--inverse: oklch(from var(--sf-color-neutral) clamp(0.05, calc(l - 0.4), 0.35) c h); @@ -176,7 +176,7 @@ --sf-color-danger: var(--sf-color-danger-source-light); --sf-color-text: oklch(from var(--sf-color-neutral-source-light) clamp(0.05, calc(l - 0.4 - var(--sf-contrast-bias)), 0.35) c h); - --sf-color-text--secondary: oklch(from var(--sf-color-neutral-source-light) clamp(0.15, calc(l - 0.25 - var(--sf-contrast-bias)), 0.45) c h); + --sf-color-text--subtle: oklch(from var(--sf-color-neutral-source-light) clamp(0.15, calc(l - 0.25 - var(--sf-contrast-bias)), 0.45) c h); --sf-color-text--placeholder: oklch(from var(--sf-color-neutral-source-light) clamp(0.45, calc(l + 0.15), 0.75) c h); --sf-color-text--disabled: oklch(from var(--sf-color-neutral-source-light) clamp(0.55, calc(l + 0.25), 0.82) c h); --sf-color-text--inverse: oklch(from var(--sf-color-neutral-source-light) clamp(0.85, calc(l + 0.4), 0.98) c h); diff --git a/core/tokens.css b/core/tokens.css index 6aafd3b9..82d8a893 100644 --- a/core/tokens.css +++ b/core/tokens.css @@ -443,7 +443,7 @@ oklch(from var(--sf-color-neutral-source-light) clamp(0.35, calc(l - var(--sf-contrast-bias)), 0.62) c h), oklch(from var(--sf-color-neutral) clamp(0.48, calc(l + var(--sf-contrast-bias)), 0.74) c h) ); - --sf-color-text--secondary: light-dark( + --sf-color-text--subtle: light-dark( oklch(from var(--sf-color-neutral-source-light) clamp(0.15, calc(l - 0.25 - var(--sf-contrast-bias)), 0.45) c h), oklch(from var(--sf-color-neutral) clamp(0.55, calc(l + 0.1 + var(--sf-contrast-bias)), 0.90) c h) ); @@ -1490,7 +1490,7 @@ /* Global text-contrast knob. Nudges derived reading-text colours toward the extremes (darker in light mode, lighter in dark mode). Positive = more contrast; 0 = no change. Consumed by - --sf-color-text, --sf-color-text--secondary, --sf-color-heading. */ + --sf-color-text, --sf-color-text--subtle, --sf-color-heading. */ --sf-contrast-bias: 0; /* Lightness crossover for text-on-color auto-contrast. Colours with L above this value get dark text; below get light text. diff --git a/demo/index.html b/demo/index.html index 91ba7a9b..7247962d 100644 --- a/demo/index.html +++ b/demo/index.html @@ -62,7 +62,7 @@ display: block; padding: var(--sf-space-2xs) var(--sf-space-s); border-radius: var(--sf-radius-s); - color: var(--sf-color-text--secondary); + color: var(--sf-color-text--subtle); text-decoration: none; font-size: var(--sf-text-s); white-space: nowrap; @@ -119,7 +119,7 @@ .demo-section h3 { font-size: var(--sf-text-m); font-weight: var(--sf-font-weight-heading); - color: var(--sf-color-text--secondary); + color: var(--sf-color-text--subtle); margin-block: var(--sf-space-m) var(--sf-space-s); } @@ -161,7 +161,7 @@ .demo-customizer-grid label { font-family: var(--sf-font-mono); font-size: var(--sf-text-xs); - color: var(--sf-color-text--secondary); + color: var(--sf-color-text--subtle); white-space: nowrap; } @@ -855,7 +855,7 @@

Text Hierarchy

Text-color roles from primary body down to disabled — use to signal importance, never to hardcode a grey.

--sf-color-text (primary)

-

--sf-color-text--secondary

+

--sf-color-text--subtle

--sf-color-text--muted

--sf-color-text--placeholder

--sf-color-text--disabled

@@ -949,7 +949,7 @@

--sf-color-text (primary)

-

--sf-color-text--secondary

+

--sf-color-text--subtle

--sf-color-text--muted

@@ -975,7 +975,7 @@

--sf-color-text (primary)

-

--sf-color-text--secondary

+

--sf-color-text--subtle

--sf-color-text--muted

@@ -995,7 +995,7 @@

Opt-in theme cross-fade
Smooth theme transition -

This card fades its background, text and border colours when the mode changes.

+

This card fades its background, text and border colours when the mode changes.

Section theme aliases .sf-theme-light / .sf-theme-dark

@@ -1144,7 +1144,7 @@

Semantic Elements (base.css)

--sf-color-primary-source-light: oklch(0.47 0.27 264); --sf-color-neutral-source-light: oklch(0.52 0.025 260); }
-
+
A blockquote. Try selecting this text to see --sf-color-selection-bg.
@@ -1375,7 +1375,7 @@

.sf-stagger

@property Color Interpolation (motion.css — sf-color-pulse)

-

+

Demonstrates smooth oklch color animation via @property-registered tokens. The box below pulses its --sf-color-primary-source-light lightness. Only works in browsers supporting @property inherits.

@@ -1915,7 +1915,7 @@

.sf-section--xs / .sf-section--l / .sf-section--xl / .sf-section--2xl

.sf-header--xs / .sf-header--s / .sf-header--m / .sf-header--l / .sf-header--xl

-

Block padding on the standard spacing scale — lighter than .sf-section. Inline gutter is left untouched.

+

Block padding on the standard spacing scale — lighter than .sf-section. Inline gutter is left untouched.

.sf-header--xs
.sf-header--s
@@ -2406,7 +2406,7 @@

Layout — Bento

tall
.sf-bento-tall
full width — .sf-bento-full
-

Column modifiers below use .sf-bento--2, .sf-bento--3, .sf-bento--6

+

Column modifiers below use .sf-bento--2, .sf-bento--3, .sf-bento--6

{#each [ { label: "muted", token: "--sf-opacity-muted", val: opacityMuted }, { label: "disabled", token: "--sf-opacity-disabled", val: opacityDisabled }, - { label: "pending", token: "--sf-state-pending-opacity", val: pendingOpacity }, ] as row (row.label)}
{row.label} diff --git a/configurator/src/components/panels/MiscPanel.svelte b/configurator/src/components/panels/MiscPanel.svelte index 83db9d19..0ad67968 100644 --- a/configurator/src/components/panels/MiscPanel.svelte +++ b/configurator/src/components/panels/MiscPanel.svelte @@ -1,7 +1,6 @@ +
@@ -5294,8 +5243,8 @@

Design tokens 7 } function openNav() { - nav.classList.add('sf-is-open'); - overlay.classList.add('sf-is-open'); + nav.classList.add('is-open'); + overlay.classList.add('is-open'); navToggle.setAttribute('aria-expanded', 'true'); navToggle.setAttribute('aria-label', 'Close navigation'); document.body.style.overflow = 'hidden'; @@ -5320,8 +5269,8 @@

Design tokens 7 } function closeNav() { - nav.classList.remove('sf-is-open'); - overlay.classList.remove('sf-is-open'); + nav.classList.remove('is-open'); + overlay.classList.remove('is-open'); navToggle.setAttribute('aria-expanded', 'false'); navToggle.setAttribute('aria-label', 'Open navigation'); document.body.style.overflow = ''; @@ -5334,7 +5283,7 @@

Design tokens 7 } navToggle.addEventListener('click', () => { - nav.classList.contains('sf-is-open') ? closeNav() : openNav(); + nav.classList.contains('is-open') ? closeNav() : openNav(); }); overlay.addEventListener('click', closeNav); @@ -5346,7 +5295,7 @@

Design tokens 7 }); document.addEventListener('keydown', e => { - if (e.key === 'Escape' && nav.classList.contains('sf-is-open')) closeNav(); + if (e.key === 'Escape' && nav.classList.contains('is-open')) closeNav(); }); // Replay an animation preset on click by re-triggering its animation. diff --git a/dist/css-custom-data.json b/dist/css-custom-data.json index 316f227e..33cc860a 100644 --- a/dist/css-custom-data.json +++ b/dist/css-custom-data.json @@ -1861,11 +1861,6 @@ "description": "Inner padding for the cover layout.\n\nDefault: `var(--sf-section-pad)`", "syntax": "*" }, - { - "name": "--sf-current-font-weight", - "description": "Inherits the current font-weight from context. Useful as a reset.\n\nDefault: `var(--sf-font-weight-bold)`", - "syntax": "*" - }, { "name": "--sf-density", "description": "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).\n\nDefault: `1`\n\n(advanced token)", @@ -2556,26 +2551,6 @@ "description": "Margin between the imposter overlay and the viewport edges.\n\nDefault: `var(--sf-space-m)`", "syntax": "*" }, - { - "name": "--sf-is-active", - "description": "Integer state flag for the active state. Set to 1 (via JS or .sf-is-active) to drive branching calc() expressions. 0 = default (inactive).\n\nDefault: `0`\n\n(advanced token)", - "syntax": "" - }, - { - "name": "--sf-is-current", - "description": "Integer state flag for the current/selected state. Set to 1 (via .sf-is-current) to drive branching calc() expressions. 0 = default.\n\nDefault: `0`\n\n(advanced token)", - "syntax": "" - }, - { - "name": "--sf-is-open", - "description": "Integer state flag for the open/expanded state. Set to 1 (via .sf-is-open) to drive branching calc() expressions. 0 = default (closed).\n\nDefault: `0`\n\n(advanced token)", - "syntax": "" - }, - { - "name": "--sf-is-pressed", - "description": "Integer state flag for the pressed state. Set to 1 (via .sf-is-pressed) to drive branching calc() expressions. 0 = default.\n\nDefault: `0`\n\n(advanced token)", - "syntax": "" - }, { "name": "--sf-leading-normal", "description": "Normal line height (~1.5). Default for body text.\n\nDefault: `1.5`", @@ -3211,11 +3186,6 @@ "description": "Per-item delay increment for the .sf-stagger utility. One knob retunes the whole staggered sequence; the class multiplies it by each child's index and by --sf-motion-scale.\n\nDefault: `75ms`", "syntax": "*" }, - { - "name": "--sf-state-pending-opacity", - "description": "Opacity for pending / loading state elements.\n\nDefault: `0.7`", - "syntax": "*" - }, { "name": "--sf-sticky-offset", "description": "top offset for position: sticky elements, accounting for the header height.\n\nDefault: `clamp( var(--sf-sticky-offset-mobile), calc((var(--sf-sticky-offset-desktop) - var(--sf-sticky-offset-mobile)) / ((var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * 1rem) * (100vw - var(--sf-fluid-min-vw) * 1rem) + var(--sf-sticky-offset-mobile)), var(--sf-sticky-offset-desktop))`", diff --git a/docs/api-audit-checklist.md b/docs/api-audit-checklist.md index 6d2df107..9d20d159 100644 --- a/docs/api-audit-checklist.md +++ b/docs/api-audit-checklist.md @@ -165,38 +165,62 @@ Realna wartość; rozważ okrojenie liczby wariantów lub przeniesienie do `full ## 2E. State classes (`.sf-is-*`) — per element -> **RESOLVED (2026-07-21)** — full audit against internal consumers, configurator -> exposure (`MiscPanel.svelte`), tier classification (`token-tiers.js`), and -> `docs/states.md`'s own "Disambiguating the overlaps" section, plus a -> comparison against Open Props / Pico.css / Bulma / Automatic.css. Verdicts below. +> **RESOLVED — ROUND 1 (2026-07-21)** — full audit against internal consumers, +> configurator exposure (`MiscPanel.svelte`), tier classification +> (`token-tiers.js`), and `docs/states.md`'s own "Disambiguating the overlaps" +> section, plus a comparison against Open Props / Pico.css / Bulma / +> Automatic.css. Result: only 3 classes had zero design defense anywhere +> (`hidden`, `readonly`, `busy`) — removed. The rest (`active`, `open`, +> `collapsed`, `expanded`, `danger`, `pending`) had a written, deliberate +> rationale — kept, pending a stricter pass. +> +> **RESOLVED — ROUND 2 (2026-07-21)** — re-ran the survivors through a +> stricter bar: *documented and intentional* is not the same question as +> *needed at this stage of a pre-1.0, lean framework*. None of the six +> cleared "most consumers need this today" — every comparable framework +> surveyed ships nothing like the `--sf-is-*` custom-property flag +> mechanism, `.sf-is-expanded` was pure vocabulary duplication of +> `.sf-is-open`, `.sf-is-danger` had a live bug (only worked on form +> fields) and contradicted the framework's own component-variant guidance, +> and `.sf-is-pending` was two lines cheap to hand-roll. All six removed. +> `.sf-is-pressed`/`.sf-is-current` were swept in too, for consistency — +> once their inert `--sf-is-*` flag is gone they're either a functional +> duplicate of `.sf-is-selected` (`pressed`) or a single trivial declaration +> no more justified than what was just cut (`current`). `.sf-is-invisible`/ +> `.sf-is-visible` were *relocated*, not cut: single-property helpers with +> no runtime condition of their own, moved to `optional/utilities.css` as +> `.sf-invisible`/`.sf-visible` (Bulma precedent: visibility is a helper, +> not a state). Cascading removal: the `--sf-current-font-weight` and +> `--sf-state-pending-opacity` tokens lost their only consumer and were +> removed too, along with the 4 now-orphaned `@property` flag registrations. -- [x] `.sf-is-active` — ZOSTAJE (PUBLIC-ADVANCED consumer-API flag, configurator-exposed, tested) `opt` -- [x] `.sf-is-selected` — ZOSTAJE `opt` -- [x] `.sf-is-current` — ZOSTAJE (consumer-API flag + real font-weight effect) `opt` - [x] `.sf-is-disabled` — ZOSTAJE, scope note added: for elements that can't take native `disabled` (e.g. ``) `opt` - [x] `.sf-is-loading` — ZOSTAJE (no native equivalent; validated by Bulma precedent) `opt` -- [x] `.sf-is-open` — ZOSTAJE (consumer-API flag, configurator-exposed) `opt` -- [x] `.sf-is-collapsed` — ZOSTAJE (documented pair with `.sf-is-open`) `opt` -- [x] `.sf-is-expanded` — ZOSTAJE — states.md explicitly disambiguates from `.sf-is-open` (disclosure trigger vs. shown/hidden surface), not an accidental duplicate `opt` -- [x] `.sf-is-hidden` — **USUNIĘTO** — duplikat `[hidden]` (`core/reset.css` już to hartuje identycznie) ~~`opt`~~ -- [x] `.sf-is-visible` — ZOSTAJE — `docs/roadmap.md` broni jako zaplanowany hak dla „reveal on scroll" `opt` -- [x] `.sf-is-invisible` — ZOSTAJE (para do `-visible`) `opt` +- [x] `.sf-is-hidden` — **USUNIĘTO (round 1)** — duplikat `[hidden]` (`core/reset.css` już to hartuje identycznie) ~~`opt`~~ +- [x] `.sf-invisible` — ZOSTAJE, **przeniesione do `optional/utilities.css`** (`docs/roadmap.md` broni jako zaplanowany hak dla „reveal on scroll") `utilities` +- [x] `.sf-visible` — ZOSTAJE, **przeniesione do `optional/utilities.css`** `utilities` - [x] `.sf-is-invalid` — ZOSTAJE (setter tokenów, konsumowany przez `forms.css`) `opt` - [x] `.sf-is-valid` — ZOSTAJE `opt` - [x] `.sf-is-error` — ZOSTAJE `opt` - [x] `.sf-is-success` — ZOSTAJE `opt` - [x] `.sf-is-warning` — ZOSTAJE `opt` - [x] `.sf-is-info` — ZOSTAJE `opt` -- [x] `.sf-is-danger` — ZOSTAJE — states.md broni jako odrębny koncept (destructive-action context, nie wynik walidacji); implementacja dziś działa tylko na polach formularzy (`--sf-field-*`) — znana luka, poza zakresem tej rundy `opt` -- [x] `.sf-is-pending` — ZOSTAJE — states.md explicitly disambiguates from `.sf-is-loading` (optimistic UI vs. spinner mask) `opt` -- [x] `.sf-is-busy` — **USUNIĘTO** — pojedyncza deklaracja `cursor: progress`, `[aria-busy]` samo wystarcza ~~`opt`~~ -- [x] `.sf-is-readonly` — **USUNIĘTO** — duplikat `:read-only`, błędnie blokował zaznaczanie tekstu ~~`opt`~~ -- [x] `.sf-is-pressed` — ZOSTAJE (consumer-API flag + real background effect) `opt` - -**Bilans:** 28 → 25 klas. Usunięto 3 (`hidden`, `readonly`, `busy`) — wszystkie bez wsparcia w configuratorze/tierach/architekturze i z jasnym natywnym zamiennikiem. Reszta pierwotnej listy „do usunięcia" (active/open/collapsed/expanded/danger/pending) została **cofnięta** po znalezieniu: żywego panelu w `configurator/src/components/panels/MiscPanel.svelte`, klasyfikacji tier w `scripts/token-tiers.js`, wyjątku w `scripts/check-macro-catalog.js`, i jawnej obrony w `docs/states.md` § „Disambiguating the overlaps" oraz `docs/roadmap.md`. Dodano `docs/states.md` § „Prefer native state" z tabelą zamienników natywnych. -- [ ] `.sf-is-highlighted` `opt` -- [ ] `.sf-is-empty` `opt` -- [ ] `.sf-is-skeleton` `opt` +- [x] `.sf-is-danger` — **USUNIĘTO (round 2)** — identyczna implementacja co `-invalid`/`-error` (nigdy nie działała jako "destructive-action context" poza polem formularza); sprzeczna z `migration.md` ("visual variants belong in your own component CSS") ~~`opt`~~ +- [x] `.sf-is-pending` — **USUNIĘTO (round 2)** — dwie linie taniej do odtworzenia niż do utrzymania; `.sf-is-loading` pokrywa częstszy przypadek ~~`opt`~~ +- [x] `.sf-is-busy` — **USUNIĘTO (round 1)** — pojedyncza deklaracja `cursor: progress`, `[aria-busy]` samo wystarcza ~~`opt`~~ +- [x] `.sf-is-readonly` — **USUNIĘTO (round 1)** — duplikat `:read-only`, błędnie blokował zaznaczanie tekstu ~~`opt`~~ +- [x] `.sf-is-active` — **USUNIĘTO (round 2)** — czysta flaga `calc()`, zero konsumentów, zero odpowiednika w żadnym z 4 zbadanych frameworków ~~`opt`~~ +- [x] `.sf-is-open` — **USUNIĘTO (round 2)** — j.w. ~~`opt`~~ +- [x] `.sf-is-collapsed` — **USUNIĘTO (round 2)** — j.w. ~~`opt`~~ +- [x] `.sf-is-expanded` — **USUNIĘTO (round 2)** — czysty duplikat wygody nazewniczej nad `.sf-is-open` ~~`opt`~~ +- [x] `.sf-is-pressed` — **USUNIĘTO (round 2)** — bez flagi staje się dosłownym duplikatem `.sf-is-selected` (ten sam token tła) ~~`opt`~~ +- [x] `.sf-is-current` — **USUNIĘTO (round 2)** — dla spójności z resztą: trywialna, niepotwierdzona realnym zapotrzebowaniem ~~`opt`~~ +- [x] `.sf-is-selected` — ZOSTAJE `opt` +- [x] `.sf-is-highlighted` — ZOSTAJE `opt` +- [x] `.sf-is-empty` — ZOSTAJE (bez zmian tej rundy) `opt` +- [x] `.sf-is-skeleton` — ZOSTAJE (świadoma decyzja usera — brak natywnego odpowiednika) `opt` + +**Bilans końcowy:** 28 → **15** w `states.css` + **2** przeniesione do `utilities.css` (17 łącznie zostaje, 11 usuniętych). Kaskadowo usunięte też: tokeny `--sf-current-font-weight`, `--sf-state-pending-opacity`, i 4 rejestracje `@property` (`--sf-is-active/-current/-pressed/-open`) — wszystkie osierocone przez usunięcie jedynego konsumenta. ## 2F. Motion classes — per element diff --git a/docs/api-index.json b/docs/api-index.json index da161b63..a97e1ddb 100644 --- a/docs/api-index.json +++ b/docs/api-index.json @@ -25,30 +25,30 @@ "optimal" ], "counts": { - "total": 1067, + "total": 1053, "by_type": { - "class": 320, - "token": 747 + "class": 312, + "token": 741 }, "by_tier": { "INTERNAL": 1, - "PUBLIC": 1013, - "PUBLIC-ADVANCED": 53 + "PUBLIC": 1003, + "PUBLIC-ADVANCED": 49 }, "by_role": { - "consumption": 489, - "knob": 258 + "consumption": 488, + "knob": 253 }, - "tokens": 747, - "classes": 320, - "sf_classes": 287, - "is_classes": 25, + "tokens": 741, + "classes": 312, + "sf_classes": 289, + "is_classes": 15, "unprefixed_classes": 8, "by_category": { "Accessibility": 8, "Component tokens": 46, "Components": 30, - "Core tokens": 616, + "Core tokens": 610, "Forms": 1, "Layout primitives": 145, "Layout tokens": 53, @@ -56,9 +56,9 @@ "Macro tokens": 32, "Motion / animation": 22, "Print": 4, - "State classes": 25, + "State classes": 15, "Theme utilities": 3, - "Utilities": 31 + "Utilities": 33 } }, "schema": { @@ -9718,32 +9718,6 @@ "optimal" ] }, - { - "name": "--sf-current-font-weight", - "type": "token", - "tier": "PUBLIC", - "role": "consumption", - "namespace": "current", - "category": "Core tokens", - "area": "core", - "group": "Font weights", - "description": "Inherits the current font-weight from context. Useful as a reset.", - "value": "var(--sf-font-weight-bold)", - "aliasOf": "--sf-font-weight-bold", - "registered": false, - "animatable": false, - "syntax": null, - "inherits": null, - "optional": false, - "layer": "slashed.tokens", - "sourceFiles": [ - "core/tokens.css" - ], - "bundles": [ - "full", - "optimal" - ] - }, { "name": "--sf-density", "type": "token", @@ -13329,58 +13303,6 @@ "optimal" ] }, - { - "name": "--sf-is-active", - "type": "token", - "tier": "PUBLIC-ADVANCED", - "role": "knob", - "namespace": "is", - "category": "Core tokens", - "area": "core", - "group": "INTERACTION STATE FLAGS", - "description": "Integer state flag for the active state. Set to 1 (via JS or .sf-is-active) to drive branching calc() expressions. 0 = default (inactive).", - "value": "0", - "aliasOf": null, - "registered": true, - "animatable": true, - "syntax": "", - "inherits": true, - "optional": false, - "layer": "slashed.tokens", - "sourceFiles": [ - "core/tokens.css" - ], - "bundles": [ - "full", - "optimal" - ] - }, - { - "name": "--sf-is-current", - "type": "token", - "tier": "PUBLIC-ADVANCED", - "role": "knob", - "namespace": "is", - "category": "Core tokens", - "area": "core", - "group": "INTERACTION STATE FLAGS", - "description": "Integer state flag for the current/selected state. Set to 1 (via .sf-is-current) to drive branching calc() expressions. 0 = default.", - "value": "0", - "aliasOf": null, - "registered": true, - "animatable": true, - "syntax": "", - "inherits": true, - "optional": false, - "layer": "slashed.tokens", - "sourceFiles": [ - "core/tokens.css" - ], - "bundles": [ - "full", - "optimal" - ] - }, { "name": "--sf-is-dark", "type": "token", @@ -13407,58 +13329,6 @@ "optimal" ] }, - { - "name": "--sf-is-open", - "type": "token", - "tier": "PUBLIC-ADVANCED", - "role": "knob", - "namespace": "is", - "category": "Core tokens", - "area": "core", - "group": "INTERACTION STATE FLAGS", - "description": "Integer state flag for the open/expanded state. Set to 1 (via .sf-is-open) to drive branching calc() expressions. 0 = default (closed).", - "value": "0", - "aliasOf": null, - "registered": true, - "animatable": true, - "syntax": "", - "inherits": true, - "optional": false, - "layer": "slashed.tokens", - "sourceFiles": [ - "core/tokens.css" - ], - "bundles": [ - "full", - "optimal" - ] - }, - { - "name": "--sf-is-pressed", - "type": "token", - "tier": "PUBLIC-ADVANCED", - "role": "knob", - "namespace": "is", - "category": "Core tokens", - "area": "core", - "group": "INTERACTION STATE FLAGS", - "description": "Integer state flag for the pressed state. Set to 1 (via .sf-is-pressed) to drive branching calc() expressions. 0 = default.", - "value": "0", - "aliasOf": null, - "registered": true, - "animatable": true, - "syntax": "", - "inherits": true, - "optional": false, - "layer": "slashed.tokens", - "sourceFiles": [ - "core/tokens.css" - ], - "bundles": [ - "full", - "optimal" - ] - }, { "name": "--sf-leading-normal", "type": "token", @@ -16761,32 +16631,6 @@ "optimal" ] }, - { - "name": "--sf-state-pending-opacity", - "type": "token", - "tier": "PUBLIC", - "role": "knob", - "namespace": "state", - "category": "Core tokens", - "area": "core", - "group": "Opacity", - "description": "Opacity for pending / loading state elements.", - "value": "0.7", - "aliasOf": null, - "registered": false, - "animatable": false, - "syntax": null, - "inherits": null, - "optional": false, - "layer": "slashed.tokens", - "sourceFiles": [ - "core/tokens.css" - ], - "bundles": [ - "full", - "optimal" - ] - }, { "name": "--sf-sticky-offset", "type": "token", @@ -23748,95 +23592,25 @@ ] }, { - "name": "sf-is-active", + "name": "sf-invisible", "type": "class", "tier": "PUBLIC", - "selector": ".sf-is-active", - "prefix": "sf-is", - "kind": "state", - "category": "State classes", - "area": "states", - "group": "ACTIVE / SELECTED / CURRENT", - "description": "Active state — applied to the currently interacted-with element (e.g. pressed button, active menu item). Sets --sf-is-active to 1 for calc() branching.", - "isVariant": false, - "baseClass": null, - "optional": false, - "layer": "slashed.states", - "sourceFiles": [ - "core/states.css" - ], - "bundles": [ - "full", - "optimal" - ] - }, - { - "name": "sf-is-collapsed", - "type": "class", - "tier": "PUBLIC", - "selector": ".sf-is-collapsed", - "prefix": "sf-is", - "kind": "state", - "category": "State classes", - "area": "states", - "group": "OPEN / CLOSED", - "description": "Collapsed state for disclosure widgets — hides the expandable region. Pair with sf-is-expanded or sf-is-open. Components add transitions.", - "isVariant": false, - "baseClass": null, - "optional": false, - "layer": "slashed.states", - "sourceFiles": [ - "core/states.css" - ], - "bundles": [ - "full", - "optimal" - ] - }, - { - "name": "sf-is-current", - "type": "class", - "tier": "PUBLIC", - "selector": ".sf-is-current", - "prefix": "sf-is", - "kind": "state", - "category": "State classes", - "area": "states", - "group": "ACTIVE / SELECTED / CURRENT", - "description": "Current/selected state — marks the active page in navigation or the selected item in a list. Sets --sf-is-current to 1 for calc() branching.", - "isVariant": false, - "baseClass": null, - "optional": false, - "layer": "slashed.states", - "sourceFiles": [ - "core/states.css" - ], - "bundles": [ - "full", - "optimal" - ] - }, - { - "name": "sf-is-danger", - "type": "class", - "tier": "PUBLIC", - "selector": ".sf-is-danger", - "prefix": "sf-is", - "kind": "state", - "category": "State classes", - "area": "states", - "group": "VALIDATION / FEEDBACK", - "description": "Danger status state — applies danger color tokens for destructive actions (e.g. a delete confirmation dialog) and form validation errors.", + "selector": ".sf-invisible", + "prefix": "sf", + "kind": "utility", + "category": "Utilities", + "area": "utilities", + "group": "VISIBILITY HELPERS (.sf-invisible / .sf-visible)", + "description": "Visibility helper (optional/utilities.css) — hides the element visually but keeps it in the layout and accessibility tree (visibility: hidden). Not a runtime state: no JS/ARIA condition of its own.", "isVariant": false, "baseClass": null, - "optional": false, - "layer": "slashed.states", + "optional": true, + "layer": "slashed.utilities", "sourceFiles": [ - "core/states.css" + "optional/utilities.css" ], "bundles": [ - "full", - "optimal" + "full" ] }, { @@ -23965,30 +23739,7 @@ "category": "State classes", "area": "states", "group": "VALIDATION / FEEDBACK", - "description": "Error state — alias for sf-is-danger. Applies danger color tokens to indicate a validation failure or system error. Visual styling is identical to sf-is-invalid.", - "isVariant": false, - "baseClass": null, - "optional": false, - "layer": "slashed.states", - "sourceFiles": [ - "core/states.css" - ], - "bundles": [ - "full", - "optimal" - ] - }, - { - "name": "sf-is-expanded", - "type": "class", - "tier": "PUBLIC", - "selector": ".sf-is-expanded", - "prefix": "sf-is", - "kind": "state", - "category": "State classes", - "area": "states", - "group": "OPEN / CLOSED", - "description": "Expanded state for disclosure widgets — shows the expandable region. Inverse of sf-is-collapsed. Components add transitions.", + "description": "Error state — general (not form-field-specific) negative feedback. Applies danger color tokens to indicate a validation failure or system error. Visual styling is identical to sf-is-invalid.", "isVariant": false, "baseClass": null, "optional": false, @@ -24010,7 +23761,7 @@ "kind": "state", "category": "State classes", "area": "states", - "group": "ACTIVE / SELECTED / CURRENT", + "group": "SELECTED / HIGHLIGHTED", "description": "Highlighted state — applies a highlight background. Useful for search result highlighting and keyboard-navigated lists.", "isVariant": false, "baseClass": null, @@ -24070,29 +23821,6 @@ "optimal" ] }, - { - "name": "sf-is-invisible", - "type": "class", - "tier": "PUBLIC", - "selector": ".sf-is-invisible", - "prefix": "sf-is", - "kind": "state", - "category": "State classes", - "area": "states", - "group": "VISIBILITY", - "description": "Invisible state — hides the element visually but keeps it in the layout and accessibility tree (visibility: hidden).", - "isVariant": false, - "baseClass": null, - "optional": false, - "layer": "slashed.states", - "sourceFiles": [ - "core/states.css" - ], - "bundles": [ - "full", - "optimal" - ] - }, { "name": "sf-is-loading", "type": "class", @@ -24118,75 +23846,6 @@ "optimal" ] }, - { - "name": "sf-is-open", - "type": "class", - "tier": "PUBLIC", - "selector": ".sf-is-open", - "prefix": "sf-is", - "kind": "state", - "category": "State classes", - "area": "states", - "group": "OPEN / CLOSED", - "description": "Open/shown state for modals, dropdowns, and drawers — counterpart to sf-is-collapsed. Sets --sf-is-open to 1 for calc() branching.", - "isVariant": false, - "baseClass": null, - "optional": false, - "layer": "slashed.states", - "sourceFiles": [ - "core/states.css" - ], - "bundles": [ - "full", - "optimal" - ] - }, - { - "name": "sf-is-pending", - "type": "class", - "tier": "PUBLIC", - "selector": ".sf-is-pending", - "prefix": "sf-is", - "kind": "state", - "category": "State classes", - "area": "states", - "group": "LOADING / ASYNC FEEDBACK", - "description": "Pending state for optimistic UI — content remains visible but dimmed while an async request is in flight. Differs from sf-is-loading (which hides content entirely).", - "isVariant": false, - "baseClass": null, - "optional": false, - "layer": "slashed.states", - "sourceFiles": [ - "core/states.css" - ], - "bundles": [ - "full", - "optimal" - ] - }, - { - "name": "sf-is-pressed", - "type": "class", - "tier": "PUBLIC", - "selector": ".sf-is-pressed", - "prefix": "sf-is", - "kind": "state", - "category": "State classes", - "area": "states", - "group": "ACTIVE / SELECTED / CURRENT", - "description": "Pressed/toggled-on state for toggle buttons. Sets --sf-is-pressed to 1 for calc() branching and signals the selected state to components.", - "isVariant": false, - "baseClass": null, - "optional": false, - "layer": "slashed.states", - "sourceFiles": [ - "core/states.css" - ], - "bundles": [ - "full", - "optimal" - ] - }, { "name": "sf-is-selected", "type": "class", @@ -24196,7 +23855,7 @@ "kind": "state", "category": "State classes", "area": "states", - "group": "ACTIVE / SELECTED / CURRENT", + "group": "SELECTED / HIGHLIGHTED", "description": "Selected state — marks an item as selected in a list, table row, or grid cell. Visual specifics (background, border) are applied by component styles.", "isVariant": false, "baseClass": null, @@ -24279,29 +23938,6 @@ "optimal" ] }, - { - "name": "sf-is-visible", - "type": "class", - "tier": "PUBLIC", - "selector": ".sf-is-visible", - "prefix": "sf-is", - "kind": "state", - "category": "State classes", - "area": "states", - "group": "VISIBILITY", - "description": "Visible state — forces the element to be visible (visibility: visible). Use to un-hide an element that inherited visibility: hidden from a parent.", - "isVariant": false, - "baseClass": null, - "optional": false, - "layer": "slashed.states", - "sourceFiles": [ - "core/states.css" - ], - "bundles": [ - "full", - "optimal" - ] - }, { "name": "sf-is-warning", "type": "class", @@ -26701,6 +26337,28 @@ "optimal" ] }, + { + "name": "sf-visible", + "type": "class", + "tier": "PUBLIC", + "selector": ".sf-visible", + "prefix": "sf", + "kind": "utility", + "category": "Utilities", + "area": "utilities", + "group": "VISIBILITY HELPERS (.sf-invisible / .sf-visible)", + "description": "Visibility helper (optional/utilities.css) — forces the element to be visible (visibility: visible). Use to un-hide an element that inherited visibility: hidden from a parent.", + "isVariant": false, + "baseClass": null, + "optional": true, + "layer": "slashed.utilities", + "sourceFiles": [ + "optional/utilities.css" + ], + "bundles": [ + "full" + ] + }, { "name": "skip-link", "type": "class", diff --git a/docs/api-index.md b/docs/api-index.md index 99175e18..9a9f0373 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). -**1067 elements** — 747 tokens, 320 classes. +**1053 elements** — 741 tokens, 312 classes. | Tier | Count | Meaning | |---|---|---| -| PUBLIC | 1013 | Everyday surface. SemVer-stable. | -| PUBLIC-ADVANCED | 53 | Same SemVer guarantee; niche/powerful. | +| PUBLIC | 1003 | Everyday surface. SemVer-stable. | +| PUBLIC-ADVANCED | 49 | Same SemVer guarantee; niche/powerful. | | INTERNAL | 1 | Implementation detail; may change without a major bump. | -## Tokens (747) +## Tokens (741) ### 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 (616) +### Core tokens (610) | Token | Tier | Role | Namespace | Default | Description | |---|---|---|---|---|---| @@ -378,7 +378,6 @@ and a short description. The machine-readable companion (with all columns) is | `--sf-content-gap` | PUBLIC | consumption | content | `var(--sf-space-s)` | Tight gap within related content (stack, flow, prose). Override globally to retune all tight primitives at once. | | `--sf-contrast-bias` | PUBLIC-ADVANCED | knob | contrast | `0` | Global text-contrast nudge. Positive values push derived reading-text colors toward the extremes (darker in light mode, lighter in dark). Default 0 (neutral). | | `--sf-contrast-threshold` | PUBLIC-ADVANCED | knob | contrast | `0.6` | OKLCH lightness crossover for auto-contrast on colored surfaces. Colors with L above this get dark text; below get light text. Default 0.6. | -| `--sf-current-font-weight` | PUBLIC | consumption | current | `var(--sf-font-weight-bold)` | Inherits the current font-weight from context. Useful as a reset. | | `--sf-density` | PUBLIC-ADVANCED | knob | density | `1` | 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-display-l-line-height` | PUBLIC | knob | display | `1` | Line height for large display text (--sf-text-display-l). Very tight to avoid excessive gaps at oversized sizes. | | `--sf-display-m-line-height` | PUBLIC | knob | display | `1.05` | Line height for medium display text (--sf-text-display-m). | @@ -494,11 +493,7 @@ and a short description. The machine-readable companion (with all columns) is | `--sf-icon-s` | PUBLIC | knob | icon | `1em` | Small icon size (~16px). Dense UI chrome. | | `--sf-icon-xl` | PUBLIC | knob | icon | `3em` | Extra-large icon size (~32px). Feature icons. | | `--sf-icon-xs` | PUBLIC | knob | icon | `0.875em` | Extra-small icon size (~12px). Inline indicators. | -| `--sf-is-active` | PUBLIC-ADVANCED | knob | is | `0` | Integer state flag for the active state. Set to 1 (via JS or .sf-is-active) to drive branching calc() expressions. 0 = default (inactive). | -| `--sf-is-current` | PUBLIC-ADVANCED | knob | is | `0` | Integer state flag for the current/selected state. Set to 1 (via .sf-is-current) to drive branching calc() expressions. 0 = default. | | `--sf-is-dark` | INTERNAL | knob | is | `0` | Mode flag — 0 in light mode, 1 in dark mode. Set automatically by [data-theme='dark'] and prefers-color-scheme. Drives dark-mode formula branches in calc(). | -| `--sf-is-open` | PUBLIC-ADVANCED | knob | is | `0` | Integer state flag for the open/expanded state. Set to 1 (via .sf-is-open) to drive branching calc() expressions. 0 = default (closed). | -| `--sf-is-pressed` | PUBLIC-ADVANCED | knob | is | `0` | Integer state flag for the pressed state. Set to 1 (via .sf-is-pressed) to drive branching calc() expressions. 0 = default. | | `--sf-leading-normal` | PUBLIC | knob | leading | `1.5` | Normal line height (~1.5). Default for body text. | | `--sf-leading-relaxed` | PUBLIC | knob | leading | `1.625` | Relaxed line height (~1.65). Long-form reading text. | | `--sf-leading-snug` | PUBLIC | knob | leading | `1.3` | Snug line height (~1.35). Use for sub-headings. | @@ -598,7 +593,6 @@ and a short description. The machine-readable companion (with all columns) is | `--sf-space-xl` | PUBLIC | consumption | space | `calc(clamp(calc(var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), 2) * 1rem), calc((var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), 2) - var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), 2)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), 2) * 1rem), calc(var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), 2) * 1rem)) * var(--sf-space-scale))` | 32px-equivalent spacing. Feature row gaps, generous section padding. | | `--sf-space-xs` | PUBLIC | consumption | space | `calc(clamp(calc(var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), -2) * 1rem), calc((var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), -2) - var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), -2)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), -2) * 1rem), calc(var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), -2) * 1rem)) * var(--sf-space-scale))` | 8px-equivalent spacing. Compact list rows, tab padding. | | `--sf-stagger-step` | PUBLIC | knob | stagger | `75ms` | Per-item delay increment for the .sf-stagger utility. One knob retunes the whole staggered sequence; the class multiplies it by each child's index and by --sf-motion-scale. | -| `--sf-state-pending-opacity` | PUBLIC | knob | state | `0.7` | Opacity for pending / loading state elements. | | `--sf-sticky-offset` | PUBLIC | consumption | sticky | `clamp( var(--sf-sticky-offset-mobile), calc((var(--sf-sticky-offset-desktop) - var(--sf-sticky-offset-mobile)) / ((var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * 1rem) * (100vw - var(--sf-fluid-min-vw) * 1rem) + var(--sf-sticky-offset-mobile)), var(--sf-sticky-offset-desktop))` | top offset for position: sticky elements, accounting for the header height. | | `--sf-sticky-offset-desktop` | PUBLIC | consumption | sticky | `var(--sf-header-height-desktop)` | Sticky offset at desktop breakpoints. | | `--sf-sticky-offset-mobile` | PUBLIC | consumption | sticky | `var(--sf-header-height-mobile)` | Sticky offset at mobile breakpoints. | @@ -787,7 +781,7 @@ and a short description. The machine-readable companion (with all columns) is | `--sf-surface-bg-size` | PUBLIC | knob | surface | `cover` | background-size for the .sf-surface-bg image. Default: cover. | | `--sf-surface-color` | PUBLIC | consumption | surface | `var(--sf-color-base)` | Input for the generic .sf-surface macro. Set any color (including palette shades); the macro derives background, auto-contrast foreground, and the contextual token set from it. | -## Classes (320) +## Classes (312) ### Accessibility (8) @@ -1085,34 +1079,24 @@ and a short description. The machine-readable companion (with all columns) is | `.print-no-color` | PUBLIC | print | — | Strips color information in print (print-color-adjust: economy). Defers to the printer's ink-saving mode for backgrounds and non-critical color fills. | | `.print-only` | PUBLIC | print | — | Hides the element on screen (display: none) but shows it in @media print. Use for print-only headers, footers, and supplementary content not needed on screen. | -### State classes (25) +### State classes (15) | Class | Tier | Kind | Group | Description | |---|---|---|---|---| -| `.sf-is-active` | PUBLIC | state | ACTIVE / SELECTED / CURRENT | Active state — applied to the currently interacted-with element (e.g. pressed button, active menu item). Sets --sf-is-active to 1 for calc() branching. | -| `.sf-is-collapsed` | PUBLIC | state | OPEN / CLOSED | Collapsed state for disclosure widgets — hides the expandable region. Pair with sf-is-expanded or sf-is-open. Components add transitions. | -| `.sf-is-current` | PUBLIC | state | ACTIVE / SELECTED / CURRENT | Current/selected state — marks the active page in navigation or the selected item in a list. Sets --sf-is-current to 1 for calc() branching. | -| `.sf-is-danger` | PUBLIC | state | VALIDATION / FEEDBACK | Danger status state — applies danger color tokens for destructive actions (e.g. a delete confirmation dialog) and form validation errors. | | `.sf-is-disabled` | PUBLIC | state | INTERACTIVITY | Disabled state — dims the element (--sf-opacity-disabled), removes pointer events, and sets cursor: not-allowed. Inherited by child elements that re-enable pointer events. | | `.sf-is-draggable` | PUBLIC | state | DRAG & DROP | Draggable state — shows a grab cursor to indicate the element can be dragged. Apply before a drag interaction begins. | | `.sf-is-dragging` | PUBLIC | state | DRAG & DROP | Active drag state — shows a grabbing cursor while the element is being dragged. Apply during the drag event. | | `.sf-is-drop-target` | PUBLIC | state | DRAG & DROP | Drop-target state — applies a visual highlight to indicate a valid drag-and-drop destination. Toggle on drag-over events. | | `.sf-is-empty` | PUBLIC | state | EMPTY STATE | Empty state — hides the element when it has no child nodes (:empty). Used for dynamic lists or containers that may render empty. | -| `.sf-is-error` | PUBLIC | state | VALIDATION / FEEDBACK | Error state — alias for sf-is-danger. Applies danger color tokens to indicate a validation failure or system error. Visual styling is identical to sf-is-invalid. | -| `.sf-is-expanded` | PUBLIC | state | OPEN / CLOSED | Expanded state for disclosure widgets — shows the expandable region. Inverse of sf-is-collapsed. Components add transitions. | -| `.sf-is-highlighted` | PUBLIC | state | ACTIVE / SELECTED / CURRENT | Highlighted state — applies a highlight background. Useful for search result highlighting and keyboard-navigated lists. | +| `.sf-is-error` | PUBLIC | state | VALIDATION / FEEDBACK | Error state — general (not form-field-specific) negative feedback. Applies danger color tokens to indicate a validation failure or system error. Visual styling is identical to sf-is-invalid. | +| `.sf-is-highlighted` | PUBLIC | state | SELECTED / HIGHLIGHTED | Highlighted state — applies a highlight background. Useful for search result highlighting and keyboard-navigated lists. | | `.sf-is-info` | PUBLIC | state | VALIDATION / FEEDBACK | Info status state — applies info color tokens. Use for neutral informational messages and hints. | | `.sf-is-invalid` | PUBLIC | state | VALIDATION / FEEDBACK | Invalid state — applies danger color tokens to form fields with validation failures. Identical visual output to sf-is-error; the name signals form-field context. | -| `.sf-is-invisible` | PUBLIC | state | VISIBILITY | Invisible state — hides the element visually but keeps it in the layout and accessibility tree (visibility: hidden). | | `.sf-is-loading` | PUBLIC | state | LOADING / ASYNC FEEDBACK | Loading state — hides the element's text (color: transparent), removes pointer events, and renders a spinner via ::after. Use for buttons and containers awaiting async results. | -| `.sf-is-open` | PUBLIC | state | OPEN / CLOSED | Open/shown state for modals, dropdowns, and drawers — counterpart to sf-is-collapsed. Sets --sf-is-open to 1 for calc() branching. | -| `.sf-is-pending` | PUBLIC | state | LOADING / ASYNC FEEDBACK | Pending state for optimistic UI — content remains visible but dimmed while an async request is in flight. Differs from sf-is-loading (which hides content entirely). | -| `.sf-is-pressed` | PUBLIC | state | ACTIVE / SELECTED / CURRENT | Pressed/toggled-on state for toggle buttons. Sets --sf-is-pressed to 1 for calc() branching and signals the selected state to components. | -| `.sf-is-selected` | PUBLIC | state | ACTIVE / SELECTED / CURRENT | Selected state — marks an item as selected in a list, table row, or grid cell. Visual specifics (background, border) are applied by component styles. | +| `.sf-is-selected` | PUBLIC | state | SELECTED / HIGHLIGHTED | Selected state — marks an item as selected in a list, table row, or grid cell. Visual specifics (background, border) are applied by component styles. | | `.sf-is-skeleton` | PUBLIC | state | LOADING / ASYNC FEEDBACK | Skeleton loading placeholder state. Applies a shimmer animation over the element to indicate that content is loading. Works on both img and non-media elements. | | `.sf-is-success` | PUBLIC | state | VALIDATION / FEEDBACK | Success status state — applies success color tokens. Use for confirmation messages, completed form steps, and positive feedback. | | `.sf-is-valid` | PUBLIC | state | VALIDATION / FEEDBACK | Valid state — applies success color tokens to form fields that have passed validation. Visual styling is identical to sf-is-success; the name signals form-field context. | -| `.sf-is-visible` | PUBLIC | state | VISIBILITY | Visible state — forces the element to be visible (visibility: visible). Use to un-hide an element that inherited visibility: hidden from a parent. | | `.sf-is-warning` | PUBLIC | state | VALIDATION / FEEDBACK | Warning status state — applies warning color tokens. Use for cautionary messages and non-critical alerts. | ### Theme utilities (3) @@ -1123,7 +1107,7 @@ and a short description. The machine-readable companion (with all columns) is | `.sf-theme-light` | PUBLIC | theme | — | Forces light colour scheme on the element and its subtree (class alias of [data-theme="light"]). | | `.sf-theme-transition` | PUBLIC | theme | — | Opt-in animated light/dark theme crossfade. Apply to (or a subtree) so color tokens transition smoothly when [data-theme] changes. Duration controlled by --sf-theme-transition-duration. | -### Utilities (31) +### Utilities (33) | Class | Tier | Kind | Group | Description | |---|---|---|---|---| @@ -1139,6 +1123,7 @@ and a short description. The machine-readable companion (with all columns) is | `.sf-hover-sink` | PUBLIC | utility | HOVER-TRANSFORM UTILITIES (.sf-hover-*) | Hover-triggered transform: translates the element down slightly on hover. | | `.sf-hover-slide-end` | PUBLIC | utility | HOVER-TRANSFORM UTILITIES (.sf-hover-*) | Hover-triggered transform: translates the element toward the inline-end on hover. | | `.sf-hover-slide-start` | PUBLIC | utility | HOVER-TRANSFORM UTILITIES (.sf-hover-*) | Hover-triggered transform: translates the element toward the inline-start on hover. | +| `.sf-invisible` | PUBLIC | utility | VISIBILITY HELPERS (.sf-invisible / .sf-visible) | Visibility helper (optional/utilities.css) — hides the element visually but keeps it in the layout and accessibility tree (visibility: hidden). Not a runtime state: no JS/ARIA condition of its own. | | `.sf-list-none` | PUBLIC | utility | LIST RESET (.sf-list-none) | Drops marker + inline start-padding in one class, on any