css-is-awesome 1.8.2 → 1.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +8 -4
- package/CHANGELOG.md +15 -0
- package/README.md +11 -0
- package/bin/README.md +3 -1
- package/bin/add-recipe.cjs +94 -0
- package/bin/analyze.cjs +191 -0
- package/bin/cia.cjs +20 -2
- package/dist/tokens.d.ts +1 -1
- package/llm.txt +2 -0
- package/package.json +1 -1
package/AGENTS.md
CHANGED
|
@@ -337,10 +337,14 @@ The SDK is an optional peer dep — `npm install -D @modelcontextprotocol/sdk zo
|
|
|
337
337
|
|
|
338
338
|
## Other tooling (shipped)
|
|
339
339
|
|
|
340
|
-
- **`cia` CLI** — ships as `bin/cia.cjs`, exposed as the `cia` bin.
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
340
|
+
- **`cia` CLI** — ships as `bin/cia.cjs`, exposed as the `cia` bin. Three verbs:
|
|
341
|
+
`npx cia migrate tailwind|bootstrap [path]` parses another system's config and
|
|
342
|
+
dumps a cia theme; `npx cia add <recipe>` (`--list` to browse) copies a recipe
|
|
343
|
+
from the book into the project — own the pattern; `npx cia analyze [path]`
|
|
344
|
+
audits a project's stylesheets against the installed API — dead `cia.*` symbols,
|
|
345
|
+
the `space()` 1–9 scale trap, hard-coded hex colors, BEM chains — with a health
|
|
346
|
+
score and CI-ready exit codes. Run any verb with `--help`. (`cia init` remains
|
|
347
|
+
planned.)
|
|
344
348
|
- **JSON token export** — DTCG-format token list in `figma-tokens/`.
|
|
345
349
|
- **`llm.txt`** — at the repo root and served from the docs site; single-fetch
|
|
346
350
|
summary for any AI agent. Also readable over MCP via `read_llm_txt`.
|
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,18 @@
|
|
|
1
|
+
## [1.9.1](https://github.com/Jerry2d3d/css-is-awesome/compare/v1.9.0...v1.9.1) (2026-09-06)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Features
|
|
5
|
+
|
|
6
|
+
* **site:** the blog becomes two tracks - engineering + CSS discoveries (EPIC-06) ([dc4319f](https://github.com/Jerry2d3d/css-is-awesome/commit/dc4319ff106b6885247a67c738752a79d7228022))
|
|
7
|
+
|
|
8
|
+
# [1.9.0](https://github.com/Jerry2d3d/css-is-awesome/compare/v1.8.2...v1.9.0) (2026-09-05)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Features
|
|
12
|
+
|
|
13
|
+
* **cli:** cia add (recipe registry) + cia analyze (design-system health) ([194c253](https://github.com/Jerry2d3d/css-is-awesome/commit/194c253a35eec68c68e2f2ca7402fd4e72a4d6ad))
|
|
14
|
+
* **site:** /docs/browser-support - the dated two-tier support matrix ([d2e4604](https://github.com/Jerry2d3d/css-is-awesome/commit/d2e4604c6a331b467bdcd71b30ba2b7f7fe90f68))
|
|
15
|
+
|
|
1
16
|
## [1.8.2](https://github.com/Jerry2d3d/css-is-awesome/compare/v1.8.1...v1.8.2) (2026-09-05)
|
|
2
17
|
|
|
3
18
|
|
package/README.md
CHANGED
|
@@ -221,6 +221,17 @@ npx cia migrate bootstrap ./scss/_variables.scss
|
|
|
221
221
|
|
|
222
222
|
Both accept `--help` for the full option list. Prose walkthroughs live at [`/docs/migration-tailwind`](https://cssisawesome.com/docs/migration-tailwind/) and [`/docs/migration-bootstrap`](https://cssisawesome.com/docs/migration-bootstrap/).
|
|
223
223
|
|
|
224
|
+
The CLI also carries the registry and the health check:
|
|
225
|
+
|
|
226
|
+
```bash
|
|
227
|
+
npx cia add --list # browse the recipe book
|
|
228
|
+
npx cia add bottom-nav # copy a recipe into your project — you own the pattern
|
|
229
|
+
npx cia analyze src/styles # design-system health: dead cia.* symbols, the
|
|
230
|
+
# space() scale trap, hard-coded colors, BEM creep
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
`cia analyze` reads the real API surface from the installed package and exits non-zero on errors, so it slots straight into CI.
|
|
234
|
+
|
|
224
235
|
## Print / PDF (zero JS)
|
|
225
236
|
|
|
226
237
|
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:
|
package/bin/README.md
CHANGED
|
@@ -7,7 +7,7 @@ cia's CLI entry points. **These are the only JavaScript files cia ships** — th
|
|
|
7
7
|
| Bin | File | What it does |
|
|
8
8
|
|---|---|---|
|
|
9
9
|
| `css-is-awesome-mcp` | `../mcp/server.cjs` | MCP stdio server for AI agents (themes, mixins, recipes, etc.) |
|
|
10
|
-
| `cia` | `./cia.cjs` | The CLI —
|
|
10
|
+
| `cia` | `./cia.cjs` | The CLI router — `migrate` (tailwind/bootstrap), `add` (recipe registry), `analyze` (design-system health) |
|
|
11
11
|
|
|
12
12
|
Invoke either via `npx` from a consumer project that has cia installed:
|
|
13
13
|
|
|
@@ -15,6 +15,8 @@ Invoke either via `npx` from a consumer project that has cia installed:
|
|
|
15
15
|
npx css-is-awesome-mcp # MCP server (stdio; configure via .mcp.json)
|
|
16
16
|
npx cia --help # CLI help
|
|
17
17
|
npx cia migrate tailwind ./tailwind.config.js
|
|
18
|
+
npx cia add bottom-nav # copy a recipe into the project — own the pattern
|
|
19
|
+
npx cia analyze src/styles # health check: dead symbols, space() trap, hex, BEM
|
|
18
20
|
```
|
|
19
21
|
|
|
20
22
|
## Subcommand files
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* cia add — copy a recipe from the installed package into the project.
|
|
3
|
+
*
|
|
4
|
+
* The recipes book ships inside the npm package (scss/recipes/*.md). This
|
|
5
|
+
* command copies one into the consumer's tree so they OWN the pattern —
|
|
6
|
+
* the registry model: own the generated code, don't import an opaque
|
|
7
|
+
* component. Markdown pattern recipes only; the opt-in SCSS recipes
|
|
8
|
+
* (e.g. bare-tags) are `@use`d, not copied.
|
|
9
|
+
*/
|
|
10
|
+
'use strict';
|
|
11
|
+
|
|
12
|
+
const fs = require('fs');
|
|
13
|
+
const path = require('path');
|
|
14
|
+
|
|
15
|
+
const RECIPES_DIR = path.join(__dirname, '..', 'scss', 'recipes');
|
|
16
|
+
|
|
17
|
+
const HELP = `cia add — copy a recipe into your project
|
|
18
|
+
|
|
19
|
+
Usage:
|
|
20
|
+
cia add <recipe> [options]
|
|
21
|
+
cia add --list
|
|
22
|
+
|
|
23
|
+
Options:
|
|
24
|
+
--list List available recipes.
|
|
25
|
+
--out <path> Output path. Default: ./cia-recipes/<recipe>.md
|
|
26
|
+
--force Overwrite if the target file exists.
|
|
27
|
+
|
|
28
|
+
Examples:
|
|
29
|
+
cia add --list
|
|
30
|
+
cia add bottom-nav
|
|
31
|
+
cia add mobile-nav --out docs/patterns/mobile-nav.md
|
|
32
|
+
`;
|
|
33
|
+
|
|
34
|
+
function fail(message) {
|
|
35
|
+
process.stderr.write(`cia add: ${message}\n`);
|
|
36
|
+
process.exit(1);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function listRecipes() {
|
|
40
|
+
return fs
|
|
41
|
+
.readdirSync(RECIPES_DIR)
|
|
42
|
+
.filter((f) => f.endsWith('.md') && !f.startsWith('_') && f !== 'README.md')
|
|
43
|
+
.map((f) => f.replace(/\.md$/, ''))
|
|
44
|
+
.sort();
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function frontmatterDescription(raw) {
|
|
48
|
+
const m = raw.match(/^---[\s\S]*?\ndescription:\s*(.+?)\r?\n[\s\S]*?---/);
|
|
49
|
+
return m ? m[1].trim() : '';
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async function run(args) {
|
|
53
|
+
if (!args.length || args[0] === '-h' || args[0] === '--help' || args[0] === 'help') {
|
|
54
|
+
process.stdout.write(HELP);
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (args.includes('--list')) {
|
|
59
|
+
for (const slug of listRecipes()) {
|
|
60
|
+
const raw = fs.readFileSync(path.join(RECIPES_DIR, `${slug}.md`), 'utf8');
|
|
61
|
+
process.stdout.write(`${slug.padEnd(16)} ${frontmatterDescription(raw)}\n`);
|
|
62
|
+
}
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const slug = args[0];
|
|
67
|
+
const recipes = listRecipes();
|
|
68
|
+
if (!recipes.includes(slug)) {
|
|
69
|
+
fail(`unknown recipe '${slug}'. Available: ${recipes.join(', ')}.`);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const outFlag = args.indexOf('--out');
|
|
73
|
+
const outPath =
|
|
74
|
+
outFlag !== -1 && args[outFlag + 1]
|
|
75
|
+
? path.resolve(args[outFlag + 1])
|
|
76
|
+
: path.resolve('cia-recipes', `${slug}.md`);
|
|
77
|
+
|
|
78
|
+
if (fs.existsSync(outPath) && !args.includes('--force')) {
|
|
79
|
+
fail(`${path.relative(process.cwd(), outPath)} already exists. Pass --force to overwrite.`);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
fs.mkdirSync(path.dirname(outPath), { recursive: true });
|
|
83
|
+
fs.copyFileSync(path.join(RECIPES_DIR, `${slug}.md`), outPath);
|
|
84
|
+
|
|
85
|
+
const rel = path.relative(process.cwd(), outPath);
|
|
86
|
+
process.stdout.write(
|
|
87
|
+
`✓ ${slug} → ${rel}\n\n` +
|
|
88
|
+
`The recipe is yours now — correct HTML, the cia mixin calls, and the\n` +
|
|
89
|
+
`a11y checklist. Read it, copy the pattern into your stack, keep the\n` +
|
|
90
|
+
`checklist. Docs: https://cssisawesome.com/docs/recipes/${slug}/\n`,
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
module.exports = { run };
|
package/bin/analyze.cjs
ADDED
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* cia analyze — design-system health check for a consumer project.
|
|
3
|
+
*
|
|
4
|
+
* Reads the REAL API surface from the installed package's SCSS sources
|
|
5
|
+
* (the same files the MCP server serves), then audits the project's
|
|
6
|
+
* stylesheets for:
|
|
7
|
+
* - cia.* calls that don't resolve (dead symbols — typos, removals)
|
|
8
|
+
* - the space() scale trap (numbered scale is 1–9; unknown keys pass
|
|
9
|
+
* through raw and silently invalidate the declaration)
|
|
10
|
+
* - hard-coded hex colors (values should come from tokens)
|
|
11
|
+
* - BEM-style class names (__ / -- chains are forbidden in cia projects)
|
|
12
|
+
* - hand-written grid-template-areas (the layout mixins own the maps)
|
|
13
|
+
*
|
|
14
|
+
* Zero dependencies, filesystem only — same philosophy as the MCP server.
|
|
15
|
+
*/
|
|
16
|
+
'use strict';
|
|
17
|
+
|
|
18
|
+
const fs = require('fs');
|
|
19
|
+
const path = require('path');
|
|
20
|
+
|
|
21
|
+
const PKG_SCSS = path.join(__dirname, '..', 'scss');
|
|
22
|
+
|
|
23
|
+
const HELP = `cia analyze — design-system health check
|
|
24
|
+
|
|
25
|
+
Usage:
|
|
26
|
+
cia analyze [path] [options]
|
|
27
|
+
|
|
28
|
+
Scans [path] (default: current directory) for *.scss files and audits
|
|
29
|
+
them against the installed css-is-awesome API.
|
|
30
|
+
|
|
31
|
+
Options:
|
|
32
|
+
--namespace <ns> Extra namespace(s) to treat as cia (comma-separated).
|
|
33
|
+
Auto-detected per file from @use lines; use this when
|
|
34
|
+
imports are aliased through an intermediate file.
|
|
35
|
+
--json Machine-readable report on stdout.
|
|
36
|
+
--strict Exit 1 on warnings too (default: errors only).
|
|
37
|
+
|
|
38
|
+
Examples:
|
|
39
|
+
cia analyze
|
|
40
|
+
cia analyze src/styles --json
|
|
41
|
+
cia analyze src --namespace m,l
|
|
42
|
+
`;
|
|
43
|
+
|
|
44
|
+
// ── API surface discovery ───────────────────────────────────────────────────
|
|
45
|
+
|
|
46
|
+
function collectSymbols() {
|
|
47
|
+
const symbols = new Set();
|
|
48
|
+
const files = [];
|
|
49
|
+
const top = ['_mixins.scss', '_layout.scss', '_animations.scss', '_generator.scss'];
|
|
50
|
+
for (const f of top) files.push(path.join(PKG_SCSS, f));
|
|
51
|
+
const compDir = path.join(PKG_SCSS, 'components');
|
|
52
|
+
for (const f of fs.readdirSync(compDir)) {
|
|
53
|
+
if (f.endsWith('.scss')) files.push(path.join(compDir, f));
|
|
54
|
+
}
|
|
55
|
+
for (const file of files) {
|
|
56
|
+
let src = '';
|
|
57
|
+
try {
|
|
58
|
+
src = fs.readFileSync(file, 'utf8');
|
|
59
|
+
} catch {
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
for (const m of src.matchAll(/@(?:mixin|function)\s+([a-zA-Z][\w-]*)/g)) {
|
|
63
|
+
if (!m[1].startsWith('_')) symbols.add(m[1]);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
// Icon mixins are forwarded with an icon- prefix by the /api barrel.
|
|
67
|
+
const icons = path.join(PKG_SCSS, '_icons.scss');
|
|
68
|
+
try {
|
|
69
|
+
const src = fs.readFileSync(icons, 'utf8');
|
|
70
|
+
for (const m of src.matchAll(/@(?:mixin|function)\s+([a-zA-Z][\w-]*)/g)) {
|
|
71
|
+
if (!m[1].startsWith('_')) symbols.add(`icon-${m[1]}`);
|
|
72
|
+
}
|
|
73
|
+
} catch {
|
|
74
|
+
/* icons module optional */
|
|
75
|
+
}
|
|
76
|
+
return symbols;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// ── project scan ────────────────────────────────────────────────────────────
|
|
80
|
+
|
|
81
|
+
function walkScss(dir, out = []) {
|
|
82
|
+
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
83
|
+
if (entry.name === 'node_modules' || entry.name.startsWith('.')) continue;
|
|
84
|
+
const full = path.join(dir, entry.name);
|
|
85
|
+
if (entry.isDirectory()) walkScss(full, out);
|
|
86
|
+
else if (entry.name.endsWith('.scss')) out.push(full);
|
|
87
|
+
}
|
|
88
|
+
return out;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const CIA_SOURCE = /^(?:css-is-awesome(?:\/|$)|pkg:css-is-awesome|api$|mixins$|layout$|animations$|generator$|icons$|components(?:\/|$))/;
|
|
92
|
+
|
|
93
|
+
function detectNamespaces(src, extra) {
|
|
94
|
+
const ns = new Set(extra);
|
|
95
|
+
for (const m of src.matchAll(/@use\s+['"]([^'"]+)['"]\s+as\s+([a-zA-Z][\w-]*)/g)) {
|
|
96
|
+
if (CIA_SOURCE.test(m[1])) ns.add(m[2]);
|
|
97
|
+
}
|
|
98
|
+
// `@use 'css-is-awesome/api';` without `as` → namespace is the last segment.
|
|
99
|
+
for (const m of src.matchAll(/@use\s+['"]([^'"]+)['"]\s*;/g)) {
|
|
100
|
+
if (CIA_SOURCE.test(m[1])) ns.add(m[1].split('/').pop().replace(/\.scss$/, ''));
|
|
101
|
+
}
|
|
102
|
+
return ns;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function stripComments(src) {
|
|
106
|
+
return src.replace(/\/\*[\s\S]*?\*\//g, '').replace(/\/\/[^\n]*/g, '');
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function analyzeFile(file, symbols, extraNs) {
|
|
110
|
+
const raw = fs.readFileSync(file, 'utf8').replace(/\r\n/g, '\n');
|
|
111
|
+
const src = stripComments(raw);
|
|
112
|
+
const ns = detectNamespaces(src, extraNs);
|
|
113
|
+
const findings = [];
|
|
114
|
+
if (ns.size) {
|
|
115
|
+
const nsPattern = [...ns].map((n) => n.replace(/[-]/g, '\\-')).join('|');
|
|
116
|
+
const callRe = new RegExp(`\\b(?:${nsPattern})\\.([a-zA-Z][\\w-]*)`, 'g');
|
|
117
|
+
for (const m of src.matchAll(callRe)) {
|
|
118
|
+
const sym = m[1];
|
|
119
|
+
if (!symbols.has(sym)) {
|
|
120
|
+
findings.push({ level: 'error', rule: 'unknown-symbol', detail: `cia.${sym} does not exist in the installed API` });
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
// space() scale trap: numbered keys are 1–9; higher/fractional numbers
|
|
124
|
+
// pass through raw and silently invalidate the declaration.
|
|
125
|
+
const spaceRe = new RegExp(`\\b(?:${nsPattern})\\.space\\(\\s*(\\d+(?:\\.\\d+)?)\\s*\\)`, 'g');
|
|
126
|
+
for (const m of src.matchAll(spaceRe)) {
|
|
127
|
+
const n = Number(m[1]);
|
|
128
|
+
if (!Number.isInteger(n) || n > 9) {
|
|
129
|
+
findings.push({ level: 'error', rule: 'space-scale', detail: `space(${m[1]}) — the numbered scale is 1–9; this emits a unitless number and the browser drops the declaration. Use grid(n) for 4px multiples or pass an explicit unit.` });
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
for (const m of src.matchAll(/#(?:[0-9a-fA-F]{3,4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})\b/g)) {
|
|
134
|
+
findings.push({ level: 'warn', rule: 'hard-coded-color', detail: `${m[0]} — values should come from tokens (cia.color(...) / var(--...))` });
|
|
135
|
+
}
|
|
136
|
+
for (const m of src.matchAll(/\.[a-zA-Z][\w]*(?:__|--)[\w-]+/g)) {
|
|
137
|
+
findings.push({ level: 'warn', rule: 'bem', detail: `${m[0]} — BEM chains are forbidden; use semantic single-class names` });
|
|
138
|
+
}
|
|
139
|
+
if (/(?:^|[{;\s])grid-template-areas\s*:/m.test(src)) {
|
|
140
|
+
findings.push({ level: 'info', rule: 'hand-written-areas', detail: 'grid-template-areas written by hand — the layout mixins (page-layout / layout / area) own the maps' });
|
|
141
|
+
}
|
|
142
|
+
return { file, namespaces: [...ns], findings };
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// ── report ──────────────────────────────────────────────────────────────────
|
|
146
|
+
|
|
147
|
+
async function run(args) {
|
|
148
|
+
if (args[0] === '-h' || args[0] === '--help' || args[0] === 'help') {
|
|
149
|
+
process.stdout.write(HELP);
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
const json = args.includes('--json');
|
|
153
|
+
const strict = args.includes('--strict');
|
|
154
|
+
const nsFlag = args.indexOf('--namespace');
|
|
155
|
+
const extraNs = nsFlag !== -1 && args[nsFlag + 1] ? args[nsFlag + 1].split(',') : [];
|
|
156
|
+
const target = path.resolve(args.find((a) => !a.startsWith('--') && a !== extraNs.join(',')) || '.');
|
|
157
|
+
|
|
158
|
+
if (!fs.existsSync(target)) {
|
|
159
|
+
process.stderr.write(`cia analyze: path not found: ${target}\n`);
|
|
160
|
+
process.exit(1);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const symbols = collectSymbols();
|
|
164
|
+
const files = walkScss(target);
|
|
165
|
+
const results = files.map((f) => analyzeFile(f, symbols, extraNs)).filter((r) => r.findings.length || r.namespaces.length);
|
|
166
|
+
|
|
167
|
+
const counts = { error: 0, warn: 0, info: 0 };
|
|
168
|
+
for (const r of results) for (const f of r.findings) counts[f.level]++;
|
|
169
|
+
const ciaFiles = results.filter((r) => r.namespaces.length).length;
|
|
170
|
+
const health = Math.max(0, 100 - counts.error * 10 - counts.warn * 2 - counts.info);
|
|
171
|
+
|
|
172
|
+
if (json) {
|
|
173
|
+
process.stdout.write(JSON.stringify({ target, files: files.length, ciaFiles, apiSymbols: symbols.size, counts, health, results }, null, 2) + '\n');
|
|
174
|
+
} else {
|
|
175
|
+
process.stdout.write(`\ncia analyze — ${path.relative(process.cwd(), target) || '.'}\n`);
|
|
176
|
+
process.stdout.write(`${files.length} scss file(s), ${ciaFiles} using cia, ${symbols.size} API symbols known\n\n`);
|
|
177
|
+
for (const r of results) {
|
|
178
|
+
if (!r.findings.length) continue;
|
|
179
|
+
process.stdout.write(`${path.relative(process.cwd(), r.file)}\n`);
|
|
180
|
+
for (const f of r.findings) {
|
|
181
|
+
const mark = f.level === 'error' ? '✗' : f.level === 'warn' ? '⚠' : 'ℹ';
|
|
182
|
+
process.stdout.write(` ${mark} [${f.rule}] ${f.detail}\n`);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
process.stdout.write(`\nDesign-system health: ${health}% (${counts.error} error, ${counts.warn} warn, ${counts.info} info)\n`);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
if (counts.error > 0 || (strict && counts.warn > 0)) process.exit(1);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
module.exports = { run };
|
package/bin/cia.cjs
CHANGED
|
@@ -27,11 +27,17 @@ Usage:
|
|
|
27
27
|
|
|
28
28
|
Commands:
|
|
29
29
|
migrate <tool> [path] Convert another design system's config to a cia
|
|
30
|
-
theme. Tools: tailwind | bootstrap
|
|
30
|
+
theme. Tools: tailwind | bootstrap.
|
|
31
|
+
add <recipe> Copy a recipe from the book into your project
|
|
32
|
+
(own the pattern). \`cia add --list\` to browse.
|
|
33
|
+
analyze [path] Design-system health check: dead cia.* symbols,
|
|
34
|
+
the space() scale trap, hard-coded colors, BEM,
|
|
35
|
+
hand-written area maps.
|
|
31
36
|
|
|
32
37
|
Examples:
|
|
33
38
|
cia migrate tailwind ./tailwind.config.js
|
|
34
|
-
cia
|
|
39
|
+
cia add bottom-nav
|
|
40
|
+
cia analyze src/styles
|
|
35
41
|
|
|
36
42
|
Run \`cia <command> --help\` for command-specific help.
|
|
37
43
|
`;
|
|
@@ -106,6 +112,18 @@ async function main() {
|
|
|
106
112
|
fail(`unknown migrate tool '${tool}'. Available: tailwind, bootstrap.`);
|
|
107
113
|
}
|
|
108
114
|
|
|
115
|
+
if (command === 'add') {
|
|
116
|
+
const { run } = require('./add-recipe.cjs');
|
|
117
|
+
await run(rest);
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (command === 'analyze') {
|
|
122
|
+
const { run } = require('./analyze.cjs');
|
|
123
|
+
await run(rest);
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
|
|
109
127
|
fail(`unknown command '${command}'. Run \`cia --help\` for usage.`);
|
|
110
128
|
}
|
|
111
129
|
|
package/dist/tokens.d.ts
CHANGED
package/llm.txt
CHANGED
|
@@ -48,6 +48,7 @@ silent.
|
|
|
48
48
|
- **Mixins** — `cia.btn`, `cia.card`, `cia.accordion`, `cia.modal`, `cia.tooltip`, `cia.dropdown`, `cia.tabs`, `cia.copy-button`, `cia.hamburger`, `cia.drawer`, `cia.sheet`, `cia.dock`, `cia.stepper`, `cia.progress`, `cia.wizard-shell`, `cia.sidebar`, `cia.toolbar`, `cia.stack`, `cia.cluster`, `cia.switcher`, `cia.cover`, `cia.frame`, `cia.media`, `cia.contain`, `cia.focus-ring`, `cia.sr-only`, `cia.print`, `cia.print-base`, `cia.print-hidden`, `cia.print-only`, `cia.color`, `cia.space`, `cia.radius`, `cia.shadow`, `cia.font`, `cia.transition`, `cia.animate`, plus many more.
|
|
49
49
|
- **24 themes / 8 families** — boilerplate, sketchbook, press, prism, cupertino, glass, graphite, terminal. Each family ships three files: an unsuffixed dual-mode base (both modes via `light-dark()`) plus pinned `-light` and `-dark` single-mode variants for `<link media>` pairing. Every one has exactly one SCSS source under `scss/themes/`; `public/themes/<name>/theme.css` is BUILD OUTPUT and is gated against its source by `npm run check:theme-drift` — never hand-edit it. MCP `list_themes` returns **24**. Say **24 themes across 8 families** when you need one number.
|
|
50
50
|
- **6 zero-JS interactive components** — accordion (`<details name>`), modal (`<dialog>`), tooltip (`popover="hint"`), dropdown (`[popover]` — the mixin re-asserts the UA's closed state and restores `display: flex` only under `:popover-open`, so menus never render permanently open on popover markup), tabs (radio + `:has()`), copy-button (Clipboard API via consumer-wired JS).
|
|
51
|
+
- **CLI** (`npx cia`) — `migrate tailwind|bootstrap` (config → cia theme), `add <recipe>` (copy a recipe from the book into the project), `analyze [path]` (audit stylesheets against the installed API: dead `cia.*` symbols, the `space()` 1–9 trap, hard-coded hex, BEM; health score, CI exit codes).
|
|
51
52
|
- **Mobile navigation family** — `cia.hamburger` / `cia.drawer` / `cia.sheet` / `cia.dock` ride `[popover]` + CSS Grid, zero JS; recipes `mobile-nav` (hamburger + drawer) and `bottom-nav` (dock + sheets). House rule: **on phones things take the space they're in** — a `cia.dropdown` menu opens 1px under its full-width trigger at the trigger's exact width via CSS anchor positioning (`position-try-fallbacks: flip-block` flips it above at the screen bottom; set `width: auto` at `&[popover]` specificity or the UA's `[popover] { width: fit-content }` and the mixin's inset reset win). Full spec: AGENTS.md quick decision #8 and `/docs/mobile`.
|
|
52
53
|
- **163 contract tokens — 127 required + 36 optional** — surfaces, ink, lines, colors, type, radius, shadow, blur, glow, motion, z-index, spacing, semantic aliases.
|
|
53
54
|
- **Spacing is themeable, and the numbered scale is the knob.** Themes declare `--space-0` … `--space-9` (contract-required). The t-shirt names (`--space-md`, `--space-lg` …) are contract-OPTIONAL and emitted by the library as `var()` aliases onto the numbered steps. `space(4)` compiles to `var(--space-4)`, so **theme the numbered step, never the alias** — setting `--space-md` alone leaves every component untouched. Library defaults emit under `:where(:root)` (specificity 0,0,0) so any theme declaration outranks them regardless of load order.
|
|
@@ -101,6 +102,7 @@ Newspaper by day, hacker terminal by night. Most design systems give you dark mo
|
|
|
101
102
|
| Mixin reference (every public mixin) | `src/app/docs/mixins/page.tsx` |
|
|
102
103
|
| Component examples | `src/app/docs/components/*/page.tsx` (6 components) |
|
|
103
104
|
| Mobile playbook (layouts, dropdown doctrine, lessons) | `src/app/docs/mobile/page.tsx` |
|
|
105
|
+
| Browser support matrix (Baseline floor + progressive tiers) | `src/app/docs/browser-support/page.tsx` |
|
|
104
106
|
| Theme authoring (full walkthrough) | `src/app/docs/authoring/themes/page.tsx` |
|
|
105
107
|
| Theme contract (127 required + 36 optional, machine-readable) | `scripts/theme-contract.json` |
|
|
106
108
|
| Theme pairing (`<link media>` recipe) | `src/app/docs/themes/pairing/page.tsx` |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "css-is-awesome",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.9.1",
|
|
4
4
|
"description": "A token-driven SCSS design system with light/dark theming, semantic color tokens, and a 800+ LOC mixin API.",
|
|
5
5
|
"homepage": "https://github.com/Jerry2d3d/css-is-awesome#readme",
|
|
6
6
|
"bugs": {
|