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/README.md
ADDED
|
@@ -0,0 +1,380 @@
|
|
|
1
|
+
# css-is-awesome
|
|
2
|
+
|
|
3
|
+
> A tiny, mixin-first SCSS design system with one-file theme swap.
|
|
4
|
+
|
|
5
|
+
[](https://github.com/Jerry2d3d/css-is-awesome/actions/workflows/ci.yml) [](./package.json) [](./LICENSE) [](https://github.com/semantic-release/semantic-release)
|
|
6
|
+
|
|
7
|
+
**Bring your own selectors. We bring the design system.** One CSS file per theme — drop it in and the page restyles, no markup change. 24 themes. Zero JavaScript in the npm package. Six browser-native interactive components. Small enough to read in an afternoon.
|
|
8
|
+
|
|
9
|
+
> **Shipped in 1.0.0:** a **recipes book** for building any component in any framework using cia mixins — `dialog`, `combobox` and `print-to-pdf` today, with `datepicker`, `data-table` and `command-palette` queued. AI agents read recipes via MCP and generate components in your stack; humans read them at `/docs/recipes`.
|
|
10
|
+
|
|
11
|
+
## For AI agents — start here
|
|
12
|
+
|
|
13
|
+
**Read [`llm.txt`](./llm.txt) first.** One file, the whole system: install path, hard rules, the mixin vocabulary, and the traps that make agents write wrong cia code. It ships in the npm package, so it's at `node_modules/css-is-awesome/llm.txt` in any project that has cia.
|
|
14
|
+
|
|
15
|
+
**Then connect the MCP server** and stop guessing at signatures. It answers from the real source — 30 tools covering themes, mixins, functions, tokens, recipes and components.
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install -D @modelcontextprotocol/sdk zod # required — npm will NOT install these for you
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
```json
|
|
22
|
+
{
|
|
23
|
+
"mcpServers": {
|
|
24
|
+
"css-is-awesome": {
|
|
25
|
+
"command": "node",
|
|
26
|
+
"args": ["node_modules/css-is-awesome/mcp/server.cjs"]
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
The SDK and `zod` are declared as *optional* peer dependencies, so a plain `npm install css-is-awesome` skips them and the server exits with `@modelcontextprotocol/sdk is not installed`. Install both. `npx css-is-awesome-mcp` does **not** work around this — npx fetches the package but not its optional peers.
|
|
33
|
+
|
|
34
|
+
Why it matters more here than for older frameworks: no model has memorised cia's API the way it has memorised Tailwind's class names. Without `llm.txt` or MCP, an agent will confidently invent a Tailwind-shaped API. With them, it reads the real thing. Details at [`/docs/mcp`](./src/app/docs/mcp/page.tsx).
|
|
35
|
+
|
|
36
|
+
## Three ways to use it
|
|
37
|
+
|
|
38
|
+
> Full breakdown in [THREE-TIERS.md](./THREE-TIERS.md). cia is **mixin-first** since v0.8 — utility classes are an opt-in convenience.
|
|
39
|
+
|
|
40
|
+
### 1. SCSS with mixin API (recommended)
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
npm install css-is-awesome
|
|
44
|
+
npm install -D sass
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
```scss
|
|
48
|
+
@use 'css-is-awesome' as cia;
|
|
49
|
+
|
|
50
|
+
.checkout-cta { @include cia.btn(primary); }
|
|
51
|
+
.faq-item { @include cia.accordion; }
|
|
52
|
+
.modal { @include cia.modal; }
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
```html
|
|
56
|
+
<!-- One theme file: no data-theme attribute required -->
|
|
57
|
+
<link rel="stylesheet" href="/cia/themes/boilerplate/theme.css">
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Author your own class names; the mixin handles the styling. Mixins for buttons, forms, layout, typography, color, motion, plus the six zero-JS components: `accordion`, `modal`, `tooltip`, `dropdown`, `tabs`, `copy-button` — plus print-to-PDF via a pure-CSS `@media print` layer. Full reference at [`/docs/mixins`](https://github.com/Jerry2d3d/css-is-awesome/blob/main/src/app/docs/mixins/page.tsx).
|
|
61
|
+
|
|
62
|
+
**Two imports, two jobs.** Emit the tokens once from your root/global stylesheet (`@use 'css-is-awesome';` or `<link>` a theme file), then import the **zero-emit authoring barrel** in each component stylesheet:
|
|
63
|
+
|
|
64
|
+
```scss
|
|
65
|
+
// Card.module.scss — emits no :root, safe under Next.js CSS Modules pure mode
|
|
66
|
+
@use 'css-is-awesome/api' as cia;
|
|
67
|
+
|
|
68
|
+
.product-card { @include cia.card-base($shadow: 2); }
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
> **Next.js / Turbopack.** The barrel works under Turbopack — the showcase app runs Next 16 on Turbopack for both `dev` and `build` and imports `css-is-awesome/scss/api` in every stylesheet.
|
|
72
|
+
>
|
|
73
|
+
> Sass doesn't read package.json `exports`, so add `node_modules` to `sassOptions.loadPaths`.
|
|
74
|
+
>
|
|
75
|
+
> One reported hazard, stated with its provenance: a consumer whose own styles directory was on `loadPaths` hit `Two forwarded modules both define a mixin named stack`. cia's internal forwards are all `./`-relative, and standard Sass resolves those against the importing file **without** consulting load paths — `validate-package` asserts the barrel survives a hostile `styles/_mixins.scss` ahead of `node_modules`, and it does. So this appears to be a bundler-resolver deviation rather than Sass behaviour. If you hit it, drop your styles directory from `loadPaths`.
|
|
76
|
+
|
|
77
|
+
### 2. Drop-in CSS (zero build)
|
|
78
|
+
|
|
79
|
+
```html
|
|
80
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/css-is-awesome@1/public/themes/boilerplate/theme.css">
|
|
81
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/css-is-awesome@1/dist/css-is-awesome.min.css">
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Theme first (sets the tokens), library second. **No `data-theme` attribute needed** — a single theme file styles the page on its own. Swap the URL to swap the theme; the HTML never changes. Bundle tiers — `dist/tokens.css` (2.2 KB gz, `:where(:root)` vars only), `dist/css-is-awesome.core.min.css` (2.4 KB gz, tokens + resets), `dist/css-is-awesome.min.css` (7.3 KB gz, full).
|
|
85
|
+
|
|
86
|
+
### 3. Bare tags (opt-in Pico-mode)
|
|
87
|
+
|
|
88
|
+
```scss
|
|
89
|
+
@use 'css-is-awesome/scss/recipes/bare-tags';
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
```html
|
|
93
|
+
<button>Save</button>
|
|
94
|
+
<table>…</table>
|
|
95
|
+
<input type="email">
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
One line styles the whole site. Zero classes. Wrapped in `:where()` (specificity `0,0,0`), so any consumer selector — even another bare tag — wins automatically. No `@layer`, no cascade pollution.
|
|
99
|
+
|
|
100
|
+
## Themes
|
|
101
|
+
|
|
102
|
+
**One file per theme since v0.8.** Each theme file emits `:root, :root[data-theme="<name>"]`, so **dropping one in restyles the page with no markup change** — the `data-theme` attribute is optional for the single-file case. Set `<html data-theme="...">` only when you load the all-in-one bundle (`public/theme.css`), where every theme shares one file and the attribute is the only thing telling them apart.
|
|
103
|
+
|
|
104
|
+
**8 families, 24 files.** Each family ships a dual-mode base (`<name>`, both light + dark via native `light-dark()`) plus single-mode `<name>-light` and `<name>-dark` variants for pairing.
|
|
105
|
+
|
|
106
|
+
| Theme | Mood | Modes |
|
|
107
|
+
|---------------|---------------------------------------------------------------|-------|
|
|
108
|
+
| boilerplate | Neutral slate + clean blue, system fonts, drop-in starter | both |
|
|
109
|
+
| sketchbook | Warm washi paper / charcoal at night, sumi ink, indigo accent | both (brand default) |
|
|
110
|
+
| press | Editorial newsprint / night-edition, Playfair serif, press-red | both |
|
|
111
|
+
| prism | Vercel/Linear/Radix aesthetic, refined blue, neutral grays | both |
|
|
112
|
+
| cupertino | macOS AppKit, SF Pro, system blue, vibrancy blurs | both |
|
|
113
|
+
| glass | visionOS glassmorphism, iOS indigo, blur asymmetric per mode | both (Pattern C) |
|
|
114
|
+
| graphite | Brushed silver / machined dark aluminum, SF system stack | both |
|
|
115
|
+
| terminal | VT100 phosphor green, zero radii, CRT glow | dark-only base (`terminal-light` is the daylight companion) |
|
|
116
|
+
|
|
117
|
+
**Pair two themes per mode** with native `<link media>`:
|
|
118
|
+
```html
|
|
119
|
+
<link rel="stylesheet" href="/themes/sketchbook-light/theme.css" media="(prefers-color-scheme: light)">
|
|
120
|
+
<link rel="stylesheet" href="/themes/terminal-dark/theme.css" media="(prefers-color-scheme: dark)">
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Newspaper by day, hacker terminal by night. No JS, no mixin — pure browser behavior. Most design systems give you dark mode; cia lets you ship a second brand at night. See [`/docs/themes/pairing`](./src/app/docs/themes/pairing/page.tsx).
|
|
124
|
+
|
|
125
|
+
Each theme is one file of CSS custom properties. Tokens only — no component rules. See `public/themes/<name>/theme.css` for the compiled output and `scss/themes/<name>.scss` for the sources. Full contract documented in [THEMING.md](./THEMING.md).
|
|
126
|
+
|
|
127
|
+
The library's own default tokens emit under `:where(:root)` (specificity `0,0,0`), so any theme declaration outranks them regardless of load order. That's `:where()`, not `@layer` — see the print section below for why cia refuses layers.
|
|
128
|
+
|
|
129
|
+
### Edit a theme or make your own
|
|
130
|
+
|
|
131
|
+
**You can.** Themes are open files. Edit any token, make brand-new themes, mix and match — cia treats themes as data, not internal magic.
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
# 1. Copy an existing theme as a starting point
|
|
135
|
+
cp scss/themes/boilerplate.scss scss/themes/midnight.scss
|
|
136
|
+
|
|
137
|
+
# 2. Edit tokens (see scss/themes/*.scss for the pattern + light-dark() usage)
|
|
138
|
+
# Wrap in @include cia.theme('midnight') { ... }
|
|
139
|
+
|
|
140
|
+
# 3. Build to public/themes/midnight/theme.css (also regenerates public/theme.css)
|
|
141
|
+
npm run build:css:themes
|
|
142
|
+
|
|
143
|
+
# 4. Validate against the token contract + a11y audit
|
|
144
|
+
node scripts/theme-validator.js public/themes/midnight/theme.css
|
|
145
|
+
|
|
146
|
+
# 5. Use it — just link the file. No data-theme attribute needed.
|
|
147
|
+
# <link rel="stylesheet" href="/themes/midnight/theme.css">
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
Full authoring walkthrough: [`/docs/authoring/themes`](./src/app/docs/authoring/themes/page.tsx). The contract (127 required + 36 optional tokens) is at [`scripts/theme-contract.json`](./scripts/theme-contract.json).
|
|
151
|
+
|
|
152
|
+
## Token contract
|
|
153
|
+
|
|
154
|
+
Every theme declares the same slots: **surfaces · ink · lines · primary · seal · accent · code · type · space · radius · shadow · blur · glow · motion**. Components read tokens, themes set tokens, nothing else. 127 required, 36 optional.
|
|
155
|
+
|
|
156
|
+
**Themes own the spacing scale.** A theme declares the numbered scale `--space-0` … `--space-9` (contract-required), which is exactly what `cia.space(4)` compiles to — so a theme can ship tighter or airier rhythm without touching a component. The six t-shirt names (`--space-2xs/xs/sm/md/lg/xl`) are optional; the library emits them as references (`--space-md: var(--space-4)`), so they track the numbered scale automatically.
|
|
157
|
+
|
|
158
|
+
Component-shape tokens (`--btn-radius`, `--card-radius`, `--input-radius`, `--modal-radius`, `--badge-radius`, `--tag-radius`) are optional too, and cascade from the generic radii by default — `--btn-radius: var(--radius-md, 0.25rem)`. Set the generic `--radius-*` scale to move everything; set a component token to make one thing an exception.
|
|
159
|
+
|
|
160
|
+
## Icons (two systems, very different setup costs)
|
|
161
|
+
|
|
162
|
+
**SVG pack — self-contained, drop-in.** 49 glyphs vendored from Lucide ship at `public/icons/core/<name>.svg`. Nothing to install.
|
|
163
|
+
|
|
164
|
+
```scss
|
|
165
|
+
@use 'css-is-awesome/api' as cia;
|
|
166
|
+
|
|
167
|
+
.save-btn { @include cia.icon-svg(check); } // tinted via currentColor
|
|
168
|
+
.next { @include cia.icon-svg-text(arrow-right, $position: after); }
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
**Adding your own glyph is genuinely drop-in — no registration step.** Put `star.svg` in `public/icons/core/` and `cia.icon-svg(star)` works immediately; the name resolves straight to a filename. `npm run validate-icons` checks the 49 contract glyphs are present and does not object to extras.
|
|
172
|
+
|
|
173
|
+
Every icon emits a per-glyph custom property, so a theme can swap one without touching SCSS:
|
|
174
|
+
|
|
175
|
+
```css
|
|
176
|
+
mask: var(--cia-icon-check, url('/icons/core/check.svg')) center / contain no-repeat;
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
```scss
|
|
180
|
+
:root[data-theme='terminal'] { --cia-icon-check: url('/themes/terminal/icons/check.svg'); }
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
Resolution order is **per-theme override → core pack → 404**.
|
|
184
|
+
|
|
185
|
+
**Font Awesome — bring your own fonts.** `cia.icon-fa()`, `icon-fa-icon()`, `icon-fa-text()` and `icon-fa-spin()` exist for teams already on FA. They map a name through `$icon-fa-map` (55 entries) to a codepoint and set the FA font family:
|
|
186
|
+
|
|
187
|
+
```css
|
|
188
|
+
.a { font-family: "Font Awesome 6 Free"; font-weight: 900; content: "\f00c"; }
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
⚠️ **cia ships no Font Awesome files** — FA has its own licence, so vendoring it would be wrong. The `fa-*` mixins compile to valid CSS but render as tofu until you:
|
|
192
|
+
|
|
193
|
+
1. supply the `.woff2` files yourself,
|
|
194
|
+
2. put them where `$theme-fa-path` points (default `/webfonts` — this directory does **not** exist in the package),
|
|
195
|
+
3. call `@include cia.icon-fa-load;` once at your root.
|
|
196
|
+
|
|
197
|
+
Missing font files don't error, so a silent tofu box is the failure mode. If you only want icons that work out of the box, use the SVG pack.
|
|
198
|
+
|
|
199
|
+
## Recipes (build components without a component library)
|
|
200
|
+
|
|
201
|
+
cia ships **no component library** — deliberately. Interactive patterns arrive as *recipes*: portable markdown files at [`scss/recipes/`](./scss/recipes/) that give you the correct HTML, the `cia.X` mixin calls to style it, and an a11y checklist graded against WCAG 2.2 AA. Copy the pattern into your own framework; you own the component, cia owns the styling and the accessibility homework.
|
|
202
|
+
|
|
203
|
+
**Shipped:** `dialog`, `combobox`, `print-to-pdf`. Queued for 1.0.0: `datepicker`, `data-table`, `command-palette`.
|
|
204
|
+
|
|
205
|
+
Humans read them at [`/docs/recipes`](./src/app/docs/recipes/page.tsx); AI agents pull them over MCP with `list_recipes` / `get_recipe`.
|
|
206
|
+
|
|
207
|
+
## Migrating from Tailwind or Bootstrap
|
|
208
|
+
|
|
209
|
+
The `cia` CLI converts another design system's config into a cia theme, so you start from your existing colors and spacing instead of a blank file:
|
|
210
|
+
|
|
211
|
+
```bash
|
|
212
|
+
npx cia migrate tailwind ./tailwind.config.js # auto-detects tailwind.config.* if omitted
|
|
213
|
+
npx cia migrate bootstrap ./scss/_variables.scss
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
Both accept `--help` for the full option list. Prose walkthroughs live at [`/docs/migration-tailwind`](./src/app/docs/migration-tailwind/page.tsx) and [`/docs/migration-bootstrap`](./src/app/docs/migration-bootstrap/page.tsx).
|
|
217
|
+
|
|
218
|
+
## Print / PDF (zero JS)
|
|
219
|
+
|
|
220
|
+
Print support is a pure-CSS layer — the browser's native **Print → Save as PDF** is the generator, and cia ships no JavaScript for it:
|
|
221
|
+
|
|
222
|
+
```scss
|
|
223
|
+
@use 'css-is-awesome/api' as cia;
|
|
224
|
+
|
|
225
|
+
// Once, in a GLOBAL stylesheet — never inside a component module.
|
|
226
|
+
// It emits its own :root block plus @page, so don't wrap it in a selector.
|
|
227
|
+
@include cia.print-base; // optional: ($size, $margin, $freeze-animations)
|
|
228
|
+
|
|
229
|
+
.site-nav { @include cia.print-hidden; } // drop chrome on paper
|
|
230
|
+
.print-note { @include cia.print-only; } // reveal paper-only content
|
|
231
|
+
.invoice { @include cia.print { border: 1px solid; } } // bare @media print wrapper
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
`print-base` also collapses animations to zero duration and pins them to their final frame, so a page snapshotted mid-entrance-fade doesn't print as invisible text. It deliberately does **not** force `opacity: 1` or `transform: none` — that would fix the fade while flattening every intentional use of the same properties (a 0.15 watermark, a 0.4 disabled control, a stamp rotated `-4deg`). Elements that were never animating are left untouched. Read `--is-print` (`0` on screen, `1` on paper) for custom effects. Full walkthrough: the [`print-to-pdf`](./scss/recipes/print-to-pdf.md) recipe.
|
|
235
|
+
|
|
236
|
+
### Why the print mixins use `!important`
|
|
237
|
+
|
|
238
|
+
cia avoids `!important` everywhere else. The print layer is the exception, and it's deliberate.
|
|
239
|
+
|
|
240
|
+
**`@media` contributes no specificity.** `print-hidden` is included *inside* your selector, so the rule it generates has exactly that selector's specificity — and any later declaration at equal specificity wins, including in print. Measured in a browser:
|
|
241
|
+
|
|
242
|
+
| | result in print |
|
|
243
|
+
|---|---|
|
|
244
|
+
| `display: var(--print-hide) !important` | `none` ✓ hides |
|
|
245
|
+
| same rule without `!important` | `flex` ✗ prints anyway |
|
|
246
|
+
|
|
247
|
+
The competing `display` usually isn't yours — it's a utility class or a component library cia can never see. Without `!important` the mixin fails *silently, on paper only*, which is the worst place to discover it.
|
|
248
|
+
|
|
249
|
+
**`@layer` would be worse, not better.** Layered CSS always loses to unlayered CSS. If cia's print rules lived in a layer and your own CSS is unlayered (the normal case), your `display: flex` would win and the nav would print. cia can't require consumers to adopt layers — see [`.agent/decisions/decided/04-at-layer-decision.md`](./.agent/decisions/decided/04-at-layer-decision.md). `!important` also *inverts* layer order, so the two don't compose the way you'd expect.
|
|
250
|
+
|
|
251
|
+
The scope is kept narrow: 8 `!important` declarations, all inside `@media print`, all doing one of two jobs — beating a `display` rule, or beating an author `animation` shorthand. The values stay variable-driven (`--print-hide`, `--print-show`), so you can still override behaviour without fighting the mixin.
|
|
252
|
+
|
|
253
|
+
## MCP server (for AI agents)
|
|
254
|
+
|
|
255
|
+
cia ships a Model Context Protocol stdio server (JSON-RPC over stdio, protocol `2024-11-05`) at [`mcp/server.cjs`](./mcp/server.cjs), exposed as the `css-is-awesome-mcp` bin. It's in the `files` manifest, so it lands in every consumer's `node_modules`. Any MCP-aware client (Claude Code, Cursor, Aider, Gemini, Copilot) can then query cia's real design system — mixin signatures, tokens, themes, recipes — instead of guessing, without grep-walking the repo. Exposes **30 tools** across 8 families (themes, mixins, functions, tokens · 127 required of them, animations, components, recipes, doc readers) plus `assemble_prompt` (context bundles) and `resolve_size` (snap design px values to cia's 4px grid). Full reference: [`/docs/mcp`](./src/app/docs/mcp/page.tsx).
|
|
256
|
+
|
|
257
|
+
**Setup is two steps — do both, or the server won't start.**
|
|
258
|
+
|
|
259
|
+
1. Install the SDK peer deps. The MCP SDK needs `@modelcontextprotocol/sdk` + `zod`; they're declared as *optional* peers so npm skips them by default. Without them the server exits and your MCP client shows only a generic "failed to connect":
|
|
260
|
+
|
|
261
|
+
```bash
|
|
262
|
+
npm install -D @modelcontextprotocol/sdk zod
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
2. Add to your client's `.mcp.json` (the `npx` form uses the shipped bin and is CWD-independent):
|
|
266
|
+
|
|
267
|
+
```json
|
|
268
|
+
{
|
|
269
|
+
"mcpServers": {
|
|
270
|
+
"css-is-awesome": {
|
|
271
|
+
"command": "npx",
|
|
272
|
+
"args": ["css-is-awesome-mcp"]
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
Equivalent explicit path: `"command": "node", "args": ["node_modules/css-is-awesome/mcp/server.cjs"]`.
|
|
279
|
+
|
|
280
|
+
## Running the docs site locally
|
|
281
|
+
|
|
282
|
+
```bash
|
|
283
|
+
git clone https://github.com/Jerry2d3d/css-is-awesome.git
|
|
284
|
+
cd css-is-awesome
|
|
285
|
+
npm install
|
|
286
|
+
npm run dev # http://localhost:5173
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
The docs site is a Next.js 15 app at `src/` that dogfoods the library — every page uses CSS Modules composed from the same tokens and mixins the library ships.
|
|
290
|
+
|
|
291
|
+
## Scripts
|
|
292
|
+
|
|
293
|
+
| Script | Does |
|
|
294
|
+
|---|---|
|
|
295
|
+
| `npm run dev` | Next.js docs site on port 5173 |
|
|
296
|
+
| `npm run build` | Static-exports docs site to `out/` |
|
|
297
|
+
| `npm run build:css` | Compile library SCSS to `dist/css-is-awesome.css` |
|
|
298
|
+
| `npm run build:css:all` | Compile all bundles (full + core + utilities + minified) + themes + token types |
|
|
299
|
+
| `npm run build:css:themes` | Rebuild the 24 per-theme CSS files in `public/themes/` **and** regenerate the all-in-one `public/theme.css` bundle |
|
|
300
|
+
| `npm run check:theme-drift` | Rebuild the themes into a scratch copy and fail if the committed artifacts don't match their SCSS sources |
|
|
301
|
+
| `npm run build:token-types` | Generate `dist/tokens.d.ts` from the contract |
|
|
302
|
+
| `npm run dtcg-to-scss` | Convert DTCG-format design tokens into cia SCSS |
|
|
303
|
+
| `npm run lint` | ESLint on the Next.js app |
|
|
304
|
+
| `npm run lint:scss` | Stylelint on the SCSS library |
|
|
305
|
+
| `npm run validate-themes` | Validate every theme against the 127-token contract + WCAG 2.2 AA contrast (FAIL-by-default since v0.7; checks both `light-dark()` branches and reports the worse) |
|
|
306
|
+
| `npm run validate-icons` | Validate the `core` icon pack against the 49-glyph contract |
|
|
307
|
+
| `npm run validate-api` | Assert the `css-is-awesome/api` barrel stays zero-emit |
|
|
308
|
+
| `npm run validate-package` | Pack + install into a temp project and compile every documented `@use` form — catches breakage that in-repo checks can't see |
|
|
309
|
+
| `npm run pack:consumer` | Pack and install this build into a local consumer (defaults to `../boiler-project-ai`); `--dry-run` supported |
|
|
310
|
+
| `npm test` | Playwright suite — axe a11y checks + per-theme visual snapshots |
|
|
311
|
+
|
|
312
|
+
## Testing
|
|
313
|
+
|
|
314
|
+
Eleven checks, all gated in CI on every PR. Each one exists because the failure it catches actually happened.
|
|
315
|
+
|
|
316
|
+
| Check | What it proves |
|
|
317
|
+
|---|---|
|
|
318
|
+
| `lint` / `lint:scss` | ESLint on the site, stylelint on the library |
|
|
319
|
+
| `validate-themes` | every theme declares all 127 required contract tokens and meets WCAG 2.2 AA on 22 audited pairs — evaluating **both** `light-dark()` branches and keeping the worse result. Fails the build by default |
|
|
320
|
+
| `check:theme-drift` | the committed `public/themes/**` and `public/theme.css` artifacts still match their SCSS sources |
|
|
321
|
+
| `validate-icons` | the 49-glyph core pack is intact (extras allowed) |
|
|
322
|
+
| `validate-api` | the `/api` barrel still emits zero CSS until a mixin is called |
|
|
323
|
+
| `validate-package` | packs → installs into a temp project → compiles all **10** documented `@use` specifiers |
|
|
324
|
+
| `coverage:api` | calls **174/174** public mixins + functions and asserts the output |
|
|
325
|
+
| `coverage:mcp` | calls **30/30** MCP tools over stdio |
|
|
326
|
+
| `test` | Playwright — 50 tests: route smoke, axe a11y, per-theme visual snapshots, theme-editor behaviour |
|
|
327
|
+
|
|
328
|
+
**Call-and-assert coverage.** SCSS has no line-coverage tooling, so cia measures whether every part of the public API is actually callable: parse every public `@mixin`/`@function`, generate a fixture that calls it, compile, and assert it works — no `null` leaking into CSS, functions return a value, mixins emit. **174/174 SCSS units and 30/30 MCP tools**, with CI failing below 98%. A unit with no fixture counts as uncovered, so skipping a test lowers the number rather than hiding.
|
|
329
|
+
|
|
330
|
+
**What 100% means here:** every public mixin and function is invoked and produces sane output. It catches renames, broken signatures and undefined variables — it found one on its first run, an undefined `$icon-size` that broke four icon mixins. It does **not** prove the CSS is visually correct; that's a deliberate trade against golden-file snapshots, which would churn dozens of files on any token change. Page-level visual correctness is covered by the Playwright snapshots instead.
|
|
331
|
+
|
|
332
|
+
**Size is gated, not just claimed.** `npm run size-budget` fails CI when any bundle outgrows its budget, and names the docs that quote the number. Raising a budget is a deliberate edit in `scripts/size-budget.mjs`, in the same commit as the growth — the point isn't that the number never moves, it's that it never moves silently. (These figures had drifted to be overstated by up to 2× while nothing measured them.)
|
|
333
|
+
|
|
334
|
+
**Known gaps**, stated plainly: **a11y runs on routes, not component states** — axe checks a set of pages; individual component states are not swept. (Cross-engine coverage used to be the gap here; the suite now runs chromium, firefox and webkit, which matters because cia leans on `light-dark()`, `:has()`, `[popover]` and `mask`.)
|
|
335
|
+
|
|
336
|
+
Full detail: [`/docs/testing`](./src/app/docs/testing/page.tsx).
|
|
337
|
+
|
|
338
|
+
## Size (gzipped)
|
|
339
|
+
|
|
340
|
+
| Bundle | Size | Use case |
|
|
341
|
+
|---|---|---|
|
|
342
|
+
| `dist/tokens.css` | 2.2 KB | Tokens only (`:where(:root)` CSS variables, no rules) — the purest mixin-first emit |
|
|
343
|
+
| `dist/css-is-awesome.core.min.css` | 2.4 KB | Tokens + resets, no utilities or components |
|
|
344
|
+
| `dist/css-is-awesome.utilities.min.css` | 4.1 KB | Every `cia-*` utility class, nothing else |
|
|
345
|
+
| `dist/css-is-awesome.min.css` | 7.3 KB | Full bundle (everything) |
|
|
346
|
+
| Per-theme `themes/<name>/theme.css` | 1.5–3.4 KB | One file per theme, both modes via `light-dark()`, drop-in with no markup change |
|
|
347
|
+
| **JavaScript shipped in package** | **0 KB** | Zero. Period. JS-driven features ship as separate add-on packages. |
|
|
348
|
+
|
|
349
|
+
## Status
|
|
350
|
+
|
|
351
|
+
**1.0.0.** The mixin API, functions, token contract, and theme architecture are stable and under strict SemVer — breaking changes require a major bump. See [`VERSIONING.md`](./VERSIONING.md) for the policy.
|
|
352
|
+
|
|
353
|
+
The 1.0 surface is the v0.8 mixin-first reframe — twelve mixin renames, theme system collapsed to 8 single-file theme families, six zero-JS components, intrinsic-layout vocabulary, opt-in utilities — plus the recipes book, the Tailwind/Bootstrap migration on-ramp, print/PDF support, and the 30-tool MCP server. The npm package ships ZERO JavaScript by hard rule.
|
|
354
|
+
|
|
355
|
+
See [CHANGELOG.md](./CHANGELOG.md) for the full history and [MIGRATION.md](./MIGRATION.md) for the v0.7 → v0.8 upgrade path.
|
|
356
|
+
|
|
357
|
+
For the deep authoring reference (tier decisions, mixin contracts, agent rules), read [`AGENTS.md`](./AGENTS.md).
|
|
358
|
+
|
|
359
|
+
## Launch mode / feature flags
|
|
360
|
+
|
|
361
|
+
The site ships with a runtime feature-flag gate driven by `public/flags.json`:
|
|
362
|
+
|
|
363
|
+
```json
|
|
364
|
+
{
|
|
365
|
+
"version": 1,
|
|
366
|
+
"comingSoon": false,
|
|
367
|
+
"comingSoonMessage": "...",
|
|
368
|
+
"announcement": { "active": false, "id": "", "status": "info", "message": "", "href": "" }
|
|
369
|
+
}
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
**Coming soon.** Set `"comingSoon": true` and the site renders a full-page "we're building this" overlay on every route (the theme picker is still visible). Flip back to `false` to restore the normal docs experience. No rebuild required — the flag is fetched at runtime.
|
|
373
|
+
|
|
374
|
+
**Announcements.** Set `announcement.active: true` plus a unique `announcement.id` to show a dismissible banner on every route. Visitors can dismiss; the dismissal persists in `sessionStorage` keyed by `id`, so changing the id re-shows the banner.
|
|
375
|
+
|
|
376
|
+
Flag changes on a static host are effective after the next hard reload.
|
|
377
|
+
|
|
378
|
+
## License
|
|
379
|
+
|
|
380
|
+
MIT. See [LICENSE](./LICENSE).
|