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.
- package/AGENTS.md +16 -3
- package/CHANGELOG.md +41 -0
- package/CONTRACT.md +15 -0
- package/README.md +36 -28
- package/THEMING.md +1 -0
- package/bin/README.md +4 -4
- package/bin/analyze.cjs +88 -1
- package/bin/cia.cjs +30 -5
- package/bin/migrate-chakra.cjs +249 -0
- package/bin/migrate-mui.cjs +363 -0
- package/bin/migrate-tailwind.cjs +3 -1
- package/dist/css-is-awesome.css +516 -0
- package/dist/css-is-awesome.min.css +1 -1
- package/dist/css-is-awesome.utilities.css +516 -0
- package/dist/css-is-awesome.utilities.min.css +1 -1
- package/dist/tokens.d.ts +4 -2
- package/llm.txt +20 -10
- package/mcp/server.cjs +7 -2
- package/package.json +7 -4
- package/public/theme.css +293 -237
- package/public/themes/boilerplate/theme.css +11 -10
- package/public/themes/boilerplate-dark/theme.css +10 -9
- package/public/themes/boilerplate-light/theme.css +10 -9
- package/public/themes/cupertino/theme.css +11 -10
- package/public/themes/cupertino-dark/theme.css +11 -10
- package/public/themes/cupertino-light/theme.css +11 -10
- package/public/themes/glass/theme.css +11 -10
- package/public/themes/glass-dark/theme.css +11 -10
- package/public/themes/glass-light/theme.css +11 -10
- package/public/themes/graphite/theme.css +11 -10
- package/public/themes/graphite-dark/theme.css +11 -10
- package/public/themes/graphite-light/theme.css +11 -10
- package/public/themes/press/theme.css +11 -10
- package/public/themes/press-dark/theme.css +11 -10
- package/public/themes/press-light/theme.css +11 -10
- package/public/themes/prism/theme.css +11 -10
- package/public/themes/prism-dark/theme.css +11 -10
- package/public/themes/prism-light/theme.css +11 -10
- package/public/themes/sketchbook/theme.css +11 -10
- package/public/themes/sketchbook-dark/theme.css +11 -10
- package/public/themes/sketchbook-light/theme.css +10 -9
- package/public/themes/terminal/theme.css +11 -10
- package/public/themes/terminal-dark/theme.css +11 -10
- package/public/themes/terminal-light/theme.css +11 -10
- package/scripts/theme-a11y.js +11 -31
- package/scripts/theme-contract.json +1 -0
- package/scss/_spacing-scale.scss +25 -0
- package/scss/_utilities.scss +44 -9
- package/scss/components/_accordion.scss +1 -1
- package/scss/components/_data.scss +2 -2
- package/scss/components/_feedback.scss +1 -1
- package/scss/components/_forms.scss +16 -2
- package/scss/components/_navigation.scss +1 -1
- package/scss/examples/_usage.scss +3 -3
- package/scss/recipes/admin-dashboard-layout.md +277 -0
- package/scss/recipes/app-shell.md +339 -0
- package/scss/recipes/auth-flow.md +345 -0
- package/scss/recipes/confirm-dialog.md +248 -0
- package/scss/recipes/data-table.md +382 -0
- package/scss/recipes/datepicker.md +428 -0
- package/scss/recipes/form-validation-async.md +340 -0
- package/scss/recipes/form-validation-html5.md +306 -0
- package/scss/recipes/form-validation-react-hook-form.md +224 -0
- package/scss/recipes/form-validation-success-states.md +290 -0
- package/scss/recipes/form-validation-zod.md +270 -0
- package/scss/recipes/i18n-date-formatting.md +172 -0
- package/scss/recipes/i18n-number-currency.md +145 -0
- package/scss/recipes/i18n-pluralization.md +163 -0
- package/scss/recipes/multi-step-wizard.md +365 -0
- package/scss/recipes/otp-input.md +310 -0
- package/scss/recipes/rtl-layout.md +273 -0
- package/scss/themes/boilerplate-dark.scss +2 -10
- package/scss/themes/boilerplate-light.scss +2 -10
- package/scss/themes/boilerplate.scss +2 -10
- package/scss/themes/cupertino-dark.scss +2 -10
- package/scss/themes/cupertino-light.scss +2 -10
- package/scss/themes/cupertino.scss +2 -10
- package/scss/themes/glass-dark.scss +2 -10
- package/scss/themes/glass-light.scss +2 -10
- package/scss/themes/glass.scss +2 -10
- package/scss/themes/graphite-dark.scss +2 -10
- package/scss/themes/graphite-light.scss +2 -10
- package/scss/themes/graphite.scss +2 -10
- package/scss/themes/press-dark.scss +2 -10
- package/scss/themes/press-light.scss +2 -10
- package/scss/themes/press.scss +2 -10
- package/scss/themes/prism-dark.scss +2 -10
- package/scss/themes/prism-light.scss +2 -10
- package/scss/themes/prism.scss +2 -10
- package/scss/themes/sketchbook-dark.scss +2 -10
- package/scss/themes/sketchbook-light.scss +2 -10
- package/scss/themes/sketchbook.scss +2 -10
- package/scss/themes/terminal-dark.scss +2 -10
- package/scss/themes/terminal-light.scss +2 -10
- 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
|
|
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
|
-
|
|
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
|
-
|
|
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 —
|
|
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
|
|
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`)
|
|
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
|
-
**
|
|
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
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
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
|
-
|
|
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
|
-
|
|
313
|
-
|
|
314
|
-
|
|
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
|
-
|
|
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.
|
|
391
|
-
| `dist/css-is-awesome.core.min.css` | 2.
|
|
392
|
-
| `dist/css-is-awesome.utilities.min.css` | 4.
|
|
393
|
-
| `dist/css-is-awesome.min.css` | 7.
|
|
394
|
-
| Per-theme `themes/<name>/theme.css` | 1.
|
|
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
|
-
| `
|
|
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
|
|
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:
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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') {
|