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.
- package/AGENTS.md +343 -0
- package/CHANGELOG.md +1036 -0
- package/CLAUDE.md +9 -0
- package/CONTRACT.md +580 -0
- package/GEMINI.md +9 -0
- package/LICENSE +21 -0
- package/LICENSE-third-party +88 -0
- package/MIGRATION.md +312 -0
- package/README.md +380 -0
- package/ROADMAP.md +714 -0
- package/THEMING.md +290 -0
- package/THREE-TIERS.md +158 -0
- package/VERSIONING.md +200 -0
- package/_index.scss +34 -0
- package/api.scss +16 -0
- package/bin/README.md +57 -0
- package/bin/cia.cjs +114 -0
- package/bin/migrate-bootstrap.cjs +427 -0
- package/bin/migrate-tailwind.cjs +832 -0
- package/css-is-awesome.instructions.md +533 -0
- package/dist/css-is-awesome.core.css +598 -0
- package/dist/css-is-awesome.core.min.css +1 -0
- package/dist/css-is-awesome.css +3733 -0
- package/dist/css-is-awesome.min.css +1 -0
- package/dist/css-is-awesome.utilities.css +2445 -0
- package/dist/css-is-awesome.utilities.min.css +1 -0
- package/dist/tokens.css +508 -0
- package/dist/tokens.d.ts +266 -0
- package/dist/tokens.min.css +1 -0
- package/figma-tokens/README.md +57 -0
- package/figma-tokens/primitives-brand.scss +121 -0
- package/figma-tokens/semantic-dark.scss +61 -0
- package/figma-tokens/semantic-light.scss +61 -0
- package/figma-tokens/tokens.json +144 -0
- package/llm.txt +183 -0
- package/mcp/server.cjs +1495 -0
- package/package.json +199 -0
- package/public/icons/README.md +278 -0
- package/public/icons/arrow-right.svg +4 -0
- package/public/icons/check.svg +4 -0
- package/public/icons/chevron-down.svg +4 -0
- package/public/icons/close.svg +4 -0
- package/public/icons/core/arrow-down.svg +13 -0
- package/public/icons/core/arrow-left.svg +13 -0
- package/public/icons/core/arrow-right.svg +13 -0
- package/public/icons/core/arrow-up.svg +13 -0
- package/public/icons/core/bell.svg +13 -0
- package/public/icons/core/calendar.svg +15 -0
- package/public/icons/core/check.svg +12 -0
- package/public/icons/core/chevron-down.svg +12 -0
- package/public/icons/core/chevron-left.svg +12 -0
- package/public/icons/core/chevron-right.svg +12 -0
- package/public/icons/core/chevron-up.svg +12 -0
- package/public/icons/core/clock.svg +13 -0
- package/public/icons/core/close.svg +13 -0
- package/public/icons/core/copy.svg +13 -0
- package/public/icons/core/download.svg +14 -0
- package/public/icons/core/edit.svg +13 -0
- package/public/icons/core/error.svg +14 -0
- package/public/icons/core/external-link.svg +14 -0
- package/public/icons/core/eye-off.svg +15 -0
- package/public/icons/core/eye.svg +13 -0
- package/public/icons/core/filter.svg +12 -0
- package/public/icons/core/heart.svg +12 -0
- package/public/icons/core/help.svg +14 -0
- package/public/icons/core/home.svg +13 -0
- package/public/icons/core/info.svg +14 -0
- package/public/icons/core/loading.svg +12 -0
- package/public/icons/core/lock.svg +13 -0
- package/public/icons/core/mail.svg +13 -0
- package/public/icons/core/menu.svg +14 -0
- package/public/icons/core/message.svg +12 -0
- package/public/icons/core/minus.svg +12 -0
- package/public/icons/core/more-horizontal.svg +14 -0
- package/public/icons/core/pause.svg +13 -0
- package/public/icons/core/play.svg +12 -0
- package/public/icons/core/plus.svg +13 -0
- package/public/icons/core/refresh.svg +15 -0
- package/public/icons/core/save.svg +14 -0
- package/public/icons/core/search.svg +13 -0
- package/public/icons/core/settings.svg +13 -0
- package/public/icons/core/share.svg +14 -0
- package/public/icons/core/sort.svg +15 -0
- package/public/icons/core/star.svg +12 -0
- package/public/icons/core/success.svg +13 -0
- package/public/icons/core/trash.svg +16 -0
- package/public/icons/core/unlock.svg +13 -0
- package/public/icons/core/upload.svg +14 -0
- package/public/icons/core/user.svg +13 -0
- package/public/icons/core/users.svg +15 -0
- package/public/icons/core/warning.svg +14 -0
- package/public/icons/download.svg +4 -0
- package/public/icons/edit.svg +4 -0
- package/public/icons/menu.svg +4 -0
- package/public/icons/search.svg +4 -0
- package/public/theme.css +4179 -0
- package/public/themes/README.md +102 -0
- package/public/themes/boilerplate/theme.css +154 -0
- package/public/themes/boilerplate-dark/theme.css +171 -0
- package/public/themes/boilerplate-light/theme.css +171 -0
- package/public/themes/cupertino/theme.css +165 -0
- package/public/themes/cupertino-dark/theme.css +200 -0
- package/public/themes/cupertino-light/theme.css +227 -0
- package/public/themes/glass/theme.css +190 -0
- package/public/themes/glass-dark/theme.css +201 -0
- package/public/themes/glass-light/theme.css +229 -0
- package/public/themes/graphite/theme.css +155 -0
- package/public/themes/graphite-dark/theme.css +223 -0
- package/public/themes/graphite-light/theme.css +191 -0
- package/public/themes/press/theme.css +189 -0
- package/public/themes/press-dark/theme.css +199 -0
- package/public/themes/press-light/theme.css +235 -0
- package/public/themes/prism/theme.css +159 -0
- package/public/themes/prism-dark/theme.css +185 -0
- package/public/themes/prism-light/theme.css +188 -0
- package/public/themes/sketchbook/theme.css +152 -0
- package/public/themes/sketchbook-dark/theme.css +194 -0
- package/public/themes/sketchbook-light/theme.css +188 -0
- package/public/themes/terminal/theme.css +159 -0
- package/public/themes/terminal-dark/theme.css +229 -0
- package/public/themes/terminal-light/theme.css +152 -0
- package/scripts/README.md +70 -0
- package/scripts/icon-contract.json +67 -0
- package/scripts/icon-validator.js +205 -0
- package/scripts/prepare-dist.mjs +65 -0
- package/scripts/theme-a11y.js +415 -0
- package/scripts/theme-contract.json +171 -0
- package/scripts/theme-validator.js +674 -0
- package/scss/README.md +55 -0
- package/scss/_animations-utilities.scss +107 -0
- package/scss/_animations.scss +248 -0
- package/scss/_app-styles.scss +15 -0
- package/scss/_generator.scss +174 -0
- package/scss/_icons.scss +475 -0
- package/scss/_index.scss +17 -0
- package/scss/_layout.scss +597 -0
- package/scss/_mixins.scss +1036 -0
- package/scss/_system.scss +291 -0
- package/scss/_utilities.scss +377 -0
- package/scss/api.scss +61 -0
- package/scss/components/_accordion.scss +72 -0
- package/scss/components/_buttons.scss +171 -0
- package/scss/components/_copy-button.scss +69 -0
- package/scss/components/_data.scss +190 -0
- package/scss/components/_feedback.scss +197 -0
- package/scss/components/_forms.scss +272 -0
- package/scss/components/_index.scss +17 -0
- package/scss/components/_navigation.scss +186 -0
- package/scss/components/_overlay.scss +259 -0
- package/scss/components/_stepper.scss +159 -0
- package/scss/components/_tabs.scss +72 -0
- package/scss/core.scss +54 -0
- package/scss/examples/_theming.scss +107 -0
- package/scss/examples/_usage.scss +317 -0
- package/scss/main.scss +64 -0
- package/scss/recipes/README.md +110 -0
- package/scss/recipes/_bare-tags.scss +232 -0
- package/scss/recipes/_recipe-template.md +119 -0
- package/scss/recipes/combobox.md +509 -0
- package/scss/recipes/dialog.md +265 -0
- package/scss/recipes/print-to-pdf.md +263 -0
- package/scss/theme/README.md +67 -0
- package/scss/theme/_brand.scss +18 -0
- package/scss/theme/_colors-dark.scss +68 -0
- package/scss/theme/_colors-light.scss +70 -0
- package/scss/theme/_components.scss +94 -0
- package/scss/theme/_icons.scss +104 -0
- package/scss/theme/_index.scss +62 -0
- package/scss/theme/_shadows.scss +65 -0
- package/scss/theme/_shape.scss +13 -0
- package/scss/theme/_typography.scss +9 -0
- package/scss/theme/registry.json +447 -0
- package/scss/themes/boilerplate-dark.scss +212 -0
- package/scss/themes/boilerplate-light.scss +209 -0
- package/scss/themes/boilerplate.scss +181 -0
- package/scss/themes/cupertino-dark.scss +238 -0
- package/scss/themes/cupertino-light.scss +266 -0
- package/scss/themes/cupertino.scss +194 -0
- package/scss/themes/glass-dark.scss +238 -0
- package/scss/themes/glass-light.scss +266 -0
- package/scss/themes/glass.scss +220 -0
- package/scss/themes/graphite-dark.scss +260 -0
- package/scss/themes/graphite-light.scss +228 -0
- package/scss/themes/graphite.scss +182 -0
- package/scss/themes/press-dark.scss +237 -0
- package/scss/themes/press-light.scss +273 -0
- package/scss/themes/press.scss +241 -0
- package/scss/themes/prism-dark.scss +217 -0
- package/scss/themes/prism-light.scss +220 -0
- package/scss/themes/prism.scss +190 -0
- package/scss/themes/sketchbook-dark.scss +231 -0
- package/scss/themes/sketchbook-light.scss +231 -0
- package/scss/themes/sketchbook.scss +178 -0
- package/scss/themes/terminal-dark.scss +267 -0
- package/scss/themes/terminal-light.scss +175 -0
- package/scss/themes/terminal.scss +185 -0
- package/scss/tokens.scss +23 -0
- 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`.
|