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/THEMING.md ADDED
@@ -0,0 +1,290 @@
1
+ # Theming Guide
2
+
3
+ css-is-awesome is fully themeable. Pick the method that fits your stack.
4
+
5
+ ## Built-in theme names
6
+
7
+ 24 themes, in 8 families. Each family ships three files:
8
+
9
+ - `<family>` — dual-mode, both light and dark in one file via native `light-dark()`
10
+ - `<family>-light` — light only
11
+ - `<family>-dark` — dark only
12
+
13
+ The families are `boilerplate`, `sketchbook`, `press`, `prism`, `cupertino`,
14
+ `glass`, `graphite`, `terminal`. (`terminal` is dark-only by design — CRT
15
+ phosphor; `terminal-light` is its daylight companion.)
16
+
17
+ ### The selector model — why `data-theme` is usually optional
18
+
19
+ Every theme file emits:
20
+
21
+ ```css
22
+ :root, :root[data-theme="press-dark"] { … }
23
+ ```
24
+
25
+ The bare `:root` half means **dropping a single theme file in restyles the page
26
+ with no markup change**. Link it, or copy it over your `theme.css`, and you're
27
+ done — you do *not* need to set `<html data-theme="…">` to match.
28
+
29
+ `data-theme` is still **required** for the all-in-one bundle (`public/theme.css`),
30
+ which carries all 24 themes in one file. There the bare `:root` is omitted, and
31
+ the attribute is the only thing distinguishing one theme from another:
32
+
33
+ ```html
34
+ <link rel="stylesheet" href="/theme.css">
35
+ <html data-theme="press-dark">
36
+ ```
37
+
38
+ If you're authoring your own theme with the `theme()` mixin, the same switch is
39
+ the `$standalone` argument:
40
+
41
+ ```scss
42
+ @mixin theme($name, $scheme: light dark, $standalone: true) { … }
43
+ ```
44
+
45
+ `$standalone: true` (the default) emits `:root, :root[data-theme="<name>"]`.
46
+ Pass `$standalone: false` when the theme is going into a multi-theme bundle.
47
+
48
+ ### Library defaults sit under `:where(:root)`
49
+
50
+ cia's own default token values emit under `:where(:root)`, specificity `0,0,0`.
51
+ Any theme declaration — `:root`, `[data-theme]`, whatever — outranks them
52
+ regardless of load order. That's `:where()`, deliberately, not `@layer`; cia
53
+ never asks a consumer to adopt layers.
54
+
55
+ See [`CHANGELOG.md`](./CHANGELOG.md) for the timeline and
56
+ [`MIGRATION.md`](./MIGRATION.md) for upgrade steps.
57
+
58
+ ---
59
+
60
+ ## Quick Brand Swap (3 lines)
61
+
62
+ Replace the default Royal Blue brand with your own colors — just set these CSS custom properties:
63
+
64
+ ```css
65
+ :root {
66
+ --brand-primary: #E11D48;
67
+ --action-primary-default: #E11D48;
68
+ --action-primary-hover: #BE123C;
69
+ }
70
+ ```
71
+
72
+ That's it. Every button, link accent, and focus ring now uses your brand color.
73
+
74
+ Load this *after* the theme file — a theme's `:root` block and your `:root`
75
+ block have the same specificity, so source order decides.
76
+
77
+ ---
78
+
79
+ ## Method 1: CSS Custom Properties (Runtime)
80
+
81
+ Override any token at `:root`. Works with the compiled CSS — no build step needed.
82
+
83
+ ```css
84
+ :root {
85
+ /* Brand */
86
+ --brand-primary: #E11D48;
87
+ --brand-primary-hover: #BE123C;
88
+ --brand-primary-active: #9F1239;
89
+
90
+ /* Actions (tie to brand) */
91
+ --action-primary-default: #E11D48;
92
+ --action-primary-hover: #BE123C;
93
+ --action-primary-active: #9F1239;
94
+
95
+ /* Typography */
96
+ --font-primary: "Poppins", sans-serif;
97
+
98
+ /* Shape */
99
+ --radius-md: 0.5rem;
100
+ --radius-lg: 1rem;
101
+
102
+ /* Spacing — the numbered scale is the source of truth */
103
+ --space-4: 1.25rem;
104
+ }
105
+ ```
106
+
107
+ ### Spacing is themeable
108
+
109
+ Set the **numbered** scale (`--space-0` … `--space-9`). That's what
110
+ `cia.space(4)` compiles to — `var(--space-4)` — so changing `--space-4` changes
111
+ every component that asks for step 4.
112
+
113
+ The t-shirt names (`--space-2xs/xs/sm/md/lg/xl`) are optional aliases, emitted
114
+ as *references* into that scale:
115
+
116
+ ```css
117
+ --space-xs: var(--space-1);
118
+ --space-sm: var(--space-2);
119
+ --space-md: var(--space-4);
120
+ --space-lg: var(--space-5);
121
+ --space-xl: var(--space-6);
122
+ ```
123
+
124
+ So they follow the numbered scale automatically — you rarely need to set them.
125
+ Setting `--space-md` to a literal only re-points that one alias; it does not
126
+ change what `space(4)` resolves to.
127
+
128
+ ### Component shape tokens
129
+
130
+ `--btn-radius`, `--card-radius`, `--input-radius`, `--modal-radius`,
131
+ `--badge-radius` and `--tag-radius` are what the components actually read, and
132
+ they cascade from the generic scale by default:
133
+
134
+ ```css
135
+ --btn-radius: var(--radius-md, 0.25rem);
136
+ ```
137
+
138
+ Move `--radius-*` to reshape everything; set one component token to make a
139
+ single exception. (There is no `--radius-button` / `--radius-card` /
140
+ `--radius-input` / `--radius-modal` / `--radius-avatar` / `--radius-badge` —
141
+ those names were removed because nothing read them.)
142
+
143
+ ### Available token families
144
+
145
+ | Prefix | Examples |
146
+ |--------|----------|
147
+ | `--brand-*` | `primary`, `secondary`, `accent` + hover/active |
148
+ | `--background-*` | `default`, `subtle`, `navbar` |
149
+ | `--surface-*` | `default`, `subtle`, `muted`, `emphasis` |
150
+ | `--text-*` | `primary`, `secondary`, `muted`, `inverse`, `link` |
151
+ | `--border-*` | `default`, `subtle`, `emphasis`, `focus` |
152
+ | `--action-primary-*` | `default`, `hover`, `active` |
153
+ | `--action-secondary-*` | `default`, `hover`, `active` |
154
+ | `--success/warning/error/info-*` | `default`, `hover`, `subtle`, `text` |
155
+ | `--font-*` | `primary`, `serif`, `mono` |
156
+ | `--font-size-*` | `xs` through `6xl` |
157
+ | `--font-weight-*` | `light`, `normal`, `medium`, `semibold`, `bold`, `black` |
158
+ | `--line-height-*` | `none`, `tight`, `snug`, `normal`, `relaxed`, `loose` |
159
+ | `--radius-*` | `none`, `sm`, `md`, `lg`, `xl`, `2xl`, `full` |
160
+ | `--*-radius` | `btn`, `card`, `input`, `modal`, `badge`, `tag` — optional, default to the `--radius-*` scale |
161
+ | `--shadow-*` | `sm`, `md`, `lg`, `xl`, `2xl`, `inner`, `none` |
162
+ | `--space-*` | `0` through `9` (the scale themes declare) — plus optional aliases `2xs`, `xs`, `sm`, `md`, `lg`, `xl` |
163
+ | `--z-*` | `hide`, `base`, `dropdown`, `sticky`, `fixed`, `backdrop`, `modal`, `popover`, `tooltip`, `toast` |
164
+ | `--duration-*` | `instant`, `fast`, `normal`, `slow`, `slower` |
165
+ | `--opacity-*` | `0` through `100` |
166
+
167
+ ---
168
+
169
+ ## Method 2: SCSS Overrides (Compile-Time)
170
+
171
+ Override theme maps when importing. Full control over every token.
172
+
173
+ ```scss
174
+ @use "css-is-awesome/scss/theme" with (
175
+ $brand: (
176
+ primary: #E11D48,
177
+ primary-hover: #BE123C,
178
+ primary-active: #9F1239,
179
+ secondary: #8B5CF6,
180
+ secondary-hover:#7C3AED,
181
+ secondary-active:#6D28D9,
182
+ accent: #06B6D4,
183
+ accent-hover: #0891B2,
184
+ )
185
+ );
186
+
187
+ @use "css-is-awesome/scss/generator";
188
+ @include generator.generate-theme;
189
+ ```
190
+
191
+ You can override any theme map: `$brand`, `$colors-light`, `$colors-dark`, `$fonts`, `$radius`, `$shadows-light`, `$shadows-dark`.
192
+
193
+ ---
194
+
195
+ ## Method 3: Multi-Brand Theming
196
+
197
+ Use `data-theme` attributes for brand + mode combinations. Scope them the way
198
+ cia's own themes do — `:root[data-theme="…"]`, specificity `0,2,0` — so they
199
+ outrank any bare `:root` block from a dropped-in theme file:
200
+
201
+ ```css
202
+ :root[data-theme="brand-a-light"] {
203
+ --brand-primary: #E11D48;
204
+ --action-primary-default: #E11D48;
205
+ --action-primary-hover: #BE123C;
206
+ --action-primary-active: #9F1239;
207
+ }
208
+
209
+ :root[data-theme="brand-a-dark"] {
210
+ --brand-primary: #FB7185;
211
+ --action-primary-default: #FB7185;
212
+ --action-primary-hover: #F43F5E;
213
+ --action-primary-active: #E11D48;
214
+ --background-default: #0A0A0A;
215
+ --surface-default: #151515;
216
+ --text-primary: #FFF;
217
+ --border-default: #2A2A2A;
218
+ }
219
+ ```
220
+
221
+ Switch brands in JS:
222
+
223
+ ```js
224
+ document.documentElement.setAttribute("data-theme", "brand-a-dark");
225
+ ```
226
+
227
+ See `scss/examples/_theming.scss` for complete Brand A (Rose) and Brand B (Teal) examples.
228
+
229
+ ---
230
+
231
+ ## Theme Switching
232
+
233
+ **cia ships zero JavaScript.** There is no `ciaTheme` object and no
234
+ theme-switching script in the package — switching is four lines you own.
235
+
236
+ ### You may not need to switch at all
237
+
238
+ A dual-mode theme (the unsuffixed ones: `sketchbook`, `prism`, `glass` …)
239
+ already follows the operating system. Its colours are `light-dark()` pairs and
240
+ its `color-scheme` is `light dark`, so light and dark both work with no
241
+ attribute, no script, and no listener. Ship one file and you are done.
242
+
243
+ You only need the code below to let a user override the OS — a manual
244
+ light/dark toggle, or a picker across several themes.
245
+
246
+ ### Switching modes
247
+
248
+ Set `color-scheme` on the root element:
249
+
250
+ ```js
251
+ document.documentElement.style.colorScheme = 'dark'; // or 'light', or '' for auto
252
+ ```
253
+
254
+ ### Switching themes
255
+
256
+ Set the attribute. This requires the multi-theme bundle
257
+ (`public/theme.css`), which carries every theme keyed by `data-theme`:
258
+
259
+ ```js
260
+ document.documentElement.dataset.theme = 'prism';
261
+ ```
262
+
263
+ With a single theme file you do not need this — the file's bare `:root`
264
+ already applies. See [The selector model](#the-selector-model--why-data-theme-is-usually-optional).
265
+
266
+ ### Avoiding the flash
267
+
268
+ Persisting a choice means reading it before first paint. Inline this in
269
+ `<head>`, above your stylesheet links — an external script would load too late
270
+ and the wrong theme would paint first:
271
+
272
+ ```html
273
+ <script>
274
+ try {
275
+ const t = localStorage.getItem('theme');
276
+ if (t) document.documentElement.dataset.theme = t;
277
+ const m = localStorage.getItem('mode');
278
+ if (m) document.documentElement.style.colorScheme = m;
279
+ } catch (e) { /* private mode — fall through to the default */ }
280
+ </script>
281
+ ```
282
+
283
+ Then write to `localStorage` whenever the user chooses. That is the whole
284
+ feature; a dependency would not make it shorter.
285
+
286
+ ---
287
+
288
+ ## Figma Token Sync
289
+
290
+ Import `figma-tokens/tokens.json` into [Tokens Studio for Figma](https://tokens.studio/) to keep design and code in sync. The JSON follows the Tokens Studio format with `value` and `type` fields.
package/THREE-TIERS.md ADDED
@@ -0,0 +1,158 @@
1
+ # Three Tiers, One Source of Truth
2
+
3
+ css-is-awesome ships three authoring surfaces for the same components. Pick the tier that matches your stack — they all resolve to the same mixin output, so styling stays consistent across an app that mixes them.
4
+
5
+ > One router mixin per component. Three doors into it.
6
+
7
+ ---
8
+
9
+ ## Tier 1 — Drop-in CSS + HTML (no build)
10
+
11
+ ```html
12
+ <link rel="stylesheet" href="css-is-awesome.min.css">
13
+ <link rel="stylesheet" href="theme-sketchbook.css">
14
+
15
+ <main class="cia-container">
16
+ <h1>Welcome</h1>
17
+ <a class="cia-btn-primary" href="/start">Get started</a>
18
+ <a class="cia-btn-outline" href="/docs">Read docs</a>
19
+ <article class="cia-card">
20
+ <h4>Sketchbook theme</h4>
21
+ <p>Warm paper, sumi ink, indigo accent.</p>
22
+ </article>
23
+ </main>
24
+ ```
25
+
26
+ **Audience:** designers, marketing pages, prototypes, anyone without a build step.
27
+ **Rules:** one class per element. No BEM, no `__element` / `--modifier` chains. One CSS file, one theme file, ship.
28
+
29
+ ---
30
+
31
+ ## Tier 2 — SCSS mixins + HTML (with build)
32
+
33
+ ```scss
34
+ // component styles (Card.module.scss, app.scss, …)
35
+ @use 'css-is-awesome/api' as cia; // one zero-emit barrel — the whole API
36
+
37
+ .hero-cta {
38
+ @include cia.btn(primary, $px: 6, $r: full);
39
+ @include cia.elevation(2);
40
+ }
41
+ .checkout-cancel { @include cia.btn(outline); }
42
+ .product-card { @include cia.card-base($shadow: 2); }
43
+ ```
44
+
45
+ > Prefer granular imports? `@use 'css-is-awesome/scss/components/buttons' as b;` etc. still work — the `/api` barrel just bundles them under one namespace.
46
+
47
+ ```html
48
+ <a class="hero-cta" href="/buy">Buy now</a>
49
+ <a class="checkout-cancel" href="/cart">Back to cart</a>
50
+ <article class="product-card">…</article>
51
+ ```
52
+
53
+ **Audience:** product teams that want their own domain vocabulary in markup (`hero-cta`, `product-card`) without giving up a design system.
54
+ **Rules:** author your own class names. Variant is an argument to the mixin, not a class modifier. Every parameter overridable.
55
+
56
+ ### The rule that makes Tier 2 work: change the input, not the output
57
+
58
+ Your class name, cia's mixin inside it, and **every customization goes through the mixin's arguments**. The mixin is a knob-board — each look-and-feel dimension is an input, so restyling never means writing the CSS the mixin already controls.
59
+
60
+ ```scss
61
+ // ✅ change the knob
62
+ .filter-bar { @include cia.flex($direction: column, $align: start, $gap: 2); }
63
+
64
+ // ❌ call the mixin, then fight it
65
+ .filter-bar {
66
+ @include cia.flex($justify: between);
67
+ align-items: flex-start; // $align: start already does this
68
+ flex-direction: column; // $direction: column already does this
69
+ }
70
+ ```
71
+
72
+ Both compile. The second is worse in a way that compounds: those two hand-written lines are now invisible to the design system. They don't follow a token, they don't respond to a theme swap, and the next person can't tell whether they were a deliberate exception or a missing argument.
73
+
74
+ **If a visual dimension can only be reached by overriding in CSS, that is a missing input — add it to the mixin.** That's the rule cia holds itself to, and it's why `space()`, `transition()` and `animate()` all accept raw values as well as scale keys: a consumer should never have to abandon a mixin to keep one specific number.
75
+
76
+ ### Where raw CSS *is* correct
77
+
78
+ Not everything is cia's job, and pretending otherwise produces worse code than writing plain CSS. Write ordinary CSS when the thing you are styling is genuinely yours:
79
+
80
+ - **Bespoke identity** — a rotated stamp, a brand illustration, a logo's overflow trick. Preserve the exact values; don't token-swap something whose specific-ness is the point.
81
+ - **One-off geometry** — a `clip-path`, a `grid-template-areas` for a layout only this page has, an animation of your own product's mascot.
82
+ - **Anything cia has no mixin for.** Reach for a mixin first, check `get_mixin` over MCP if unsure, and write CSS when the answer is genuinely "there isn't one."
83
+
84
+ The split to hold in your head: **cia owns the system values — colour, spacing, type, radius, motion, elevation. You own the composition and the things that make your product look like itself.** Mixing the two is fine. Re-implementing the first half by hand is the mistake.
85
+
86
+ ---
87
+
88
+ ## Tier 3 — Bare tags (opt-in Pico-mode)
89
+
90
+ ```scss
91
+ // app.scss — one line styles the whole site
92
+ @use 'css-is-awesome/scss/recipes/bare-tags';
93
+ ```
94
+
95
+ ```html
96
+ <h1>Welcome</h1>
97
+ <button>Save</button>
98
+ <table>…</table>
99
+ <input type="email">
100
+ ```
101
+
102
+ **Audience:** content-heavy sites, blog posts, READMEs rendered as HTML, anywhere the author doesn't want to think about classes.
103
+ **Rules:** zero classes required. The recipe styles every common bare tag at specificity `0,0,1` — no `@layer`, no `:where()`. Any class-based selector you add wins automatically.
104
+
105
+ ---
106
+
107
+ ## The same button across all three tiers
108
+
109
+ ```html
110
+ <!-- Tier 1 -->
111
+ <button class="cia-btn-primary">Save</button>
112
+
113
+ <!-- Tier 2 -->
114
+ <button class="save-btn">Save</button>
115
+ ```
116
+ ```scss
117
+ .save-btn { @include b.btn(primary); }
118
+ ```
119
+ ```html
120
+ <!-- Tier 3 -->
121
+ <button>Save</button>
122
+ ```
123
+
124
+ All three resolve to the same `btn(primary)` mixin output. Mix them in one app — a Tier 3 bare `<button>` and a Tier 1 `.cia-btn-primary` render identically.
125
+
126
+ ---
127
+
128
+ ## Architecture
129
+
130
+ - **Single source of truth.** One mixin per component (`btn`, `card`, `input`, `alert`, …) with private internals.
131
+ - **Router pattern.** `btn(variant)` dispatches to private mixins. Variant is an arg, not a class modifier — that's why there's no BEM.
132
+ - **Tier 1** = router output baked into single utility classes (`.cia-btn-primary`, `.cia-btn-outline`, …).
133
+ - **Tier 2** = direct router `@include` in author SCSS under custom class names.
134
+ - **Tier 3** = router `@include` applied to bare tag selectors via the recipe.
135
+ - **Tier 4 (React).** Components in `src/` wrap the same mixins through CSS Modules. Same output, framework-aware ergonomics.
136
+
137
+ Change the router, every tier updates. Add a variant once, every tier gets it.
138
+
139
+ ---
140
+
141
+ ## Picking a tier
142
+
143
+ | You're building… | Use |
144
+ |---|---|
145
+ | A landing page, prototype, or a static site with no build | Tier 1 |
146
+ | A product app where designers want semantic class names | Tier 2 |
147
+ | A content site, blog, or generated HTML where classes are noise | Tier 3 |
148
+ | A React/Next app | Tier 4 — see `src/` and the docs site |
149
+
150
+ Tiers compose. A Tier 2 product can drop in a Tier 3 recipe for its `/blog` route, and a Tier 1 marketing page can sit next to a Tier 4 app under the same theme file.
151
+
152
+ ---
153
+
154
+ ## See also
155
+
156
+ - [README.md](./README.md) — install, quick start, scripts
157
+ - [THEMING.md](./THEMING.md) — token contract, custom themes, dark mode
158
+ - [css-is-awesome.instructions.md](./css-is-awesome.instructions.md) — authoring rules in one page
package/VERSIONING.md ADDED
@@ -0,0 +1,200 @@
1
+ # Versioning Policy
2
+
3
+ How `css-is-awesome` versions its public surfaces, deprecates old APIs, and records changes. This document owns the **policy**; release mechanics (tag, build, publish) live in [`CONTRIBUTING.md`](./CONTRIBUTING.md).
4
+
5
+ ## Public surfaces
6
+
7
+ `css-is-awesome` ships three versioned public surfaces. A change to any of them is a versioned change.
8
+
9
+ | Surface | Lives in | Consumed as |
10
+ | ------------------- | ------------------------------------------ | ------------------------------------------ |
11
+ | CSS class names | `dist/*.css` | HTML / SCSS / React class strings |
12
+ | SCSS mixin API | `scss/` (`_mixins.scss`, `_layout.scss`, …) | Authors who `@use "css-is-awesome/scss/main"` |
13
+ | Token contract | [`CONTRACT.md`](./CONTRACT.md) + [`scripts/theme-contract.json`](./scripts/theme-contract.json) | Themes declared in a `:root { … }` block |
14
+
15
+ The **library version** lives in [`package.json`](./package.json). The **contract version** lives in the `version` field of `scripts/theme-contract.json`. They move together on most MAJOR bumps but are independent: a library MINOR can ship without bumping the contract.
16
+
17
+ ---
18
+
19
+ ## 1. Semantic Versioning
20
+
21
+ This project follows [SemVer 2.0.0](https://semver.org/spec/v2.0.0.html) — `MAJOR.MINOR.PATCH`.
22
+
23
+ ### MAJOR — `x.0.0`
24
+
25
+ Any change that can break a consumer upgrading blindly.
26
+
27
+ | Change | Example |
28
+ | ------------------------------------------------------------------------- | ------------------------------------------------------ |
29
+ | Public CSS class renamed or removed | `.btn-primary` → `.btn-cta` |
30
+ | SCSS mixin renamed, removed, or breaking signature change | `m.btn($variant)` now requires `$size` |
31
+ | SCSS mixin default changes rendered output | `m.card()` default radius flips from `md` → `lg` |
32
+ | Contract: required token renamed or removed | `--surface-default` → `--surface-base` |
33
+ | Contract: `version` field bumps to a new major (`"1"` → `"2"`) | Required-token removal in `scripts/theme-contract.json` |
34
+ | React component removed, renamed, or incompatible prop change | `<Button variant>` values narrowed |
35
+ | Peer-dependency floor rises | `react: >=18` → `react: >=19` |
36
+
37
+ ### MINOR — `0.x.0`
38
+
39
+ Additive, non-breaking changes.
40
+
41
+ | Change | Example |
42
+ | ------------------------------------------------------------ | ------------------------------------------------- |
43
+ | New public CSS class | `.cia-grid-auto-fit` added |
44
+ | New public SCSS mixin | `m.cluster($gap)` added |
45
+ | New optional token added to contract (`"1"` → `"1.1"`) | `--dropdown-offset-y` added to component section |
46
+ | New React component | `<DataTable>` added |
47
+ | Additive component prop with a sensible default | `<Button loading>` added, defaults to `false` |
48
+ | New utility class (`.cia-*`) | `.cia-text-balance` added |
49
+
50
+ ### PATCH — `0.0.x`
51
+
52
+ Internal-only or visually-identical changes.
53
+
54
+ | Change | Example |
55
+ | --------------------------------------------------------- | -------------------------------------------------- |
56
+ | Bug fix that does not change public API | Focus ring no longer clipped on `.btn-ghost` |
57
+ | Documentation content change | Typo fix in `CONTRACT.md` |
58
+ | Build output micro-optimization with no visual change | Redundant `0` removed from compiled shadows |
59
+ | Security patch that does not change public API | Upstream Sass `@import` path sanitization |
60
+ | Dependency bump within an already-allowed range | `sass ^1.97.1` → `sass ^1.98.0` |
61
+
62
+ ---
63
+
64
+ ## 2. Pre-1.0 rules
65
+
66
+ While the library is pre-1.0 (`0.x.x`), the rules above apply with one carve-out: we reserve the right to ship a genuinely-breaking change as a **MINOR** bump if it is the right call for the system's long-term shape. Every such change is:
67
+
68
+ 1. Called out loudly in the `CHANGELOG.md` entry under `### Changed` with a **BREAKING** prefix.
69
+ 2. Called out again in the release notes with a migration snippet.
70
+
71
+ **`1.0.0` locks the contract.** After 1.0, breaking changes require a MAJOR bump, no exceptions.
72
+
73
+ ---
74
+
75
+ ## 3. Deprecation policy
76
+
77
+ Every public symbol — CSS class, SCSS mixin, React prop, contract token — follows the same lifecycle: **deprecate → warn → remove**.
78
+
79
+ ### Lifecycle
80
+
81
+ 1. **Mark** the symbol with an inline `@deprecated` comment citing the replacement and the intended removal version.
82
+ 2. **Warn** at use-time:
83
+ - React components / hooks → `console.warn(…)` once per session (dedupe by symbol name).
84
+ - SCSS mixins → `@warn "m.old-name is deprecated, use m.new-name (removed in 1.0)";`.
85
+ - CSS classes / tokens → no runtime warning possible; rely on the `@deprecated` JSDoc and changelog.
86
+ 3. **Announce** in the next MINOR release's `CHANGELOG.md` under `### Deprecated`.
87
+ 4. **Keep functional** for **at least one full MINOR release cycle** after the deprecation lands.
88
+ 5. **Remove** only in a MAJOR bump.
89
+
90
+ ### Example — deprecating a mixin
91
+
92
+ ```scss
93
+ // scss/_mixins.scss
94
+ /// @deprecated Use `m.btn-primary` with `$bg: action-secondary-default` override.
95
+ /// Removed in 1.0.
96
+ @mixin btn-secondary($size: md) {
97
+ @warn "m.btn-secondary is deprecated; use m.btn-primary with $bg: action-secondary-default. Removed in 1.0.";
98
+ @include btn-primary($size, $bg: action-secondary-default);
99
+ }
100
+ ```
101
+
102
+ ### Example — deprecating a token
103
+
104
+ A deprecated contract token:
105
+
106
+ 1. Stays in `scripts/theme-contract.json` under `required` so existing themes keep validating.
107
+ 2. Gets a `> **Deprecated — removed in contract v2.**` note in its row in `CONTRACT.md`.
108
+ 3. Bumps the contract `version` by a minor step (e.g. `"1.1"` → `"1.2"`).
109
+ 4. Is removed from `required` only when the contract `version` bumps to the next major (`"2"`), which coincides with the next library MAJOR.
110
+
111
+ ---
112
+
113
+ ## 4. Changelog format
114
+
115
+ Changelog follows [Keep a Changelog 1.1.0](https://keepachangelog.com/en/1.1.0/) verbatim. Each release entry uses these sections, in this order, omitting any that are empty:
116
+
117
+ - `Added` — new features.
118
+ - `Changed` — changes to existing functionality.
119
+ - `Deprecated` — soon-to-be-removed features.
120
+ - `Removed` — removed features.
121
+ - `Fixed` — bug fixes.
122
+ - `Security` — vulnerabilities and mitigations.
123
+
124
+ Entries are written in past tense, grouped by section, and link issue / PR numbers. The top of [`CHANGELOG.md`](./CHANGELOG.md) always carries an `## [Unreleased]` section where in-flight changes accumulate between releases.
125
+
126
+ ### Example release block
127
+
128
+ ```md
129
+ ## [0.6.0] - 2026-05-12
130
+
131
+ ### Added
132
+ - `<DataTable>` component with generic row typing and optional pagination (#142).
133
+ - Theme validator now supports `--watch` mode (#138).
134
+
135
+ ### Changed
136
+ - `Pagination` props: native `HTMLAttributes.onChange` is `Omit`ped so the custom `onChange(page)` stops colliding. Non-breaking for existing consumers (#140).
137
+
138
+ ### Deprecated
139
+ - `m.btn-secondary` — use `m.btn-primary` with `$bg: action-secondary-default` override. Removed in 1.0 (#143).
140
+
141
+ ### Fixed
142
+ - Theme picker no longer injected duplicate `<link>` elements on first paint (#145).
143
+ ```
144
+
145
+ ---
146
+
147
+ ## 5. Conventional Commits → Changelog
148
+
149
+ Commits follow [Conventional Commits](https://www.conventionalcommits.org/). The prefix maps to a changelog section:
150
+
151
+ | Commit prefix | Changelog section |
152
+ | ----------------------------------------- | ------------------------------- |
153
+ | `feat:` | `Added` |
154
+ | `feat!:` or `BREAKING CHANGE:` footer | `Changed` (breaking — MAJOR) |
155
+ | `fix:` | `Fixed` |
156
+ | `perf:` | `Changed` |
157
+ | `docs:` | *omit* |
158
+ | `refactor:` | *omit* |
159
+ | `test:` | *omit* |
160
+ | `chore:` | *omit* |
161
+ | `build:` / `ci:` | *omit* |
162
+
163
+ A commit can carry a `Deprecates:` footer to force an entry under `### Deprecated`, or a `Security:` footer to force `### Security`, regardless of prefix.
164
+
165
+ ---
166
+
167
+ ## 6. Release process
168
+
169
+ The policy in this document tells you **what** a version number means. The mechanics of cutting a release — tagging, building `dist/*.css`, validating themes, publishing to npm — live in [`CONTRIBUTING.md`](./CONTRIBUTING.md) and are automated per Epic 5.
170
+
171
+ Every release, at minimum:
172
+
173
+ 1. Promotes `## [Unreleased]` to `## [x.y.z] - YYYY-MM-DD` in `CHANGELOG.md`.
174
+ 2. Bumps `version` in `package.json` per the rules in §1.
175
+ 3. Bumps `version` in `scripts/theme-contract.json` if the contract changed.
176
+ 4. Runs `npm run validate-themes` — must pass.
177
+ 5. Rebuilds `dist/*.css` via `npm run build:css:all`.
178
+ 6. Tags the commit `vX.Y.Z` and pushes.
179
+
180
+ ---
181
+
182
+ ## 7. How to read the version
183
+
184
+ Two version numbers, two files:
185
+
186
+ | Version | Source | Bumps on |
187
+ | ------------------- | ------------------------------------------------------------- | ------------------------------------------------------------------------- |
188
+ | Library version | `version` field of [`package.json`](./package.json) | Any change to CSS / SCSS / React public surfaces per §1. |
189
+ | Contract version | `version` field of [`scripts/theme-contract.json`](./scripts/theme-contract.json) | Contract-only changes (new optional token → minor; required token renamed or removed → major). |
190
+
191
+ They usually move together on a library MAJOR. They move independently on MINOR and PATCH.
192
+
193
+ ---
194
+
195
+ ## See also
196
+
197
+ - [`CHANGELOG.md`](./CHANGELOG.md) — the actual change log.
198
+ - [`CONTRACT.md`](./CONTRACT.md) — the token contract.
199
+ - [`CONTRIBUTING.md`](./CONTRIBUTING.md) — release mechanics and contributor workflow.
200
+ - [`ROADMAP.md`](./ROADMAP.md) — where the library is headed toward `1.0.0`.
package/_index.scss ADDED
@@ -0,0 +1,34 @@
1
+ // ============================================================================
2
+ // Package-root shim for `@use 'css-is-awesome'`
3
+ // ============================================================================
4
+ // Sass does NOT read package.json "exports". It resolves a bare specifier by
5
+ // looking for a file on disk relative to a load path — for `css-is-awesome`
6
+ // that means `node_modules/css-is-awesome/_index.scss`. Without this file the
7
+ // README's headline example fails on a clean install.
8
+ //
9
+ // This file must behave the way `exports["."].sass` does, or Sass users and
10
+ // bundler users get different results from the same import. It forwards BOTH
11
+ // halves deliberately:
12
+ //
13
+ // @forward './scss/index' — the authoring API (mixins, functions, token
14
+ // maps) plus the global .cia-anim-* utilities.
15
+ // Without this, `@use 'css-is-awesome' as cia;`
16
+ // exports no members and `cia.btn()` fails to
17
+ // resolve: scss/main.scss uses `@use`, not
18
+ // `@forward`, so it re-exports nothing.
19
+ //
20
+ // @use './scss/main' — emits the tokens, resets and base rules.
21
+ // Without this the import prints no `:root` block
22
+ // at all, contradicting the documented "emit the
23
+ // tokens once at your root" model. That exact
24
+ // regression shipped briefly and is now asserted
25
+ // by validate-package.
26
+ //
27
+ // Sass evaluates each module once, so modules shared by the two halves
28
+ // (animations-utilities in particular) are not emitted twice — verified.
29
+ //
30
+ // This is the GLOBAL entry point: it emits ~72 KB and belongs in a root
31
+ // stylesheet, included once. Component stylesheets want `css-is-awesome/api`,
32
+ // which is zero-emit and safe under Next.js CSS Modules pure mode.
33
+ @forward './scss/index';
34
+ @use './scss/main';