Skip to content

Commit 655755f

Browse files
committed
refactor(states)!: remove speculative interaction-state flags, relocate visibility helpers
Second pass on .sf-is-* after holding the six previously-defended survivors (active, open, collapsed, expanded, pressed, current, danger, pending) to a stricter bar: "documented and intentional" is a different question from "needed at this stage of a pre-1.0, lean framework." None of them clear "most consumers need this today": - .sf-is-active/.sf-is-open/.sf-is-collapsed/.sf-is-pressed/.sf-is-current only ever set an inheritable --sf-is-* custom property for a hypothetical consumer's own calc() to branch on, with zero consumers anywhere in this codebase or in any of the four comparable frameworks surveyed (Open Props, Pico.css, Bulma, Automatic.css ship nothing like it). - .sf-is-expanded was pure vocabulary duplication of .sf-is-open (identical rule, no distinct behaviour). - .sf-is-danger had a live correctness bug (only ever worked on form fields via --sf-field-*, never the "destructive-action button" case its own docs described) and duplicated .sf-is-invalid/.sf-is-error's implementation. - .sf-is-pending was two lines cheap to hand-roll, with .sf-is-loading already covering the common "mask with a spinner" case. All eight removed, along with the now-orphaned --sf-is-active/-current/ -pressed/-open @Property registrations and the --sf-current-font-weight / --sf-state-pending-opacity tokens that lost their only consumer. .sf-is-invisible/.sf-is-visible were relocated, not cut: single-property helpers with no runtime condition of their own (unlike .sf-is-selected, which paints something), moved to optional/utilities.css as .sf-invisible/ .sf-visible — matching Bulma's precedent of treating visibility as a helper, not a state. demo/index.html's own mobile-nav drawer used .sf-is-open as a plain JS-toggled hook (its own #nav-overlay/#demo-nav rules, independent of states.css's flag mechanism) — renamed to a demo-local .is-open class, which doubles as a worked example of "write your own toggle class" guidance. user-manual/system-kolorow.md's stepper example updated the same way. Added docs/states.md § "Prefer native state" with a table of native pseudo-classes/attributes/ARIA states to reach for before adding a .sf-is-* class, and a documented history of both removal rounds. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CAsUv13o2SAi7Tnsx93EtV
1 parent f9f8630 commit 655755f

35 files changed

Lines changed: 397 additions & 1187 deletions

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
<p align="center">
66
A cascade-layer CSS framework you never compile.<br>
7-
747 design tokens, automatic dark mode, fluid type &amp; spacing — no build step, no Node, no JavaScript.
7+
741 design tokens, automatic dark mode, fluid type &amp; spacing — no build step, no Node, no JavaScript.
88
</p>
99

1010
<p align="center">
@@ -67,7 +67,7 @@ Every letter of SLASHED is a design commitment, and each one is checkable agains
6767

6868
## What's inside
6969

70-
- **747 design tokens** (`--sf-*`) covering color, typography, spacing, layout, borders, shadows, motion, and z-index — catalogued in a machine-readable [API index](docs/api-index.json).
70+
- **741 design tokens** (`--sf-*`) covering color, typography, spacing, layout, borders, shadows, motion, and z-index — catalogued in a machine-readable [API index](docs/api-index.json).
7171
- **Layout primitives**`.sf-container`, `.sf-stack`, `.sf-grid`, `.sf-cluster`, `.sf-sidebar`, `.sf-switcher`, `.sf-cover`, `.sf-center`, `.sf-frame`, `.sf-reel`, `.sf-bento`, and more ([docs/layout.md](docs/layout.md)).
7272
- **Macros** — recipes like `.sf-prose`, `.sf-flow`, `.sf-truncate`, `.sf-aspect`, `.sf-scroll-shadow`, `.sf-surface` ([docs/macros.md](docs/macros.md)).
7373
- **State classes**`.sf-is-*` classes mapped to ARIA semantics ([docs/states.md](docs/states.md)).

badges/badge-optimal.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"schemaVersion": 1,
33
"label": "optimal",
4-
"message": "19.5 kB gzip",
4+
"message": "19.4 kB gzip",
55
"color": "brightgreen",
66
"namedLogo": "css3"
77
}

configurator/src/components/panels/EffectsPanel.svelte

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@
2828
let blur = $derived(parseNum(overrides["--sf-blur"], 12, "px"));
2929
let opacityMuted = $derived(parseNum(overrides["--sf-opacity-muted"], 0.5));
3030
let opacityDisabled = $derived(parseNum(overrides["--sf-opacity-disabled"], 0.45));
31-
let pendingOpacity = $derived(parseNum(overrides["--sf-state-pending-opacity"], 0.7));
3231
let scrollbarThumb = $derived(overrides["--sf-scrollbar-thumb"] ?? "");
3332
let scrollbarTrack = $derived(overrides["--sf-scrollbar-track"] ?? "");
3433
@@ -84,19 +83,11 @@
8483
onChange={(v) => onSet("--sf-opacity-disabled", String(v))}
8584
onReset={() => onReset("--sf-opacity-disabled")}
8685
/>
87-
<SliderRow
88-
label="Pending opacity" value={pendingOpacity} min={0.1} max={1} step={0.05}
89-
help="--sf-state-pending-opacity — opacity of elements in loading/pending state"
90-
overridden={"--sf-state-pending-opacity" in overrides}
91-
onChange={(v) => onSet("--sf-state-pending-opacity", String(v))}
92-
onReset={() => onReset("--sf-state-pending-opacity")}
93-
/>
9486
<!-- Opacity preview -->
9587
<div class="bg-black/4 dark:bg-white/4 rounded-xl border border-black/8 dark:border-white/8 p-3 space-y-2">
9688
{#each [
9789
{ label: "muted", token: "--sf-opacity-muted", val: opacityMuted },
9890
{ label: "disabled", token: "--sf-opacity-disabled", val: opacityDisabled },
99-
{ label: "pending", token: "--sf-state-pending-opacity", val: pendingOpacity },
10091
] as row (row.label)}
10192
<div class="flex items-center gap-3">
10293
<span class="text-[9px] text-slate-400 dark:text-slate-600 w-14">{row.label}</span>

configurator/src/components/panels/MiscPanel.svelte

Lines changed: 3 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
<script lang="ts">
22
import SliderRow from '../inputs/SliderRow.svelte';
33
import ColorInput from '../inputs/ColorInput.svelte';
4-
import Toggle from '../inputs/Toggle.svelte';
54
import Section from '../inputs/Section.svelte';
65
import { SIZE_SCALE } from '../../lib/variableScales';
76
@@ -60,7 +59,7 @@
6059
let showObjectFit = $state(false);
6160
let showPrint = $state(false);
6261
let showSafeArea = $state(false);
63-
let showStateFlags = $state(false);
62+
let showFieldMarker = $state(false);
6463
6564
function getSizeValue(t: typeof SIZE_TOKENS[0]): number {
6665
return parseNum(overrides[t.token]?.replace("rem",""), t.default);
@@ -456,35 +455,8 @@
456455

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

459-
<!-- STATE FLAGS -->
460-
<Section title="State flags" bind:open={showStateFlags}>
461-
<p class="text-[10px] text-slate-400 dark:text-slate-600 leading-relaxed">
462-
Boolean CSS custom property flags (0 = off, 1 = on). Components toggle these to activate state-specific styles.
463-
Overriding globally forces all matching elements into that state — useful for testing and demos.
464-
</p>
465-
<div class="space-y-1.5">
466-
{#each [
467-
{ label: "sf-is-active", token: "--sf-is-active", note: "Active/selected state" },
468-
{ label: "sf-is-current", token: "--sf-is-current", note: "Current page/item" },
469-
{ label: "sf-is-open", token: "--sf-is-open", note: "Open/expanded state" },
470-
{ label: "sf-is-pressed", token: "--sf-is-pressed", note: "Pressed/depressed state" },
471-
] as f (f.token)}
472-
{@const cur = overrides[f.token] ?? "0"}
473-
<div class="flex items-center gap-2 py-0.5">
474-
<Toggle
475-
size="sm"
476-
checked={cur === "1"}
477-
ariaLabel={f.label}
478-
onToggle={() => cur === "1" ? onReset(f.token) : onSet(f.token, "1")}
479-
/>
480-
<span class="text-[9px] font-mono text-slate-700 dark:text-slate-300 w-24 shrink-0">{f.label}</span>
481-
<span class="text-[9px] text-slate-400 dark:text-slate-600 flex-1">{f.note}</span>
482-
{#if f.token in overrides}
483-
<button onclick={() => onReset(f.token)} class="text-[8px] text-slate-500 hover:text-rose-600 dark:hover:text-rose-400 cursor-pointer shrink-0">reset</button>
484-
{/if}
485-
</div>
486-
{/each}
487-
</div>
458+
<!-- FIELD MARKER -->
459+
<Section title="Field marker" bind:open={showFieldMarker}>
488460
<div class="flex items-center gap-2 pt-1">
489461
<span class="text-[10px] font-semibold text-slate-600 dark:text-slate-400 w-24 shrink-0">Required marker</span>
490462
<input

configurator/src/data/api-index.generated.json

Lines changed: 6 additions & 132 deletions
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,21 @@
22
"_sync": {
33
"generatedBy": "configurator/scripts/sync-api.mjs",
44
"source": "docs/api-index.json",
5-
"tokensHash": "554ed4dfb519",
5+
"tokensHash": "d99951a174ca",
66
"bundles": [
77
"full",
88
"optimal"
99
],
1010
"counts": {
11-
"tokens": 747,
11+
"tokens": 741,
1212
"byTier": {
13-
"PUBLIC": 693,
14-
"PUBLIC-ADVANCED": 53,
13+
"PUBLIC": 691,
14+
"PUBLIC-ADVANCED": 49,
1515
"INTERNAL": 1
1616
},
1717
"byCategory": {
1818
"Layout tokens": 53,
19-
"Core tokens": 616,
19+
"Core tokens": 610,
2020
"Macro tokens": 32,
2121
"Component tokens": 46
2222
}
@@ -7792,27 +7792,6 @@
77927792
"optimal"
77937793
]
77947794
},
7795-
{
7796-
"name": "--sf-current-font-weight",
7797-
"tier": "PUBLIC",
7798-
"role": "consumption",
7799-
"namespace": "current",
7800-
"category": "Core tokens",
7801-
"group": "Font weights",
7802-
"description": "Numeric font-weight scale plus named aliases for common roles.",
7803-
"note": "Inherits the current font-weight from context. Useful as a reset.",
7804-
"value": "var(--sf-font-weight-bold)",
7805-
"aliasOf": "--sf-font-weight-bold",
7806-
"registered": false,
7807-
"syntax": null,
7808-
"fallbackOnly": false,
7809-
"optional": false,
7810-
"layer": "slashed.tokens",
7811-
"bundles": [
7812-
"full",
7813-
"optimal"
7814-
]
7815-
},
78167795
{
78177796
"name": "--sf-density",
78187797
"tier": "PUBLIC-ADVANCED",
@@ -10708,48 +10687,6 @@
1070810687
"optimal"
1070910688
]
1071010689
},
10711-
{
10712-
"name": "--sf-is-active",
10713-
"tier": "PUBLIC-ADVANCED",
10714-
"role": "knob",
10715-
"namespace": "is",
10716-
"category": "Core tokens",
10717-
"group": "INTERACTION STATE FLAGS",
10718-
"description": "Integer flags toggled by JS or CSS :has() to drive numeric branching in calc() expressions. Set to 1 to activate the state; 0 is the default off state.",
10719-
"note": "Integer state flag for the active state. Set to 1 (via JS or .sf-is-active) to drive branching calc() expressions. 0 = default (inactive).",
10720-
"value": "0",
10721-
"aliasOf": null,
10722-
"registered": true,
10723-
"syntax": "<integer>",
10724-
"fallbackOnly": false,
10725-
"optional": false,
10726-
"layer": "slashed.tokens",
10727-
"bundles": [
10728-
"full",
10729-
"optimal"
10730-
]
10731-
},
10732-
{
10733-
"name": "--sf-is-current",
10734-
"tier": "PUBLIC-ADVANCED",
10735-
"role": "knob",
10736-
"namespace": "is",
10737-
"category": "Core tokens",
10738-
"group": "INTERACTION STATE FLAGS",
10739-
"description": "Integer flags toggled by JS or CSS :has() to drive numeric branching in calc() expressions. Set to 1 to activate the state; 0 is the default off state.",
10740-
"note": "Integer state flag for the current/selected state. Set to 1 (via .sf-is-current) to drive branching calc() expressions. 0 = default.",
10741-
"value": "0",
10742-
"aliasOf": null,
10743-
"registered": true,
10744-
"syntax": "<integer>",
10745-
"fallbackOnly": false,
10746-
"optional": false,
10747-
"layer": "slashed.tokens",
10748-
"bundles": [
10749-
"full",
10750-
"optimal"
10751-
]
10752-
},
1075310690
{
1075410691
"name": "--sf-is-dark",
1075510692
"tier": "INTERNAL",
@@ -10771,48 +10708,6 @@
1077110708
"optimal"
1077210709
]
1077310710
},
10774-
{
10775-
"name": "--sf-is-open",
10776-
"tier": "PUBLIC-ADVANCED",
10777-
"role": "knob",
10778-
"namespace": "is",
10779-
"category": "Core tokens",
10780-
"group": "INTERACTION STATE FLAGS",
10781-
"description": "Integer flags toggled by JS or CSS :has() to drive numeric branching in calc() expressions. Set to 1 to activate the state; 0 is the default off state.",
10782-
"note": "Integer state flag for the open/expanded state. Set to 1 (via .sf-is-open) to drive branching calc() expressions. 0 = default (closed).",
10783-
"value": "0",
10784-
"aliasOf": null,
10785-
"registered": true,
10786-
"syntax": "<integer>",
10787-
"fallbackOnly": false,
10788-
"optional": false,
10789-
"layer": "slashed.tokens",
10790-
"bundles": [
10791-
"full",
10792-
"optimal"
10793-
]
10794-
},
10795-
{
10796-
"name": "--sf-is-pressed",
10797-
"tier": "PUBLIC-ADVANCED",
10798-
"role": "knob",
10799-
"namespace": "is",
10800-
"category": "Core tokens",
10801-
"group": "INTERACTION STATE FLAGS",
10802-
"description": "Integer flags toggled by JS or CSS :has() to drive numeric branching in calc() expressions. Set to 1 to activate the state; 0 is the default off state.",
10803-
"note": "Integer state flag for the pressed state. Set to 1 (via .sf-is-pressed) to drive branching calc() expressions. 0 = default.",
10804-
"value": "0",
10805-
"aliasOf": null,
10806-
"registered": true,
10807-
"syntax": "<integer>",
10808-
"fallbackOnly": false,
10809-
"optional": false,
10810-
"layer": "slashed.tokens",
10811-
"bundles": [
10812-
"full",
10813-
"optimal"
10814-
]
10815-
},
1081610711
{
1081710712
"name": "--sf-leading-normal",
1081810713
"tier": "PUBLIC",
@@ -11198,7 +11093,7 @@
1119811093
"namespace": "opacity",
1119911094
"category": "Core tokens",
1120011095
"group": "Opacity",
11201-
"description": "Opacity tokens for specific UI states: --sf-opacity-disabled dims non-interactive elements; --sf-state-pending-opacity dims elements in an async/loading state.",
11096+
"description": "Opacity tokens for specific UI states: --sf-opacity-disabled dims non-interactive elements.",
1120211097
"note": "Opacity for disabled UI elements (typically 0.4–0.5).",
1120311098
"value": "0.45",
1120411099
"aliasOf": null,
@@ -13480,27 +13375,6 @@
1348013375
"optimal"
1348113376
]
1348213377
},
13483-
{
13484-
"name": "--sf-state-pending-opacity",
13485-
"tier": "PUBLIC",
13486-
"role": "knob",
13487-
"namespace": "state",
13488-
"category": "Core tokens",
13489-
"group": "Opacity",
13490-
"description": "Opacity tokens for specific UI states: --sf-opacity-disabled dims non-interactive elements; --sf-state-pending-opacity dims elements in an async/loading state.",
13491-
"note": "Opacity for pending / loading state elements.",
13492-
"value": "0.7",
13493-
"aliasOf": null,
13494-
"registered": false,
13495-
"syntax": null,
13496-
"fallbackOnly": false,
13497-
"optional": false,
13498-
"layer": "slashed.tokens",
13499-
"bundles": [
13500-
"full",
13501-
"optimal"
13502-
]
13503-
},
1350413378
{
1350513379
"name": "--sf-sticky-offset",
1350613380
"tier": "PUBLIC",

0 commit comments

Comments
 (0)