css-is-awesome 1.1.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 (198) hide show
  1. package/AGENTS.md +343 -0
  2. package/CHANGELOG.md +1036 -0
  3. package/CLAUDE.md +9 -0
  4. package/CONTRACT.md +580 -0
  5. package/GEMINI.md +9 -0
  6. package/LICENSE +21 -0
  7. package/LICENSE-third-party +88 -0
  8. package/MIGRATION.md +312 -0
  9. package/README.md +380 -0
  10. package/ROADMAP.md +714 -0
  11. package/THEMING.md +290 -0
  12. package/THREE-TIERS.md +158 -0
  13. package/VERSIONING.md +200 -0
  14. package/_index.scss +34 -0
  15. package/api.scss +16 -0
  16. package/bin/README.md +57 -0
  17. package/bin/cia.cjs +114 -0
  18. package/bin/migrate-bootstrap.cjs +427 -0
  19. package/bin/migrate-tailwind.cjs +832 -0
  20. package/css-is-awesome.instructions.md +533 -0
  21. package/dist/css-is-awesome.core.css +598 -0
  22. package/dist/css-is-awesome.core.min.css +1 -0
  23. package/dist/css-is-awesome.css +3733 -0
  24. package/dist/css-is-awesome.min.css +1 -0
  25. package/dist/css-is-awesome.utilities.css +2445 -0
  26. package/dist/css-is-awesome.utilities.min.css +1 -0
  27. package/dist/tokens.css +508 -0
  28. package/dist/tokens.d.ts +266 -0
  29. package/dist/tokens.min.css +1 -0
  30. package/figma-tokens/README.md +57 -0
  31. package/figma-tokens/primitives-brand.scss +121 -0
  32. package/figma-tokens/semantic-dark.scss +61 -0
  33. package/figma-tokens/semantic-light.scss +61 -0
  34. package/figma-tokens/tokens.json +144 -0
  35. package/llm.txt +183 -0
  36. package/mcp/server.cjs +1495 -0
  37. package/package.json +199 -0
  38. package/public/icons/README.md +278 -0
  39. package/public/icons/arrow-right.svg +4 -0
  40. package/public/icons/check.svg +4 -0
  41. package/public/icons/chevron-down.svg +4 -0
  42. package/public/icons/close.svg +4 -0
  43. package/public/icons/core/arrow-down.svg +13 -0
  44. package/public/icons/core/arrow-left.svg +13 -0
  45. package/public/icons/core/arrow-right.svg +13 -0
  46. package/public/icons/core/arrow-up.svg +13 -0
  47. package/public/icons/core/bell.svg +13 -0
  48. package/public/icons/core/calendar.svg +15 -0
  49. package/public/icons/core/check.svg +12 -0
  50. package/public/icons/core/chevron-down.svg +12 -0
  51. package/public/icons/core/chevron-left.svg +12 -0
  52. package/public/icons/core/chevron-right.svg +12 -0
  53. package/public/icons/core/chevron-up.svg +12 -0
  54. package/public/icons/core/clock.svg +13 -0
  55. package/public/icons/core/close.svg +13 -0
  56. package/public/icons/core/copy.svg +13 -0
  57. package/public/icons/core/download.svg +14 -0
  58. package/public/icons/core/edit.svg +13 -0
  59. package/public/icons/core/error.svg +14 -0
  60. package/public/icons/core/external-link.svg +14 -0
  61. package/public/icons/core/eye-off.svg +15 -0
  62. package/public/icons/core/eye.svg +13 -0
  63. package/public/icons/core/filter.svg +12 -0
  64. package/public/icons/core/heart.svg +12 -0
  65. package/public/icons/core/help.svg +14 -0
  66. package/public/icons/core/home.svg +13 -0
  67. package/public/icons/core/info.svg +14 -0
  68. package/public/icons/core/loading.svg +12 -0
  69. package/public/icons/core/lock.svg +13 -0
  70. package/public/icons/core/mail.svg +13 -0
  71. package/public/icons/core/menu.svg +14 -0
  72. package/public/icons/core/message.svg +12 -0
  73. package/public/icons/core/minus.svg +12 -0
  74. package/public/icons/core/more-horizontal.svg +14 -0
  75. package/public/icons/core/pause.svg +13 -0
  76. package/public/icons/core/play.svg +12 -0
  77. package/public/icons/core/plus.svg +13 -0
  78. package/public/icons/core/refresh.svg +15 -0
  79. package/public/icons/core/save.svg +14 -0
  80. package/public/icons/core/search.svg +13 -0
  81. package/public/icons/core/settings.svg +13 -0
  82. package/public/icons/core/share.svg +14 -0
  83. package/public/icons/core/sort.svg +15 -0
  84. package/public/icons/core/star.svg +12 -0
  85. package/public/icons/core/success.svg +13 -0
  86. package/public/icons/core/trash.svg +16 -0
  87. package/public/icons/core/unlock.svg +13 -0
  88. package/public/icons/core/upload.svg +14 -0
  89. package/public/icons/core/user.svg +13 -0
  90. package/public/icons/core/users.svg +15 -0
  91. package/public/icons/core/warning.svg +14 -0
  92. package/public/icons/download.svg +4 -0
  93. package/public/icons/edit.svg +4 -0
  94. package/public/icons/menu.svg +4 -0
  95. package/public/icons/search.svg +4 -0
  96. package/public/theme.css +4179 -0
  97. package/public/themes/README.md +102 -0
  98. package/public/themes/boilerplate/theme.css +154 -0
  99. package/public/themes/boilerplate-dark/theme.css +171 -0
  100. package/public/themes/boilerplate-light/theme.css +171 -0
  101. package/public/themes/cupertino/theme.css +165 -0
  102. package/public/themes/cupertino-dark/theme.css +200 -0
  103. package/public/themes/cupertino-light/theme.css +227 -0
  104. package/public/themes/glass/theme.css +190 -0
  105. package/public/themes/glass-dark/theme.css +201 -0
  106. package/public/themes/glass-light/theme.css +229 -0
  107. package/public/themes/graphite/theme.css +155 -0
  108. package/public/themes/graphite-dark/theme.css +223 -0
  109. package/public/themes/graphite-light/theme.css +191 -0
  110. package/public/themes/press/theme.css +189 -0
  111. package/public/themes/press-dark/theme.css +199 -0
  112. package/public/themes/press-light/theme.css +235 -0
  113. package/public/themes/prism/theme.css +159 -0
  114. package/public/themes/prism-dark/theme.css +185 -0
  115. package/public/themes/prism-light/theme.css +188 -0
  116. package/public/themes/sketchbook/theme.css +152 -0
  117. package/public/themes/sketchbook-dark/theme.css +194 -0
  118. package/public/themes/sketchbook-light/theme.css +188 -0
  119. package/public/themes/terminal/theme.css +159 -0
  120. package/public/themes/terminal-dark/theme.css +229 -0
  121. package/public/themes/terminal-light/theme.css +152 -0
  122. package/scripts/README.md +70 -0
  123. package/scripts/icon-contract.json +67 -0
  124. package/scripts/icon-validator.js +205 -0
  125. package/scripts/prepare-dist.mjs +65 -0
  126. package/scripts/theme-a11y.js +415 -0
  127. package/scripts/theme-contract.json +171 -0
  128. package/scripts/theme-validator.js +674 -0
  129. package/scss/README.md +55 -0
  130. package/scss/_animations-utilities.scss +107 -0
  131. package/scss/_animations.scss +248 -0
  132. package/scss/_app-styles.scss +15 -0
  133. package/scss/_generator.scss +174 -0
  134. package/scss/_icons.scss +475 -0
  135. package/scss/_index.scss +17 -0
  136. package/scss/_layout.scss +597 -0
  137. package/scss/_mixins.scss +1036 -0
  138. package/scss/_system.scss +291 -0
  139. package/scss/_utilities.scss +377 -0
  140. package/scss/api.scss +61 -0
  141. package/scss/components/_accordion.scss +72 -0
  142. package/scss/components/_buttons.scss +171 -0
  143. package/scss/components/_copy-button.scss +69 -0
  144. package/scss/components/_data.scss +190 -0
  145. package/scss/components/_feedback.scss +197 -0
  146. package/scss/components/_forms.scss +272 -0
  147. package/scss/components/_index.scss +17 -0
  148. package/scss/components/_navigation.scss +186 -0
  149. package/scss/components/_overlay.scss +259 -0
  150. package/scss/components/_stepper.scss +159 -0
  151. package/scss/components/_tabs.scss +72 -0
  152. package/scss/core.scss +54 -0
  153. package/scss/examples/_theming.scss +107 -0
  154. package/scss/examples/_usage.scss +317 -0
  155. package/scss/main.scss +64 -0
  156. package/scss/recipes/README.md +110 -0
  157. package/scss/recipes/_bare-tags.scss +232 -0
  158. package/scss/recipes/_recipe-template.md +119 -0
  159. package/scss/recipes/combobox.md +509 -0
  160. package/scss/recipes/dialog.md +265 -0
  161. package/scss/recipes/print-to-pdf.md +263 -0
  162. package/scss/theme/README.md +67 -0
  163. package/scss/theme/_brand.scss +18 -0
  164. package/scss/theme/_colors-dark.scss +68 -0
  165. package/scss/theme/_colors-light.scss +70 -0
  166. package/scss/theme/_components.scss +94 -0
  167. package/scss/theme/_icons.scss +104 -0
  168. package/scss/theme/_index.scss +62 -0
  169. package/scss/theme/_shadows.scss +65 -0
  170. package/scss/theme/_shape.scss +13 -0
  171. package/scss/theme/_typography.scss +9 -0
  172. package/scss/theme/registry.json +447 -0
  173. package/scss/themes/boilerplate-dark.scss +212 -0
  174. package/scss/themes/boilerplate-light.scss +209 -0
  175. package/scss/themes/boilerplate.scss +181 -0
  176. package/scss/themes/cupertino-dark.scss +238 -0
  177. package/scss/themes/cupertino-light.scss +266 -0
  178. package/scss/themes/cupertino.scss +194 -0
  179. package/scss/themes/glass-dark.scss +238 -0
  180. package/scss/themes/glass-light.scss +266 -0
  181. package/scss/themes/glass.scss +220 -0
  182. package/scss/themes/graphite-dark.scss +260 -0
  183. package/scss/themes/graphite-light.scss +228 -0
  184. package/scss/themes/graphite.scss +182 -0
  185. package/scss/themes/press-dark.scss +237 -0
  186. package/scss/themes/press-light.scss +273 -0
  187. package/scss/themes/press.scss +241 -0
  188. package/scss/themes/prism-dark.scss +217 -0
  189. package/scss/themes/prism-light.scss +220 -0
  190. package/scss/themes/prism.scss +190 -0
  191. package/scss/themes/sketchbook-dark.scss +231 -0
  192. package/scss/themes/sketchbook-light.scss +231 -0
  193. package/scss/themes/sketchbook.scss +178 -0
  194. package/scss/themes/terminal-dark.scss +267 -0
  195. package/scss/themes/terminal-light.scss +175 -0
  196. package/scss/themes/terminal.scss +185 -0
  197. package/scss/tokens.scss +23 -0
  198. package/scss/utilities-only.scss +4 -0
package/AGENTS.md ADDED
@@ -0,0 +1,343 @@
1
+ # AGENTS.md — css-is-awesome
2
+
3
+ This file is the entry point for AI coding agents (Aider, Codex, Cursor, Claude Code, Gemini, Copilot, etc.) working in or with css-is-awesome.
4
+
5
+ ## What this library is
6
+
7
+ A token-driven SCSS design system with a **single mixin-router per component**. **Mixin-first since v0.8** — the mixin is the API; the class/tag/selector is the consumer's choice. The npm package ships **zero JavaScript** by hard rule.
8
+
9
+ **Every mixin is a knob-board.** Each look/feel dimension is an *input*, so a consumer can restyle any mixin at any time by changing an argument — row→column is just `@include cia.flex($direction: column)`, never a hand-written `flex-direction`. Customization lives in the mixin's arguments; the consumer stays one line. **If a visual dimension can only be reached by overriding in CSS, that's a missing input — add it to the mixin.** Fewer SCSS lines always wins.
10
+
11
+ **v1.0 architecture (locked 2026-05-23):** humans-first, AI-second. The 5-pillar priority is **(1) users first, (2) tokens, (3) theme editor on the website, (4) mixin-first speed, (5) AI as composer via recipes book + MCP server**. v1.0 ships the recipes book + theme editor polish + Tailwind/Bootstrap migration CLI + playground + MCP polish. No separate React component library (Jerry's call — recipes are the deliverable). Full backlog: [`roadmap/epics/v1-0/`](./roadmap/epics/v1-0/).
12
+
13
+ Three authoring tiers, in primary-to-fallback order:
14
+
15
+ - **Tier 2 (primary)** — per component: `@use 'css-is-awesome/api' as cia;` then `.your-class { @include cia.btn(primary); }`. The `/api` barrel is zero-emit (safe inside a `.module.scss`); the root bundle emits the tokens once. SCSS build required.
16
+ - **Tier 1 (opt-in)** — drop-in CSS classes (`.cia-btn`). Default-off in Sass path; opt in via `@use cia with ($utilities: true)`. Pre-built CDN bundles still ship every utility.
17
+ - **Tier 3 (opt-in Pico-mode)** — `@use 'css-is-awesome/scss/recipes/bare-tags';` one line styles every common HTML element. Wrapped in `:where()` (specificity 0,0,0) so consumer styles always win.
18
+
19
+ ## Quick decisions for an AI agent
20
+
21
+ When asked to add a UI element, follow this order:
22
+
23
+ 1. **Mixin-first, inputs-first.** `.your-class { @include cia.btn(primary); }` — write your own selector, `@include` the mixin. Need a variation? **Pass it as an input** — `@include cia.flex($direction: column)`, `@include cia.card-base($shadow: 2, $r: xl)` — never hand-write the CSS an input already controls. Fewer SCSS lines always wins. This is the v0.8 primary API.
24
+ 2. **Match the project's tier.** If they're already on Tier 1 classes (`<button class="cia-btn">`), stay there.
25
+ 3. **Never invent `cia-*` class names.** That prefix is library-owned. Consumer code uses its own naming.
26
+ 4. **All values come from tokens.** Never hardcode `#3A5FCD`, `1rem`, `8px`. Use `cia.color(primary)`, `cia.space(4)`, `cia.radius(md)`.
27
+ 5. **No BEM.** No `__element` / `--modifier` chains. `cia-` is a single-class namespace prefix, not BEM.
28
+ 6. **No JavaScript.** Cia ships zero JS in the npm package. The 6 interactive components (accordion, modal, tooltip, dropdown, tabs, copy-button) use native HTML primitives — `<details name>`, `<dialog>`, `[popover]`, radio + `:has()`. **This rule binds cia, not you.** If you're *consuming* cia (building an app/component library on top of it), write JavaScript/framework components freely — React, SVG charts, interactivity, all of it — and use cia purely for styling (mixins + tokens). Compose the mixins to build any visual you want; you are not limited to cia's pre-made component mixins.
29
+ 7. **Style semantic state off ARIA, not a parallel `data-*`.** When a state has an ARIA source of truth, hook your styles to *that* attribute: `[aria-selected="true"]`, `[aria-expanded="true"]`, `[aria-invalid="true"]`, `:disabled, [aria-disabled="true"]`, `[aria-pressed="true"]`, `[aria-checked="true"]`, `[aria-current]`, `[role="tab"]`. Then the state can't be styled without setting the ARIA a screen reader needs — **accessible-by-construction**, one source of truth for looks + a11y. Reserve `data-*` for **cosmetic-only** variants (`data-size`, `data-variant`, `data-color`) that carry no ARIA meaning. cia's own components already do this where native HTML doesn't cover it (`[aria-current="page"]`, `[aria-selected="true"]`, `[role="option"]`).
30
+
31
+ ## Install
32
+
33
+ ```bash
34
+ npm install css-is-awesome
35
+ # Tier 2 also wants:
36
+ npm install -D sass
37
+ ```
38
+
39
+ For SCSS imports through Sass:
40
+ ```bash
41
+ sass app.scss app.css --load-path=node_modules
42
+ ```
43
+ Or with the modern package importer:
44
+ ```bash
45
+ sass app.scss app.css --pkg-importer=node
46
+ # Then prefix imports: @use 'pkg:css-is-awesome/scss/...'
47
+ ```
48
+
49
+ ## Tier 2 example (the v0.8 primary path)
50
+
51
+ ```scss
52
+ @use 'css-is-awesome/api' as cia; // zero-emit authoring barrel — safe in a .module.scss
53
+
54
+ .hero-cta { @include cia.btn(primary); }
55
+ .product-card { @include cia.card-base($shadow: 2); }
56
+ .faq-item { @include cia.accordion; }
57
+ .confirm-dlg { @include cia.modal; }
58
+ ```
59
+
60
+ ```html
61
+ <a class="hero-cta" href="/buy">Buy now</a>
62
+ <article class="product-card">…</article>
63
+ <details name="faq" class="faq-item"><summary>Q?</summary><div>A.</div></details>
64
+ <dialog class="confirm-dlg">…</dialog>
65
+ ```
66
+
67
+ The cia barrel re-exports every mixin: layout, typography, color, motion, helpers, plus every component. One `@use 'css-is-awesome/api'` gives you the whole API — and it emits **zero CSS** until you call a mixin, so it is safe inside a `.module.scss` under Next.js CSS Modules pure mode.
68
+
69
+ **Two imports, two jobs.** Keep tokens and mixins in separate places:
70
+
71
+ - **Root / global** (once) — emit the tokens: `<link>` a theme CSS file, or `@use 'css-is-awesome';` in your global stylesheet. This prints `:root { --… }`.
72
+ - **Each component** (`Card.module.scss`) — `@use 'css-is-awesome/api' as cia;` and only call mixins. **Never** `@use 'css-is-awesome'` (the bundle) from a component file — it re-emits `:root`, which CSS Modules pure mode rejects.
73
+
74
+ **Next.js / Turbopack.** The forwarding barrel **works** under Turbopack. The Boiler showcase runs Next 16.1.1 on Turbopack for both `dev` and `build` and imports `css-is-awesome/scss/api` in all 116 of its stylesheets. Do **not** steer consumers to the leaf module — `scss/mixins` exposes only the ~42 core mixins and none of `btn`, `card-base`, `stack`, `grid` or `animate`, so it forces hand-inlined CSS.
75
+
76
+ Two real setup notes:
77
+
78
+ 1. Sass doesn't read package.json `exports`, so add `node_modules` to `sassOptions.loadPaths`.
79
+ 2. **If you hit `Two forwarded modules both define a mixin named stack`, drop your own styles directory from `loadPaths`.** Provenance matters here: that was reported by one consumer, and it is **not** reproducible in standard Sass. cia's internal forwards are all `./`-relative, which Sass resolves against the importing file without consulting load paths — `validate-package` asserts the barrel compiles even with a hostile `styles/_mixins.scss` ahead of `node_modules`. Treat it as a bundler-resolver deviation, not a Sass rule, and don't repeat it as one.
80
+
81
+ The two-import split (tokens at root, mixins per component) applies on every toolchain.
82
+
83
+ ## Theme system (1 file per theme, drop-in by default)
84
+
85
+ Every theme emits **two selectors at once**:
86
+
87
+ ```css
88
+ :root, :root[data-theme="<name>"] { … }
89
+ ```
90
+
91
+ The bare `:root` is the product promise: drop one theme file in as your `theme.css` and the page restyles with **no markup change**. The `[data-theme]` half is what lets several themes coexist in one document. Which half you rely on depends on how you ship:
92
+
93
+ - **One theme file on its own** — `<html data-theme="…">` is **optional**. Link the file and you're done. (Setting it anyway is harmless and still correct.)
94
+ - **The multi-theme bundle** `public/theme.css` (all 24 blocks in one file) — `<html data-theme="…">` is **required**. Every block would collide on a shared `:root`, so the bundle is built with `$standalone: false`, which drops the bare `:root` and leaves only the attribute selector.
95
+
96
+ This is a fixed rule now because it used to be three rules: the shipped themes disagreed three ways — 9 emitted `:root[data-theme=x]`, 7 emitted `[data-theme=x]`, and 5 emitted a bare `:root`. Only the last group actually worked when dropped in alone. **Never hand-write a theme's selector — call `cia.theme()` and let it emit both halves.**
97
+
98
+ Library defaults (spacing scale, z-layers, font sizes) are emitted under **`:where(:root)`**, specificity (0,0,0), so any theme declaration outranks them regardless of load order. Deliberately `:where()` and **not** `@layer` — cia is unlayered by decision and that rule stands.
99
+
100
+ **24 themes, 8 families.** Each family ships three files: the family name itself (e.g. `sketchbook`), plus explicit `-light` and `-dark` siblings. The unsuffixed parent carries **both** modes in one file via `light-dark()`, so the browser auto-swaps on OS `prefers-color-scheme`; the suffixed siblings pin one mode with `color-scheme: light` / `dark` for consumers who want a fixed brand. The eight families are boilerplate, sketchbook, press, prism, cupertino, glass, graphite, terminal. **`terminal` is the one asymmetry** — its unsuffixed file is dark-only (sacred VT100 phosphor), so `terminal` and `terminal-light` are different brands rather than two modes of one.
101
+
102
+ So: `scss/themes/` has **24** `.scss` sources, `public/themes/` builds **24** directories each holding a `theme.css`, and MCP `list_themes` reports **24**. When you need one number, say **24 themes across 8 families**. All pass the WCAG 2.2 AA contrast audit by default.
103
+
104
+ ```html
105
+ <!-- Single file: data-theme is OPTIONAL -->
106
+ <link rel="stylesheet" href="node_modules/css-is-awesome/public/themes/boilerplate/theme.css">
107
+
108
+ <!-- The all-in-one bundle: data-theme is REQUIRED -->
109
+ <link rel="stylesheet" href="node_modules/css-is-awesome/public/theme.css">
110
+ <html data-theme="boilerplate"> <!-- any of the 24 theme names -->
111
+ ```
112
+
113
+ ### Themes are open — edit or create your own
114
+
115
+ **Consumers can edit any shipped theme and make brand-new themes.** Themes are data, not internal magic. Three ways:
116
+
117
+ 1. **Edit a shipped theme in place** — open `scss/themes/<name>.scss`, change tokens, run `npm run build:css:themes`.
118
+ 2. **Copy + rename** — `cp scss/themes/boilerplate.scss scss/themes/mybrand.scss`, edit, build, validate, ship. Set `<html data-theme="mybrand">` (or just serve the file as your `theme.css` and skip the attribute).
119
+ 3. **Override at consumer level** — `:root[data-theme="boilerplate"] { --action-primary-default: #ff0066; }` in your own SCSS. No fork needed.
120
+
121
+ > **`public/themes/**/theme.css` and `public/theme.css` are GENERATED. Never hand-edit them.** `npm run build:css:themes` builds every theme *and* regenerates the bundle, and it is part of `npm run build:css:all`. `npm run check:theme-drift` rebuilds into a scratch copy and fails if the committed artifacts don't match the SCSS sources — CI runs it *before* `validate-themes`, because `validate-themes` reads the committed CSS and would otherwise happily green-light a stale artifact. Edit `scss/themes/<name>.scss`, then rebuild.
122
+
123
+ Authoring template (in your own project — a theme file is a global stylesheet, so it may emit `:root`):
124
+ ```scss
125
+ // your-project/themes/midnight.scss
126
+ @use 'css-is-awesome/api' as cia;
127
+
128
+ // @mixin theme($name, $scheme: light dark, $standalone: true)
129
+ @include cia.theme('midnight') {
130
+ --background-default: light-dark(#f5f5f7, #0a0a0e);
131
+ --text-primary: light-dark(#0a0a0e, #f5f5f7);
132
+ --action-primary-default: light-dark(#3A5FCD, #60a5fa);
133
+ @include cia.states(action-primary); // derives hover/active
134
+
135
+ /* Spacing is themeable — declare the NUMBERED scale, it is contract-required */
136
+ --space-0: 0; --space-1: 0.25rem; --space-2: 0.5rem; /* … through --space-9 */
137
+
138
+ /* ... ~120 more tokens — see scripts/theme-contract.json for the full slot list */
139
+ }
140
+ ```
141
+
142
+ `$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.
143
+
144
+ The validator (`node scripts/theme-validator.js`) enforces the token contract — **127 required + 36 optional = 163 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`.
145
+
146
+ ### Theming spacing (new — read this before you set a size token)
147
+
148
+ A theme must declare the **numbered** scale `--space-0` … `--space-9`. Those ten are contract-required. The six t-shirt names (`--space-2xs/xs/sm/md/lg/xl`) are **optional**, and the library emits them as `var()` references — `--space-md: var(--space-4)` — so setting a numbered step moves its alias with it.
149
+
150
+ Why it matters: components call `cia.space(4)`, which resolves to `var(--space-4)`. The t-shirt names used to emit as *independent literals*, so a theme that only set `--space-md` changed a variable nothing read. Swapping a theme repainted colors but never re-proportioned the page. **Set the numbered step; don't set only an alias.**
151
+
152
+ ### Radius tokens: use the per-component knobs
153
+
154
+ `--radius-avatar`, `--radius-badge`, `--radius-button`, `--radius-card`, `--radius-input` and `--radius-modal` were removed from the contract — nothing ever read them, so any advice to "set `--radius-button`" was advice that could not work. The knobs that *do* work are `--btn-radius`, `--card-radius`, `--input-radius`, `--modal-radius`, `--badge-radius`, `--tag-radius` (all optional), and they cascade from the generic radii: `--btn-radius: var(--radius-md, 0.25rem)`. Set `--radius-md` to move everything; set `--btn-radius` to move just buttons.
155
+
156
+ **Paired themes (two brands by mode)** — no JS, no mixin:
157
+ ```html
158
+ <link rel="stylesheet" href="/themes/sketchbook.css" media="(prefers-color-scheme: light)">
159
+ <link rel="stylesheet" href="/themes/terminal.css" media="(prefers-color-scheme: dark)">
160
+ ```
161
+
162
+ 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.
163
+
164
+ 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 v1; missing tokens always fail). 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.
165
+
166
+ ### Theme init (Next.js / SSR consumers)
167
+
168
+ This section only applies when you ship **more than one theme** (the bundle, or a runtime theme switcher). If you ship a single theme file, its bare `:root` already styles the first paint and there is nothing to set — skip the snippet.
169
+
170
+ Setting `data-theme` in a `useEffect` causes a flash-of-default-theme before hydration. The fix is an inline `<script>` in `<head>` that runs synchronously before paint and sets the attribute from storage or system preference. css-is-awesome is a styling-only package, so there is no helper to import — paste the snippet directly into your layout:
171
+
172
+ ```tsx
173
+ // app/layout.tsx (Next.js App Router) — paste this <script> in <head>
174
+ export default function RootLayout({ children }) {
175
+ return (
176
+ <html lang="en" suppressHydrationWarning>
177
+ <head>
178
+ <script
179
+ dangerouslySetInnerHTML={{
180
+ __html: `(function(){try{var s=localStorage.getItem('cia-theme');
181
+ if(s){document.documentElement.setAttribute('data-theme',s);return;}
182
+ if(window.matchMedia('(prefers-color-scheme: dark)').matches){
183
+ document.documentElement.setAttribute('data-theme','prism-dark');return;}
184
+ document.documentElement.setAttribute('data-theme','prism-light');
185
+ }catch(e){document.documentElement.setAttribute('data-theme','prism-light');}})();`,
186
+ }}
187
+ />
188
+ </head>
189
+ <body>{children}</body>
190
+ </html>
191
+ );
192
+ }
193
+ ```
194
+
195
+ `suppressHydrationWarning` on `<html>` is required — the inline script mutates the DOM before React hydrates, so React would otherwise warn about a server/client mismatch on `data-theme`. Adjust the storage key and theme names (`'cia-theme'`, `'prism-light'`, `'prism-dark'`) to match your app.
196
+
197
+ ## Icons (1 pack, override per theme)
198
+
199
+ The default `core` icon pack ships at `public/icons/core/<name>.svg` —
200
+ 49 glyphs vendored from Lucide (ISC + MIT, see `LICENSE-third-party`).
201
+ Use the existing mixins; the call signatures are unchanged:
202
+
203
+ ```scss
204
+ @use 'css-is-awesome/scss/icons' as i; // the icon pack imports on its own
205
+
206
+ @include i.svg(check); // tinted via currentColor
207
+ @include i.svg-text(arrow-right, $position: after);
208
+ ```
209
+
210
+ > Icons are also on the main barrel under the `icon-` prefix (`cia.icon-svg(check)`) if you prefer a single `@use 'css-is-awesome/api'`.
211
+
212
+ Compiled output emits a per-glyph custom property fallback so a theme
213
+ can override one icon without rebuilding SCSS:
214
+
215
+ ```css
216
+ mask: var(--cia-icon-check, url('/icons/core/check.svg')) center / contain no-repeat;
217
+ ```
218
+
219
+ To override `check` for one theme, drop the replacement SVG at
220
+ `public/themes/<theme>/icons/core/check.svg` and declare
221
+ `--cia-icon-check: url('/themes/<theme>/icons/core/check.svg')` inside
222
+ that theme's `:root`/`[data-theme]` block. Resolution order is
223
+ **per-theme override → core pack → 404**.
224
+
225
+ ### Adding your own glyph (drop-in)
226
+
227
+ You don't have to register a new glyph in the contract just to use it.
228
+ The `core` pack and any custom pack you create both work as plain
229
+ folders — drop an SVG in and call the mixin:
230
+
231
+ ```bash
232
+ cp my-flag.svg public/icons/core/flag.svg
233
+ ```
234
+
235
+ ```scss
236
+ @include i.svg(flag); // tinted via currentColor — works immediately
237
+ ```
238
+
239
+ No JSON edit, no `validate-icons` run required. The contract validator
240
+ only enforces the canonical 49-glyph `core` set; everything beyond that
241
+ is opt-in. Add a glyph to `scripts/icon-contract.json` only when every
242
+ pack must declare it.
243
+
244
+ For new packs (e.g. `editor`, `files`), set `$theme-icon-pack: editor`
245
+ in your SCSS and drop SVGs at `public/icons/editor/*.svg` — same
246
+ no-registration drop-in pattern.
247
+
248
+ See [`CONTRACT.md` → Icons contract](./CONTRACT.md#icons-contract) for
249
+ the full spec, the canonical 49-glyph list, naming conventions, and
250
+ when to bump the contract. Validate the contract pack with
251
+ `npm run validate-icons`.
252
+
253
+ ### ⚠️ `fa-*` is bring-your-own-font — prefer the SVG pack
254
+
255
+ `fa`, `fa-icon`, `fa-text` and `fa-spin` exist for teams already on Font
256
+ Awesome. They map a name through `$icon-fa-map` (55 entries) to a
257
+ codepoint and set the FA font family — nothing more:
258
+
259
+ ```css
260
+ .a { font-family: "Font Awesome 6 Free"; font-weight: 900; content: "\f00c"; }
261
+ ```
262
+
263
+ **cia ships no Font Awesome files** (its licence is not ours to vendor).
264
+ `$theme-fa-path` defaults to `/webfonts`, a directory that does **not**
265
+ exist in the package, and `fa-load` is never called by cia. So `fa-*`
266
+ compiles to valid CSS and renders as a **tofu box** until the consumer
267
+ supplies the woff2 files, points `$theme-fa-path` at them, and calls
268
+ `@include i.fa-load;` once at the root.
269
+
270
+ Missing font files do not error. **Default to `svg()` unless the user has
271
+ explicitly told you they use Font Awesome** — the SVG pack is
272
+ self-contained and needs no setup.
273
+
274
+ ## Where to read deeper
275
+
276
+ Inside this package (all whitelisted in `files`):
277
+
278
+ - **`css-is-awesome.instructions.md`** — full authoring rules (~14 KB, Cursor/Copilot pick up via `applyTo: "**"` frontmatter)
279
+ - **`README.md`** — install, scripts, links
280
+ - **`THREE-TIERS.md`** — full tier explanation with examples
281
+ - **`THEMING.md`** — theme contract and dark-mode pattern
282
+ - **`CONTRACT.md`** — the token contract (every theme must declare every slot)
283
+ - **`CHANGELOG.md`** — version history
284
+
285
+ ## Common gotchas for AI agents
286
+
287
+ - **Don't write BEM.** No `cia-card__title--large`. The library is anti-BEM by design.
288
+ - **Don't hardcode breakpoints.** Use `cia.media(md)` (or `cia.media-down`, `cia.media-between`). Numbers come from the contract.
289
+ - **Don't hand-edit `public/themes/**/theme.css` or `public/theme.css`.** They are build artifacts of `scss/themes/*.scss`. Edit the SCSS, run `npm run build:css:themes`, and `npm run check:theme-drift` to prove source and artifact agree.
290
+ - **Don't hand-write a theme's selector.** `@include cia.theme(name)` emits `:root, :root[data-theme="name"]` — both halves, on purpose. Writing `[data-theme=x]` yourself breaks the single-file drop-in; writing a bare `:root` yourself breaks the bundle.
291
+ - **Theme the numbered spacing scale, not the t-shirt aliases.** `--space-0`…`--space-9` are contract-required; `--space-md` and friends are optional `var()` aliases that follow them.
292
+ - **Print/PDF is a pure-CSS layer.** Include `cia.print-base` once at the stylesheet ROOT (it emits `@page`), then `cia.print-hidden` to drop chrome and `cia.print-only` to reveal paper-only content. Read `--is-print` (`0` screen / `1` paper) for custom effects. cia ships **zero JS** for it — the browser's native Print → Save as PDF is the generator.
293
+ - **Do NOT "clean up" the `!important` in the print mixins.** It is load-bearing and deliberate. `@media` contributes no specificity, so `print-hidden` carries only the specificity of the selector it is included in; a later equal-specificity `display` (usually a utility class or a component library) wins in print. Verified in a browser: with `!important` the element hides, without it it prints anyway — a silent, paper-only failure. **`@layer` does not fix this** — layered CSS always loses to unlayered CSS, so a layered print rule loses to any unlayered consumer stylesheet, and `!important` inverts layer order on top of that. cia is unlayered by decision (`.agent/decisions/decided/04-at-layer-decision.md`). Scope is 8 declarations, all inside `@media print`, all variable-driven via `--print-hide` / `--print-show`.
294
+ - **Don't ship JavaScript.** The npm package has zero `.js`/`.mjs` files. JS-dependent features ship as separate add-on packages.
295
+ - **Variants are arguments, not classes.** `cia.btn(primary)`, not `cia-btn cia-btn-primary` (Tier 1 utilities are an exception, but only at consumer level).
296
+ - **The `cia-*` prefix is library-owned.** Consumer code should use its own naming for new classes.
297
+ - **`scss/_app-styles.scss` is NOT part of the library entry.** It's a template for project-owned styles in a consuming boilerplate. Don't `@use` it from library code.
298
+ - **v0.8 mixin renames** — `cia.bp`→`cia.media`, `cia.cq`→`cia.contain`, `cia.color-raw`→`cia.color-static`, `cia.inset`→`cia.pad`, `cia.squish`→`cia.pad-asym`, `cia.font-load`→`cia.font-face`. Old names error with "undefined mixin." No aliases.
299
+
300
+ ## MCP server (SHIPPED — use it)
301
+
302
+ 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.
303
+
304
+ Wire it into your MCP client's `.mcp.json`:
305
+
306
+ ```json
307
+ {
308
+ "mcpServers": {
309
+ "css-is-awesome": {
310
+ "command": "node",
311
+ "args": ["node_modules/css-is-awesome/mcp/server.cjs"]
312
+ }
313
+ }
314
+ }
315
+ ```
316
+
317
+ 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:
318
+
319
+ - **Themes** — `list_themes`, `get_theme`, `search_themes`
320
+ - **Mixins** — `list_mixins`, `get_mixin`, `search_mixins` (real signatures — don't guess)
321
+ - **Functions** — `list_functions`, `get_function`, `search_functions`
322
+ - **Tokens** — `list_tokens`, `get_token`, `search_tokens` (127 required + 36 optional contract tokens)
323
+ - **Animations** — `list_animations`, `get_animation`
324
+ - **Components** — `list_components`, `get_component`, `search_components`
325
+ - **Recipes** — `list_recipes`, `get_recipe`
326
+ - **Doc readers** — `read_llm_txt`, `read_changelog`, `read_migration`, `read_theming`, `read_agents`, `read_contract`, `read_three_tiers`, `read_readme`, `read_versioning`
327
+ - **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)
328
+
329
+ ## Other tooling (shipped)
330
+
331
+ - **`cia` CLI** — ships as `bin/cia.cjs`, exposed as the `cia` bin. The migration
332
+ on-ramp is live: `npx cia migrate tailwind [path]` and `npx cia migrate bootstrap [path]`
333
+ parse another system's config and dump a cia theme. Run either with `--help` for
334
+ full options. (`cia init` / `cia add` remain post-1.0.)
335
+ - **JSON token export** — DTCG-format token list in `figma-tokens/`.
336
+ - **`llm.txt`** — at the repo root and served from the docs site; single-fetch
337
+ summary for any AI agent. Also readable over MCP via `read_llm_txt`.
338
+
339
+ The markdown files above, the `cia` CLI, and the MCP server are the source of truth.
340
+
341
+ ---
342
+
343
+ If you're a human reading this and want full developer docs, start at `README.md`.