css-is-awesome 1.11.1 → 1.12.1

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 (95) hide show
  1. package/AGENTS.md +16 -3
  2. package/CHANGELOG.md +41 -0
  3. package/CONTRACT.md +15 -0
  4. package/README.md +36 -28
  5. package/THEMING.md +1 -0
  6. package/bin/README.md +4 -4
  7. package/bin/analyze.cjs +88 -1
  8. package/bin/cia.cjs +30 -5
  9. package/bin/migrate-chakra.cjs +249 -0
  10. package/bin/migrate-mui.cjs +363 -0
  11. package/bin/migrate-tailwind.cjs +3 -1
  12. package/dist/css-is-awesome.css +516 -0
  13. package/dist/css-is-awesome.min.css +1 -1
  14. package/dist/css-is-awesome.utilities.css +516 -0
  15. package/dist/css-is-awesome.utilities.min.css +1 -1
  16. package/dist/tokens.d.ts +4 -2
  17. package/llm.txt +20 -10
  18. package/mcp/server.cjs +7 -2
  19. package/package.json +7 -4
  20. package/public/theme.css +293 -237
  21. package/public/themes/boilerplate/theme.css +11 -10
  22. package/public/themes/boilerplate-dark/theme.css +10 -9
  23. package/public/themes/boilerplate-light/theme.css +10 -9
  24. package/public/themes/cupertino/theme.css +11 -10
  25. package/public/themes/cupertino-dark/theme.css +11 -10
  26. package/public/themes/cupertino-light/theme.css +11 -10
  27. package/public/themes/glass/theme.css +11 -10
  28. package/public/themes/glass-dark/theme.css +11 -10
  29. package/public/themes/glass-light/theme.css +11 -10
  30. package/public/themes/graphite/theme.css +11 -10
  31. package/public/themes/graphite-dark/theme.css +11 -10
  32. package/public/themes/graphite-light/theme.css +11 -10
  33. package/public/themes/press/theme.css +11 -10
  34. package/public/themes/press-dark/theme.css +11 -10
  35. package/public/themes/press-light/theme.css +11 -10
  36. package/public/themes/prism/theme.css +11 -10
  37. package/public/themes/prism-dark/theme.css +11 -10
  38. package/public/themes/prism-light/theme.css +11 -10
  39. package/public/themes/sketchbook/theme.css +11 -10
  40. package/public/themes/sketchbook-dark/theme.css +11 -10
  41. package/public/themes/sketchbook-light/theme.css +10 -9
  42. package/public/themes/terminal/theme.css +11 -10
  43. package/public/themes/terminal-dark/theme.css +11 -10
  44. package/public/themes/terminal-light/theme.css +11 -10
  45. package/scripts/theme-a11y.js +11 -31
  46. package/scripts/theme-contract.json +1 -0
  47. package/scss/_spacing-scale.scss +25 -0
  48. package/scss/_utilities.scss +44 -9
  49. package/scss/components/_accordion.scss +1 -1
  50. package/scss/components/_data.scss +2 -2
  51. package/scss/components/_feedback.scss +1 -1
  52. package/scss/components/_forms.scss +16 -2
  53. package/scss/components/_navigation.scss +1 -1
  54. package/scss/examples/_usage.scss +3 -3
  55. package/scss/recipes/admin-dashboard-layout.md +277 -0
  56. package/scss/recipes/app-shell.md +339 -0
  57. package/scss/recipes/auth-flow.md +345 -0
  58. package/scss/recipes/confirm-dialog.md +248 -0
  59. package/scss/recipes/data-table.md +382 -0
  60. package/scss/recipes/datepicker.md +428 -0
  61. package/scss/recipes/form-validation-async.md +340 -0
  62. package/scss/recipes/form-validation-html5.md +306 -0
  63. package/scss/recipes/form-validation-react-hook-form.md +224 -0
  64. package/scss/recipes/form-validation-success-states.md +290 -0
  65. package/scss/recipes/form-validation-zod.md +270 -0
  66. package/scss/recipes/i18n-date-formatting.md +172 -0
  67. package/scss/recipes/i18n-number-currency.md +145 -0
  68. package/scss/recipes/i18n-pluralization.md +163 -0
  69. package/scss/recipes/multi-step-wizard.md +365 -0
  70. package/scss/recipes/otp-input.md +310 -0
  71. package/scss/recipes/rtl-layout.md +273 -0
  72. package/scss/themes/boilerplate-dark.scss +2 -10
  73. package/scss/themes/boilerplate-light.scss +2 -10
  74. package/scss/themes/boilerplate.scss +2 -10
  75. package/scss/themes/cupertino-dark.scss +2 -10
  76. package/scss/themes/cupertino-light.scss +2 -10
  77. package/scss/themes/cupertino.scss +2 -10
  78. package/scss/themes/glass-dark.scss +2 -10
  79. package/scss/themes/glass-light.scss +2 -10
  80. package/scss/themes/glass.scss +2 -10
  81. package/scss/themes/graphite-dark.scss +2 -10
  82. package/scss/themes/graphite-light.scss +2 -10
  83. package/scss/themes/graphite.scss +2 -10
  84. package/scss/themes/press-dark.scss +2 -10
  85. package/scss/themes/press-light.scss +2 -10
  86. package/scss/themes/press.scss +2 -10
  87. package/scss/themes/prism-dark.scss +2 -10
  88. package/scss/themes/prism-light.scss +2 -10
  89. package/scss/themes/prism.scss +2 -10
  90. package/scss/themes/sketchbook-dark.scss +2 -10
  91. package/scss/themes/sketchbook-light.scss +2 -10
  92. package/scss/themes/sketchbook.scss +2 -10
  93. package/scss/themes/terminal-dark.scss +2 -10
  94. package/scss/themes/terminal-light.scss +2 -10
  95. package/scss/themes/terminal.scss +2 -10
package/AGENTS.md CHANGED
@@ -308,9 +308,22 @@ Inside this package (all whitelisted in `files`):
308
308
 
309
309
  ## MCP server (SHIPPED — use it)
310
310
 
311
- cia ships a Model Context Protocol stdio server (JSON-RPC over stdio, `serverInfo` name `css-is-awesome` (version read from package.json), protocol `2024-11-05`) at `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`. **Prefer querying it over guessing** — it returns cia's real mixin signatures, tokens, themes, and recipes.
311
+ cia ships a Model Context Protocol stdio server (JSON-RPC over stdio, `serverInfo` name `css-is-awesome`, protocol `2024-11-05`) at `mcp/server.cjs`. It's in the `files` manifest, so it lands in every consumer's `node_modules`. **Prefer querying it over guessing** — it returns cia's real mixin signatures, tokens, themes, and recipes.
312
312
 
313
- Wire it into your MCP client's `.mcp.json`:
313
+ Two ways to run it — prefer the dedicated `npx css-is-awesome-mcp` package (zero install, SDK is a real dependency, no separate peer-install step):
314
+
315
+ ```json
316
+ {
317
+ "mcpServers": {
318
+ "css-is-awesome": {
319
+ "command": "npx",
320
+ "args": ["css-is-awesome-mcp"]
321
+ }
322
+ }
323
+ }
324
+ ```
325
+
326
+ Or run this in-repo copy directly — needs its SDK peer deps installed manually first (`npm install -D @modelcontextprotocol/sdk zod` in the client project, since they're optional peers):
314
327
 
315
328
  ```json
316
329
  {
@@ -323,7 +336,7 @@ Wire it into your MCP client's `.mcp.json`:
323
336
  }
324
337
  ```
325
338
 
326
- The SDK is an optional peer dep — `npm install -D @modelcontextprotocol/sdk zod` in the client project to run it. It exposes **30 tools** across 8 families:
339
+ Either way it exposes **30 tools** across 8 families:
327
340
 
328
341
  - **Themes** — `list_themes`, `get_theme`, `search_themes`
329
342
  - **Mixins** — `list_mixins`, `get_mixin`, `search_mixins` (real signatures — don't guess)
package/CHANGELOG.md CHANGED
@@ -1,3 +1,44 @@
1
+ ## [1.12.1](https://github.com/Jerry2d3d/css-is-awesome/compare/v1.12.0...v1.12.1) (2026-09-11)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * **mcp:** remove colliding css-is-awesome-mcp bin entry, point docs at the new package ([f2ac41c](https://github.com/Jerry2d3d/css-is-awesome/commit/f2ac41c4d6056a1d13c19c5dcb48386acfb0fcda))
7
+
8
+ # [1.12.0](https://github.com/Jerry2d3d/css-is-awesome/compare/v1.11.1...v1.12.0) (2026-09-11)
9
+
10
+
11
+ ### Bug Fixes
12
+
13
+ * **a11y:** unlabeled checklist checkboxes and non-focusable code blocks ([d98a92c](https://github.com/Jerry2d3d/css-is-awesome/commit/d98a92c549a2b74c3cff976d0fdfd61031162585))
14
+ * **ci:** raise utilities size budget to 5.0 KB, correct stale bundle-size docs ([0e447f1](https://github.com/Jerry2d3d/css-is-awesome/commit/0e447f16e33d5a39cbcedb3b44caab79e4920f23))
15
+ * **theme-editor:** density slider + fix override specificity bug affecting every row ([3977043](https://github.com/Jerry2d3d/css-is-awesome/commit/39770434e5f1819b57c16e502de15e252c5cf0d5))
16
+ * **theme:** recurse into at-rules when bundling themes ([ca6397b](https://github.com/Jerry2d3d/css-is-awesome/commit/ca6397bcdcbcfc6bcb784253855b0af74d94ae74))
17
+
18
+
19
+ ### Features
20
+
21
+ * **cli:** npx cia migrate chakra — Chakra UI theme object -> cia theme.scss ([2bcc95c](https://github.com/Jerry2d3d/css-is-awesome/commit/2bcc95c32058b2b1f60378e6718b772363fd77a2))
22
+ * **cli:** npx cia migrate mui — MUI theme object -> cia theme.scss ([4f1d682](https://github.com/Jerry2d3d/css-is-awesome/commit/4f1d6828fc13fa1929f78127ab7c94503f55afb0))
23
+ * **cli:** off-scale-length and missing-focus-visible analyzer rules ([d7bb8d9](https://github.com/Jerry2d3d/css-is-awesome/commit/d7bb8d96598b9897f130a152ad024ccc13a0cbbb))
24
+ * **editor:** print preview persists per theme and seeds from overrides ([404e494](https://github.com/Jerry2d3d/css-is-awesome/commit/404e494051b9d3893c8ab18762da8044a389f5fd))
25
+ * **editor:** token-consumer map, used-by list, live contrast readout ([a52f74e](https://github.com/Jerry2d3d/css-is-awesome/commit/a52f74e968f340d1c9f6b2ed8a02b8f16457432b))
26
+ * **recipes:** admin-dashboard-layout recipe + live demo ([6e79812](https://github.com/Jerry2d3d/css-is-awesome/commit/6e79812980abbd90391507bd06db06b65820c1e2))
27
+ * **recipes:** app-shell recipe - navbar + control panel + footer + modal ([a5abd6e](https://github.com/Jerry2d3d/css-is-awesome/commit/a5abd6e68287a1d99978e8956d601c170cbff657))
28
+ * **recipes:** auth-flow recipe + live demo ([7e566e2](https://github.com/Jerry2d3d/css-is-awesome/commit/7e566e274fd84be8eab0ae877e3c222a1adc8c1e))
29
+ * **recipes:** confirm-dialog recipe + live demo ([24977c2](https://github.com/Jerry2d3d/css-is-awesome/commit/24977c2214efc5588d90c601da70c2db1ed3b1f5))
30
+ * **recipes:** data-table recipe + live demo ([cbcdbb3](https://github.com/Jerry2d3d/css-is-awesome/commit/cbcdbb384f2d44f25071d593391d0cb4a6bd5d0d))
31
+ * **recipes:** datepicker recipe + live demo ([90ea515](https://github.com/Jerry2d3d/css-is-awesome/commit/90ea515f8c2ab044a4957be642295d188cf60cb3))
32
+ * **recipes:** form validation recipes + live demos ([78ae02d](https://github.com/Jerry2d3d/css-is-awesome/commit/78ae02d4c29458d60feb90f0135faaedd7684b26))
33
+ * **recipes:** i18n-date-formatting recipe + live demo ([689152a](https://github.com/Jerry2d3d/css-is-awesome/commit/689152a4ebb55b5c19285bfcd6f4c81f1a090b1d))
34
+ * **recipes:** i18n-number-currency recipe + live demo ([a4ec9a3](https://github.com/Jerry2d3d/css-is-awesome/commit/a4ec9a35b80d9a4d6fb8f5333107694ef9e5ccf3))
35
+ * **recipes:** i18n-pluralization recipe + live demo ([fe6a3f0](https://github.com/Jerry2d3d/css-is-awesome/commit/fe6a3f0272de3d848b5306800e3ecbca84d09e1c))
36
+ * **recipes:** multi-step-wizard recipe + live demo ([932358e](https://github.com/Jerry2d3d/css-is-awesome/commit/932358e54d0550ecf7017d48266f833646634d58))
37
+ * **recipes:** otp-input recipe + live demo ([af99b86](https://github.com/Jerry2d3d/css-is-awesome/commit/af99b86ab08f8357e908bd19533ea3bd55eab548))
38
+ * **rtl:** audit + fix non-logical CSS, add a logical utility set ([35704fd](https://github.com/Jerry2d3d/css-is-awesome/commit/35704fda8941c56eea16070813845d15295f897f))
39
+ * **rtl:** recipe + /docs/rtl live demo ([b18bc04](https://github.com/Jerry2d3d/css-is-awesome/commit/b18bc04910b9db02c0091ccaa4e93ab551d13326))
40
+ * **themes:** --space-unit density knob — one var drives all 9 spacing steps ([a1638c7](https://github.com/Jerry2d3d/css-is-awesome/commit/a1638c72338309055f752e6940220a9a8cd6be99))
41
+
1
42
  ## [1.11.1](https://github.com/Jerry2d3d/css-is-awesome/compare/v1.11.0...v1.11.1) (2026-09-09)
2
43
 
3
44
 
package/CONTRACT.md CHANGED
@@ -305,6 +305,21 @@ Paper themes declare these as `none` / `transparent` so a swap to a glass or pho
305
305
 
306
306
  ---
307
307
 
308
+ ## Print (optional)
309
+
310
+ Four tokens describing the printed page. They're **optional** — `cia.print-base` (included once, at the stylesheet root) emits every one of them on `:root` inside `@media print` with a clean ink-on-white default, and every print rule reads them via `var(--print-*)`. A theme MAY override them in its own `@media print` block for a paper identity (Press does, for a newsprint look); a theme that sets none of them prints the plain default.
311
+
312
+ | Token | Type | Default | Purpose |
313
+ | --------------- | ------ | --------- | ------------------------------------- |
314
+ | `--print-ink` | color | `#000` | Body text, links, code text on paper |
315
+ | `--print-paper` | color | `#fff` | Backgrounds on paper |
316
+ | `--print-line` | color | `#999` | Borders, rules, hairlines on paper |
317
+ | `--print-muted` | color | `#666` | Printed URLs, captions, secondary text |
318
+
319
+ `print-base` also rebinds the theme's own colour tokens (`--ink`, `--surface-*`, `--border-*`, `--code-*`) onto this palette inside `@media print`, so every theme — dark-only ones included — prints legible ink-on-paper with no per-theme work required. See [`/docs/print`](https://cssisawesome.com/docs/print) and the [`letterhead` recipe](./scss/recipes/letterhead.md).
320
+
321
+ ---
322
+
308
323
  ## Component overrides (optional)
309
324
 
310
325
  These are per-component tokens a theme MAY override to change how a single family of components renders (buttons, cards, inputs, etc.) without touching the library or rebuilding SCSS. They are **optional** — the library emits every one of them on `:root` with a sensible default, and every component mixin reads them via `var(--<key>, <library-default>)`. A theme that sets none of them renders exactly the same as today.
package/README.md CHANGED
@@ -8,7 +8,7 @@
8
8
 
9
9
  **Docs:** [cssisawesome.com](https://cssisawesome.com/) · **Install:** `npm install css-is-awesome`
10
10
 
11
- > **The recipes book:** build any component in any framework using cia mixins — seven recipes today (`dialog`, `combobox`, `print-to-pdf`, `print-spec`, `letterhead`, `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/).
11
+ > **The recipes book:** build any component in any framework using cia mixins — 16 recipes today, including `dialog`, `combobox`, `datepicker`, `data-table`, `app-shell`, five form-validation patterns (HTML5, react-hook-form, Zod, async, success-states), `rtl-layout`, `print-to-pdf`, `print-spec`, `letterhead`, `mobile-nav` and `bottom-nav` — with `command-palette` and five more (`confirm-dialog`, `auth-flow`, `multi-step-wizard`, `otp-input`, `admin-dashboard-layout`) 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
 
@@ -31,7 +31,7 @@ npm install -D @modelcontextprotocol/sdk zod # required — npm will NOT insta
31
31
  }
32
32
  ```
33
33
 
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.
34
+ The SDK and `zod` are declared as *optional* peer dependencies, so a plain `npm install css-is-awesome` skips them and this in-repo copy exits with `@modelcontextprotocol/sdk is not installed`. Install both — or skip this entirely and run `npx css-is-awesome-mcp` instead, the dedicated zero-install package (see the MCP section below), which ships the SDK as a real dependency.
35
35
 
36
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
 
@@ -228,10 +228,11 @@ npx cia add --list # browse the recipe book
228
228
  npx cia add bottom-nav # copy a recipe into your project — you own the pattern
229
229
  npx cia analyze src/styles # design-system health: dead cia.* symbols, the
230
230
  # space() scale trap, off-contract tokens (typos),
231
- # hard-coded colors, BEM creep
231
+ # off-scale lengths, hard-coded colors, BEM creep,
232
+ # missing focus-visible styling
232
233
  ```
233
234
 
234
- `cia analyze` reads the real API surface from the installed package and exits non-zero on errors, so it slots straight into CI. It's deliberately low-noise about color: a hex used as a `var(--token, #hex)` fallback is token-driven (not flagged), and a literal inside `@media print` is an intentional paper colour (print escapes theme colours by design). Off-contract-token findings only fire on a **near-miss** of a real token — a typo like `--inkk` — never on your own custom tokens. The default output is a **graded report** — a health score, a section per concern (Contract / Spacing / Color / Naming / Layout / API) with a `✓` when clean, and a suggested fix on each finding; add `--verbose` for the flat per-file list or `--json` for the machine shape.
235
+ `cia analyze` reads the real API surface from the installed package and exits non-zero on errors, so it slots straight into CI. It's deliberately low-noise about color: a hex used as a `var(--token, #hex)` fallback is token-driven (not flagged), and a literal inside `@media print` is an intentional paper colour (print escapes theme colours by design). Off-contract-token findings only fire on a **near-miss** of a real token — a typo like `--inkk` — never on your own custom tokens. Off-scale-length findings suggest the nearest named step (e.g. `--radius-md`) for a literal `border-radius`/`padding`/`margin`/`gap` value, as a hint toward using a token — never a claim about your active theme's exact pixel value, since themes are free to set their own numbers (Terminal sets every `--radius-*` to `0`, deliberately). The default output is a **graded report** — a health score, a section per concern (Contract / Spacing / Color / Naming / Layout / API / Accessibility) with a `✓` when clean, and a suggested fix on each finding; add `--verbose` for the flat per-file list or `--json` for the machine shape. Full rule reference: [`/docs/analyzer`](https://cssisawesome.com/docs/analyzer/).
235
236
 
236
237
  ## Print / PDF (zero JS)
237
238
 
@@ -297,30 +298,37 @@ The scope is kept narrow: 8 `!important` declarations, all inside `@media print`
297
298
 
298
299
  ## MCP server (for AI agents)
299
300
 
300
- 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/).
301
+ cia ships a Model Context Protocol stdio server (JSON-RPC over stdio, protocol `2024-11-05`) exposing **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). 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/).
301
302
 
302
- **Setup is two steps — do both, or the server won't start.**
303
+ **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.
303
304
 
304
- 1. Install the SDK peer deps. The MCP SDK needs `@modelcontextprotocol/sdk` + `zod`; they're declared as *optional* peers so npm skips them by default. Without them the server exits and your MCP client shows only a generic "failed to connect":
305
-
306
- ```bash
307
- npm install -D @modelcontextprotocol/sdk zod
308
- ```
305
+ ```json
306
+ {
307
+ "mcpServers": {
308
+ "css-is-awesome": {
309
+ "command": "npx",
310
+ "args": ["css-is-awesome-mcp"]
311
+ }
312
+ }
313
+ }
314
+ ```
309
315
 
310
- 2. Add to your client's `.mcp.json` (the `npx` form uses the shipped bin and is CWD-independent):
316
+ **Alternative — the copy already in your `node_modules`:** cia's own `files` manifest ships [`mcp/server.cjs`](./mcp/server.cjs) too, for anyone who'd rather not add a second package. This copy needs its SDK peer deps installed manually first, since they're declared as *optional* peers (so a plain `npm install css-is-awesome` doesn't pull JS into a CSS-only install):
311
317
 
312
- ```json
313
- {
314
- "mcpServers": {
315
- "css-is-awesome": {
316
- "command": "npx",
317
- "args": ["css-is-awesome-mcp"]
318
- }
319
- }
320
- }
321
- ```
318
+ ```bash
319
+ npm install -D @modelcontextprotocol/sdk zod
320
+ ```
322
321
 
323
- Equivalent explicit path: `"command": "node", "args": ["node_modules/css-is-awesome/mcp/server.cjs"]`.
322
+ ```json
323
+ {
324
+ "mcpServers": {
325
+ "css-is-awesome": {
326
+ "command": "node",
327
+ "args": ["node_modules/css-is-awesome/mcp/server.cjs"]
328
+ }
329
+ }
330
+ }
331
+ ```
324
332
 
325
333
  ## Docs site
326
334
 
@@ -387,11 +395,11 @@ Full detail: [`/docs/testing`](https://cssisawesome.com/docs/testing/).
387
395
 
388
396
  | Bundle | Size | Use case |
389
397
  |---|---|---|
390
- | `dist/tokens.css` | 2.2 KB | Tokens only (`:where(:root)` CSS variables, no rules) — the purest mixin-first emit |
391
- | `dist/css-is-awesome.core.min.css` | 2.4 KB | Tokens + resets, no utilities or components |
392
- | `dist/css-is-awesome.utilities.min.css` | 4.1 KB | Every `cia-*` utility class, nothing else |
393
- | `dist/css-is-awesome.min.css` | 7.3 KB | Full bundle (everything) |
394
- | Per-theme `themes/<name>/theme.css` | 1.5–3.4 KB | One file per theme, both modes via `light-dark()`, drop-in with no markup change |
398
+ | `dist/tokens.css` | 2.25 KB | Tokens only (`:where(:root)` CSS variables, no rules) — the purest mixin-first emit |
399
+ | `dist/css-is-awesome.core.min.css` | 2.38 KB | Tokens + resets, no utilities or components |
400
+ | `dist/css-is-awesome.utilities.min.css` | 4.75 KB | Every `cia-*` utility class, nothing else |
401
+ | `dist/css-is-awesome.min.css` | 7.96 KB | Full bundle (everything) |
402
+ | 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 |
395
403
  | **JavaScript shipped in package** | **0 KB** | Zero. Period. JS-driven features ship as separate add-on packages. |
396
404
 
397
405
  ## Status
package/THEMING.md CHANGED
@@ -163,6 +163,7 @@ those names were removed because nothing read them.)
163
163
  | `--z-*` | `hide`, `base`, `dropdown`, `sticky`, `fixed`, `backdrop`, `modal`, `popover`, `tooltip`, `toast` |
164
164
  | `--duration-*` | `instant`, `fast`, `normal`, `slow`, `slower` |
165
165
  | `--opacity-*` | `0` through `100` |
166
+ | `--print-*` | `ink`, `paper`, `line`, `muted` — optional, override in your own `@media print` block to restyle paper (defaults to ink-on-white; see [`CONTRACT.md`](./CONTRACT.md#print-optional)) |
166
167
 
167
168
  ---
168
169
 
package/bin/README.md CHANGED
@@ -6,19 +6,19 @@ cia's CLI entry points. **These are the only JavaScript files cia ships** — th
6
6
 
7
7
  | Bin | File | What it does |
8
8
  |---|---|---|
9
- | `css-is-awesome-mcp` | `../mcp/server.cjs` | MCP stdio server for AI agents (themes, mixins, recipes, etc.) |
10
- | `cia` | `./cia.cjs` | The CLI router — `migrate` (tailwind/bootstrap), `add` (recipe registry), `analyze` (design-system health) |
9
+ | `cia` | `./cia.cjs` | The CLI router — `migrate` (tailwind/bootstrap/mui/chakra), `add` (recipe registry), `analyze` (design-system health) |
11
10
 
12
- Invoke either via `npx` from a consumer project that has cia installed:
11
+ Invoke via `npx` from a consumer project that has cia installed:
13
12
 
14
13
  ```bash
15
- npx css-is-awesome-mcp # MCP server (stdio; configure via .mcp.json)
16
14
  npx cia --help # CLI help
17
15
  npx cia migrate tailwind ./tailwind.config.js
18
16
  npx cia add bottom-nav # copy a recipe into the project — own the pattern
19
17
  npx cia analyze src/styles # health check: dead symbols, space() trap, hex, BEM
20
18
  ```
21
19
 
20
+ **MCP server is NOT registered here** — `../mcp/server.cjs` ships in the `files` manifest but deliberately has no `bin` entry. It used to (`css-is-awesome-mcp`), but that collided with the separate [`css-is-awesome-mcp`](https://www.npmjs.com/package/css-is-awesome-mcp) package's own identically-named bin: since that package depends on `css-is-awesome`, npm links both packages' bins into the same `node_modules/.bin/`, and whichever wins is undefined — in practice it silently ran this repo's copy instead of the dedicated package's. Removed here so there's exactly one owner of that command name. Run this in-repo copy directly via `node mcp/server.cjs` (see the README's MCP section for both invocation paths).
21
+
22
22
  ## Subcommand files
23
23
 
24
24
  `cia.cjs` is the router. Each subcommand lives in its own sibling file and is loaded lazily so consumers who only use one path don't pay the require cost of the others.
package/bin/analyze.cjs CHANGED
@@ -54,6 +54,10 @@ Usage:
54
54
  Scans [path] (default: current directory) for *.scss files and audits
55
55
  them against the installed css-is-awesome API.
56
56
 
57
+ Rules: unknown-symbol, space-scale, off-contract-token, off-scale-length,
58
+ hard-coded-color, bem, hand-written-areas, missing-focus-visible.
59
+ Full reference: https://cssisawesome.com/docs/analyzer
60
+
57
61
  Options:
58
62
  --namespace <ns> Extra namespace(s) to treat as cia (comma-separated).
59
63
  Auto-detected per file from @use lines; use this when
@@ -173,6 +177,85 @@ function stripPrintBlocks(src) {
173
177
  return out;
174
178
  }
175
179
 
180
+ // Reference-scale hints for the off-scale-length rule. Sourced from Sketchbook
181
+ // (public/themes/sketchbook/theme.css) — CONTRACT.md's own designated
182
+ // "reference implementation." These are HINTS, not a value guarantee: real
183
+ // themes intentionally diverge (Terminal flattens every --radius-* to 0), so
184
+ // a suggestion names the nearest named STEP, never asserts the consumer's
185
+ // active theme actually holds this exact pixel value.
186
+ const SCALE_HINTS = [
187
+ { token: '--radius-sm', px: 2 },
188
+ { token: '--radius-md', px: 3 },
189
+ { token: '--radius-lg', px: 6 },
190
+ { token: '--radius-xl', px: 12 },
191
+ { token: '--radius-full', px: 9999 },
192
+ { token: '--space-1', px: 8 },
193
+ { token: '--space-2', px: 12 },
194
+ { token: '--space-3', px: 14 },
195
+ { token: '--space-4', px: 16 },
196
+ { token: '--space-5', px: 24 },
197
+ { token: '--space-6', px: 32 },
198
+ { token: '--space-7', px: 48 },
199
+ { token: '--space-8', px: 64 },
200
+ { token: '--space-9', px: 96 },
201
+ ];
202
+ const LENGTH_PROP_RE = /(?:^|[{;\s])(border-radius|padding|margin|gap)\s*:\s*([^;{}]+);/g;
203
+
204
+ // "7px" / "0.75rem" -> px, or null if not a plain single-value length
205
+ // (percentages, calc(), keywords like "auto" are left alone — out of scope).
206
+ function parseLengthPx(raw) {
207
+ const m = /^(-?\d*\.?\d+)(px|rem)$/.exec(raw.trim());
208
+ if (!m) return null;
209
+ return m[2] === 'rem' ? parseFloat(m[1]) * 16 : parseFloat(m[1]);
210
+ }
211
+
212
+ // off-scale-length: a literal border-radius/padding/margin/gap value that's
213
+ // close to a scale step — almost certainly meant to be that token. Values
214
+ // already routed through var() (with or without a literal fallback) were
215
+ // stripped by the caller, so only genuine literals reach here. `0` is never
216
+ // flagged: it's unambiguous and a legitimate theme choice in its own right
217
+ // (Terminal sets every --radius-* to 0) — flagging it would be a guaranteed
218
+ // false positive for exactly that theme's consumers.
219
+ function offScaleLength(varStrippedSrc) {
220
+ const findings = [];
221
+ for (const m of varStrippedSrc.matchAll(LENGTH_PROP_RE)) {
222
+ const prop = m[1];
223
+ for (const tok of m[2].trim().split(/\s+/)) {
224
+ if (tok === '0' || tok === '0px' || tok === '0rem') continue;
225
+ const px = parseLengthPx(tok);
226
+ if (px == null || px === 0) continue;
227
+ let best = null;
228
+ let bestDiff = Infinity;
229
+ for (const hint of SCALE_HINTS) {
230
+ const diff = Math.abs(hint.px - px);
231
+ if (diff < bestDiff) { bestDiff = diff; best = hint; }
232
+ }
233
+ if (best && bestDiff > 0 && bestDiff <= 2) {
234
+ findings.push({ level: 'warn', rule: 'off-scale-length', detail: `${prop}: ${tok} — close to the reference scale's ${best.token}; consider the token instead of a literal (exact px varies by theme)` });
235
+ }
236
+ }
237
+ }
238
+ return findings;
239
+ }
240
+
241
+ // missing-focus-visible: a file styles :hover/:active on something
242
+ // button/link-shaped but never mentions :focus-visible or focus-ring
243
+ // anywhere in the same file — keyboard users likely get no visible
244
+ // feedback. File-level co-occurrence, not selector-pairing: SCSS commonly
245
+ // nests hover/active under `&` while focus-ring is set once for the whole
246
+ // component elsewhere in the file (see scss/components/_buttons.scss),
247
+ // so per-selector adjacency would false-positive on that exact shape.
248
+ const INTERACTIVE_SHAPE_RE = /(?:^|[^\w-])(?:button|a)(?:[.:#[\s{,]|$)|\[role\s*=\s*["']?button["']?\]|\.[\w-]*btn[\w-]*/i;
249
+ const HOVER_ACTIVE_RE = /:(?:hover|active)\b/;
250
+ const FOCUS_VISIBLE_RE = /:focus-visible\b|focus-ring/i;
251
+
252
+ function missingFocusVisible(strippedSrc) {
253
+ if (!HOVER_ACTIVE_RE.test(strippedSrc)) return [];
254
+ if (!INTERACTIVE_SHAPE_RE.test(strippedSrc)) return [];
255
+ if (FOCUS_VISIBLE_RE.test(strippedSrc)) return [];
256
+ 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
+ }
258
+
176
259
  function analyzeFile(file, symbols, contractTokens, extraNs) {
177
260
  const raw = fs.readFileSync(file, 'utf8').replace(/\r\n/g, '\n');
178
261
  const src = stripComments(raw);
@@ -222,6 +305,8 @@ function analyzeFile(file, symbols, contractTokens, extraNs) {
222
305
  for (const m of stripVarExpr(stripPrintBlocks(src)).matchAll(/#(?:[0-9a-fA-F]{3,4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})\b/g)) {
223
306
  findings.push({ level: 'warn', rule: 'hard-coded-color', detail: `${m[0]} — values should come from tokens (cia.color(...) / var(--...))` });
224
307
  }
308
+ findings.push(...offScaleLength(stripVarExpr(src)));
309
+ findings.push(...missingFocusVisible(src));
225
310
  for (const m of src.matchAll(/\.[a-zA-Z][\w]*(?:__|--)[\w-]+/g)) {
226
311
  findings.push({ level: 'warn', rule: 'bem', detail: `${m[0]} — BEM chains are forbidden; use semantic single-class names` });
227
312
  }
@@ -240,13 +325,15 @@ const RULE_CATEGORY = {
240
325
  'unknown-symbol': 'API',
241
326
  'space-scale': 'Spacing',
242
327
  'off-contract-token': 'Contract',
328
+ 'off-scale-length': 'Spacing',
243
329
  'hard-coded-color': 'Color',
244
330
  bem: 'Naming',
245
331
  'hand-written-areas': 'Layout',
332
+ 'missing-focus-visible': 'Accessibility',
246
333
  };
247
334
  // The categories with rules implemented today — shown ✓ when clean so a passing
248
335
  // audit reads as coverage, not silence.
249
- const IMPLEMENTED_CATEGORIES = ['API', 'Contract', 'Spacing', 'Color', 'Naming', 'Layout'];
336
+ const IMPLEMENTED_CATEGORIES = ['API', 'Contract', 'Spacing', 'Color', 'Naming', 'Layout', 'Accessibility'];
250
337
 
251
338
  // Details are authored as "claim — suggested fix"; split so the graded report
252
339
  // can put the fix on its own `→` line.
package/bin/cia.cjs CHANGED
@@ -5,9 +5,12 @@
5
5
  * Subcommand router. Each subcommand lives in its own file under bin/ and
6
6
  * exposes a `run(args)` async function.
7
7
  *
8
- * Status: both converters shipped (EPIC-03 migration on-ramp, 6/6).
8
+ * Status: 4 converters shipped (v1.0 EPIC-03 tailwind+bootstrap; v1.2
9
+ * EPIC-05 mui+chakra).
9
10
  * migrate tailwind — parse tailwind.config.* + dump theme JSON
10
11
  * migrate bootstrap — parse Bootstrap SCSS/CSS vars + dump theme JSON
12
+ * migrate mui — parse a MUI createTheme() result + dump theme JSON
13
+ * migrate chakra — parse a Chakra extendTheme() result + dump theme JSON
11
14
  *
12
15
  * cia core ships ZERO JavaScript in the `files` manifest. The CLI lives in
13
16
  * `bin/` which is explicitly allowed per the architecture lock — same path
@@ -27,7 +30,7 @@ Usage:
27
30
 
28
31
  Commands:
29
32
  migrate <tool> [path] Convert another design system's config to a cia
30
- theme. Tools: tailwind | bootstrap.
33
+ theme. Tools: tailwind | bootstrap | mui | chakra.
31
34
  add <recipe> Copy a recipe from the book into your project
32
35
  (own the pattern). \`cia add --list\` to browse.
33
36
  analyze [path] Design-system health check: dead cia.* symbols,
@@ -58,7 +61,18 @@ Tools:
58
61
  via Bootstrap convention.
59
62
  Run \`cia migrate bootstrap --help\` for full options.
60
63
 
61
- Common options (both tools):
64
+ mui Read a MUI (Material UI v5+) theme module (createTheme({...})
65
+ result) and write a cia theme.scss. Maps palette.primary/
66
+ secondary/error/warning/info/success, text, background,
67
+ spacing, shape.borderRadius, typography.fontFamily.
68
+ Run \`cia migrate mui --help\` for full options.
69
+
70
+ chakra Read a Chakra UI (v2) theme module (extendTheme({...})
71
+ result) and write a cia theme.scss. Maps colors, space,
72
+ radii, fonts, fontSizes.
73
+ Run \`cia migrate chakra --help\` for full options.
74
+
75
+ Common options (all tools):
62
76
  --name <name> Theme name. Default: migrated
63
77
  --out <path> Custom output path. Default: ./cia-themes/<name>.scss
64
78
  --json Skip the file write and dump JSON to stdout (pipe-safe).
@@ -66,7 +80,8 @@ Common options (both tools):
66
80
  Examples:
67
81
  cia migrate tailwind ./tailwind.config.js
68
82
  cia migrate bootstrap ./scss/_variables.scss --name acme
69
- cia migrate bootstrap ./scss/_variables.scss --json | jq '.cia.report'
83
+ cia migrate mui ./src/theme.ts --name acme
84
+ cia migrate chakra ./src/theme.ts --json | jq '.cia.report'
70
85
  `;
71
86
 
72
87
  function fail(message, exit = 1) {
@@ -109,7 +124,17 @@ async function main() {
109
124
  await run(migrateArgs);
110
125
  return;
111
126
  }
112
- fail(`unknown migrate tool '${tool}'. Available: tailwind, bootstrap.`);
127
+ if (tool === 'mui') {
128
+ const { run } = require('./migrate-mui.cjs');
129
+ await run(migrateArgs);
130
+ return;
131
+ }
132
+ if (tool === 'chakra') {
133
+ const { run } = require('./migrate-chakra.cjs');
134
+ await run(migrateArgs);
135
+ return;
136
+ }
137
+ fail(`unknown migrate tool '${tool}'. Available: tailwind, bootstrap, mui, chakra.`);
113
138
  }
114
139
 
115
140
  if (command === 'add') {