css-is-awesome 1.8.0 → 1.8.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/AGENTS.md CHANGED
@@ -26,7 +26,12 @@ When asked to add a UI element, follow this order:
26
26
  4. **All values come from tokens.** Never hardcode `#3A5FCD`, `1rem`, `8px`. Use `cia.color(primary)`, `cia.space(4)`, `cia.radius(md)`.
27
27
  5. **No BEM.** No `__element` / `--modifier` chains. `cia-` is a single-class namespace prefix, not BEM.
28
28
  6. **No JavaScript.** Cia ships zero JS in the npm package. The 6 interactive components (accordion, modal, tooltip, dropdown, tabs, copy-button) use native HTML primitives — `<details name>`, `<dialog>`, `[popover]`, radio + `:has()`. Mobile navigation follows the same doctrine: the `hamburger` / `drawer` / `sheet` / `dock` mixin family rides `[popover]` + CSS Grid — see the `mobile-nav` recipe (hamburger + drawer) and the `bottom-nav` recipe (dock + sheets). **This rule binds cia, not you.** If you're *consuming* cia (building an app/component library on top of it), write JavaScript/framework components freely — React, SVG charts, interactivity, all of it — and use cia purely for styling (mixins + tokens). Compose the mixins to build any visual you want; you are not limited to cia's pre-made component mixins.
29
- 7. **Pages stack by named grid areas the layout mixins own the shell.** Every page's structure is declared once as `grid-template-areas` (`"navbar" "hero" … "footer"`), and each section claims its slot with `grid-area`. Never hand-write that: use `cia.page-layout(default | sidebar-left | sidebar-right | holy-grail)` for a full viewport shell (100dvh, sticky footer, auto mobile collapse), `cia.layout((sidebar content toc), $tracks: …)` for custom rows of regions, and `cia.page-header` / `cia.page-main` / `cia.page-footer` / `cia.area(name)` for the children. Named areas are the single source of truth for stacking — mobile is just a different `grid-template-areas`, not a pile of margin overrides. Baseline since 2020.
29
+ 7. **Grid is the skeleton; Flex is the quick moves.** Three levels, strictly:
30
+ - **The page shell is CSS Grid with landmark-named areas.** The body's areas ARE the document's landmarks — `nav`, `main`, `footer` — so the area map reads like the page and screen readers get the structure for free. Declared once via `cia.page-layout(default | sidebar-left | sidebar-right | holy-grail)` (100dvh, sticky footer, auto mobile collapse) or `cia.layout((sidebar content toc), $tracks: …)`; children claim slots with `cia.page-header` / `cia.page-main` / `cia.page-footer` / `cia.area(name)`. Baseline since 2020.
31
+ - **The doctrine scales inward: any control-dense region gets its own named-area grid.** A docs article (`header / demo / usage / tabs / footer`), a selections rail (`filter / list`), a dashboard — when a region has many controls, name its rows with `cia.layout(...)` too. Nested grids all the way down where density warrants; the grid's `gap` is the region's entire vertical rhythm (children carry no rhythm margins).
32
+ - **Flex lives at the leaves** — simple rows and columns you flip with one command: `@include cia.flex($direction: column)` turns a row into a stack. Flex arranges the contents of a slot the grid gave it; it never builds the page.
33
+
34
+ Mobile is just a different `grid-template-areas` map — restack, hide (with `display: none`; omitting an area does not hide its element), reorder — never a pile of margin overrides. Never hand-write `grid-template-areas`; the layout mixins own it.
30
35
  8. **On phones, things take the space they're in.** Below the mobile breakpoint an interactive surface fills its *container* — 100% of the column it lives in, inside the page's existing padding and formatting (never edge-to-edge past the page's gutters, never a floating mid-width popup). Triggers stretch to 100% with `justify-content: space-between` (label left, affordance right). A top-layer popover menu can't size to its container directly, so match its trigger via CSS anchor positioning (`anchor-name` on the trigger; `inset-inline: anchor(start) anchor(end)` under `@supports (anchor-name: --a)`) — opening directly under the trigger, with `position-try-fallbacks: flip-block` so it flips above when it would run off the bottom of the screen — with viewport-minus-gutters as the no-anchor fallback — and set it at `&[popover]` specificity, since the dropdown mixin's `inset: unset` reset otherwise wins. Also set `width: auto`: the UA's `[popover] { width: fit-content }` otherwise beats both inset edges and the menu hugs its content. `cia.sheet`, `cia.drawer($side: top|bottom)` and `cia.dock` already obey the rule. **Deliberate exception — code blocks:** on phones, code is allowed to run off into a horizontal scroll *inside its own box* (`white-space: pre; overflow-x: auto`) — never wrap, shrink, or reflow code to fit, and never let it widen the page; the copy button carries usability for long lines.
31
36
  9. **Style semantic state off ARIA, not a parallel `data-*`.** When a state has an ARIA source of truth, hook your styles to *that* attribute: `[aria-selected="true"]`, `[aria-expanded="true"]`, `[aria-invalid="true"]`, `:disabled, [aria-disabled="true"]`, `[aria-pressed="true"]`, `[aria-checked="true"]`, `[aria-current]`, `[role="tab"]`. Then the state can't be styled without setting the ARIA a screen reader needs — **accessible-by-construction**, one source of truth for looks + a11y. Reserve `data-*` for **cosmetic-only** variants (`data-size`, `data-variant`, `data-color`) that carry no ARIA meaning. cia's own components already do this where native HTML doesn't cover it (`[aria-current="page"]`, `[aria-selected="true"]`, `[role="option"]`).
32
37
 
@@ -275,11 +280,11 @@ self-contained and needs no setup.
275
280
 
276
281
  ## Where to read deeper
277
282
 
278
- Live docs site: **https://jerry2d3d.github.io/css-is-awesome/** — mixin reference, recipes, theme authoring, MCP setup.
283
+ Live docs site: **https://cssisawesome.com** (mirror: https://jerry2d3d.github.io/css-is-awesome/) — mixin reference, recipes, theme authoring, MCP setup, mobile playbook.
279
284
 
280
285
  Inside this package (all whitelisted in `files`):
281
286
 
282
- - **`css-is-awesome.instructions.md`** — full authoring rules (~14 KB, Cursor/Copilot pick up via `applyTo: "**"` frontmatter)
287
+ - **`css-is-awesome.instructions.md`** — full authoring rules (~25 KB, Cursor/Copilot pick up via `applyTo: "**"` frontmatter)
283
288
  - **`README.md`** — install, scripts, links
284
289
  - **`THREE-TIERS.md`** — full tier explanation with examples
285
290
  - **`THEMING.md`** — theme contract and dark-mode pattern
@@ -344,4 +349,4 @@ The markdown files above, the `cia` CLI, and the MCP server are the source of tr
344
349
 
345
350
  ---
346
351
 
347
- If you're a human reading this and want full developer docs, start at `README.md` or the docs site: https://jerry2d3d.github.io/css-is-awesome/.
352
+ If you're a human reading this and want full developer docs, start at `README.md` or the docs site: https://cssisawesome.com.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,31 @@
1
+ ## [1.8.2](https://github.com/Jerry2d3d/css-is-awesome/compare/v1.8.1...v1.8.2) (2026-09-05)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * **site:** ComponentDoc must be a client component ([fd6ab31](https://github.com/Jerry2d3d/css-is-awesome/commit/fd6ab3117fb06d37c9ee1da4d1071324cae87338))
7
+ * **tabs:** tab list is a <nav> - the div list shifted every panel index ([5099145](https://github.com/Jerry2d3d/css-is-awesome/commit/50991455c0646ffa7ec468166d29d239cbc4c235)), closes [#1](https://github.com/Jerry2d3d/css-is-awesome/issues/1)
8
+
9
+
10
+ ### Features
11
+
12
+ * **site:** component docs are data - DocEntry registry + article grid (phase 3) ([98d2419](https://github.com/Jerry2d3d/css-is-awesome/commit/98d2419af9b543a6d09888d54a4f1d4b1ca757f6))
13
+ * **site:** docs rail anatomy (Boiler handoff, phases 1+2) + the full layout doctrine written down ([fa1fa8b](https://github.com/Jerry2d3d/css-is-awesome/commit/fa1fa8b4e182793f1806d4e13af051394ad7cf8b)), closes [#7](https://github.com/Jerry2d3d/css-is-awesome/issues/7)
14
+
15
+ ## [1.8.1](https://github.com/Jerry2d3d/css-is-awesome/compare/v1.8.0...v1.8.1) (2026-09-04)
16
+
17
+
18
+ ### Bug Fixes
19
+
20
+ * **docs:** full documentation currency sweep - root markdown, site pages, three theme sources ([d64642a](https://github.com/Jerry2d3d/css-is-awesome/commit/d64642ab58c8dbeffb1c205df181115a9a5864d9))
21
+ * **mobile:** docs footer yields to the dock below lg ([65eb664](https://github.com/Jerry2d3d/css-is-awesome/commit/65eb66465a7c5c193bd4f6a0eb5c3b8296ebbbda))
22
+ * **site:** footer on the blog, themes, and docs routes ([1bd98b2](https://github.com/Jerry2d3d/css-is-awesome/commit/1bd98b2356b49a2b8da60abcd4a8d3222fd23c21))
23
+
24
+
25
+ ### Features
26
+
27
+ * **site:** GitHub and npm links in the landing nav ([b797625](https://github.com/Jerry2d3d/css-is-awesome/commit/b79762514844ad703c5894a7e32485a9d90b0753))
28
+
1
29
  # [1.8.0](https://github.com/Jerry2d3d/css-is-awesome/compare/v1.7.0...v1.8.0) (2026-09-04)
2
30
 
3
31
 
package/CONTRACT.md CHANGED
@@ -3,11 +3,11 @@
3
3
  Every theme declares these tokens. The [theme validator](./scripts/theme-validator.js) enforces completeness on every PR against the machine-readable companion at [`scripts/theme-contract.json`](./scripts/theme-contract.json).
4
4
 
5
5
  - **Source of truth:** this document.
6
- - **Reference implementation:** [`public/theme.css`](./public/theme.css) (Sketchbook).
6
+ - **Reference implementation:** [`public/themes/sketchbook/theme.css`](./public/themes/sketchbook/theme.css) (Sketchbook). (`public/theme.css` is the all-theme bundle.)
7
7
  - **Validate a theme:** `node scripts/theme-validator.js <path-to-theme.css>`
8
8
  - **Validate every theme in the repo:** `npm run validate-themes`
9
9
 
10
- A theme file is a single `:root { … }` block plus (optionally) an `@import` for fonts. No component CSS lives in a theme file. Tokens only.
10
+ A theme file is a single token block plus (optionally) an `@import` for fonts — authored via `cia.theme()`, which emits it as `:root, :root[data-theme="<name>"]`. No component CSS lives in a theme file. Tokens only.
11
11
 
12
12
  ---
13
13
 
@@ -18,7 +18,7 @@ Library mixins resolve every token through a `var(--token, fallback)` pattern, s
18
18
  | Mixin call | Emitted CSS | Required token |
19
19
  | -------------------------------- | ---------------------------------------- | -------------------- |
20
20
  | `m.color(surface-default)` | `var(--surface-default, #fff)` | `--surface-default` |
21
- | `m.space(md)` | `var(--space-md, 1rem)` | `--space-md` |
21
+ | `m.space(4)` | `var(--space-4, 1rem)` | `--space-4` |
22
22
  | `m.radius(lg)` | `var(--radius-lg, 0.5rem)` | `--radius-lg` |
23
23
  | `m.shadow(md)` | `var(--shadow-md, …)` | `--shadow-md` |
24
24
  | `m.font-family(primary)` | `var(--font-primary, sans-serif)` | `--font-primary` |
@@ -218,14 +218,22 @@ These are the raw pigment tokens. Semantic aliases below reference them. In othe
218
218
 
219
219
  ## Space scale
220
220
 
221
- | Token | Type | Example | Purpose |
222
- | ------------- | ------ | --------- | --------------------------- |
223
- | `--space-2xs` | length | `0.25rem` | Hairline gap / 2xs padding |
224
- | `--space-xs` | length | `0.5rem` | Tight gap / xs padding |
225
- | `--space-sm` | length | `0.75rem` | Compact gap / sm padding |
226
- | `--space-md` | length | `1rem` | Default gap / md padding |
227
- | `--space-lg` | length | `1.5rem` | Comfortable gap / lg padding |
228
- | `--space-xl` | length | `2rem` | Section gap / xl padding |
221
+ The **numbered scale is the source of truth and is contract-required**: a theme declares `--space-0` … `--space-9`, and `cia.space(N)` compiles to `var(--space-N)` — so a theme can re-proportion the page, not just recolor it.
222
+
223
+ | Token | Type | Example (default rhythm) | Purpose |
224
+ | --------------------------- | ------ | --------------------------------- | -------------------------------- |
225
+ | `--space-0` … `--space-9` | length | `0`, `0.25rem`, `0.5rem`, … `6rem` | The numbered scale **required** |
226
+
227
+ The t-shirt names are **optional aliases**. The library emits `xs`–`xl` as `var()` references into the numbered scale, so they follow it automatically; `--space-2xs` sits outside the numbered scale and emits as a literal:
228
+
229
+ | Token | Type | Emitted as | Purpose |
230
+ | ------------- | ------ | ------------------ | --------------------------- |
231
+ | `--space-2xs` | length | `0.25rem` (literal) | Hairline gap / 2xs padding |
232
+ | `--space-xs` | length | `var(--space-1)` | Tight gap / xs padding |
233
+ | `--space-sm` | length | `var(--space-2)` | Compact gap / sm padding |
234
+ | `--space-md` | length | `var(--space-4)` | Default gap / md padding |
235
+ | `--space-lg` | length | `var(--space-5)` | Comfortable gap / lg padding |
236
+ | `--space-xl` | length | `var(--space-6)` | Section gap / xl padding |
229
237
 
230
238
  ---
231
239
 
@@ -425,9 +433,20 @@ onto `--paper` so the math reflects what users actually see.
425
433
  | `--warning-text` on `--warning-subtle` | 4.5 : 1 | text |
426
434
  | `--error-text` on `--error-subtle` | 4.5 : 1 | text |
427
435
  | `--info-text` on `--info-subtle` | 4.5 : 1 | text |
428
- | `--border-default` on `--paper` | 3.0 : 1 | non-text |
436
+ | `--border-default` on `--paper` | 3.0 : 1 | non-text (decorative — reported as info, never FAIL) |
429
437
  | `--border-focus` on `--paper` | 3.0 : 1 | non-text |
430
438
  | `--shu` on `--paper` | 3.0 : 1 | non-text |
439
+ | `--code-ink` on `--code-bg` | 4.5 : 1 | text |
440
+ | `--code-muted` on `--code-bg` | 4.5 : 1 | text |
441
+ | `--code-accent` on `--code-bg` | 4.5 : 1 | text |
442
+ | `--code-blue` on `--code-bg` | 4.5 : 1 | text |
443
+ | `--code-green` on `--code-bg` | 4.5 : 1 | text |
444
+
445
+ That's **22 audited pairs per theme**, the five `--code-*` pairs included —
446
+ syntax highlighting is body text, so it carries the full 4.5:1 requirement.
447
+ For dual-mode themes the audit evaluates **both** `light-dark()` branches and
448
+ keeps the worse result. `--border-default` is treated as decorative per WCAG
449
+ 2.2 SC 1.4.11 and reports as info, not FAIL.
431
450
 
432
451
  Each pair is reported as **PASS**, **WARN** (close to threshold; `--text-tertiary`
433
452
  and `--ink-faint` warn when they pass the 3:1 large-text bar but fall below the
@@ -450,7 +469,7 @@ The contract is versioned via `scripts/theme-contract.json` (`version: "1"`).
450
469
  - **Minor bump** (`"1" → "1.1"`): adds OPTIONAL tokens. Existing themes remain valid.
451
470
  - **Major bump** (`"1" → "2"`): renames or removes REQUIRED tokens. Existing themes must migrate.
452
471
 
453
- Any PR that adds a new `m.color(X)` / `m.space(X)` / `m.radius(X)` reference in the library must add `--X` to both this document and `scripts/theme-contract.json`, and add a declaration to every theme in `public/theme.css` and `public/themes/*/theme.css`. The `npm run validate-themes` check in CI will block the merge otherwise.
472
+ Any PR that adds a new `m.color(X)` / `m.space(X)` / `m.radius(X)` reference in the library must add `--X` to both this document and `scripts/theme-contract.json`, and add a declaration to every theme source in `scss/themes/*.scss` (then `npm run build:css:themes` the CSS under `public/` is generated, never hand-edited). The `npm run validate-themes` check in CI will block the merge otherwise.
454
473
 
455
474
  ---
456
475
 
package/README.md CHANGED
@@ -6,9 +6,9 @@
6
6
 
7
7
  **Bring your own selectors. We bring the design system.** One CSS file per theme — drop it in and the page restyles, no markup change. 24 themes. Zero JavaScript in the npm package. Six browser-native interactive components. Small enough to read in an afternoon.
8
8
 
9
- **Docs:** [jerry2d3d.github.io/css-is-awesome](https://jerry2d3d.github.io/css-is-awesome/) · **Install:** `npm install css-is-awesome`
9
+ **Docs:** [cssisawesome.com](https://cssisawesome.com/) · **Install:** `npm install css-is-awesome`
10
10
 
11
- > **Shipped in 1.0.0:** a **recipes book** for building any component in any framework using cia mixins — five recipes today (`dialog`, `combobox`, `print-to-pdf`, `mobile-nav`, `bottom-nav`), with `datepicker`, `data-table` and `command-palette` queued. AI agents read recipes via MCP and generate components in your stack; humans read them at [`/docs/recipes`](https://jerry2d3d.github.io/css-is-awesome/docs/recipes/).
11
+ > **Shipped in 1.0.0:** a **recipes book** for building any component in any framework using cia mixins — five recipes today (`dialog`, `combobox`, `print-to-pdf`, `mobile-nav`, `bottom-nav`), with `datepicker`, `data-table` and `command-palette` queued. AI agents read recipes via MCP and generate components in your stack; humans read them at [`/docs/recipes`](https://cssisawesome.com/docs/recipes/).
12
12
 
13
13
  ## For AI agents — start here
14
14
 
@@ -33,7 +33,7 @@ npm install -D @modelcontextprotocol/sdk zod # required — npm will NOT insta
33
33
 
34
34
  The SDK and `zod` are declared as *optional* peer dependencies, so a plain `npm install css-is-awesome` skips them and the server exits with `@modelcontextprotocol/sdk is not installed`. Install both. `npx css-is-awesome-mcp` does **not** work around this — npx fetches the package but not its optional peers.
35
35
 
36
- Why it matters more here than for older frameworks: no model has memorised cia's API the way it has memorised Tailwind's class names. Without `llm.txt` or MCP, an agent will confidently invent a Tailwind-shaped API. With them, it reads the real thing. Details at [`/docs/mcp`](https://jerry2d3d.github.io/css-is-awesome/docs/mcp/).
36
+ Why it matters more here than for older frameworks: no model has memorised cia's API the way it has memorised Tailwind's class names. Without `llm.txt` or MCP, an agent will confidently invent a Tailwind-shaped API. With them, it reads the real thing. Details at [`/docs/mcp`](https://cssisawesome.com/docs/mcp/).
37
37
 
38
38
  ## Three ways to use it
39
39
 
@@ -59,7 +59,7 @@ npm install -D sass
59
59
  <link rel="stylesheet" href="/cia/themes/boilerplate/theme.css">
60
60
  ```
61
61
 
62
- Author your own class names; the mixin handles the styling. Mixins for buttons, forms, layout, typography, color, motion, plus the six zero-JS components: `accordion`, `modal`, `tooltip`, `dropdown`, `tabs`, `copy-button` — plus the mobile navigation family (`hamburger`, `drawer`, `sheet`, `dock`) and print-to-PDF via a pure-CSS `@media print` layer. Full reference at [`/docs/mixins`](https://jerry2d3d.github.io/css-is-awesome/docs/mixins/).
62
+ Author your own class names; the mixin handles the styling. Mixins for buttons, forms, layout, typography, color, motion, plus the six zero-JS components: `accordion`, `modal`, `tooltip`, `dropdown`, `tabs`, `copy-button` — plus the mobile navigation family (`hamburger`, `drawer`, `sheet`, `dock`) and print-to-PDF via a pure-CSS `@media print` layer. Full reference at [`/docs/mixins`](https://cssisawesome.com/docs/mixins/).
63
63
 
64
64
  **Two imports, two jobs.** Emit the tokens once from your root/global stylesheet (`@use 'css-is-awesome';` or `<link>` a theme file), then import the **zero-emit authoring barrel** in each component stylesheet:
65
65
 
@@ -122,7 +122,7 @@ One line styles the whole site. Zero classes. Wrapped in `:where()` (specificity
122
122
  <link rel="stylesheet" href="/themes/terminal-dark/theme.css" media="(prefers-color-scheme: dark)">
123
123
  ```
124
124
 
125
- Newspaper by day, hacker terminal by night. No JS, no mixin — pure browser behavior. Most design systems give you dark mode; cia lets you ship a second brand at night. See [`/docs/themes/pairing`](https://jerry2d3d.github.io/css-is-awesome/docs/themes/pairing/).
125
+ Newspaper by day, hacker terminal by night. No JS, no mixin — pure browser behavior. Most design systems give you dark mode; cia lets you ship a second brand at night. See [`/docs/themes/pairing`](https://cssisawesome.com/docs/themes/pairing/).
126
126
 
127
127
  Each theme is one file of CSS custom properties. Tokens only — no component rules. See `public/themes/<name>/theme.css` for the compiled output and `scss/themes/<name>.scss` for the sources. Full contract documented in [THEMING.md](./THEMING.md).
128
128
 
@@ -149,7 +149,7 @@ node scripts/theme-validator.js public/themes/midnight/theme.css
149
149
  # <link rel="stylesheet" href="/themes/midnight/theme.css">
150
150
  ```
151
151
 
152
- Full authoring walkthrough: [`/docs/authoring/themes`](https://jerry2d3d.github.io/css-is-awesome/docs/authoring/themes/). The contract (127 required + 36 optional tokens) is at [`scripts/theme-contract.json`](./scripts/theme-contract.json).
152
+ Full authoring walkthrough: [`/docs/authoring/themes`](https://cssisawesome.com/docs/authoring/themes/). The contract (127 required + 36 optional tokens) is at [`scripts/theme-contract.json`](./scripts/theme-contract.json).
153
153
 
154
154
  ## Token contract
155
155
 
@@ -204,9 +204,11 @@ cia ships **no component library** — deliberately. Interactive patterns arrive
204
204
 
205
205
  **Shipped:** `dialog`, `combobox`, `print-to-pdf`, `mobile-nav`, `bottom-nav`. Queued next: `datepicker`, `data-table`, `command-palette`.
206
206
 
207
- **Mobile navigation is system API, not an exercise for the reader.** Four mixins `cia.hamburger`, `cia.drawer`, `cia.sheet`, `cia.dock` cover both phone layouts: the **flex layout** (one fluid nav/main/footer shell that reshapes with the screen, hamburger opening a slide-in drawer — recipe [`mobile-nav`](./scss/recipes/mobile-nav.md)) and the **app layout** (a fixed bottom dock whose slots open slide-up sheets — recipe [`bottom-nav`](./scss/recipes/bottom-nav.md)). Zero JavaScript: everything rides CSS Grid and the native Popover API the browser owns open state, `aria-expanded`, Esc and light dismiss. The docs site dogfoods the app layout on phones. The same doctrine extends to dropdowns: on phones an interactive surface takes the space it's in, so a `cia.dropdown` menu opens 1px under its full-width trigger at the trigger's exact width CSS anchor positioning, flipping above when the screen bottom would clip it. The full spec is the [mobile playbook](https://jerry2d3d.github.io/css-is-awesome/docs/mobile/).
207
+ **Layout doctrine: Grid is the skeleton, Flex is the quick moves.** The page shell is CSS Grid with landmark-named areas (`nav / main / footer` the map reads like the page); any control-dense region inside gets its own named-area grid whose `gap` carries all vertical rhythm; flex lives at the leaves for one-command flips (`cia.flex($direction: column)`). `cia.page-layout()` and `cia.layout()` own the mapsmobile is a different area map, never margin overrides.
208
208
 
209
- Humans read them at [`/docs/recipes`](https://jerry2d3d.github.io/css-is-awesome/docs/recipes/); AI agents pull them over MCP with `list_recipes` / `get_recipe`.
209
+ **Mobile navigation is system API, not an exercise for the reader.** Four mixins — `cia.hamburger`, `cia.drawer`, `cia.sheet`, `cia.dock` — cover both phone layouts: the **flex layout** (one fluid nav/main/footer shell that reshapes with the screen, hamburger opening a slide-in drawer — recipe [`mobile-nav`](./scss/recipes/mobile-nav.md)) and the **app layout** (a fixed bottom dock whose slots open slide-up sheets — recipe [`bottom-nav`](./scss/recipes/bottom-nav.md)). Zero JavaScript: everything rides CSS Grid and the native Popover API — the browser owns open state, `aria-expanded`, Esc and light dismiss. The docs site dogfoods the app layout on phones. The same doctrine extends to dropdowns: on phones an interactive surface takes the space it's in, so a `cia.dropdown` menu opens 1px under its full-width trigger at the trigger's exact width — CSS anchor positioning, flipping above when the screen bottom would clip it. The full spec is the [mobile playbook](https://cssisawesome.com/docs/mobile/).
210
+
211
+ Humans read them at [`/docs/recipes`](https://cssisawesome.com/docs/recipes/); AI agents pull them over MCP with `list_recipes` / `get_recipe`.
210
212
 
211
213
  ## Migrating from Tailwind or Bootstrap
212
214
 
@@ -217,7 +219,7 @@ npx cia migrate tailwind ./tailwind.config.js # auto-detects tailwind.config.*
217
219
  npx cia migrate bootstrap ./scss/_variables.scss
218
220
  ```
219
221
 
220
- Both accept `--help` for the full option list. Prose walkthroughs live at [`/docs/migration-tailwind`](https://jerry2d3d.github.io/css-is-awesome/docs/migration-tailwind/) and [`/docs/migration-bootstrap`](https://jerry2d3d.github.io/css-is-awesome/docs/migration-bootstrap/).
222
+ Both accept `--help` for the full option list. Prose walkthroughs live at [`/docs/migration-tailwind`](https://cssisawesome.com/docs/migration-tailwind/) and [`/docs/migration-bootstrap`](https://cssisawesome.com/docs/migration-bootstrap/).
221
223
 
222
224
  ## Print / PDF (zero JS)
223
225
 
@@ -256,7 +258,7 @@ The scope is kept narrow: 8 `!important` declarations, all inside `@media print`
256
258
 
257
259
  ## MCP server (for AI agents)
258
260
 
259
- cia ships a Model Context Protocol stdio server (JSON-RPC over stdio, protocol `2024-11-05`) at [`mcp/server.cjs`](./mcp/server.cjs), exposed as the `css-is-awesome-mcp` bin. It's in the `files` manifest, so it lands in every consumer's `node_modules`. Any MCP-aware client (Claude Code, Cursor, Aider, Gemini, Copilot) can then query cia's real design system — mixin signatures, tokens, themes, recipes — instead of guessing, without grep-walking the repo. Exposes **30 tools** across 8 families (themes, mixins, functions, tokens · 127 required of them, animations, components, recipes, doc readers) plus `assemble_prompt` (context bundles) and `resolve_size` (snap design px values to cia's 4px grid). Full reference: [`/docs/mcp`](https://jerry2d3d.github.io/css-is-awesome/docs/mcp/).
261
+ cia ships a Model Context Protocol stdio server (JSON-RPC over stdio, protocol `2024-11-05`) at [`mcp/server.cjs`](./mcp/server.cjs), exposed as the `css-is-awesome-mcp` bin. It's in the `files` manifest, so it lands in every consumer's `node_modules`. Any MCP-aware client (Claude Code, Cursor, Aider, Gemini, Copilot) can then query cia's real design system — mixin signatures, tokens, themes, recipes — instead of guessing, without grep-walking the repo. Exposes **30 tools** across 8 families (themes, mixins, functions, tokens · 127 required of them, animations, components, recipes, doc readers) plus `assemble_prompt` (context bundles) and `resolve_size` (snap design px values to cia's 4px grid). Full reference: [`/docs/mcp`](https://cssisawesome.com/docs/mcp/).
260
262
 
261
263
  **Setup is two steps — do both, or the server won't start.**
262
264
 
@@ -283,7 +285,7 @@ cia ships a Model Context Protocol stdio server (JSON-RPC over stdio, protocol `
283
285
 
284
286
  ## Docs site
285
287
 
286
- The docs site is live at **https://jerry2d3d.github.io/css-is-awesome/** it auto-deploys from `main` via GitHub Pages. To run it locally:
288
+ The docs site is live at **https://cssisawesome.com** (production — Vercel, deployed from the `prod-css-is-awesome` branch), with a GitHub Pages mirror at **https://jerry2d3d.github.io/css-is-awesome/** that auto-deploys from `main`. To run it locally:
287
289
 
288
290
  ```bash
289
291
  git clone https://github.com/Jerry2d3d/css-is-awesome.git
@@ -292,7 +294,7 @@ npm install
292
294
  npm run dev # http://localhost:5173
293
295
  ```
294
296
 
295
- The docs site is a Next.js 15 app at `src/` that dogfoods the library — every page uses CSS Modules composed from the same tokens and mixins the library ships.
297
+ The docs site is a Next.js 16 app at `src/` that dogfoods the library — every page uses CSS Modules composed from the same tokens and mixins the library ships.
296
298
 
297
299
  ## Scripts
298
300
 
@@ -317,7 +319,7 @@ The docs site is a Next.js 15 app at `src/` that dogfoods the library — every
317
319
 
318
320
  ## Testing
319
321
 
320
- Eleven checks, all gated in CI on every PR. Each one exists because the failure it catches actually happened.
322
+ Twelve checks, all gated in CI on every PR. Each one exists because the failure it catches actually happened.
321
323
 
322
324
  | Check | What it proves |
323
325
  |---|---|
@@ -327,11 +329,12 @@ Eleven checks, all gated in CI on every PR. Each one exists because the failure
327
329
  | `validate-icons` | the 49-glyph core pack is intact (extras allowed) |
328
330
  | `validate-api` | the `/api` barrel still emits zero CSS until a mixin is called |
329
331
  | `validate-package` | packs → installs into a temp project → compiles all **10** documented `@use` specifiers |
330
- | `coverage:api` | calls **174/174** public mixins + functions and asserts the output |
332
+ | `coverage:api` | calls **183/183** public mixins + functions and asserts the output |
331
333
  | `coverage:mcp` | calls **30/30** MCP tools over stdio |
332
- | `test` | Playwright 50 tests: route smoke, axe a11y, per-theme visual snapshots, theme-editor behaviour |
334
+ | `validate-recipes` | every recipe's SCSS compiles and its documented mixin calls resolve |
335
+ | `test` | Playwright — route smoke, axe a11y, per-theme visual snapshots, theme-editor behaviour, across three engines |
333
336
 
334
- **Call-and-assert coverage.** SCSS has no line-coverage tooling, so cia measures whether every part of the public API is actually callable: parse every public `@mixin`/`@function`, generate a fixture that calls it, compile, and assert it works — no `null` leaking into CSS, functions return a value, mixins emit. **174/174 SCSS units and 30/30 MCP tools**, with CI failing below 98%. A unit with no fixture counts as uncovered, so skipping a test lowers the number rather than hiding.
337
+ **Call-and-assert coverage.** SCSS has no line-coverage tooling, so cia measures whether every part of the public API is actually callable: parse every public `@mixin`/`@function`, generate a fixture that calls it, compile, and assert it works — no `null` leaking into CSS, functions return a value, mixins emit. **183/183 SCSS units and 30/30 MCP tools**, with CI failing below 98%. A unit with no fixture counts as uncovered, so skipping a test lowers the number rather than hiding.
335
338
 
336
339
  **What 100% means here:** every public mixin and function is invoked and produces sane output. It catches renames, broken signatures and undefined variables — it found one on its first run, an undefined `$icon-size` that broke four icon mixins. It does **not** prove the CSS is visually correct; that's a deliberate trade against golden-file snapshots, which would churn dozens of files on any token change. Page-level visual correctness is covered by the Playwright snapshots instead.
337
340
 
@@ -339,7 +342,7 @@ Eleven checks, all gated in CI on every PR. Each one exists because the failure
339
342
 
340
343
  **Known gaps**, stated plainly: **a11y runs on routes, not component states** — axe checks a set of pages; individual component states are not swept. (Cross-engine coverage used to be the gap here; the suite now runs chromium, firefox and webkit, which matters because cia leans on `light-dark()`, `:has()`, `[popover]` and `mask`.)
341
344
 
342
- Full detail: [`/docs/testing`](https://jerry2d3d.github.io/css-is-awesome/docs/testing/).
345
+ Full detail: [`/docs/testing`](https://cssisawesome.com/docs/testing/).
343
346
 
344
347
  ## Size (gzipped)
345
348
 
package/ROADMAP.md CHANGED
@@ -138,7 +138,7 @@ Shared chrome + reusable building blocks now live in `src/components/`:
138
138
  - [ ] Sections: Tokens (color/spacing/type grids — DONE), Utilities (searchable table — TODO), Mixins API (TODO), Migration from Bootstrap (started; needs polish).
139
139
  - [x] Live color swatches, spacing visualizers, type scale preview — `/docs/tokens` reads `getComputedStyle` after mount and re-resolves on theme swap via `useThemeAttribute`.
140
140
  - [x] Copy-to-clipboard code snippets — every `<Example.Code>` has a Copy button (CopyButton client island, secure-context fallback to execCommand).
141
- - [ ] Deploy to the external host + link from README + `package.json` `homepage` (Pages workflow exists; live URL not yet linked from README).
141
+ - [x] Deploy to the external host + link from README done: GitHub Pages mirror live and linked, and production at https://cssisawesome.com on Vercel since 2026-09-04 (deploys from the `prod-css-is-awesome` branch).
142
142
 
143
143
  ### New pages
144
144
  - [x] **`/themes` gallery** — all 6 themes with live-swap preview + download per tile. Uses `ThemeTile` component. (Now at `/themes/gallery`; `/themes` is the editor.)
@@ -265,9 +265,10 @@ out-of-date copy that contradicts the rest of the site. Fix the
265
265
  content before doing any CSS work on these files so the dogfood
266
266
  pass doesn't restyle copy that's about to be replaced.
267
267
 
268
- **`/blog`** — All 7 posts are fiction with `href="#"`. Dates run
269
- Feb–Apr 2026 and excerpts read like real posts that don't exist.
270
- Decide:
268
+ **`/blog`** — *Resolved 2026-08-17: a real markdown-driven `/blog` +
269
+ `/blog/[slug]` shipped with 7 posts written from the commit history
270
+ (see Phase 5.95) — effectively Path A, all seven.* Original decision
271
+ for the record:
271
272
  - [ ] **Path A — Ship a subset for real.** Pick 2-3 of the 7 drafted
272
273
  topics ("Why the overflow stays", "Five voices, one system",
273
274
  "Planning a CLI and an MCP server" are the strongest hooks) and
@@ -341,7 +342,7 @@ docs-site quality only.
341
342
 
342
343
  ## Phase 5.95 — Post-1.0 hardening (2026-08-17 → 08-18)
343
344
 
344
- **v1.0.0 was cut 2026-08-17** (`253610a`, tagged) at 24 of 42 stories, deliberately: no external users yet, so the SemVer commitment cost nothing. **Not published to npm** the `@1` CDN URLs will 404 until it is.
345
+ **v1.0.0 was cut 2026-08-17** (`253610a`, tagged) at 24 of 42 stories, deliberately: no external users yet, so the SemVer commitment cost nothing. **First published to npm 2026-09-01**; semantic-release has cut every version since (the current one is always the `version` field of `package.json` never hand-type it).
345
346
 
346
347
  What the cut surfaced, in the order it hurt:
347
348
 
@@ -353,7 +354,7 @@ What the cut surfaced, in the order it hurt:
353
354
  - [x] **Visual baselines stale since 2026-05-03**, keeping CI red. Keyed by `{platform}`, win32 set regenerated. **Linux set still needs one manual run of the "Update visual snapshots" workflow.**
354
355
  - [x] Release friction: `pack:consumer` collapses the three-step pack → re-pin → install dance into one command.
355
356
 
356
- **Still open:** publish to npm; run the snapshot workflow once to green CI; the 18 remaining v1.0 stories (Playground is 0/7).
357
+ **Still open:** run the snapshot workflow once to create the linux baseline set; the remaining v1.0 stories (Playground is 0/7). ~~Publish to npm~~ — done 2026-09-01.
357
358
 
358
359
  ---
359
360
 
@@ -361,6 +362,8 @@ What the cut surfaced, in the order it hurt:
361
362
 
362
363
  **Locked 2026-05-23** after long architecture synthesis (panel review + Gemini external read + Jerry instinct refinement).
363
364
 
365
+ > **Status 2026-09-04:** v1.0.0 cut 2026-08-17, first npm publish 2026-09-01; releases continue via semantic-release. The sprint checklist below is preserved as written — unchecked items are genuinely still open (see the "Definition of the best" list for per-item status).
366
+
364
367
  **Goal:** Ship v1.0 as a humans-first design system whose AI-friendliness is the bonus. Five tracks, ~42 user stories, ~18-26 working days.
365
368
 
366
369
  ### Priority ladder (the v1.0 pitch order)
@@ -412,11 +415,11 @@ See [`roadmap/epics/v1-0/post-v1-ideas.md`](./roadmap/epics/v1-0/post-v1-ideas.m
412
415
 
413
416
  ### Definition of "the best out there" after this sprint
414
417
 
415
- - [ ] Migration CLI handles real Tailwind + Bootstrap configs end-to-end
416
- - [ ] 11+ recipes shipped across overlay / input / data / navigation / feedback
418
+ - [x] Migration CLI handles real Tailwind + Bootstrap configs end-to-end (`npx cia migrate` shipped)
419
+ - [ ] 11+ recipes shipped across overlay / input / data / navigation / feedback (5 shipped as of 2026-09-04: dialog, combobox, print-to-pdf, mobile-nav, bottom-nav)
417
420
  - [ ] `/playground` functional in-browser; every recipe page links to a starter URL
418
421
  - [ ] Inline contrast validator runs live in the theme editor
419
- - [ ] First 3 blog posts published with citations + dated decision receipts
422
+ - [x] Blog shipped for real — markdown-driven `/blog` with 7 posts written from the commit history (2026-08-17)
420
423
  - [ ] boilerplate-slim ships React + Angular + HTML (34 each)
421
424
  - [ ] `/showcase` rebuilt + `/docs/migrate-from-shadcn` guide published
422
425
  - [ ] Boiler dogfoods cia + ≥5 external testers have built something real
@@ -483,7 +486,7 @@ The original Phase 6 ("Ecosystem v1.1+") below describes legacy items some of wh
483
486
  - [x] GitHub Action: CI (build + lint), Release (semver + npm publish + changelog) — semantic-release wired up since v0.7.
484
487
  - [x] Badge suite in README (npm version, license, semantic-release) — shipped.
485
488
  - [x] Contribution guide + issue templates — `CONTRIBUTING.md` + `CONTRIBUTING-THEMES.md` shipped.
486
- - [ ] TypeScript token definitions (`tokens.d.ts`) — type-safe token access from JS
489
+ - [x] TypeScript token definitions (`tokens.d.ts`) — shipped; generated from the contract via `npm run build:token-types`, exported as `css-is-awesome/tokens.d.ts`
487
490
  - [ ] PostCSS plugin for tree-shaking unused utilities
488
491
  - [ ] Starter templates: plain HTML, Vite, Next.js, Astro
489
492
  - [ ] Storybook or Ladle instance — lives in Gremlin UI's repo, not here (see Phase 8).
@@ -494,11 +497,11 @@ The original Phase 6 ("Ecosystem v1.1+") below describes legacy items some of wh
494
497
 
495
498
  **Goal:** Move from "another design system" to "the obvious choice for SCSS-first teams who want zero-JS theming." Items from the Gemini critique that widen the moat once the foundations are stable.
496
499
 
497
- - [ ] **Zero-JS interactive components** — tabs, accordion, modal, popover, tooltip built on `:has()`, the popover API, and `@container`. Biggest moat vs shadcn — they need a runtime, we don't.
500
+ - [x] **Zero-JS interactive components** — shipped in v0.8/v1.0: accordion, modal, tooltip, dropdown, tabs, copy-button on `<details name>`, `<dialog>`, `[popover]`, radio + `:has()`; plus the mobile navigation family (hamburger / drawer / sheet / dock, 2026-08). Biggest moat vs shadcn — they need a runtime, we don't.
498
501
  - [x] **A11y linter inside `theme-validator.js`** — WCAG 2.2 AA contrast checks on 17 token pairs per theme. **FAIL-by-default as of v0.7** (commit `4e1bbf1`, 2026-05-11) after triaging every theme; zero FAILs across all 22 theme blocks. `--allow-a11y-fail` opts out; `--strict` is retained as a no-op alias for backwards compat. `--border-default` is treated as decorative (informational only) per WCAG 2.2 SC 1.4.11.
499
- - [ ] **SCSS↔TS token bridge** — generate `tokens.d.ts` from the contract so consumers get type-safe token access in JS/TS.
500
- - [ ] **Intrinsic layout mixins** — stack/cluster/switcher (Every Layout patterns) as first-class mixins so consumers stop hand-rolling flex utilities.
501
- - [ ] **Tailwind→Awesome migration CLI** — parses Tailwind class strings in a project and suggests `cia-*` utility or mixin equivalents; lowest-friction path for migrants.
502
+ - [x] **SCSS↔TS token bridge** — shipped: `npm run build:token-types` generates `dist/tokens.d.ts` from the contract.
503
+ - [x] **Intrinsic layout mixins** — shipped in the v1.0 surface: `stack` / `cluster` / `switcher` / `sidebar` as first-class mixins.
504
+ - [x] **Tailwind→Awesome migration CLI** — shipped as `npx cia migrate tailwind` (config-level conversion into a cia theme; the class-string-scanning variant described here was not built).
502
505
  - [ ] **Component depth audit** — catalog gaps vs Bootstrap (modal, toast, popover, tooltip, accordion, breadcrumb, pagination, badge, avatar, dropdown, offcanvas) and prioritize zero-JS implementations.
503
506
 
504
507
  ---
@@ -541,9 +544,9 @@ Naming "Gremlin UI" / "Gremlin Components" / "Companion Boilerplate" — all ret
541
544
 
542
545
  1. ~~**Utility naming:** stay close to Bootstrap (`.p-3`) or Tailwind-like (`.p-md`)?~~ **Decided:** Tailwind-style (`.cia-p-md`) with `cia-` prefix.
543
546
  2. ~~**Namespace:** should utilities be prefixed?~~ **Decided:** yes, `cia-` on everything.
544
- 3. **Default CSS output:** include utilities by default or opt-in? Still open.
545
- 4. **Browser support target:** modern evergreen only, or include a legacy build? Still open.
546
- 5. **Sizing scale:** refactor `xs/sm/md/lg/xl/2xl/3xl/4xl` → numbered `1–9` with aliases? **Pending blocks the theme token contract.**
547
+ 3. ~~**Default CSS output:** include utilities by default or opt-in?~~ **Decided (v0.8):** opt-in on the Sass path (`$utilities: true`); pre-built CDN bundles still ship every utility.
548
+ 4. **Browser support target:** modern evergreen only, or include a legacy build? Still open (in practice: modern evergreen — the system leans on `light-dark()`, `:has()`, `[popover]`).
549
+ 5. ~~**Sizing scale:** refactor `xs/sm/md/lg/xl/2xl/3xl/4xl` → numbered `1–9` with aliases?~~ **Decided (locked 2026-05-04):** numbered scale is the source of truth; t-shirt names are optional aliases.
547
550
 
548
551
  ---
549
552
 
@@ -567,12 +570,12 @@ Items 1-13, 15, 16, the a11y linter triage, and the theme-editor import round-tr
567
570
  19. ~~A11y linter triage~~ — done 2026-05-11 (commit `4e1bbf1`). Zero FAILs across all 22 theme blocks. `--border-default` reclassified as decorative (info status) per WCAG 2.2 SC 1.4.11. Validator now FAILs by default; `--allow-a11y-fail` opts out; `--strict` retained as a no-op alias.
568
571
  20. ~~Theme-editor import~~ — upload `.css` to keep editing landed 2026-05-12 (uncommitted on `main`; round-trips a previously-downloaded theme file).
569
572
 
570
- ### Open / next up — Phase 5 is the live gate
573
+ ### Open / next up — Phase 5 was the live gate (since resolved)
571
574
 
572
- **Phase 5 (publish v0.7) is the correct next step.** A11y is at zero FAILs, validator is FAIL-by-default, tarball is clean, boilerplate theme + Lucide icons ship, theme editor round-trips. Only the publish command itself + a downstream smoke install remain.
575
+ **Phase 5 (publish v0.7) was the correct next step at the time.** A11y is at zero FAILs, validator is FAIL-by-default, tarball is clean, boilerplate theme + Lucide icons ship, theme editor round-trips. Only the publish command itself + a downstream smoke install remain.
573
576
 
574
- 8. **`npm publish` 0.7.0** bump version, `npm publish --access public`. Awaiting explicit user go.
575
- 9. **Boilerplate consumer install from registry** depends on #8.
577
+ 8. ~~**`npm publish`**~~ — done. The package shipped via semantic-release through the 0.7/0.8 line; the 1.x line first published to the public registry 2026-09-01.
578
+ 9. ~~**Boilerplate consumer install**~~ done. boiler-project-ai consumes cia (all components + app pages migrated to the `cia` import, 2026-07-11).
576
579
 
577
580
  ### After v0.7 ships
578
581
 
@@ -583,9 +586,9 @@ Items 1-13, 15, 16, the a11y linter triage, and the theme-editor import round-tr
583
586
 
584
587
  ### Smaller items still on the board
585
588
 
586
- - **Utilities searchable table** at `/docs/utilities` (placeholder content currently). Last big docs gap before launch.
587
- - **Mixins API reference** at `/docs/mixins`. Last big docs gap before launch.
588
- - **README homepage URL** + Pages-deploy verification.
589
+ - ~~**Utilities searchable table** at `/docs/utilities`~~ — shipped (route live on the docs site).
590
+ - ~~**Mixins API reference** at `/docs/mixins`~~ shipped (README links it as the full reference).
591
+ - ~~**README homepage URL** + Pages-deploy verification~~ — done; Pages mirror + cssisawesome.com production both live.
589
592
  - **Icon index page** listing every glyph in every pack.
590
593
  - **Pull Sketchbook docs-flourishes** (seal, draft stamp, brush rules) out of any future "base system" port.
591
594
 
@@ -599,7 +602,7 @@ Items 1-13, 15, 16, the a11y linter triage, and the theme-editor import round-tr
599
602
 
600
603
  All of the following must be true before Season 1 production begins:
601
604
 
602
- - [ ] cia v1.0.0 published to npm
605
+ - [x] cia published to npm (first publish 2026-09-01; 1.x line live)
603
606
  - [ ] All v1.0 epic acceptance criteria met (42 stories shipped or explicitly punted)
604
607
  - [ ] No planned mixin renames or API changes within the next 6 months
605
608
  - [ ] Recipes book stable — at least 11 recipes shipped and not changing
package/THREE-TIERS.md CHANGED
@@ -9,8 +9,8 @@ css-is-awesome ships three authoring surfaces for the same components. Pick the
9
9
  ## Tier 1 — Drop-in CSS + HTML (no build)
10
10
 
11
11
  ```html
12
+ <link rel="stylesheet" href="themes/sketchbook/theme.css">
12
13
  <link rel="stylesheet" href="css-is-awesome.min.css">
13
- <link rel="stylesheet" href="theme-sketchbook.css">
14
14
 
15
15
  <main class="cia-container">
16
16
  <h1>Welcome</h1>
@@ -100,7 +100,7 @@ The split to hold in your head: **cia owns the system values — colour, spacing
100
100
  ```
101
101
 
102
102
  **Audience:** content-heavy sites, blog posts, READMEs rendered as HTML, anywhere the author doesn't want to think about classes.
103
- **Rules:** zero classes required. The recipe styles every common bare tag at specificity `0,0,1` — no `@layer`, no `:where()`. Any class-based selector you add wins automatically.
103
+ **Rules:** zero classes required. The recipe styles every common bare tag wrapped in `:where()` (specificity `0,0,0`) — no `@layer`. Any selector you add — even another bare tag — wins automatically.
104
104
 
105
105
  ---
106
106
 
package/VERSIONING.md CHANGED
@@ -9,7 +9,7 @@ How `css-is-awesome` versions its public surfaces, deprecates old APIs, and reco
9
9
  | Surface | Lives in | Consumed as |
10
10
  | ------------------- | ------------------------------------------ | ------------------------------------------ |
11
11
  | CSS class names | `dist/*.css` | HTML / SCSS / React class strings |
12
- | SCSS mixin API | `scss/` (`_mixins.scss`, `_layout.scss`, …) | Authors who `@use "css-is-awesome/scss/main"` |
12
+ | SCSS mixin API | `scss/` (`_mixins.scss`, `_layout.scss`, …) | Authors who `@use 'css-is-awesome'` / `@use 'css-is-awesome/api'` |
13
13
  | Token contract | [`CONTRACT.md`](./CONTRACT.md) + [`scripts/theme-contract.json`](./scripts/theme-contract.json) | Themes declared in a `:root { … }` block |
14
14
 
15
15
  The **library version** lives in [`package.json`](./package.json). The **contract version** lives in the `version` field of `scripts/theme-contract.json`. They move together on most MAJOR bumps but are independent: a library MINOR can ship without bumping the contract.
@@ -31,8 +31,7 @@ Any change that can break a consumer upgrading blindly.
31
31
  | SCSS mixin default changes rendered output | `m.card()` default radius flips from `md` → `lg` |
32
32
  | Contract: required token renamed or removed | `--surface-default` → `--surface-base` |
33
33
  | Contract: `version` field bumps to a new major (`"1"` → `"2"`) | Required-token removal in `scripts/theme-contract.json` |
34
- | React component removed, renamed, or incompatible prop change | `<Button variant>` values narrowed |
35
- | Peer-dependency floor rises | `react: >=18` → `react: >=19` |
34
+ | Optional-peer floor rises | `@modelcontextprotocol/sdk` minimum raised |
36
35
 
37
36
  ### MINOR — `0.x.0`
38
37
 
@@ -43,8 +42,7 @@ Additive, non-breaking changes.
43
42
  | New public CSS class | `.cia-grid-auto-fit` added |
44
43
  | New public SCSS mixin | `m.cluster($gap)` added |
45
44
  | New optional token added to contract (`"1"` → `"1.1"`) | `--dropdown-offset-y` added to component section |
46
- | New React component | `<DataTable>` added |
47
- | Additive component prop with a sensible default | `<Button loading>` added, defaults to `false` |
45
+ | New theme or recipe shipped | `prism` family added; `mobile-nav` recipe added |
48
46
  | New utility class (`.cia-*`) | `.cia-text-balance` added |
49
47
 
50
48
  ### PATCH — `0.0.x`
@@ -61,14 +59,14 @@ Internal-only or visually-identical changes.
61
59
 
62
60
  ---
63
61
 
64
- ## 2. Pre-1.0 rules
62
+ ## 2. Pre-1.0 rules (historical)
65
63
 
66
- While the library is pre-1.0 (`0.x.x`), the rules above apply with one carve-out: we reserve the right to ship a genuinely-breaking change as a **MINOR** bump if it is the right call for the system's long-term shape. Every such change is:
64
+ While the library was pre-1.0 (`0.x.x`), the rules above applied with one carve-out: we reserved the right to ship a genuinely-breaking change as a **MINOR** bump when it was the right call for the system's long-term shape (the v0.7 theme renames and the v0.8 mixin-first reframe both used it). Every such change was:
67
65
 
68
- 1. Called out loudly in the `CHANGELOG.md` entry under `### Changed` with a **BREAKING** prefix.
66
+ 1. Called out loudly in the `CHANGELOG.md` entry with a **BREAKING** prefix.
69
67
  2. Called out again in the release notes with a migration snippet.
70
68
 
71
- **`1.0.0` locks the contract.** After 1.0, breaking changes require a MAJOR bump, no exceptions.
69
+ **`1.0.0` locked the contract** (cut 2026-08-17). Breaking changes now require a MAJOR bump, no exceptions.
72
70
 
73
71
  ---
74
72
 
@@ -80,9 +78,8 @@ Every public symbol — CSS class, SCSS mixin, React prop, contract token — fo
80
78
 
81
79
  1. **Mark** the symbol with an inline `@deprecated` comment citing the replacement and the intended removal version.
82
80
  2. **Warn** at use-time:
83
- - React components / hooks `console.warn(…)` once per session (dedupe by symbol name).
84
- - SCSS mixins`@warn "m.old-name is deprecated, use m.new-name (removed in 1.0)";`.
85
- - CSS classes / tokens → no runtime warning possible; rely on the `@deprecated` JSDoc and changelog.
81
+ - SCSS mixins`@warn "m.old-name is deprecated, use m.new-name (removed in 2.0)";`.
82
+ - CSS classes / tokens no runtime warning possible; rely on the `@deprecated` comment and changelog.
86
83
  3. **Announce** in the next MINOR release's `CHANGELOG.md` under `### Deprecated`.
87
84
  4. **Keep functional** for **at least one full MINOR release cycle** after the deprecation lands.
88
85
  5. **Remove** only in a MAJOR bump.
@@ -92,9 +89,9 @@ Every public symbol — CSS class, SCSS mixin, React prop, contract token — fo
92
89
  ```scss
93
90
  // scss/_mixins.scss
94
91
  /// @deprecated Use `m.btn-primary` with `$bg: action-secondary-default` override.
95
- /// Removed in 1.0.
92
+ /// Removed in 2.0.
96
93
  @mixin btn-secondary($size: md) {
97
- @warn "m.btn-secondary is deprecated; use m.btn-primary with $bg: action-secondary-default. Removed in 1.0.";
94
+ @warn "m.btn-secondary is deprecated; use m.btn-primary with $bg: action-secondary-default. Removed in 2.0.";
98
95
  @include btn-primary($size, $bg: action-secondary-default);
99
96
  }
100
97
  ```
@@ -112,70 +109,63 @@ A deprecated contract token:
112
109
 
113
110
  ## 4. Changelog format
114
111
 
115
- Changelog follows [Keep a Changelog 1.1.0](https://keepachangelog.com/en/1.1.0/) verbatim. Each release entry uses these sections, in this order, omitting any that are empty:
112
+ **[`CHANGELOG.md`](./CHANGELOG.md) is GENERATED never edit it by hand.** [semantic-release](https://github.com/semantic-release/semantic-release) writes each release entry from the Conventional Commits that landed since the previous tag, grouped under the conventional-changelog headings:
116
113
 
117
- - `Added` — new features.
118
- - `Changed` — changes to existing functionality.
119
- - `Deprecated` — soon-to-be-removed features.
120
- - `Removed` — removed features.
121
- - `Fixed` — bug fixes.
122
- - `Security` — vulnerabilities and mitigations.
114
+ - `Features` — from `feat:` commits.
115
+ - `Bug Fixes` — from `fix:` commits.
116
+ - `Performance Improvements` — from `perf:` commits.
123
117
 
124
- Entries are written in past tense, grouped by section, and link issue / PR numbers. The top of [`CHANGELOG.md`](./CHANGELOG.md) always carries an `## [Unreleased]` section where in-flight changes accumulate between releases.
118
+ **Version pace the number belongs to the PACKAGE, not the website.**
119
+ The docs site lives in this repo but ships nowhere in the npm tarball, so
120
+ site-only work must not spend minor versions. The rules (enforced by a
121
+ `releaseRules` override in `.releaserc.json`, adopted 2026-09-04 after
122
+ site `feat` commits marched 1.1 → 1.8 in a week):
125
123
 
126
- ### Example release block
124
+ - Library API work (new/changed mixins, tokens, recipes, packaged docs) —
125
+ normal Conventional Commits semantics: `feat` → minor, `fix` → patch.
126
+ - Site-only work — scope it `(site)` or `(docs-site)`: `feat(site)` is
127
+ demoted to a **patch**, `fix(site)` patches, `chore`/`docs` don't
128
+ release at all. Prefer `chore(site)` when nothing packaged changed and
129
+ no npm release is needed — production deploys come from the
130
+ `prod-css-is-awesome` branch, not from npm.
131
+ - MAJOR (2.0.0) is reserved for real breaking changes to the public
132
+ surfaces above — nothing else may reach it.
133
+ - `BREAKING CHANGES` — from `feat!:` / `fix!:` or a `BREAKING CHANGE:` footer.
127
134
 
128
- ```md
129
- ## [0.6.0] - 2026-05-12
130
-
131
- ### Added
132
- - `<DataTable>` component with generic row typing and optional pagination (#142).
133
- - Theme validator now supports `--watch` mode (#138).
134
-
135
- ### Changed
136
- - `Pagination` props: native `HTMLAttributes.onChange` is `Omit`ped so the custom `onChange(page)` stops colliding. Non-breaking for existing consumers (#140).
137
-
138
- ### Deprecated
139
- - `m.btn-secondary` — use `m.btn-primary` with `$bg: action-secondary-default` override. Removed in 1.0 (#143).
140
-
141
- ### Fixed
142
- - Theme picker no longer injected duplicate `<link>` elements on first paint (#145).
143
- ```
135
+ Each entry links the commit (and any referenced issue / PR numbers) automatically. There is no hand-maintained `Unreleased` section — in-flight changes are simply the commits on `main` that no tag covers yet.
144
136
 
145
137
  ---
146
138
 
147
- ## 5. Conventional Commits → Changelog
139
+ ## 5. Conventional Commits → Release
148
140
 
149
- Commits follow [Conventional Commits](https://www.conventionalcommits.org/). The prefix maps to a changelog section:
141
+ Commits follow [Conventional Commits](https://www.conventionalcommits.org/). The prefix decides both the version bump and the changelog section:
150
142
 
151
- | Commit prefix | Changelog section |
152
- | ----------------------------------------- | ------------------------------- |
153
- | `feat:` | `Added` |
154
- | `feat!:` or `BREAKING CHANGE:` footer | `Changed` (breaking — MAJOR) |
155
- | `fix:` | `Fixed` |
156
- | `perf:` | `Changed` |
157
- | `docs:` | *omit* |
158
- | `refactor:` | *omit* |
159
- | `test:` | *omit* |
160
- | `chore:` | *omit* |
161
- | `build:` / `ci:` | *omit* |
143
+ | Commit prefix | Bump | Changelog section |
144
+ | ----------------------------------------- | ------- | -------------------------- |
145
+ | `feat:` | MINOR | `Features` |
146
+ | `feat!:` or `BREAKING CHANGE:` footer | MAJOR | `BREAKING CHANGES` |
147
+ | `fix:` | PATCH | `Bug Fixes` |
148
+ | `perf:` | PATCH | `Performance Improvements` |
149
+ | `docs:` | *no release* | *omit* |
150
+ | `refactor:` | *no release* | *omit* |
151
+ | `test:` | *no release* | *omit* |
152
+ | `chore:` | *no release* | *omit* |
153
+ | `build:` / `ci:` | *no release* | *omit* |
162
154
 
163
- A commit can carry a `Deprecates:` footer to force an entry under `### Deprecated`, or a `Security:` footer to force `### Security`, regardless of prefix.
155
+ Deprecations are announced in the deprecating commit's body (and land in the release notes through it), plus an inline `@deprecated` comment per §3.
164
156
 
165
157
  ---
166
158
 
167
159
  ## 6. Release process
168
160
 
169
- The policy in this document tells you **what** a version number means. The mechanics of cutting a release tagging, building `dist/*.css`, validating themes, publishing to npm — live in [`CONTRIBUTING.md`](./CONTRIBUTING.md) and are automated per Epic 5.
161
+ The policy in this document tells you **what** a version number means. The mechanics are automated: on every push to `main` that contains a releasable commit, semantic-release computes the next version from the commit messages, regenerates `CHANGELOG.md`, builds the bundles (`prepublishOnly` runs `build:css:all`), tags `vX.Y.Z`, and publishes to npm. Nobody hand-types a version number anywhere the hero, the MCP server, and the docs all read it from `package.json`.
162
+
163
+ What remains manual:
170
164
 
171
- Every release, at minimum:
165
+ 1. Bumping `version` in `scripts/theme-contract.json` when the contract itself changes (§7).
166
+ 2. The CI gates (lint, validators, coverage, size budget, Playwright) — a red PR never reaches `main`, so a release is never cut from a failing tree.
172
167
 
173
- 1. Promotes `## [Unreleased]` to `## [x.y.z] - YYYY-MM-DD` in `CHANGELOG.md`.
174
- 2. Bumps `version` in `package.json` per the rules in §1.
175
- 3. Bumps `version` in `scripts/theme-contract.json` if the contract changed.
176
- 4. Runs `npm run validate-themes` — must pass.
177
- 5. Rebuilds `dist/*.css` via `npm run build:css:all`.
178
- 6. Tags the commit `vX.Y.Z` and pushes.
168
+ Contributor-facing workflow detail lives in [`CONTRIBUTING.md`](./CONTRIBUTING.md).
179
169
 
180
170
  ---
181
171
 
@@ -185,7 +175,7 @@ Two version numbers, two files:
185
175
 
186
176
  | Version | Source | Bumps on |
187
177
  | ------------------- | ------------------------------------------------------------- | ------------------------------------------------------------------------- |
188
- | Library version | `version` field of [`package.json`](./package.json) | Any change to CSS / SCSS / React public surfaces per §1. |
178
+ | Library version | `version` field of [`package.json`](./package.json) | Any change to the CSS / SCSS / contract public surfaces per §1. |
189
179
  | Contract version | `version` field of [`scripts/theme-contract.json`](./scripts/theme-contract.json) | Contract-only changes (new optional token → minor; required token renamed or removed → major). |
190
180
 
191
181
  They usually move together on a library MAJOR. They move independently on MINOR and PATCH.
@@ -197,4 +187,4 @@ They usually move together on a library MAJOR. They move independently on MINOR
197
187
  - [`CHANGELOG.md`](./CHANGELOG.md) — the actual change log.
198
188
  - [`CONTRACT.md`](./CONTRACT.md) — the token contract.
199
189
  - [`CONTRIBUTING.md`](./CONTRIBUTING.md) — release mechanics and contributor workflow.
200
- - [`ROADMAP.md`](./ROADMAP.md) — where the library is headed toward `1.0.0`.
190
+ - [`ROADMAP.md`](./ROADMAP.md) — where the library is headed.
@@ -385,7 +385,7 @@ Library defaults emit under **`:where(:root)`** (specificity 0,0,0), so any them
385
385
 
386
386
  **Never hand-edit `public/theme.css` or `public/themes/**/theme.css`.** They are generated from `scss/themes/*.scss` and gated by `check:theme-drift`.
387
387
 
388
- See `/docs/authoring/themes` on the docs site (https://jerry2d3d.github.io/css-is-awesome/docs/authoring/themes/) for the full guide.
388
+ See `/docs/authoring/themes` on the docs site (https://cssisawesome.com/docs/authoring/themes/) for the full guide.
389
389
 
390
390
  ### Spacing is themeable — set the numbered step
391
391
 
@@ -489,7 +489,7 @@ How to reach them:
489
489
 
490
490
  - **AI agents** — `list_recipes` / `get_recipe(name)` over MCP. Prefer this over
491
491
  writing an interactive pattern from memory; the recipe encodes the a11y work.
492
- - **Humans** — `/docs/recipes` on the docs site (https://jerry2d3d.github.io/css-is-awesome/docs/recipes/), or read the markdown directly.
492
+ - **Humans** — `/docs/recipes` on the docs site (https://cssisawesome.com/docs/recipes/), or read the markdown directly.
493
493
 
494
494
  Note the two different things living in `scss/recipes/`: `<slug>.md` files are
495
495
  *pattern* recipes (read them, don't import them), while `_<slug>.scss` files —
package/dist/tokens.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- // Generated from scripts/theme-contract.json on 2026-09-04.
1
+ // Generated from scripts/theme-contract.json on 2026-09-05.
2
2
  // Do not edit by hand. Run `npm run build:token-types` to regenerate.
3
3
 
4
4
  /** Every CSS custom property cia themes are required to declare. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "css-is-awesome",
3
- "version": "1.8.0",
3
+ "version": "1.8.2",
4
4
  "description": "A token-driven SCSS design system with light/dark theming, semantic color tokens, and a 800+ LOC mixin API.",
5
5
  "homepage": "https://github.com/Jerry2d3d/css-is-awesome#readme",
6
6
  "bugs": {
package/public/theme.css CHANGED
@@ -320,11 +320,14 @@
320
320
  --error-text: #fca5a5;
321
321
  /* Space scale */
322
322
  --space-2xs: 0.25rem;
323
- --space-xs: 0.5rem;
324
- --space-sm: 0.75rem;
325
- --space-md: 1rem;
326
- --space-lg: 1.5rem;
327
- --space-xl: 2rem;
323
+ --space-xs: var(--space-1);
324
+ --space-sm: var(--space-2);
325
+ --space-md: var(--space-4);
326
+ --space-lg: var(--space-5);
327
+ --space-xl: var(--space-6);
328
+ --space-2xl: var(--space-7);
329
+ --space-3xl: var(--space-8);
330
+ --space-4xl: var(--space-9);
328
331
  /* Radius alias */
329
332
  --radius-sm: var(--r-sm);
330
333
  --radius-md: var(--r-md);
@@ -490,11 +493,14 @@
490
493
  --error-text: #b91c1c;
491
494
  /* Space scale */
492
495
  --space-2xs: 0.25rem;
493
- --space-xs: 0.5rem;
494
- --space-sm: 0.75rem;
495
- --space-md: 1rem;
496
- --space-lg: 1.5rem;
497
- --space-xl: 2rem;
496
+ --space-xs: var(--space-1);
497
+ --space-sm: var(--space-2);
498
+ --space-md: var(--space-4);
499
+ --space-lg: var(--space-5);
500
+ --space-xl: var(--space-6);
501
+ --space-2xl: var(--space-7);
502
+ --space-3xl: var(--space-8);
503
+ --space-4xl: var(--space-9);
498
504
  /* Radius alias */
499
505
  --radius-sm: var(--r-sm);
500
506
  --radius-md: var(--r-md);
@@ -3818,11 +3824,14 @@
3818
3824
  ---------------------------------------------------------- */
3819
3825
  /* Space scale (rem for accessibility) */
3820
3826
  --space-2xs: 0.25rem;
3821
- --space-xs: 0.5rem;
3822
- --space-sm: 0.75rem;
3823
- --space-md: 1rem;
3824
- --space-lg: 1.5rem;
3825
- --space-xl: 2rem;
3827
+ --space-xs: var(--space-1);
3828
+ --space-sm: var(--space-2);
3829
+ --space-md: var(--space-4);
3830
+ --space-lg: var(--space-5);
3831
+ --space-xl: var(--space-6);
3832
+ --space-2xl: var(--space-7);
3833
+ --space-3xl: var(--space-8);
3834
+ --space-4xl: var(--space-9);
3826
3835
  /* Radius alias — mirrors --r-* with standard names so
3827
3836
  `m.radius(size)` in the library finds --radius-<size>. */
3828
3837
  --radius-sm: var(--r-sm);
@@ -130,11 +130,14 @@
130
130
  --error-text: #fca5a5;
131
131
  /* Space scale */
132
132
  --space-2xs: 0.25rem;
133
- --space-xs: 0.5rem;
134
- --space-sm: 0.75rem;
135
- --space-md: 1rem;
136
- --space-lg: 1.5rem;
137
- --space-xl: 2rem;
133
+ --space-xs: var(--space-1);
134
+ --space-sm: var(--space-2);
135
+ --space-md: var(--space-4);
136
+ --space-lg: var(--space-5);
137
+ --space-xl: var(--space-6);
138
+ --space-2xl: var(--space-7);
139
+ --space-3xl: var(--space-8);
140
+ --space-4xl: var(--space-9);
138
141
  /* Radius alias */
139
142
  --radius-sm: var(--r-sm);
140
143
  --radius-md: var(--r-md);
@@ -130,11 +130,14 @@
130
130
  --error-text: #b91c1c;
131
131
  /* Space scale */
132
132
  --space-2xs: 0.25rem;
133
- --space-xs: 0.5rem;
134
- --space-sm: 0.75rem;
135
- --space-md: 1rem;
136
- --space-lg: 1.5rem;
137
- --space-xl: 2rem;
133
+ --space-xs: var(--space-1);
134
+ --space-sm: var(--space-2);
135
+ --space-md: var(--space-4);
136
+ --space-lg: var(--space-5);
137
+ --space-xl: var(--space-6);
138
+ --space-2xl: var(--space-7);
139
+ --space-3xl: var(--space-8);
140
+ --space-4xl: var(--space-9);
138
141
  /* Radius alias */
139
142
  --radius-sm: var(--r-sm);
140
143
  --radius-md: var(--r-md);
@@ -146,11 +146,14 @@
146
146
  ---------------------------------------------------------- */
147
147
  /* Space scale (rem for accessibility) */
148
148
  --space-2xs: 0.25rem;
149
- --space-xs: 0.5rem;
150
- --space-sm: 0.75rem;
151
- --space-md: 1rem;
152
- --space-lg: 1.5rem;
153
- --space-xl: 2rem;
149
+ --space-xs: var(--space-1);
150
+ --space-sm: var(--space-2);
151
+ --space-md: var(--space-4);
152
+ --space-lg: var(--space-5);
153
+ --space-xl: var(--space-6);
154
+ --space-2xl: var(--space-7);
155
+ --space-3xl: var(--space-8);
156
+ --space-4xl: var(--space-9);
154
157
  /* Radius alias — mirrors --r-* with standard names so
155
158
  `m.radius(size)` in the library finds --radius-<size>. */
156
159
  --radius-sm: var(--r-sm);
@@ -8,14 +8,22 @@
8
8
  // <div class="cia-tabs">
9
9
  // <input type="radio" name="t" id="t1" checked>
10
10
  // <input type="radio" name="t" id="t2">
11
- // <div class="cia-tab-list" role="tablist">
11
+ // <nav class="cia-tab-list" role="tablist">
12
12
  // <label for="t1" role="tab">Tab 1</label>
13
13
  // <label for="t2" role="tab">Tab 2</label>
14
- // </div>
14
+ // </nav>
15
15
  // <div class="cia-tab-panel" role="tabpanel">Content 1</div>
16
16
  // <div class="cia-tab-panel" role="tabpanel">Content 2</div>
17
17
  // </div>
18
18
  //
19
+ // ⚠️ CONTRACT: the tab list must NOT share the panels' element type.
20
+ // Panel switching indexes panels with :nth-of-type(), which counts by
21
+ // ELEMENT TYPE among siblings — a <div> tab list would be div #1, every
22
+ // panel's index would shift by one, and tab 1 would show no panel while
23
+ // the labels still highlighted correctly (we shipped exactly that; fixed
24
+ // 2026-09-04 by making the documented list a <nav>). CSS has no
25
+ // nth-of-class, so the markup carries this rule instead of the selector.
26
+ //
19
27
  // A11y baseline: keyboard nav (Tab + arrows + Space) and screen-reader text
20
28
  // are native. For full aria-selected sync ship the tiny opt-in JS shim
21
29
  // documented at /docs/components/tabs#aria.
@@ -167,11 +167,14 @@
167
167
 
168
168
  /* Space scale */
169
169
  --space-2xs: 0.25rem;
170
- --space-xs: 0.5rem;
171
- --space-sm: 0.75rem;
172
- --space-md: 1rem;
173
- --space-lg: 1.5rem;
174
- --space-xl: 2rem;
170
+ --space-xs: var(--space-1);
171
+ --space-sm: var(--space-2);
172
+ --space-md: var(--space-4);
173
+ --space-lg: var(--space-5);
174
+ --space-xl: var(--space-6);
175
+ --space-2xl: var(--space-7);
176
+ --space-3xl: var(--space-8);
177
+ --space-4xl: var(--space-9);
175
178
 
176
179
  /* Radius alias */
177
180
  --radius-sm: var(--r-sm);
@@ -164,11 +164,14 @@
164
164
 
165
165
  /* Space scale */
166
166
  --space-2xs: 0.25rem;
167
- --space-xs: 0.5rem;
168
- --space-sm: 0.75rem;
169
- --space-md: 1rem;
170
- --space-lg: 1.5rem;
171
- --space-xl: 2rem;
167
+ --space-xs: var(--space-1);
168
+ --space-sm: var(--space-2);
169
+ --space-md: var(--space-4);
170
+ --space-lg: var(--space-5);
171
+ --space-xl: var(--space-6);
172
+ --space-2xl: var(--space-7);
173
+ --space-3xl: var(--space-8);
174
+ --space-4xl: var(--space-9);
172
175
 
173
176
  /* Radius alias */
174
177
  --radius-sm: var(--r-sm);
@@ -185,11 +185,14 @@
185
185
 
186
186
  /* Space scale (rem for accessibility) */
187
187
  --space-2xs: 0.25rem;
188
- --space-xs: 0.5rem;
189
- --space-sm: 0.75rem;
190
- --space-md: 1rem;
191
- --space-lg: 1.5rem;
192
- --space-xl: 2rem;
188
+ --space-xs: var(--space-1);
189
+ --space-sm: var(--space-2);
190
+ --space-md: var(--space-4);
191
+ --space-lg: var(--space-5);
192
+ --space-xl: var(--space-6);
193
+ --space-2xl: var(--space-7);
194
+ --space-3xl: var(--space-8);
195
+ --space-4xl: var(--space-9);
193
196
 
194
197
  /* Radius alias — mirrors --r-* with standard names so
195
198
  `m.radius(size)` in the library finds --radius-<size>. */