css-is-awesome 1.19.2 → 1.21.0

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.
Files changed (41) hide show
  1. package/AGENTS.md +11 -5
  2. package/CHANGELOG.md +20 -0
  3. package/CONTRACT.md +10 -0
  4. package/MIGRATION.md +58 -0
  5. package/README.md +19 -11
  6. package/bin/analyze.cjs +47 -2
  7. package/bin/cia.cjs +10 -0
  8. package/bin/fix-theme.cjs +151 -0
  9. package/llm.txt +5 -5
  10. package/mcp/server.cjs +64 -3
  11. package/package.json +6 -3
  12. package/public/theme.css +138 -0
  13. package/public/themes/boilerplate/theme.css +6 -0
  14. package/public/themes/boilerplate-dark/theme.css +6 -0
  15. package/public/themes/boilerplate-light/theme.css +6 -0
  16. package/public/themes/cupertino/theme.css +6 -0
  17. package/public/themes/cupertino-dark/theme.css +6 -0
  18. package/public/themes/cupertino-light/theme.css +6 -0
  19. package/public/themes/glass/theme.css +6 -0
  20. package/public/themes/glass-dark/theme.css +6 -0
  21. package/public/themes/glass-light/theme.css +6 -0
  22. package/public/themes/graphite/theme.css +6 -0
  23. package/public/themes/graphite-dark/theme.css +6 -0
  24. package/public/themes/graphite-light/theme.css +6 -0
  25. package/public/themes/press/theme.css +6 -0
  26. package/public/themes/press-dark/theme.css +6 -0
  27. package/public/themes/press-light/theme.css +6 -0
  28. package/public/themes/prism/theme.css +6 -0
  29. package/public/themes/prism-dark/theme.css +6 -0
  30. package/public/themes/prism-light/theme.css +6 -0
  31. package/public/themes/sketchbook/theme.css +6 -0
  32. package/public/themes/sketchbook-dark/theme.css +6 -0
  33. package/public/themes/sketchbook-light/theme.css +6 -0
  34. package/public/themes/terminal/theme.css +6 -0
  35. package/public/themes/terminal-dark/theme.css +6 -0
  36. package/public/themes/terminal-light/theme.css +6 -0
  37. package/scripts/audit-pairs.json +171 -23
  38. package/scripts/fix-theme.cjs +184 -0
  39. package/scripts/theme-contract.json +33 -4
  40. package/scripts/theme-validator.js +40 -1
  41. package/scss/_layout.scss +129 -0
package/AGENTS.md CHANGED
@@ -148,7 +148,7 @@ Authoring template (in your own project — a theme file is a global stylesheet,
148
148
 
149
149
  `$standalone` defaults to `true` (emit `:root, :root[data-theme="<name>"]`). Pass `$standalone: false` only when your block is going into a multi-theme bundle where the bare `:root` would collide.
150
150
 
151
- The validator (`node scripts/theme-validator.js`) enforces the token contract — **127 required + 41 optional = 168 slots** — plus WCAG 2.2 AA contrast (**22 audited pairs per theme**, including five `--code-*` pairs). Themes that miss required tokens or fail contrast cannot ship without `--allow-a11y-fail`.
151
+ The validator (`node scripts/theme-validator.js`) enforces the token contract — **127 required + 49 optional = 176 slots** — plus WCAG 2.2 AA contrast (**24 audited pairs per theme**, including five `--code-*` pairs). Themes that miss required tokens or fail contrast cannot ship without `--allow-a11y-fail`.
152
152
 
153
153
  ### Theming spacing (new — read this before you set a size token)
154
154
 
@@ -168,7 +168,7 @@ Why it matters: components call `cia.space(4)`, which resolves to `var(--space-4
168
168
 
169
169
  Two `<link media>` themes still work under the new selector model: a stylesheet whose `media` doesn't match is loaded but never applied, so only the matching file's `:root` block lands.
170
170
 
171
- Validator: `node scripts/theme-validator.js path/to/theme.css` (or `--all` for every shipped theme). Every theme must declare every required contract token (127 required in contract 1.1; missing required tokens always fail, missing optional ones are reported as info). The audit also runs a WCAG 2.2 AA contrast check over 22 pairs; **a11y FAILs are fatal by default** as of v0.7. Pass `--allow-a11y-fail` to downgrade contrast failures to a report-only warning (the older `--strict` flag is accepted as a no-op alias). `--border-default` is treated as decorative per WCAG 2.2 SC 1.4.11 and reports as info, not FAIL.
171
+ Validator: `node scripts/theme-validator.js path/to/theme.css` (or `--all` for every shipped theme). Every theme must declare every required contract token (127 required in contract 1.3; missing required tokens always fail, missing optional ones are reported as info). The audit also runs a WCAG 2.2 AA contrast check over 22 pairs; **a11y FAILs are fatal by default** as of v0.7. Pass `--allow-a11y-fail` to downgrade contrast failures to a report-only warning (the older `--strict` flag is accepted as a no-op alias). `--border-default` is treated as decorative per WCAG 2.2 SC 1.4.11 and reports as info, not FAIL.
172
172
 
173
173
  ### Theme init (Next.js / SSR consumers)
174
174
 
@@ -338,15 +338,16 @@ Or run this in-repo copy directly — needs its SDK peer deps installed manually
338
338
  }
339
339
  ```
340
340
 
341
- Either way it exposes **33 tools** across 8 families:
341
+ Either way it exposes **34 tools** across 8 families:
342
342
 
343
343
  - **Themes** — `list_themes`, `get_theme`, `search_themes`
344
344
  - **Mixins** — `list_mixins`, `get_mixin`, `search_mixins` (real signatures — don't guess)
345
345
  - **Functions** — `list_functions`, `get_function`, `search_functions`
346
- - **Tokens** — `list_tokens`, `get_token`, `search_tokens` (127 required + 41 optional contract tokens)
346
+ - **Tokens** — `list_tokens`, `get_token`, `search_tokens` (127 required + 49 optional contract tokens)
347
347
  - **Animations** — `list_animations`, `get_animation`
348
348
  - **Components** — `list_components`, `get_component`, `search_components`
349
349
  - **Recipes** — `list_recipes`, `get_recipe`
350
+ - **Theme upgrades** — `fix_theme` (rewrite deprecated tokens to their replacements; returns text, never writes)
350
351
  - **Doc readers** — `read_llm_txt`, `read_changelog`, `read_migration`, `read_theming`, `read_agents`, `read_contract`, `read_three_tiers`, `read_readme`, `read_versioning`
351
352
  - **Helpers** — `assemble_prompt` (bundle context), `resolve_size` (snap a design px value to cia's 4px grid — call this whenever a design tool hands you a raw px value), `validate_theme` (run the real theme validator — contract + contrast — on a CSS string before you ship it), `theme_from_tokens` (DTCG / Tokens Studio / flat token JSON → a complete theme.css, base-inherited and validated; same function as `cia theme from-tokens`), `get_token_map` (that path → token mapping as data, or how one path resolves — read it instead of re-deriving the rules)
352
353
 
@@ -366,7 +367,12 @@ Either way it exposes **33 tools** across 8 families:
366
367
  default boilerplate), unmapped paths pass through verbatim and are reported, a `--dark` file or paired
367
368
  `color-light`/`color-dark` groups become `light-dark()`, and the validator + WCAG audit run before anything is
368
369
  written. Same function as the MCP `theme_from_tokens` tool; `npx cia theme map [--json] [--path <token.path>]` prints the design-token → cia-token mapping that verb applies (same data as the MCP `get_token_map` tool), for anyone who needs to map one token name the way the converter would. Run any verb with `--help`. (`cia init` remains
369
- planned.)
370
+ planned.) `npx cia fix-theme <theme.css> [--write]` moves a theme onto
371
+ current token names: it renames any DEPRECATED token to its replacement,
372
+ changing the property only — values, comments and ordering survive, so the
373
+ rendered theme is identical. Prints by default; writes only with `--write`;
374
+ a block already declaring the replacement is reported, never merged. Same
375
+ function as the MCP `fix_theme` tool.
370
376
  - **JSON token export** — Tokens Studio-format sample in `figma-tokens/tokens.json`; it round-trips through `cia theme from-tokens` (paired light/dark groups → one `light-dark()` theme).
371
377
  - **`llm.txt`** — at the repo root and served from the docs site; single-fetch
372
378
  summary for any AI agent. Also readable over MCP via `read_llm_txt`.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,25 @@
1
1
  # Changelog
2
2
 
3
+ # [1.21.0](https://github.com/Jerry2d3d/css-is-awesome/compare/v1.20.0...v1.21.0) (2026-09-23)
4
+
5
+
6
+ ### Features
7
+
8
+ * **cli:** cia fix-theme — rename deprecated tokens to their replacements ([665d871](https://github.com/Jerry2d3d/css-is-awesome/commit/665d87129f578d1ac38f410ce7461f37bbe24357))
9
+
10
+ # [1.20.0](https://github.com/Jerry2d3d/css-is-awesome/compare/v1.19.2...v1.20.0) (2026-09-23)
11
+
12
+
13
+ ### Bug Fixes
14
+
15
+ * **site:** correct the stale optional-token count on /docs/tokens ([c0d4db0](https://github.com/Jerry2d3d/css-is-awesome/commit/c0d4db047c858854cf54d34affc792e0c52f4427))
16
+
17
+
18
+ ### Features
19
+
20
+ * **site:** dogfood page surfaces on the home page; add them to the theme editor ([8dc3a65](https://github.com/Jerry2d3d/css-is-awesome/commit/8dc3a65778e28b57aeb67824062f26d7494e3f60))
21
+ * **themes:** page surfaces — hero and band, derived for every theme ([b984d86](https://github.com/Jerry2d3d/css-is-awesome/commit/b984d865e3463ae15e95083ff0697df0a109cf88))
22
+
3
23
  ## [1.19.2](https://github.com/Jerry2d3d/css-is-awesome/compare/v1.19.1...v1.19.2) (2026-09-23)
4
24
 
5
25
 
package/CONTRACT.md CHANGED
@@ -321,8 +321,11 @@ Every optional token belongs to exactly one **feature** in `scripts/theme-contra
321
321
  | `surfaces-extended` | `--background-elevated`, `--background-hero`, `--background-overlay`, `--background-scrim` | Extra background layers (elevated, hero, overlay, scrim) for themes that want more than the required surface set; each falls back to a required surface. |
322
322
  | `borders-extended` | `--border-card`, `--border-divider`, `--border-focus-ring`, `--border-input` | Per-context border colours (card, divider, focus ring, input); each falls back to --border-default / --border-focus. |
323
323
  | `logo` | `--logo-default`, `--logo-mark`, `--logo-monochrome`, `--logo-wordmark` | Theme-aware logo assets (default, mark, monochrome, wordmark) as url() or SVG data values for the icon/brand mixins. |
324
+ | `page-surfaces` | `--page-hero-bg`, `--page-hero-image`, `--page-hero-ink`, `--page-hero-scrim`, `--page-band-bg`, `--page-band-image`, `--page-band-ink`, `--page-band-scrim`, `--background-hero` *(deprecated)* | Two page-level surfaces a theme owns outright: a **hero** for a landing page and a **band** for section stripes and footers. Each carries a background colour, an optional gradient or `url()` image, an ink colour, and a scrim that keeps text legible over an image. **Declaring them changes nothing on its own** - a page only picks a surface up through `cia.surface(hero|band)` or `data-surface`, which is why every shipped theme defines them without moving a pixel on an existing site. Contrast over an *image* cannot be measured, so the scrim carries that weight: the mixin applies 50% black whenever an image is passed to it. |
324
325
  | `touch-target` | `--touch-target-min` | Minimum interactive target size read by form and button mixins (WCAG 2.2 SC 2.5.8); the library default is 24px. |
325
326
 
327
+ > **Deprecated - `--background-hero`.** It sat in the contract unread: no mixin consumed it, the editor could not set it, and nothing documented it. `--page-hero-bg` replaces it and **falls back through it**, so an existing declaration keeps working untouched. It stays valid until contract 2, per the lifecycle in [`VERSIONING.md`](./VERSIONING.md).
328
+
326
329
  ---
327
330
 
328
331
  ## Print (optional)
@@ -632,3 +635,10 @@ A PR that adds a new contract glyph must:
632
635
  Per-theme override glyphs are NEVER required by the contract — themes
633
636
  opt in glyph-by-glyph by declaring `--cia-icon-<name>` and shipping the
634
637
  replacement file alongside.
638
+ >
639
+ > **Deprecations are machine-readable.** Every one lives in the `deprecated` map in
640
+ > [`scripts/theme-contract.json`](./scripts/theme-contract.json) with the token it replaces,
641
+ > when it was deprecated and when it goes. The validator prints it, `get_token` returns it,
642
+ > `npx cia analyze` flags it in your stylesheets, and `npx cia fix-theme <file> --write`
643
+ > renames it for you — property only, so the theme renders identically. `check:contract`
644
+ > fails the build if a deprecation points at a token that does not exist.
package/MIGRATION.md CHANGED
@@ -2,6 +2,64 @@
2
2
 
3
3
  Breaking changes between css-is-awesome versions, and how to migrate.
4
4
 
5
+ ## Page surfaces — contract 1.3 (no action required)
6
+
7
+ **Nothing changes on your site when you upgrade.** Two new optional surfaces
8
+ exist — a `hero` for a landing page and a `band` for section stripes — and
9
+ every shipped theme now declares a background and an ink colour for both. That
10
+ sounds like a visual change and is not one, for a single reason: **cia never
11
+ applies a page surface on its own.**
12
+
13
+ A page takes a surface only when you ask for it:
14
+
15
+ ```scss
16
+ .landing { @include cia.surface(hero); } // or <body data-surface="hero">
17
+ ```
18
+
19
+ Until then the tokens sit unread, exactly as `--background-hero` has since it
20
+ was added. `scripts/test-no-auto-surface.mjs` compiles every shipped bundle and
21
+ fails if any rule reads a page-surface token or emits a `[data-surface]` rule,
22
+ so the guarantee is enforced rather than promised.
23
+
24
+ ### What changed at a glance
25
+
26
+ | Area | Before | After |
27
+ |---|---|---|
28
+ | Contract version | `1.2` | **`1.3`** — eight optional tokens, one new `page-surfaces` feature |
29
+ | Required tokens | 127 | 127, unchanged |
30
+ | `--background-hero` | optional, read by nothing | **deprecated**; `--page-hero-bg` falls back through it, so an existing declaration keeps working until contract 2 |
31
+ | Your custom theme | validates | validates — the new tokens are optional, and missing optional tokens report as info |
32
+
33
+ Nothing here needs doing. If you would rather move onto the current name now,
34
+ `npx cia fix-theme <your-theme.css>` shows what it would change and
35
+ `--write` applies it. It renames the property and nothing else, so the theme
36
+ renders identically; a block already declaring `--page-hero-bg` is reported
37
+ rather than merged.
38
+
39
+ ### If you want a hero
40
+
41
+ Declare the tokens your theme needs and apply the surface where you want it:
42
+
43
+ ```css
44
+ :root[data-theme="brand"] {
45
+ --page-hero-bg: light-dark(#f2f6fe, #121d36);
46
+ --page-hero-ink: light-dark(#09090b, #fafafa);
47
+ /* optional */
48
+ --page-hero-image: url("/hero.jpg");
49
+ }
50
+ ```
51
+
52
+ An image is a `url()` you host or a gradient, because a theme is one CSS file.
53
+ Contrast over a photo cannot be measured, so pass the image through the mixin
54
+ (`cia.surface(hero, $image: url("/hero.jpg"))`) and it lays 50% black between
55
+ the picture and your text. A colour-only surface gets no scrim — a wash over a
56
+ flat colour would only darken it.
57
+
58
+ ### If you declared `--background-hero`
59
+
60
+ Nothing to do. It still resolves. Rename it to `--page-hero-bg` whenever it
61
+ suits you; the tooling will flag it as deprecated in the meantime.
62
+
5
63
  ## v1.0 — mixin-first goes stable (published as 1.1.0)
6
64
 
7
65
  **There are no breaking changes between v0.8.1/0.8.2 and v1.0.0.** 1.0.0
package/README.md CHANGED
@@ -14,7 +14,7 @@
14
14
 
15
15
  **Read [`llm.txt`](./llm.txt) first.** One file, the whole system: install path, hard rules, the mixin vocabulary, and the traps that make agents write wrong cia code. It ships in the npm package, so it's at `node_modules/css-is-awesome/llm.txt` in any project that has cia.
16
16
 
17
- **Then connect the MCP server** and stop guessing at signatures. It answers from the real source — 33 tools covering themes, mixins, functions, tokens, recipes, components, theme validation, theme generation from design tokens and the token mapping itself.
17
+ **Then connect the MCP server** and stop guessing at signatures. It answers from the real source — 34 tools covering themes, mixins, functions, tokens, recipes, components, theme validation, theme generation from design tokens and the token mapping itself.
18
18
 
19
19
  ```json
20
20
  {
@@ -79,7 +79,7 @@ Author your own class names; the mixin handles the styling. Mixins for buttons,
79
79
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/css-is-awesome@1/dist/css-is-awesome.min.css">
80
80
  ```
81
81
 
82
- Theme first (sets the tokens), library second. **No `data-theme` attribute needed** — a single theme file styles the page on its own. Swap the URL to swap the theme; the HTML never changes. Bundle tiers — `dist/tokens.css` (2.2 KB gz, `:where(:root)` vars only), `dist/css-is-awesome.core.min.css` (2.4 KB gz, tokens + resets), `dist/css-is-awesome.min.css` (7.3 KB gz, full).
82
+ Theme first (sets the tokens), library second. **No `data-theme` attribute needed** — a single theme file styles the page on its own. Swap the URL to swap the theme; the HTML never changes. Bundle tiers — `dist/tokens.css` (2.2 KB gz, `:where(:root)` vars only), `dist/css-is-awesome.core.min.css` (2.4 KB gz, tokens + resets), `dist/css-is-awesome.min.css` (8.0 KB gz, full).
83
83
 
84
84
  ### 3. Bare tags (opt-in Pico-mode)
85
85
 
@@ -145,11 +145,13 @@ node scripts/theme-validator.js public/themes/midnight/theme.css
145
145
  # <link rel="stylesheet" href="/themes/midnight/theme.css">
146
146
  ```
147
147
 
148
- Full authoring walkthrough: [`/docs/authoring/themes`](https://cssisawesome.com/docs/authoring/themes/). The contract (127 required + 41 optional tokens) is at [`scripts/theme-contract.json`](./scripts/theme-contract.json).
148
+ A theme can also own the page behind your components: optional **hero** and **band** surfaces carry a background, an image or gradient, an ink colour and a scrim, applied with `@include cia.surface(hero)` or `<body data-surface="hero">`. Nothing is applied automatically, so adding them never moves a pixel until you ask.
149
+
150
+ Full authoring walkthrough: [`/docs/authoring/themes`](https://cssisawesome.com/docs/authoring/themes/). The contract (127 required + 49 optional tokens) is at [`scripts/theme-contract.json`](./scripts/theme-contract.json).
149
151
 
150
152
  ## Token contract
151
153
 
152
- Every theme declares the same slots: **surfaces · ink · lines · primary · seal · accent · code · type · space · radius · shadow · blur · glow · motion**. Components read tokens, themes set tokens, nothing else. 127 required, 36 optional.
154
+ Every theme declares the same slots: **surfaces · ink · lines · primary · seal · accent · code · type · space · radius · shadow · blur · glow · motion**. Components read tokens, themes set tokens, nothing else. 127 required, 49 optional.
153
155
 
154
156
  **Themes own the spacing scale.** A theme declares the numbered scale `--space-0` … `--space-9` (contract-required), which is exactly what `cia.space(4)` compiles to — so a theme can ship tighter or airier rhythm without touching a component. The six t-shirt names (`--space-2xs/xs/sm/md/lg/xl`) are optional; the library emits them as references (`--space-md: var(--space-4)`), so they track the numbered scale automatically.
155
157
 
@@ -221,6 +223,7 @@ The CLI also carries the registry and the health check:
221
223
 
222
224
  ```bash
223
225
  npx cia add --list # browse the recipe book
226
+ npx cia fix-theme t.css # move a theme onto current token names (--write to apply)
224
227
  npx cia add bottom-nav # copy a recipe into your project — you own the pattern
225
228
  npx cia analyze src/styles # design-system health: dead cia.* symbols, the
226
229
  # space() scale trap, off-contract tokens (typos),
@@ -301,7 +304,7 @@ The scope is kept narrow: 8 `!important` declarations, all inside `@media print`
301
304
 
302
305
  ## MCP server (for AI agents)
303
306
 
304
- cia ships a Model Context Protocol stdio server (JSON-RPC over stdio, protocol `2024-11-05`) exposing **33 tools** across 8 families (themes, mixins, functions, tokens · 127 required of them, animations, components, recipes, doc readers) plus `assemble_prompt` (context bundles) , `resolve_size` (snap design px values to cia's 4px grid) `validate_theme` (run the real theme validator on CSS you just wrote) `theme_from_tokens` (design-tokens JSON → a complete, validated theme.css) and `get_token_map` (that mapping as data, or how one path resolves). 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. Full reference: [`/docs/mcp`](https://cssisawesome.com/docs/mcp/).
307
+ cia ships a Model Context Protocol stdio server (JSON-RPC over stdio, protocol `2024-11-05`) exposing **34 tools** across 8 families (themes, mixins, functions, tokens · 127 required of them, animations, components, recipes, doc readers) plus `assemble_prompt` (context bundles) , `resolve_size` (snap design px values to cia's 4px grid) `validate_theme` (run the real theme validator on CSS you just wrote) `theme_from_tokens` (design-tokens JSON → a complete, validated theme.css) and `get_token_map` (that mapping as data, or how one path resolves). 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. Full reference: [`/docs/mcp`](https://cssisawesome.com/docs/mcp/).
305
308
 
306
309
  **Recommended — zero install:** use the dedicated [`css-is-awesome-mcp`](https://www.npmjs.com/package/css-is-awesome-mcp) package. It depends on `css-is-awesome` and resolves your installed version's real source, so it's never out of sync — and the MCP SDK ships as a real dependency, not an optional peer you have to remember to add.
307
310
 
@@ -364,6 +367,8 @@ It also hosts the **[playground](https://cssisawesome.com/playground/)**: write
364
367
  | `npm run build:css:all` | Compile all bundles (full + core + utilities + minified) + themes + token types |
365
368
  | `npm run build:css:themes` | Rebuild the 24 per-theme CSS files in `public/themes/` **and** regenerate the all-in-one `public/theme.css` bundle |
366
369
  | `npm run check:theme-drift` | Rebuild the themes into a scratch copy and fail if the committed artifacts don't match their SCSS sources |
370
+ | `npm run check:contract` | Diff the token contract against the last release tag and fail if the required/optional lists changed without the version bump [`VERSIONING.md`](./VERSIONING.md) requires |
371
+ | `npm run check:rtl` | Audit the SCSS for physical properties that should be logical (`margin-left` → `margin-inline-start`) |
367
372
  | `npm run build:token-types` | Generate `dist/tokens.d.ts` from the contract |
368
373
  | `npm run dtcg-to-scss` | Convert DTCG-format design tokens into cia SCSS |
369
374
  | `npm run lint` | ESLint on the Next.js app |
@@ -372,6 +377,9 @@ It also hosts the **[playground](https://cssisawesome.com/playground/)**: write
372
377
  | `npm run validate-icons` | Validate the `core` icon pack against the 49-glyph contract |
373
378
  | `npm run validate-api` | Assert the `css-is-awesome/api` barrel stays zero-emit |
374
379
  | `npm run validate-package` | Pack + install into a temp project and compile every documented `@use` form — catches breakage that in-repo checks can't see |
380
+ | `npm run test:surfaces` | Assert cia never applies a page surface automatically — the guarantee that adding hero/band tokens changes nobody's existing page |
381
+ | `npm run test:tokens` | Exercise the design-tokens converter (DTCG / Tokens Studio / flat) against fixtures |
382
+ | `npm run verify:playground` | Compile real samples through the playground's in-browser Sass path in Node |
375
383
  | `npm run pack:consumer` | Pack and install this build into a local consumer (defaults to `../boiler-project-ai`); `--dry-run` supported |
376
384
  | `npm test` | Playwright suite — axe a11y checks + per-theme visual snapshots |
377
385
 
@@ -406,18 +414,18 @@ Full detail: [`/docs/testing`](https://cssisawesome.com/docs/testing/).
406
414
 
407
415
  | Bundle | Size | Use case |
408
416
  |---|---|---|
409
- | `dist/tokens.css` | 2.25 KB | Tokens only (`:where(:root)` CSS variables, no rules) — the purest mixin-first emit |
410
- | `dist/css-is-awesome.core.min.css` | 2.38 KB | Tokens + resets, no utilities or components |
411
- | `dist/css-is-awesome.utilities.min.css` | 4.75 KB | Every `cia-*` utility class, nothing else |
412
- | `dist/css-is-awesome.min.css` | 7.96 KB | Full bundle (everything) |
413
- | Per-theme `themes/<name>/theme.css` | 1.9–3.7 KB | One file per theme, both modes via `light-dark()`, drop-in with no markup change |
417
+ | `dist/tokens.css` | 2.26 KB | Tokens only (`:where(:root)` CSS variables, no rules) — the purest mixin-first emit |
418
+ | `dist/css-is-awesome.core.min.css` | 2.42 KB | Tokens + resets, no utilities or components |
419
+ | `dist/css-is-awesome.utilities.min.css` | 4.79 KB | Every `cia-*` utility class, nothing else |
420
+ | `dist/css-is-awesome.min.css` | 7.99 KB | Full bundle (everything) |
421
+ | Per-theme `themes/<name>/theme.css` | 2.0–3.8 KB | One file per theme, both modes via `light-dark()`, drop-in with no markup change |
414
422
  | **Runtime JavaScript shipped in package** | **0 KB** | Nothing in the package is loaded by a page. The Node tooling (`cia` CLI, MCP server, validators) never reaches the browser; JS-driven UI features ship as separate add-on packages. |
415
423
 
416
424
  ## Status
417
425
 
418
426
  **Stable, [published on npm](https://www.npmjs.com/package/css-is-awesome)** (first published 2026-09-01). The mixin API, functions, token contract, and theme architecture are stable and under strict SemVer — breaking changes require a major bump. See [`VERSIONING.md`](./VERSIONING.md) for the policy.
419
427
 
420
- The 1.0 surface is the v0.8 mixin-first reframe — twelve mixin renames, theme system collapsed to 8 single-file theme families, six zero-JS components, intrinsic-layout vocabulary, opt-in utilities — plus the recipes book, the Tailwind/Bootstrap migration on-ramp, print/PDF support, the in-browser [Playground](https://cssisawesome.com/playground/), the design-tokens on-ramp (`npx cia theme from-tokens` / `theme map`), and the 33-tool MCP server (now also available zero-install via the companion [`css-is-awesome-mcp`](https://www.npmjs.com/package/css-is-awesome-mcp) package). The npm package ships ZERO runtime JavaScript by hard rule — nothing in it is loaded by a page; its Node tooling (CLI, MCP server, validators) never reaches the browser.
428
+ The 1.0 surface is the v0.8 mixin-first reframe — twelve mixin renames, theme system collapsed to 8 single-file theme families, six zero-JS components, intrinsic-layout vocabulary, opt-in utilities — plus the recipes book, the Tailwind/Bootstrap migration on-ramp, print/PDF support, the in-browser [Playground](https://cssisawesome.com/playground/), the design-tokens on-ramp (`npx cia theme from-tokens` / `theme map`), and the 34-tool MCP server (now also available zero-install via the companion [`css-is-awesome-mcp`](https://www.npmjs.com/package/css-is-awesome-mcp) package). The npm package ships ZERO runtime JavaScript by hard rule — nothing in it is loaded by a page; its Node tooling (CLI, MCP server, validators) never reaches the browser.
421
429
 
422
430
  See [CHANGELOG.md](./CHANGELOG.md) for the full history and [MIGRATION.md](./MIGRATION.md) for the v0.7 → v0.8 and v0.8 → v1.0 upgrade paths.
423
431
 
package/bin/analyze.cjs CHANGED
@@ -33,6 +33,18 @@ function loadContractTokens() {
33
33
  }
34
34
  }
35
35
 
36
+ // Tokens the contract has superseded. They still resolve — cia deprecates
37
+ // rather than deletes — so this is a warning with a one-command fix, not an
38
+ // error. Read from the same map `cia fix-theme` and the validator use.
39
+ function loadDeprecated() {
40
+ try {
41
+ const c = JSON.parse(fs.readFileSync(CONTRACT_PATH, 'utf8'));
42
+ return c.deprecated && typeof c.deprecated === 'object' ? c.deprecated : {};
43
+ } catch {
44
+ return {};
45
+ }
46
+ }
47
+
36
48
  // Tiny Levenshtein (zero-dep). Only called on var(--x) misses, so cost is trivial.
37
49
  function editDistance(a, b) {
38
50
  const m = a.length, n = b.length;
@@ -256,7 +268,7 @@ function missingFocusVisible(strippedSrc) {
256
268
  return [{ level: 'info', rule: 'missing-focus-visible', detail: 'interactive :hover/:active styling with no :focus-visible or focus-ring anywhere in this file — keyboard users may get no visible feedback' }];
257
269
  }
258
270
 
259
- function analyzeFile(file, symbols, contractTokens, extraNs) {
271
+ function analyzeFile(file, symbols, contractTokens, extraNs, deprecated) {
260
272
  const raw = fs.readFileSync(file, 'utf8').replace(/\r\n/g, '\n');
261
273
  const src = stripComments(raw);
262
274
  const ns = detectNamespaces(src, extraNs);
@@ -280,6 +292,37 @@ function analyzeFile(file, symbols, contractTokens, extraNs) {
280
292
  }
281
293
  }
282
294
  }
295
+ // deprecated-token: still resolves, but the contract names a successor.
296
+ // Catches both a declaration (--old: value) and a reference (var(--old)).
297
+ // A plain scan, not a built regex: custom-property names are distinctive
298
+ // enough that a substring plus a name-boundary check is exact, and it does
299
+ // not depend on escaping a token name into a pattern.
300
+ if (deprecated && Object.keys(deprecated).length) {
301
+ const isNameChar = (ch) => /[A-Za-z0-9_-]/.test(ch);
302
+ for (const [tok, def] of Object.entries(deprecated)) {
303
+ let at = src.indexOf(tok);
304
+ let hit = false;
305
+ while (at !== -1) {
306
+ const after = src[at + tok.length];
307
+ // Reject a longer name that merely starts with this one.
308
+ if (after === undefined || !isNameChar(after)) { hit = true; break; }
309
+ at = src.indexOf(tok, at + 1);
310
+ }
311
+ if (!hit) continue;
312
+ const to = (def && def.replacedBy) || null;
313
+ let since = '';
314
+ if (def && def.since) {
315
+ since = ' (contract ' + def.since + (def.removeIn ? ', removed in ' + def.removeIn : '') + ')';
316
+ }
317
+ findings.push({
318
+ level: 'warn',
319
+ rule: 'deprecated-token',
320
+ detail: tok + ' is deprecated' + since + (to ? ' — use ' + to : '') +
321
+ '; your value still works. Run `npx cia fix-theme <file>` to rename it.',
322
+ });
323
+ }
324
+ }
325
+
283
326
  // off-contract-token: a var(--x) that's a near-miss of a real contract token
284
327
  // (a typo → the declaration silently fails). Pure custom tokens — not close to
285
328
  // any contract token — are the consumer's own and are left alone.
@@ -325,6 +368,7 @@ const RULE_CATEGORY = {
325
368
  'unknown-symbol': 'API',
326
369
  'space-scale': 'Spacing',
327
370
  'off-contract-token': 'Contract',
371
+ 'deprecated-token': 'Contract',
328
372
  'off-scale-length': 'Spacing',
329
373
  'hard-coded-color': 'Color',
330
374
  bem: 'Naming',
@@ -364,8 +408,9 @@ async function run(args) {
364
408
 
365
409
  const symbols = collectSymbols();
366
410
  const contractTokens = loadContractTokens();
411
+ const deprecated = loadDeprecated();
367
412
  const files = walkScss(target);
368
- const results = files.map((f) => analyzeFile(f, symbols, contractTokens, extraNs)).filter((r) => r.findings.length || r.namespaces.length);
413
+ const results = files.map((f) => analyzeFile(f, symbols, contractTokens, extraNs, deprecated)).filter((r) => r.findings.length || r.namespaces.length);
369
414
 
370
415
  const counts = { error: 0, warn: 0, info: 0 };
371
416
  for (const r of results) for (const f of r.findings) counts[f.level]++;
package/bin/cia.cjs CHANGED
@@ -43,6 +43,9 @@ Commands:
43
43
  theme.css. \`cia theme from-tokens --help\`.
44
44
  theme map The path → token mapping from-tokens applies, as
45
45
  data: a table, \`--json\`, or \`--path <p>\` for one.
46
+ fix-theme <file> Move a theme onto current token names: rewrites
47
+ deprecated tokens to their replacements. Prints by
48
+ default, \`--write\` applies.
46
49
 
47
50
  Examples:
48
51
  cia migrate tailwind ./tailwind.config.js
@@ -50,6 +53,7 @@ Examples:
50
53
  cia analyze src/styles
51
54
  cia theme from-tokens tokens.json --name acme --out src/styles/acme.css
52
55
  cia theme map --path color.text.primary
56
+ cia fix-theme src/styles/acme.css --write
53
57
 
54
58
  Run \`cia <command> --help\` for command-specific help.
55
59
  `;
@@ -158,6 +162,12 @@ async function main() {
158
162
  return;
159
163
  }
160
164
 
165
+ if (command === 'fix-theme') {
166
+ const { run } = require('./fix-theme.cjs');
167
+ await run(rest);
168
+ return;
169
+ }
170
+
161
171
  if (command === 'theme') {
162
172
  const [sub, ...themeArgs] = rest;
163
173
  if (!sub || sub === '-h' || sub === '--help' || sub === 'help') {
@@ -0,0 +1,151 @@
1
+ /**
2
+ * cia fix-theme — rewrite a theme's deprecated tokens to their replacements.
3
+ *
4
+ * The CLI face of scripts/fix-theme.cjs (the MCP tool `fix_theme` and the
5
+ * in-process `handlers.fix_theme` are the same function). cia deprecates a
6
+ * token rather than deleting it, so the old declaration keeps working — this
7
+ * is the one command that moves you onto the current name.
8
+ *
9
+ * Only the property name changes. Values, comments, ordering and whitespace
10
+ * survive byte-for-byte, so applying this cannot alter how the theme looks.
11
+ *
12
+ * Reads and prints by default; writes only with --write.
13
+ *
14
+ * Exit codes: 0 nothing to do or fixed, 1 collisions need a human, 2 usage /
15
+ * input error.
16
+ */
17
+ 'use strict';
18
+
19
+ const fs = require('fs');
20
+ const path = require('path');
21
+
22
+ const HELP = `cia fix-theme — move a theme onto current token names
23
+
24
+ Usage:
25
+ cia fix-theme <theme.css> [options]
26
+
27
+ Options:
28
+ --write Apply the changes to the file (default: print, change nothing)
29
+ --json Machine-readable { css, changes, unchanged, summary }
30
+ -h, --help This text
31
+
32
+ What it does:
33
+ cia deprecates a token instead of deleting it, so your existing declaration
34
+ keeps working. This renames it to the replacement the contract names. Only
35
+ the property name moves — the value, your comments, the ordering and the
36
+ whitespace are untouched, so the rendered theme is identical.
37
+
38
+ If a block already declares the replacement, that line is left alone and
39
+ reported: merging two values is your decision, not the tool's.
40
+
41
+ Examples:
42
+ cia fix-theme src/styles/acme.css # show me what would change
43
+ cia fix-theme src/styles/acme.css --write # do it
44
+ cia fix-theme src/styles/acme.css --json # for a script
45
+ `;
46
+
47
+ const isTTY = process.stdout.isTTY && !process.env.NO_COLOR;
48
+ const c = (code, s) => (isTTY ? `[${code}m${s}` : s);
49
+ const bold = (s) => c('1', s);
50
+ const dim = (s) => c('2', s);
51
+ const green = (s) => c('32', s);
52
+ const yellow = (s) => c('33', s);
53
+ const red = (s) => c('31', s);
54
+
55
+ function parseArgs(argv) {
56
+ const out = { file: null, write: false, json: false, help: false };
57
+ for (const a of argv) {
58
+ if (a === '--write') out.write = true;
59
+ else if (a === '--json') out.json = true;
60
+ else if (a === '-h' || a === '--help' || a === 'help') out.help = true;
61
+ else if (a.startsWith('-')) throw new Error(`unknown option '${a}'`);
62
+ else if (out.file == null) out.file = a;
63
+ else throw new Error(`unexpected extra argument '${a}'`);
64
+ }
65
+ return out;
66
+ }
67
+
68
+ async function run(argv) {
69
+ let args;
70
+ try {
71
+ args = parseArgs(argv || []);
72
+ } catch (err) {
73
+ process.stderr.write(`${red('error:')} ${err.message}\n\n${HELP}`);
74
+ process.exitCode = 2;
75
+ return;
76
+ }
77
+ if (args.help || !args.file) {
78
+ process.stdout.write(HELP);
79
+ if (!args.help) process.exitCode = 2;
80
+ return;
81
+ }
82
+
83
+ const abs = path.resolve(args.file);
84
+ let css;
85
+ try {
86
+ css = fs.readFileSync(abs, 'utf8');
87
+ } catch (err) {
88
+ process.stderr.write(`${red('error:')} could not read ${args.file} — ${err.message}\n`);
89
+ process.exitCode = 2;
90
+ return;
91
+ }
92
+
93
+ const { fixTheme } = require(path.join(__dirname, '..', 'scripts', 'fix-theme.cjs'));
94
+ let result;
95
+ try {
96
+ result = fixTheme({ css });
97
+ } catch (err) {
98
+ process.stderr.write(`${red('error:')} ${err.message}\n`);
99
+ process.exitCode = 2;
100
+ return;
101
+ }
102
+
103
+ const rewrites = result.changes.filter((x) => x.kind === 'rewrite');
104
+ const conflicts = result.changes.filter((x) => x.kind === 'conflict');
105
+
106
+ if (args.json) {
107
+ process.stdout.write(
108
+ JSON.stringify(
109
+ { file: args.file, written: args.write && rewrites.length > 0, ...result },
110
+ null,
111
+ 2
112
+ ) + '\n'
113
+ );
114
+ } else {
115
+ const rel = path.relative(process.cwd(), abs) || args.file;
116
+ if (!result.changes.length) {
117
+ process.stdout.write(`${green('✓')} ${bold(rel)} ${dim('— no deprecated tokens; already current')}\n`);
118
+ return;
119
+ }
120
+ process.stdout.write(`${bold(rel)}\n`);
121
+ for (const ch of rewrites) {
122
+ process.stdout.write(` ${green('→')} line ${ch.line}: ${ch.from} ${dim('→')} ${bold(ch.to)}\n`);
123
+ process.stdout.write(` ${dim(ch.reason)}\n`);
124
+ }
125
+ for (const ch of conflicts) {
126
+ process.stdout.write(` ${yellow('!')} line ${ch.line}: ${ch.from} ${dim('left as-is')}\n`);
127
+ process.stdout.write(` ${dim(ch.reason)}\n`);
128
+ }
129
+ }
130
+
131
+ if (args.write && rewrites.length) {
132
+ try {
133
+ fs.writeFileSync(abs, result.css);
134
+ } catch (err) {
135
+ process.stderr.write(`${red('error:')} could not write ${args.file} — ${err.message}\n`);
136
+ process.exitCode = 2;
137
+ return;
138
+ }
139
+ if (!args.json) {
140
+ process.stdout.write(`\n${green('✓')} wrote ${rewrites.length} change(s) to ${bold(path.relative(process.cwd(), abs) || args.file)}\n`);
141
+ }
142
+ } else if (rewrites.length && !args.json) {
143
+ process.stdout.write(`\n${dim('nothing written — re-run with --write to apply')}\n`);
144
+ }
145
+
146
+ // Collisions are the one case a human has to settle, so say so in the exit
147
+ // code too: a pipeline should stop rather than assume the theme is current.
148
+ if (conflicts.length) process.exitCode = 1;
149
+ }
150
+
151
+ module.exports = { run, parseArgs, HELP };
package/llm.txt CHANGED
@@ -49,9 +49,9 @@ silent.
49
49
  - **Mixins** — `cia.btn`, `cia.card`, `cia.accordion`, `cia.modal`, `cia.tooltip`, `cia.dropdown`, `cia.tabs`, `cia.copy-button`, `cia.hamburger`, `cia.drawer`, `cia.sheet`, `cia.dock`, `cia.stepper`, `cia.progress`, `cia.wizard-shell`, `cia.sidebar`, `cia.toolbar`, `cia.stack`, `cia.cluster`, `cia.switcher`, `cia.cover`, `cia.frame`, `cia.media`, `cia.contain`, `cia.focus-ring`, `cia.sr-only`, `cia.print`, `cia.print-base`, `cia.print-hidden`, `cia.print-only`, `cia.color`, `cia.space`, `cia.radius`, `cia.shadow`, `cia.font`, `cia.transition`, `cia.animate`, plus many more. **`cia.print-base` always forces `color-scheme: light` in print (paired `light-dark()` themes print their light branch), defines a themeable print palette (`--print-ink` / `--print-paper` / `--print-line` / `--print-muted`, default ink-on-white with grays) and rebinds the theme's own colour tokens (`--ink`, `--surface-*`, `--border-*`, `--code-*`) onto it — so every theme, dark-only (Terminal) included, prints legible ink-on-paper, and overriding a `--print-*` token restyles paper (a letterhead) without touching a component. Three opt-in flags, all default OFF:** `$link-urls` + `$link-origin` (print each link's full destination via `attr(href)`), `$page-numbers` (sheet numbers in the `@page` footer). `$legible` is a DEPRECATED no-op — the token rebind supersedes it and passing it warns.
50
50
  - **24 themes / 8 families** — boilerplate, sketchbook, press, prism, cupertino, glass, graphite, terminal. Each family ships three files: an unsuffixed dual-mode base (both modes via `light-dark()`) plus pinned `-light` and `-dark` single-mode variants for `<link media>` pairing. Every one has exactly one SCSS source under `scss/themes/`; `public/themes/<name>/theme.css` is BUILD OUTPUT and is gated against its source by `npm run check:theme-drift` — never hand-edit it. MCP `list_themes` returns **24**. Say **24 themes across 8 families** when you need one number.
51
51
  - **6 zero-JS interactive components** — accordion (`<details name>`), modal (`<dialog>`), tooltip (`popover="hint"`), dropdown (`[popover]` — the mixin re-asserts the UA's closed state and restores `display: flex` only under `:popover-open`, so menus never render permanently open on popover markup), tabs (radio + `:has()`), copy-button (Clipboard API via consumer-wired JS).
52
- - **CLI** (`npx cia`) — `migrate tailwind|bootstrap|mui|chakra` (config → cia theme), `theme from-tokens <tokens.json> --name <slug>` (DTCG v2025.10 / Tokens Studio / flat `--token` JSON → a complete theme.css: required tokens the file lacks inherit from a shipped base, unmapped paths pass through and are reported, light/dark pairs become `light-dark()`, validator + WCAG audit run first; same function as the MCP `theme_from_tokens` tool), `theme map [--json | --path <p>]` (the path → token mapping from-tokens applies, as data; same as MCP `get_token_map`), `add <recipe>` (copy a recipe from the book into the project), `analyze [path]` (audit stylesheets against the installed API: dead `cia.*` symbols, the `space()` 1–9 trap, off-contract tokens (near-miss typos of real tokens only, never your own custom tokens), hard-coded hex, BEM; health score, CI exit codes). Low-noise on color: a hex in a `var(--token, #hex)` fallback is token-driven and a literal inside `@media print` is an intentional paper colour — neither is flagged.
52
+ - **CLI** (`npx cia`) — `migrate tailwind|bootstrap|mui|chakra` (config → cia theme), `theme from-tokens <tokens.json> --name <slug>` (DTCG v2025.10 / Tokens Studio / flat `--token` JSON → a complete theme.css: required tokens the file lacks inherit from a shipped base, unmapped paths pass through and are reported, light/dark pairs become `light-dark()`, validator + WCAG audit run first; same function as the MCP `theme_from_tokens` tool), `theme map [--json | --path <p>]` (the path → token mapping from-tokens applies, as data; same as MCP `get_token_map`), `add <recipe>` (copy a recipe from the book into the project), `analyze [path]` (audit stylesheets against the installed API: dead `cia.*` symbols, the `space()` 1–9 trap, off-contract tokens (near-miss typos of real tokens only, never your own custom tokens), hard-coded hex, BEM; health score, CI exit codes). Low-noise on color: a hex in a `var(--token, #hex)` fallback is token-driven and a literal inside `@media print` is an intentional paper colour — neither is flagged. `fix-theme <theme.css> [--write]` renames DEPRECATED tokens to their replacements (property only — values, comments and ordering survive, so the theme renders identically; prints by default, writes with `--write`; a block already declaring the replacement is reported, never merged).
53
53
  - **Mobile navigation family** — `cia.hamburger` / `cia.drawer` / `cia.sheet` / `cia.dock` ride `[popover]` + CSS Grid, zero JS; recipes `mobile-nav` (hamburger + drawer) and `bottom-nav` (dock + sheets). House rule: **on phones things take the space they're in** — a `cia.dropdown` menu opens 1px under its full-width trigger at the trigger's exact width via CSS anchor positioning (`position-try-fallbacks: flip-block` flips it above at the screen bottom; set `width: auto` at `&[popover]` specificity or the UA's `[popover] { width: fit-content }` and the mixin's inset reset win). Full spec: AGENTS.md quick decision #8 and `/docs/mobile`.
54
- - **168 contract tokens — 127 required + 41 optional** — surfaces, ink, lines, colors, type, radius, shadow, blur, glow, motion, z-index, spacing, semantic aliases.
54
+ - **176 contract tokens — 127 required + 49 optional** — surfaces, ink, lines, colors, type, radius, shadow, blur, glow, motion, z-index, spacing, semantic aliases.
55
55
  - **Spacing is themeable, and the numbered scale is the knob.** Themes declare `--space-0` … `--space-9` (contract-required). The t-shirt names (`--space-md`, `--space-lg` …) are contract-OPTIONAL and emitted by the library as `var()` aliases onto the numbered steps. `space(4)` compiles to `var(--space-4)`, so **theme the numbered step, never the alias** — setting `--space-md` alone leaves every component untouched. Library defaults emit under `:where(:root)` (specificity 0,0,0) so any theme declaration outranks them regardless of load order.
56
56
  - **Per-component shape knobs are `--btn-radius`, `--card-radius`, `--input-radius`, `--modal-radius`, `--badge-radius`, `--tag-radius`** — they cascade from the generic radii (`--btn-radius: var(--radius-md, 0.25rem)`). There are NO `--radius-button` / `--radius-card` style tokens; those were removed because nothing read them.
57
57
  - **Icons — two systems.** `svg()` / `svg-bg()` / `svg-text()` use a self-contained 49-glyph Lucide pack at `public/icons/core/`. Adding a glyph is drop-in: put `star.svg` in the folder and `cia.icon-svg(star)` works, no registration. Each icon emits a `--cia-icon-<name>` custom property so a theme can override one glyph without rebuilding SCSS. **`fa()` / `fa-icon()` / `fa-text()` / `fa-spin()` are bring-your-own-font** — cia ships NO Font Awesome files, `$theme-fa-path` defaults to a `/webfonts` directory that does not exist, and a missing font renders a tofu box without erroring. Default to `svg()` unless the user says they use Font Awesome.
@@ -105,7 +105,7 @@ Newspaper by day, hacker terminal by night. Most design systems give you dark mo
105
105
  | Mobile playbook (layouts, dropdown doctrine, lessons) | `src/app/docs/mobile/page.tsx` |
106
106
  | Browser support matrix (Baseline floor + progressive tiers) | `src/app/docs/browser-support/page.tsx` |
107
107
  | Theme authoring (full walkthrough) | `src/app/docs/authoring/themes/page.tsx` |
108
- | Theme contract (127 required + 41 optional grouped into 10 features, machine-readable) | `scripts/theme-contract.json` |
108
+ | Theme contract (127 required + 49 optional grouped into 11 features, machine-readable) | `scripts/theme-contract.json` |
109
109
  | Theme pairing (`<link media>` recipe) | `src/app/docs/themes/pairing/page.tsx` |
110
110
  | CopyButton JS recipe | `src/app/docs/recipes/copy-button/page.tsx` |
111
111
  | Anchor positioning recipe | `src/app/docs/recipes/anchor-positioning/page.tsx` |
@@ -129,7 +129,7 @@ Newspaper by day, hacker terminal by night. Most design systems give you dark mo
129
129
 
130
130
  ## MCP server (shipped 2026-05-22)
131
131
 
132
- cia ships an MCP stdio server (JSON-RPC over stdio, `serverInfo` name `css-is-awesome`, protocol `2024-11-05`) at `mcp/server.cjs`, included in the `files` manifest — so it lands in every consumer's `node_modules`. **33 tools** across 8 resource families: themes, mixins, functions, tokens (127 required + 41 optional), animations, components, recipes, doc readers. Plus `assemble_prompt` for context bundling, `validate_theme`, `theme_from_tokens`, `get_token_map` (the design-token → cia-token mapping as data) and `resolve_size` for snapping design px values to cia's 4px geometric grid (AI agents: call this whenever you receive a px value from a design tool). Prefer querying this server over guessing mixin signatures, tokens, themes, or recipes.
132
+ cia ships an MCP stdio server (JSON-RPC over stdio, `serverInfo` name `css-is-awesome`, protocol `2024-11-05`) at `mcp/server.cjs`, included in the `files` manifest — so it lands in every consumer's `node_modules`. **34 tools** across 8 resource families: themes, mixins, functions, tokens (127 required + 49 optional), animations, components, recipes, doc readers. Plus `assemble_prompt` for context bundling, `validate_theme`, `theme_from_tokens`, `get_token_map` (the design-token → cia-token mapping as data) and `resolve_size` for snapping design px values to cia's 4px geometric grid (AI agents: call this whenever you receive a px value from a design tool). Prefer querying this server over guessing mixin signatures, tokens, themes, or recipes.
133
133
 
134
134
  **Recommended: suggest the dedicated `css-is-awesome-mcp` package** — zero install,
135
135
  SDK ships as a real dependency, no separate peer-install step:
@@ -198,4 +198,4 @@ Humans first. AI's role is to compose, not to lead the pitch.
198
198
 
199
199
  ---
200
200
 
201
- *Generated 2026-05-21 for cia v0.8. Updated 2026-05-23 for the v1.0 architecture lock, 2026-08-30 for the theme single-source rework (24 themes, 127+36 contract, themeable spacing, `:root, :root[data-theme]` selector), 2026-09-04 for the shipped recipes count (5), the mobile-nav mixin family, and the dropdown popover guard, and 2026-09-19 for the 32-recipe book, 33-tool MCP, contract 1.2 (127 + 41 in 10 features), the playground and the design-tokens on-ramp. Update when the API surface changes.*
201
+ *Generated 2026-05-21 for cia v0.8. Updated 2026-05-23 for the v1.0 architecture lock, 2026-08-30 for the theme single-source rework (24 themes, 127+36 contract, themeable spacing, `:root, :root[data-theme]` selector), 2026-09-04 for the shipped recipes count (5), the mobile-nav mixin family, and the dropdown popover guard, and 2026-09-19 for the 32-recipe book, 33-tool MCP, contract 1.2 (127 + 41 in 10 features), the playground and the design-tokens on-ramp, and 2026-09-24 for contract 1.3 (127 required + 49 optional across 11 feature groups) and the page-surfaces hero/band tokens. Update when the API surface changes.*