css-is-awesome 1.19.0 → 1.19.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 +1 -1
- package/CHANGELOG.md +12 -0
- package/README.md +1 -1
- package/dist/tokens.d.ts +1 -1
- package/llm.txt +1 -1
- package/mcp/server.cjs +5 -4
- package/package.json +5 -1
- package/scripts/tokens-to-theme.cjs +59 -6
package/AGENTS.md
CHANGED
|
@@ -365,7 +365,7 @@ Either way it exposes **33 tools** across 8 families:
|
|
|
365
365
|
into a complete theme.css — every required token the file lacks inherits from a shipped base theme (`--base`,
|
|
366
366
|
default boilerplate), unmapped paths pass through verbatim and are reported, a `--dark` file or paired
|
|
367
367
|
`color-light`/`color-dark` groups become `light-dark()`, and the validator + WCAG audit run before anything is
|
|
368
|
-
written. Same function as the MCP `theme_from_tokens` tool. Run any verb with `--help`. (`cia init` remains
|
|
368
|
+
written. Same function as the MCP `theme_from_tokens` tool; `npx cia theme map [--json] [--path <token.path>]` prints the design-token → cia-token mapping that verb applies (same data as the MCP `get_token_map` tool), for anyone who needs to map one token name the way the converter would. Run any verb with `--help`. (`cia init` remains
|
|
369
369
|
planned.)
|
|
370
370
|
- **JSON token export** — Tokens Studio-format sample in `figma-tokens/tokens.json`; it round-trips through `cia theme from-tokens` (paired light/dark groups → one `light-dark()` theme).
|
|
371
371
|
- **`llm.txt`** — at the repo root and served from the docs site; single-fetch
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [1.19.1](https://github.com/Jerry2d3d/css-is-awesome/compare/v1.19.0...v1.19.1) (2026-09-23)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Bug Fixes
|
|
7
|
+
|
|
8
|
+
* **cli:** paired light/dark modes carried only colours; export the in-process entry point ([f1fe71f](https://github.com/Jerry2d3d/css-is-awesome/commit/f1fe71f8a91a8e7bf59e0b9e830585701beb9160))
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Features
|
|
12
|
+
|
|
13
|
+
* **site:** cross-link the posts that explain a browser-support row (B6.4) ([44f40d8](https://github.com/Jerry2d3d/css-is-awesome/commit/44f40d8bf278113a81f7f2b643ad52390fb1af25))
|
|
14
|
+
|
|
3
15
|
# [1.19.0](https://github.com/Jerry2d3d/css-is-awesome/compare/v1.18.0...v1.19.0) (2026-09-19)
|
|
4
16
|
|
|
5
17
|
|
package/README.md
CHANGED
|
@@ -417,7 +417,7 @@ Full detail: [`/docs/testing`](https://cssisawesome.com/docs/testing/).
|
|
|
417
417
|
|
|
418
418
|
**Stable, [published on npm](https://www.npmjs.com/package/css-is-awesome)** (first published 2026-09-01). 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.
|
|
419
419
|
|
|
420
|
-
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 33-tool MCP server (now also available zero-install via the companion [`css-is-awesome-mcp`](https://www.npmjs.com/package/css-is-awesome-mcp) package). The npm package ships ZERO runtime JavaScript by hard rule — nothing in it is loaded by a page; its Node tooling (CLI, MCP server, validators) never reaches the browser.
|
|
420
|
+
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, the in-browser [Playground](https://cssisawesome.com/playground/), the design-tokens on-ramp (`npx cia theme from-tokens` / `theme map`), and the 33-tool MCP server (now also available zero-install via the companion [`css-is-awesome-mcp`](https://www.npmjs.com/package/css-is-awesome-mcp) package). The npm package ships ZERO runtime JavaScript by hard rule — nothing in it is loaded by a page; its Node tooling (CLI, MCP server, validators) never reaches the browser.
|
|
421
421
|
|
|
422
422
|
See [CHANGELOG.md](./CHANGELOG.md) for the full history and [MIGRATION.md](./MIGRATION.md) for the v0.7 → v0.8 and v0.8 → v1.0 upgrade paths.
|
|
423
423
|
|
package/dist/tokens.d.ts
CHANGED
package/llm.txt
CHANGED
|
@@ -198,4 +198,4 @@ Humans first. AI's role is to compose, not to lead the pitch.
|
|
|
198
198
|
|
|
199
199
|
---
|
|
200
200
|
|
|
201
|
-
*Generated 2026-05-21 for cia v0.8. Updated 2026-05-23 for the v1.0 architecture lock, 2026-08-30 for the theme single-source rework (24 themes, 127+36 contract, themeable spacing, `:root, :root[data-theme]` selector),
|
|
201
|
+
*Generated 2026-05-21 for cia v0.8. Updated 2026-05-23 for the v1.0 architecture lock, 2026-08-30 for the theme single-source rework (24 themes, 127+36 contract, themeable spacing, `:root, :root[data-theme]` selector), 2026-09-04 for the shipped recipes count (5), the mobile-nav mixin family, and the dropdown popover guard, and 2026-09-19 for the 32-recipe book, 33-tool MCP, contract 1.2 (127 + 41 in 10 features), the playground and the design-tokens on-ramp. Update when the API surface changes.*
|
package/mcp/server.cjs
CHANGED
|
@@ -34,13 +34,13 @@
|
|
|
34
34
|
* "mcpServers": {
|
|
35
35
|
* "css-is-awesome": {
|
|
36
36
|
* "command": "node",
|
|
37
|
-
* "args": ["
|
|
37
|
+
* "args": ["node_modules/css-is-awesome/mcp/server.cjs"]
|
|
38
38
|
* }
|
|
39
39
|
* }
|
|
40
40
|
* }
|
|
41
41
|
*
|
|
42
|
-
* Aligned with the canonical sibling MCP shape
|
|
43
|
-
*
|
|
42
|
+
* Aligned with the canonical sibling MCP shape used across our other servers.
|
|
43
|
+
* Response envelope is `{ total, items }` for list/search;
|
|
44
44
|
* get_* tools return the full record.
|
|
45
45
|
*/
|
|
46
46
|
|
|
@@ -1402,7 +1402,8 @@ async function startServer() {
|
|
|
1402
1402
|
'{ "--token": value } map. Format is auto-detected. Every REQUIRED contract token the file does not supply ' +
|
|
1403
1403
|
'is inherited from a shipped base theme (default boilerplate) and listed in report.inherited, so the output ' +
|
|
1404
1404
|
'is always contract-complete; unmapped paths are emitted verbatim and listed in report.unmapped, never ' +
|
|
1405
|
-
'dropped. Pass `dark` (same format) or
|
|
1405
|
+
'dropped. Pass `dark` (same format), or one file with paired top-level groups — `light`/`dark` (each a full '
|
|
1406
|
+
+ 'token set) or `color-light`/`color-dark` (each a colour set) — to get ' +
|
|
1406
1407
|
'light-dark() values. Returns { css, report, validation } — validation is the same result validate_theme ' +
|
|
1407
1408
|
'gives, run on the CSS before you write it anywhere.',
|
|
1408
1409
|
inputSchema: {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "css-is-awesome",
|
|
3
|
-
"version": "1.19.
|
|
3
|
+
"version": "1.19.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": {
|
|
@@ -41,6 +41,10 @@
|
|
|
41
41
|
"./scss/main": "./scss/main.scss",
|
|
42
42
|
"./scss/tokens": "./scss/tokens.scss",
|
|
43
43
|
"./scss/mixins": "./scss/_mixins.scss",
|
|
44
|
+
"./scripts/tokens-to-theme.cjs": "./scripts/tokens-to-theme.cjs",
|
|
45
|
+
"./scripts/tokens-to-theme": "./scripts/tokens-to-theme.cjs",
|
|
46
|
+
"./scripts/theme-validator.js": "./scripts/theme-validator.js",
|
|
47
|
+
"./mcp/server.cjs": "./mcp/server.cjs",
|
|
44
48
|
"./scss/generator": "./scss/_generator.scss",
|
|
45
49
|
"./scss/utilities": "./scss/_utilities.scss",
|
|
46
50
|
"./scss/animations": "./scss/_animations.scss",
|
|
@@ -48,8 +48,11 @@
|
|
|
48
48
|
// LIGHT + DARK
|
|
49
49
|
// Pass `dark` (a second tokens object, same format) and every colour token
|
|
50
50
|
// that differs becomes `light-dark(light, dark)` with `color-scheme: light dark`.
|
|
51
|
-
// A single
|
|
52
|
-
//
|
|
51
|
+
// A single file with paired top-level groups is split automatically, and the
|
|
52
|
+
// two conventions mean different things: `color-light`/`color-dark` name a
|
|
53
|
+
// COLOUR SET (children are colours), while `light`/`dark` name a MODE whose
|
|
54
|
+
// group holds a FULL token set — colours, spacing, fonts, component
|
|
55
|
+
// overrides — which merges beside any groups shared outside the pair.
|
|
53
56
|
// Without a dark side the block is single-mode: `color-scheme: <mode>`
|
|
54
57
|
// (`mode`, default light).
|
|
55
58
|
// ============================================================================
|
|
@@ -490,6 +493,17 @@ function applyPathAliases(p) {
|
|
|
490
493
|
|
|
491
494
|
/** Returns { token, mapped: true } or { token, mapped: false } (verbatim fallback). */
|
|
492
495
|
function mapPath(tokenPath, contract) {
|
|
496
|
+
if (typeof tokenPath !== 'string' || !tokenPath.trim()) {
|
|
497
|
+
throw new Error('mapPath: first argument must be a non-empty token path, e.g. "color.text.primary"');
|
|
498
|
+
}
|
|
499
|
+
// The contract is optional: omit it and the installed one is loaded. It used
|
|
500
|
+
// to be required, but only the generic-rule branch touched it — so a path in
|
|
501
|
+
// the explicit table appeared to work and the mistake surfaced later as a
|
|
502
|
+
// TypeError on some other path. Fail clearly or not at all.
|
|
503
|
+
if (contract == null) contract = loadContract(path.join(__dirname, '..'));
|
|
504
|
+
else if (!contract.all || typeof contract.all.has !== 'function') {
|
|
505
|
+
throw new Error('mapPath: second argument must be a contract from loadContract(); omit it to load the installed contract automatically');
|
|
506
|
+
}
|
|
493
507
|
if (TOKEN_MAP[tokenPath]) return { token: TOKEN_MAP[tokenPath], mapped: true };
|
|
494
508
|
const aliased = applyPathAliases(tokenPath);
|
|
495
509
|
if (TOKEN_MAP[aliased]) return { token: TOKEN_MAP[aliased], mapped: true };
|
|
@@ -498,6 +512,26 @@ function mapPath(tokenPath, contract) {
|
|
|
498
512
|
// camelCase segments → kebab (linkHover → link-hover), one more try.
|
|
499
513
|
const kebab = `--${aliased.replace(/([a-z0-9])([A-Z])/g, '$1-$2').replace(/\./g, '-').toLowerCase()}`;
|
|
500
514
|
if (contract.all.has(kebab)) return { token: kebab, mapped: true };
|
|
515
|
+
// Flat per-component form: `component.btn-radius` → `--btn-radius`. The
|
|
516
|
+
// explicit table carries the nested shape (`component.button.radius`), and
|
|
517
|
+
// it is tried first; this catches exporters that flatten the group instead.
|
|
518
|
+
// Generic-rule first means this can never shadow a real `--component-*`.
|
|
519
|
+
const flat = aliased.replace(/^components?\./, '');
|
|
520
|
+
if (flat !== aliased) {
|
|
521
|
+
const body = flat.replace(/\./g, '-').toLowerCase();
|
|
522
|
+
// `component.btn-radius` → `--btn-radius`.
|
|
523
|
+
if (contract.all.has(`--${body}`)) return { token: `--${body}`, mapped: true };
|
|
524
|
+
// The contract names per-component overrides two ways: radius trails the
|
|
525
|
+
// component (`--card-radius`) but shadow, border and duration lead it
|
|
526
|
+
// (`--shadow-card`). A flat exporter cannot know which, so try the swap:
|
|
527
|
+
// `card-shadow` → `--shadow-card`, `button-hover-duration` →
|
|
528
|
+
// `--duration-button-hover`. Only accepted if it is a real token.
|
|
529
|
+
const cut = body.lastIndexOf('-');
|
|
530
|
+
if (cut > 0) {
|
|
531
|
+
const swapped = `--${body.slice(cut + 1)}-${body.slice(0, cut)}`;
|
|
532
|
+
if (contract.all.has(swapped)) return { token: swapped, mapped: true };
|
|
533
|
+
}
|
|
534
|
+
}
|
|
501
535
|
return { token: generic, mapped: false };
|
|
502
536
|
}
|
|
503
537
|
|
|
@@ -530,16 +564,35 @@ function loadBase(ciaRoot, base) {
|
|
|
530
564
|
// ---------------------------------------------------------------------------
|
|
531
565
|
// Paired-mode detection for a single Tokens Studio / DTCG file
|
|
532
566
|
// ---------------------------------------------------------------------------
|
|
533
|
-
|
|
567
|
+
// Two different conventions look alike and must NOT be treated alike:
|
|
568
|
+
//
|
|
569
|
+
// `color-light` / `color-dark` name a COLOUR SET. The group's children are
|
|
570
|
+
// colours (`color-light.brand.primary`), so they nest under `color.`.
|
|
571
|
+
// `light` / `dark` name a MODE. The group holds a FULL token
|
|
572
|
+
// set — `color.*`, `spacing.*`, `font.*`, `component.*` — so its
|
|
573
|
+
// children merge at the TOP level, beside any shared groups.
|
|
574
|
+
//
|
|
575
|
+
// Wrapping a mode group in `color.` (which this did for every pair until
|
|
576
|
+
// 2026-09-19) silently mis-routed every non-colour group: `spacing.unit`
|
|
577
|
+
// became `color.spacing.unit` → `--spacing-unit`, so the theme lost its
|
|
578
|
+
// density knob while `validation.ok` stayed true, because the required token
|
|
579
|
+
// was quietly inherited from the base theme instead. Reported by a consumer
|
|
580
|
+
// against boilerplate's canonical DTCG layout.
|
|
581
|
+
const PAIRS = [
|
|
582
|
+
{ light: 'color-light', dark: 'color-dark', wrap: 'color' },
|
|
583
|
+
{ light: 'colors-light', dark: 'colors-dark', wrap: 'color' },
|
|
584
|
+
{ light: 'light', dark: 'dark', wrap: null },
|
|
585
|
+
];
|
|
534
586
|
|
|
535
587
|
function splitPairedModes(tokens) {
|
|
536
588
|
if (!isPlainObject(tokens)) return null;
|
|
537
|
-
for (const
|
|
589
|
+
for (const { light: l, dark: d, wrap } of PAIRS) {
|
|
538
590
|
if (isPlainObject(tokens[l]) && isPlainObject(tokens[d])) {
|
|
539
591
|
const rest = {};
|
|
540
592
|
for (const [k, v] of Object.entries(tokens)) if (k !== l && k !== d) rest[k] = v;
|
|
541
|
-
|
|
542
|
-
const
|
|
593
|
+
// Mode-specific groups win over shared ones on a key collision.
|
|
594
|
+
const light = wrap ? { ...rest, [wrap]: tokens[l] } : { ...rest, ...tokens[l] };
|
|
595
|
+
const dark = wrap ? { ...rest, [wrap]: tokens[d] } : { ...rest, ...tokens[d] };
|
|
543
596
|
return { light, dark, groups: [l, d] };
|
|
544
597
|
}
|
|
545
598
|
}
|