css-is-awesome 1.19.2 → 1.21.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/AGENTS.md +11 -5
  2. package/CHANGELOG.md +20 -0
  3. package/CONTRACT.md +10 -0
  4. package/MIGRATION.md +58 -0
  5. package/README.md +19 -11
  6. package/bin/analyze.cjs +47 -2
  7. package/bin/cia.cjs +10 -0
  8. package/bin/fix-theme.cjs +151 -0
  9. package/llm.txt +5 -5
  10. package/mcp/server.cjs +64 -3
  11. package/package.json +6 -3
  12. package/public/theme.css +138 -0
  13. package/public/themes/boilerplate/theme.css +6 -0
  14. package/public/themes/boilerplate-dark/theme.css +6 -0
  15. package/public/themes/boilerplate-light/theme.css +6 -0
  16. package/public/themes/cupertino/theme.css +6 -0
  17. package/public/themes/cupertino-dark/theme.css +6 -0
  18. package/public/themes/cupertino-light/theme.css +6 -0
  19. package/public/themes/glass/theme.css +6 -0
  20. package/public/themes/glass-dark/theme.css +6 -0
  21. package/public/themes/glass-light/theme.css +6 -0
  22. package/public/themes/graphite/theme.css +6 -0
  23. package/public/themes/graphite-dark/theme.css +6 -0
  24. package/public/themes/graphite-light/theme.css +6 -0
  25. package/public/themes/press/theme.css +6 -0
  26. package/public/themes/press-dark/theme.css +6 -0
  27. package/public/themes/press-light/theme.css +6 -0
  28. package/public/themes/prism/theme.css +6 -0
  29. package/public/themes/prism-dark/theme.css +6 -0
  30. package/public/themes/prism-light/theme.css +6 -0
  31. package/public/themes/sketchbook/theme.css +6 -0
  32. package/public/themes/sketchbook-dark/theme.css +6 -0
  33. package/public/themes/sketchbook-light/theme.css +6 -0
  34. package/public/themes/terminal/theme.css +6 -0
  35. package/public/themes/terminal-dark/theme.css +6 -0
  36. package/public/themes/terminal-light/theme.css +6 -0
  37. package/scripts/audit-pairs.json +171 -23
  38. package/scripts/fix-theme.cjs +184 -0
  39. package/scripts/theme-contract.json +33 -4
  40. package/scripts/theme-validator.js +40 -1
  41. package/scss/_layout.scss +129 -0
package/mcp/server.cjs CHANGED
@@ -21,9 +21,10 @@
21
21
  * Sizing: resolve_size
22
22
  * Themes (build): theme_from_tokens — design-tokens JSON → validated theme.css
23
23
  * get_token_map — the path → token mapping as data (or one path)
24
+ * Themes (fix): fix_theme — rewrite deprecated tokens to their replacements
24
25
  * Prompt: assemble_prompt(intent[, args])
25
26
  *
26
- * 33 tools total.
27
+ * 34 tools total.
27
28
  *
28
29
  * Discovery model: filesystem scan, no database. Parses SCSS files with
29
30
  * focused regex (no full SCSS AST). Tokens come from the authoritative
@@ -471,7 +472,8 @@ function loadTokenContract() {
471
472
  (byCategory[category] = byCategory[category] || []).push(t);
472
473
  }
473
474
 
474
- return { required: contract.required, optional, features, byName, byCategory };
475
+ const deprecated = contract.deprecated && typeof contract.deprecated === 'object' ? contract.deprecated : {};
476
+ return { required: contract.required, optional, features, deprecated, byName, byCategory };
475
477
  }
476
478
 
477
479
  /**
@@ -726,14 +728,51 @@ const handlers = {
726
728
  // source token the same way without re-deriving the rules.
727
729
  get_token_map({ path: tokenPath } = {}) {
728
730
  const { tokenMap, resolvePath } = require(path.join(SCRIPTS_DIR, 'tokens-to-theme.cjs'));
729
- if (tokenPath == null || tokenPath === '') return tokenMap({ ciaRoot: PROJECT_ROOT });
731
+ const deprecated = getTokens().deprecated;
732
+ if (tokenPath == null || tokenPath === '') {
733
+ // `deprecated` rides along so an agent translating a design file lands
734
+ // on the CURRENT name in one call, instead of mapping to a token that
735
+ // is on its way out and finding out later.
736
+ return { ...tokenMap({ ciaRoot: PROJECT_ROOT }), deprecated };
737
+ }
730
738
  const r = resolvePath(String(tokenPath), { ciaRoot: PROJECT_ROOT });
731
739
  const entry = getTokens().byName[r.token] || null;
740
+ const dep = deprecated[r.token] || null;
732
741
  return {
733
742
  ...r,
734
743
  required: entry ? entry.required : null,
735
744
  feature: entry && !entry.required ? (entry.feature || null) : null,
736
745
  category: entry ? entry.category : null,
746
+ deprecated: dep ? { replacedBy: dep.replacedBy || null, since: dep.since || null, removeIn: dep.removeIn || null, note: dep.note || null } : null,
747
+ };
748
+ },
749
+
750
+ // Rewrite a theme's DEPRECATED token declarations to their replacements.
751
+ // Returns the corrected text and a per-line account of what moved and why;
752
+ // it never touches disk, so an agent can show the diff, ask, and only then
753
+ // write. Renames the property only — a value is never altered, so applying
754
+ // this cannot change how the theme looks.
755
+ fix_theme({ css, apply } = {}) {
756
+ if (typeof css !== 'string' || !css.trim()) {
757
+ throw new Error('fix_theme: css is required (the compiled theme CSS, not .scss source)');
758
+ }
759
+ const { fixTheme, deprecations } = require(path.join(SCRIPTS_DIR, 'fix-theme.cjs'));
760
+ const result = fixTheme({ css });
761
+ const rewrites = result.changes.filter((c) => c.kind === 'rewrite');
762
+ const conflicts = result.changes.filter((c) => c.kind === 'conflict');
763
+ return {
764
+ ...result,
765
+ // `apply` is the caller's stated intent, echoed back. This tool has no
766
+ // filesystem access in either case — saying so plainly stops an agent
767
+ // reporting "I updated your theme" when nothing was written.
768
+ applied: false,
769
+ requestedApply: Boolean(apply),
770
+ summary: result.unchanged
771
+ ? (conflicts.length
772
+ ? `Nothing rewritten: ${conflicts.length} collision(s) need a human decision.`
773
+ : 'No deprecated tokens found — this theme is already current.')
774
+ : `${rewrites.length} declaration(s) renamed${conflicts.length ? `, ${conflicts.length} left for you to resolve` : ''}. Write the returned css yourself; nothing was saved.`,
775
+ knownDeprecations: deprecations(),
737
776
  };
738
777
  },
739
778
 
@@ -876,10 +915,16 @@ const handlers = {
876
915
  referencedBy.push({ name: d.name, kind: d.kind, path: d.path });
877
916
  }
878
917
  }
918
+ const dep = getTokens().deprecated[entry.name] || null;
879
919
  return {
880
920
  name: entry.name,
881
921
  category: entry.category,
882
922
  required: entry.required,
923
+ // Contract 1.3+: null unless this token has been superseded. The old
924
+ // value keeps working — `fix_theme` renames it when the caller asks.
925
+ deprecated: dep
926
+ ? { replacedBy: dep.replacedBy || null, since: dep.since || null, removeIn: dep.removeIn || null, note: dep.note || null }
927
+ : null,
883
928
  // Contract 1.2: the feature an OPTIONAL token enables (null for required).
884
929
  feature: entry.required ? null : (entry.feature || null),
885
930
  themeValues,
@@ -1417,6 +1462,22 @@ async function startServer() {
1417
1462
  },
1418
1463
  }, async (a) => ok(handlers.theme_from_tokens(a || {})));
1419
1464
 
1465
+ server.registerTool('fix_theme', {
1466
+ description:
1467
+ 'Upgrade a theme that uses a DEPRECATED token. cia deprecates a token rather than deleting it, so the ' +
1468
+ 'old declaration keeps working — but there is a better name now, and this rewrites it for you. Pass the ' +
1469
+ 'compiled theme CSS; get back { css, changes, unchanged, summary } where every change names the line, the ' +
1470
+ 'old token, its replacement and why it moved. Only the property name changes — values, comments, ordering ' +
1471
+ 'and whitespace survive byte-for-byte, so applying it cannot alter how the theme looks. If a block already ' +
1472
+ 'declares the replacement the old line is left alone and reported as a conflict, because merging two values ' +
1473
+ 'is a judgement call. NOTHING IS WRITTEN TO DISK: this returns text, and the caller decides whether to save ' +
1474
+ 'it. Use get_token to see what supersedes a given token, or read knownDeprecations in the result.',
1475
+ inputSchema: {
1476
+ css: z.string().describe('Compiled theme CSS — the :root/[data-theme] block(s), not .scss source.'),
1477
+ apply: z.boolean().optional().describe('Your stated intent, echoed back as requestedApply. This tool cannot write files either way; you save the returned css yourself.'),
1478
+ },
1479
+ }, async (a) => ok(handlers.fix_theme(a || {})));
1480
+
1420
1481
  server.registerTool('get_token_map', {
1421
1482
  description:
1422
1483
  'The design-token → cia-token mapping that theme_from_tokens applies, as data. Without `path`: ' +
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "css-is-awesome",
3
- "version": "1.19.2",
3
+ "version": "1.21.0",
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": {
@@ -74,6 +74,7 @@
74
74
  "scripts/theme-contract.json",
75
75
  "scripts/theme-validator.js",
76
76
  "scripts/tokens-to-theme.cjs",
77
+ "scripts/fix-theme.cjs",
77
78
  "scripts/theme-a11y.js",
78
79
  "scripts/audit-pairs.json",
79
80
  "scripts/icon-validator.js",
@@ -109,7 +110,7 @@
109
110
  "build:css": "sass scss/main.scss dist/css-is-awesome.css --no-source-map",
110
111
  "build:css:core": "sass scss/core.scss dist/css-is-awesome.core.css --no-source-map",
111
112
  "build:css:tokens": "sass scss/tokens.scss dist/tokens.css --no-source-map",
112
- "build:css:themes": "node scripts/build-themes.mjs && node scripts/build-theme-bundle.mjs",
113
+ "build:css:themes": "node scripts/build-themes.mjs && node scripts/derive-page-surfaces.mjs && node scripts/build-theme-bundle.mjs",
113
114
  "build:css:utilities": "sass scss/utilities-only.scss dist/css-is-awesome.utilities.css --no-source-map",
114
115
  "build:css:min": "sass scss/main.scss dist/css-is-awesome.min.css --style=compressed --no-source-map && sass scss/core.scss dist/css-is-awesome.core.min.css --style=compressed --no-source-map && sass scss/tokens.scss dist/tokens.min.css --style=compressed --no-source-map && sass scss/utilities-only.scss dist/css-is-awesome.utilities.min.css --style=compressed --no-source-map",
115
116
  "build:token-types": "node scripts/generate-token-types.mjs",
@@ -123,6 +124,7 @@
123
124
  "validate-package": "node scripts/validate-package.mjs",
124
125
  "validate-recipes": "node scripts/validate-recipes.mjs",
125
126
  "test:tokens": "node --test scripts/test-tokens-to-theme.mjs",
127
+ "test:fix-theme": "node --test scripts/test-fix-theme.mjs",
126
128
  "size-budget": "node scripts/size-budget.mjs",
127
129
  "size-report": "node scripts/size-budget.mjs --report",
128
130
  "coverage:api": "node scripts/api-coverage.mjs",
@@ -139,7 +141,8 @@
139
141
  "check:token-consumers": "node scripts/check-token-consumer-map.mjs",
140
142
  "check:rtl": "node scripts/audit-logical-properties.mjs",
141
143
  "prebuild": "node scripts/build-playground-scss-map.mjs",
142
- "verify:playground": "node scripts/build-playground-scss-map.mjs && node scripts/verify-playground-compile.mjs"
144
+ "verify:playground": "node scripts/build-playground-scss-map.mjs && node scripts/verify-playground-compile.mjs",
145
+ "test:surfaces": "node --test scripts/test-no-auto-surface.mjs"
143
146
  },
144
147
  "keywords": [
145
148
  "css",
package/public/theme.css CHANGED
@@ -189,6 +189,12 @@
189
189
  --z-modal: 1050;
190
190
  --z-popover: 1060;
191
191
  --z-tooltip: 1070;
192
+
193
+ /* page surfaces — derived, see scripts/derive-page-surfaces.mjs */
194
+ --page-hero-bg: light-dark(#f2f6fe, #121e36);
195
+ --page-hero-ink: light-dark(#0f172a, #f8fafc);
196
+ --page-band-bg: light-dark(#f7fafe, #111b31);
197
+ --page-band-ink: light-dark(#0f172a, #f8fafc);
192
198
  }
193
199
  :root[data-theme=boilerplate-dark] {
194
200
  color-scheme: dark;
@@ -363,6 +369,12 @@
363
369
  --space-7: calc(var(--space-unit) * 24);
364
370
  --space-8: calc(var(--space-unit) * 32);
365
371
  --space-9: calc(var(--space-unit) * 48);
372
+
373
+ /* page surfaces — derived, see scripts/derive-page-surfaces.mjs */
374
+ --page-hero-bg: #121e36;
375
+ --page-hero-ink: #f8fafc;
376
+ --page-band-bg: #111b31;
377
+ --page-band-ink: #f8fafc;
366
378
  }
367
379
  :root[data-theme=boilerplate-light] {
368
380
  color-scheme: light;
@@ -537,6 +549,12 @@
537
549
  --space-7: calc(var(--space-unit) * 24);
538
550
  --space-8: calc(var(--space-unit) * 32);
539
551
  --space-9: calc(var(--space-unit) * 48);
552
+
553
+ /* page surfaces — derived, see scripts/derive-page-surfaces.mjs */
554
+ --page-hero-bg: #f2f6fe;
555
+ --page-hero-ink: #0f172a;
556
+ --page-band-bg: #f7fafe;
557
+ --page-band-ink: #0f172a;
540
558
  }
541
559
  :root[data-theme=cupertino] {
542
560
  color-scheme: light dark;
@@ -711,6 +729,12 @@
711
729
  --input-radius: var(--r-sm);
712
730
  --modal-radius: var(--r-lg);
713
731
  --tooltip-radius: var(--r-sm);
732
+
733
+ /* page surfaces — derived, see scripts/derive-page-surfaces.mjs */
734
+ --page-hero-bg: light-dark(#e6ecf3, #1e252c);
735
+ --page-hero-ink: light-dark(#1d1d1f, #f5f5f7);
736
+ --page-band-bg: light-dark(#ecf0f5, #1e2226);
737
+ --page-band-ink: light-dark(#1d1d1f, #f5f5f7);
714
738
  }
715
739
  :root[data-theme=cupertino-dark] {
716
740
  color-scheme: dark;
@@ -902,6 +926,12 @@
902
926
  --input-radius: var(--r-sm); /* 8px — text field */
903
927
  --modal-radius: var(--r-lg); /* matches panel */
904
928
  --tooltip-radius: var(--r-sm); /* bezel tooltip */
929
+
930
+ /* page surfaces — derived, see scripts/derive-page-surfaces.mjs */
931
+ --page-hero-bg: #1e252c;
932
+ --page-hero-ink: #f5f5f7;
933
+ --page-band-bg: #1e2226;
934
+ --page-band-ink: #f5f5f7;
905
935
  }
906
936
  :root[data-theme=cupertino-light] {
907
937
  color-scheme: light;
@@ -1109,6 +1139,12 @@
1109
1139
  --input-radius: var(--r-sm); /* 8px — text field */
1110
1140
  --modal-radius: var(--r-lg); /* matches panel */
1111
1141
  --tooltip-radius: var(--r-sm); /* bezel tooltip */
1142
+
1143
+ /* page surfaces — derived, see scripts/derive-page-surfaces.mjs */
1144
+ --page-hero-bg: #e6ecf3;
1145
+ --page-hero-ink: #1d1d1f;
1146
+ --page-band-bg: #ecf0f5;
1147
+ --page-band-ink: #1d1d1f;
1112
1148
  }
1113
1149
  :root[data-theme=glass] {
1114
1150
  color-scheme: light dark;
@@ -1308,6 +1344,12 @@
1308
1344
  --z-modal: 1050;
1309
1345
  --z-popover: 1060;
1310
1346
  --z-tooltip: 1070;
1347
+
1348
+ /* page surfaces — derived, see scripts/derive-page-surfaces.mjs */
1349
+ --page-hero-bg: light-dark(#ebe5fd, #101828);
1350
+ --page-hero-ink: light-dark(#1d1d1f, #f0f4ff);
1351
+ --page-band-bg: light-dark(#eee9fe, #0e1422);
1352
+ --page-band-ink: light-dark(#1d1d1f, #f0f4ff);
1311
1353
  }
1312
1354
  :root[data-theme=glass-dark] {
1313
1355
  color-scheme: dark;
@@ -1499,6 +1541,12 @@
1499
1541
  --z-modal: 1050;
1500
1542
  --z-popover: 1060;
1501
1543
  --z-tooltip: 1070;
1544
+
1545
+ /* page surfaces — derived, see scripts/derive-page-surfaces.mjs */
1546
+ --page-hero-bg: #101828;
1547
+ --page-hero-ink: #f0f4ff;
1548
+ --page-band-bg: #0e1422;
1549
+ --page-band-ink: #f0f4ff;
1502
1550
  }
1503
1551
  :root[data-theme=glass-light] {
1504
1552
  color-scheme: light;
@@ -1701,6 +1749,12 @@
1701
1749
  --z-modal: 1050;
1702
1750
  --z-popover: 1060;
1703
1751
  --z-tooltip: 1070;
1752
+
1753
+ /* page surfaces — derived, see scripts/derive-page-surfaces.mjs */
1754
+ --page-hero-bg: #ebe5fd;
1755
+ --page-hero-ink: #1d1d1f;
1756
+ --page-band-bg: #eee9fe;
1757
+ --page-band-ink: #1d1d1f;
1704
1758
  }
1705
1759
  :root[data-theme=graphite] {
1706
1760
  color-scheme: light dark;
@@ -1865,6 +1919,12 @@
1865
1919
  --z-modal: 1050;
1866
1920
  --z-popover: 1060;
1867
1921
  --z-tooltip: 1070;
1922
+
1923
+ /* page surfaces — derived, see scripts/derive-page-surfaces.mjs */
1924
+ --page-hero-bg: light-dark(#e8ebef, #1a212a);
1925
+ --page-hero-ink: light-dark(#1a1f26, #f5f5f7);
1926
+ --page-band-bg: light-dark(#eceff2, #1a1e24);
1927
+ --page-band-ink: light-dark(#1a1f26, #f5f5f7);
1868
1928
  }
1869
1929
  :root[data-theme=graphite-dark] {
1870
1930
  color-scheme: dark;
@@ -2057,6 +2117,12 @@
2057
2117
  --z-modal: 1050;
2058
2118
  --z-popover: 1060;
2059
2119
  --z-tooltip: 1070;
2120
+
2121
+ /* page surfaces — derived, see scripts/derive-page-surfaces.mjs */
2122
+ --page-hero-bg: #1a212a;
2123
+ --page-hero-ink: #f5f5f7;
2124
+ --page-band-bg: #1a1e24;
2125
+ --page-band-ink: #f5f5f7;
2060
2126
  }
2061
2127
  :root[data-theme=graphite-light] {
2062
2128
  color-scheme: light;
@@ -2239,6 +2305,12 @@
2239
2305
  --z-modal: 1050;
2240
2306
  --z-popover: 1060;
2241
2307
  --z-tooltip: 1070;
2308
+
2309
+ /* page surfaces — derived, see scripts/derive-page-surfaces.mjs */
2310
+ --page-hero-bg: #e8ebef;
2311
+ --page-hero-ink: #1a1f26;
2312
+ --page-band-bg: #eceff2;
2313
+ --page-band-ink: #1a1f26;
2242
2314
  }
2243
2315
  :root[data-theme=press] {
2244
2316
  color-scheme: light dark;
@@ -2649,6 +2721,12 @@
2649
2721
  --card-border: 1px solid var(--hair); /* thin rule instead */
2650
2722
  --input-radius: var(--r-sm); /* 2px */
2651
2723
  --modal-radius: var(--r-md); /* 3px */
2724
+
2725
+ /* page surfaces — derived, see scripts/derive-page-surfaces.mjs */
2726
+ --page-hero-bg: #1c1c1c;
2727
+ --page-hero-ink: #fafaf7;
2728
+ --page-band-bg: #161616;
2729
+ --page-band-ink: #fafaf7;
2652
2730
  }
2653
2731
  :root[data-theme=press-light] {
2654
2732
  color-scheme: light;
@@ -2859,6 +2937,12 @@
2859
2937
  --card-border: 1px solid var(--hair); /* thin ink rule instead */
2860
2938
  --input-radius: var(--r-sm); /* 2px — text field */
2861
2939
  --modal-radius: var(--r-md); /* 3px — still tight */
2940
+
2941
+ /* page surfaces — derived, see scripts/derive-page-surfaces.mjs */
2942
+ --page-hero-bg: #ededea;
2943
+ --page-hero-ink: #0a0a0a;
2944
+ --page-band-bg: #f2f2ef;
2945
+ --page-band-ink: #0a0a0a;
2862
2946
  }
2863
2947
  :root[data-theme=prism] {
2864
2948
  color-scheme: light dark;
@@ -3027,6 +3111,12 @@
3027
3111
  --card-border: 1px solid var(--hair);
3028
3112
  --input-radius: var(--r-md);
3029
3113
  --modal-radius: var(--r-lg);
3114
+
3115
+ /* page surfaces — derived, see scripts/derive-page-surfaces.mjs */
3116
+ --page-hero-bg: light-dark(#f2f6fe, #0c1019);
3117
+ --page-hero-ink: light-dark(#09090b, #fafafa);
3118
+ --page-band-bg: light-dark(#f7fafe, #0b0d13);
3119
+ --page-band-ink: light-dark(#09090b, #fafafa);
3030
3120
  }
3031
3121
  :root[data-theme=prism-dark] {
3032
3122
  color-scheme: dark;
@@ -3210,6 +3300,12 @@
3210
3300
  --card-border: 1px solid var(--hair);
3211
3301
  --input-radius: var(--r-md);
3212
3302
  --modal-radius: var(--r-lg);
3303
+
3304
+ /* page surfaces — derived, see scripts/derive-page-surfaces.mjs */
3305
+ --page-hero-bg: #0c1019;
3306
+ --page-hero-ink: #fafafa;
3307
+ --page-band-bg: #0b0d13;
3308
+ --page-band-ink: #fafafa;
3213
3309
  }
3214
3310
  :root[data-theme=prism-light] {
3215
3311
  color-scheme: light;
@@ -3393,6 +3489,12 @@
3393
3489
  --card-border: 1px solid var(--hair);
3394
3490
  --input-radius: var(--r-md);
3395
3491
  --modal-radius: var(--r-lg);
3492
+
3493
+ /* page surfaces — derived, see scripts/derive-page-surfaces.mjs */
3494
+ --page-hero-bg: #f2f6fe;
3495
+ --page-hero-ink: #09090b;
3496
+ --page-band-bg: #f7fafe;
3497
+ --page-band-ink: #09090b;
3396
3498
  }
3397
3499
  :root[data-theme=sketchbook] {
3398
3500
  color-scheme: light dark;
@@ -3554,6 +3656,12 @@
3554
3656
  --z-modal: 1050;
3555
3657
  --z-popover: 1060;
3556
3658
  --z-tooltip: 1070;
3659
+
3660
+ /* page surfaces — derived, see scripts/derive-page-surfaces.mjs */
3661
+ --page-hero-bg: light-dark(#eae8e2, #1f1f1f);
3662
+ --page-hero-ink: light-dark(#2a241e, #f4efe2);
3663
+ --page-band-bg: light-dark(#efede5, #1d1c1b);
3664
+ --page-band-ink: light-dark(#2a241e, #f4efe2);
3557
3665
  }
3558
3666
  :root[data-theme=sketchbook-dark] {
3559
3667
  color-scheme: dark;
@@ -3736,6 +3844,12 @@
3736
3844
  --z-modal: 1050;
3737
3845
  --z-popover: 1060;
3738
3846
  --z-tooltip: 1070;
3847
+
3848
+ /* page surfaces — derived, see scripts/derive-page-surfaces.mjs */
3849
+ --page-hero-bg: #1f1f1f;
3850
+ --page-hero-ink: #f4efe2;
3851
+ --page-band-bg: #1d1c1b;
3852
+ --page-band-ink: #f4efe2;
3739
3853
  }
3740
3854
  :root[data-theme=sketchbook-light] {
3741
3855
  color-scheme: light;
@@ -3927,6 +4041,12 @@
3927
4041
  --space-7: calc(var(--space-unit) * 24);
3928
4042
  --space-8: calc(var(--space-unit) * 32);
3929
4043
  --space-9: calc(var(--space-unit) * 48);
4044
+
4045
+ /* page surfaces — derived, see scripts/derive-page-surfaces.mjs */
4046
+ --page-hero-bg: #eae8e2;
4047
+ --page-hero-ink: #2a241e;
4048
+ --page-band-bg: #efede5;
4049
+ --page-band-ink: #2a241e;
3930
4050
  }
3931
4051
  :root[data-theme=terminal] {
3932
4052
  color-scheme: dark;
@@ -4095,6 +4215,12 @@
4095
4215
  --input-radius: 0;
4096
4216
  --card-shadow: var(--glow-sm);
4097
4217
  --card-border: 1px solid var(--ink-faint);
4218
+
4219
+ /* page surfaces — derived, see scripts/derive-page-surfaces.mjs */
4220
+ --page-hero-bg: #111a19;
4221
+ --page-hero-ink: #4fe078;
4222
+ --page-band-bg: #0e1513;
4223
+ --page-band-ink: #4fe078;
4098
4224
  }
4099
4225
  :root[data-theme=terminal-dark] {
4100
4226
  color-scheme: dark;
@@ -4298,6 +4424,12 @@
4298
4424
  --input-radius: 0;
4299
4425
  --card-shadow: var(--glow-sm);
4300
4426
  --card-border: 1px solid var(--ink-faint);
4427
+
4428
+ /* page surfaces — derived, see scripts/derive-page-surfaces.mjs */
4429
+ --page-hero-bg: #111a19;
4430
+ --page-hero-ink: #4fe078;
4431
+ --page-band-bg: #0e1513;
4432
+ --page-band-ink: #4fe078;
4301
4433
  }
4302
4434
  :root[data-theme=terminal-light] {
4303
4435
  color-scheme: light;
@@ -4459,4 +4591,10 @@
4459
4591
  --z-modal: 1050;
4460
4592
  --z-popover: 1060;
4461
4593
  --z-tooltip: 1070;
4594
+
4595
+ /* page surfaces — derived, see scripts/derive-page-surfaces.mjs */
4596
+ --page-hero-bg: #dadfe4;
4597
+ --page-hero-ink: #1a1a1a;
4598
+ --page-band-bg: #e0e3e6;
4599
+ --page-band-ink: #1a1a1a;
4462
4600
  }
@@ -162,4 +162,10 @@
162
162
  --z-modal: 1050;
163
163
  --z-popover: 1060;
164
164
  --z-tooltip: 1070;
165
+
166
+ /* page surfaces — derived, see scripts/derive-page-surfaces.mjs */
167
+ --page-hero-bg: light-dark(#f2f6fe, #121e36);
168
+ --page-hero-ink: light-dark(#0f172a, #f8fafc);
169
+ --page-band-bg: light-dark(#f7fafe, #111b31);
170
+ --page-band-ink: light-dark(#0f172a, #f8fafc);
165
171
  }
@@ -172,4 +172,10 @@
172
172
  --space-7: calc(var(--space-unit) * 24);
173
173
  --space-8: calc(var(--space-unit) * 32);
174
174
  --space-9: calc(var(--space-unit) * 48);
175
+
176
+ /* page surfaces — derived, see scripts/derive-page-surfaces.mjs */
177
+ --page-hero-bg: #121e36;
178
+ --page-hero-ink: #f8fafc;
179
+ --page-band-bg: #111b31;
180
+ --page-band-ink: #f8fafc;
175
181
  }
@@ -172,4 +172,10 @@
172
172
  --space-7: calc(var(--space-unit) * 24);
173
173
  --space-8: calc(var(--space-unit) * 32);
174
174
  --space-9: calc(var(--space-unit) * 48);
175
+
176
+ /* page surfaces — derived, see scripts/derive-page-surfaces.mjs */
177
+ --page-hero-bg: #f2f6fe;
178
+ --page-hero-ink: #0f172a;
179
+ --page-band-bg: #f7fafe;
180
+ --page-band-ink: #0f172a;
175
181
  }
@@ -173,4 +173,10 @@
173
173
  --input-radius: var(--r-sm);
174
174
  --modal-radius: var(--r-lg);
175
175
  --tooltip-radius: var(--r-sm);
176
+
177
+ /* page surfaces — derived, see scripts/derive-page-surfaces.mjs */
178
+ --page-hero-bg: light-dark(#e6ecf3, #1e252c);
179
+ --page-hero-ink: light-dark(#1d1d1f, #f5f5f7);
180
+ --page-band-bg: light-dark(#ecf0f5, #1e2226);
181
+ --page-band-ink: light-dark(#1d1d1f, #f5f5f7);
176
182
  }
@@ -208,4 +208,10 @@
208
208
  --input-radius: var(--r-sm); /* 8px — text field */
209
209
  --modal-radius: var(--r-lg); /* matches panel */
210
210
  --tooltip-radius: var(--r-sm); /* bezel tooltip */
211
+
212
+ /* page surfaces — derived, see scripts/derive-page-surfaces.mjs */
213
+ --page-hero-bg: #1e252c;
214
+ --page-hero-ink: #f5f5f7;
215
+ --page-band-bg: #1e2226;
216
+ --page-band-ink: #f5f5f7;
211
217
  }
@@ -235,4 +235,10 @@
235
235
  --input-radius: var(--r-sm); /* 8px — text field */
236
236
  --modal-radius: var(--r-lg); /* matches panel */
237
237
  --tooltip-radius: var(--r-sm); /* bezel tooltip */
238
+
239
+ /* page surfaces — derived, see scripts/derive-page-surfaces.mjs */
240
+ --page-hero-bg: #e6ecf3;
241
+ --page-hero-ink: #1d1d1f;
242
+ --page-band-bg: #ecf0f5;
243
+ --page-band-ink: #1d1d1f;
238
244
  }
@@ -198,4 +198,10 @@
198
198
  --z-modal: 1050;
199
199
  --z-popover: 1060;
200
200
  --z-tooltip: 1070;
201
+
202
+ /* page surfaces — derived, see scripts/derive-page-surfaces.mjs */
203
+ --page-hero-bg: light-dark(#ebe5fd, #101828);
204
+ --page-hero-ink: light-dark(#1d1d1f, #f0f4ff);
205
+ --page-band-bg: light-dark(#eee9fe, #0e1422);
206
+ --page-band-ink: light-dark(#1d1d1f, #f0f4ff);
201
207
  }
@@ -209,4 +209,10 @@
209
209
  --z-modal: 1050;
210
210
  --z-popover: 1060;
211
211
  --z-tooltip: 1070;
212
+
213
+ /* page surfaces — derived, see scripts/derive-page-surfaces.mjs */
214
+ --page-hero-bg: #101828;
215
+ --page-hero-ink: #f0f4ff;
216
+ --page-band-bg: #0e1422;
217
+ --page-band-ink: #f0f4ff;
212
218
  }
@@ -237,4 +237,10 @@
237
237
  --z-modal: 1050;
238
238
  --z-popover: 1060;
239
239
  --z-tooltip: 1070;
240
+
241
+ /* page surfaces — derived, see scripts/derive-page-surfaces.mjs */
242
+ --page-hero-bg: #ebe5fd;
243
+ --page-hero-ink: #1d1d1f;
244
+ --page-band-bg: #eee9fe;
245
+ --page-band-ink: #1d1d1f;
240
246
  }
@@ -163,4 +163,10 @@
163
163
  --z-modal: 1050;
164
164
  --z-popover: 1060;
165
165
  --z-tooltip: 1070;
166
+
167
+ /* page surfaces — derived, see scripts/derive-page-surfaces.mjs */
168
+ --page-hero-bg: light-dark(#e8ebef, #1a212a);
169
+ --page-hero-ink: light-dark(#1a1f26, #f5f5f7);
170
+ --page-band-bg: light-dark(#eceff2, #1a1e24);
171
+ --page-band-ink: light-dark(#1a1f26, #f5f5f7);
166
172
  }
@@ -231,4 +231,10 @@
231
231
  --z-modal: 1050;
232
232
  --z-popover: 1060;
233
233
  --z-tooltip: 1070;
234
+
235
+ /* page surfaces — derived, see scripts/derive-page-surfaces.mjs */
236
+ --page-hero-bg: #1a212a;
237
+ --page-hero-ink: #f5f5f7;
238
+ --page-band-bg: #1a1e24;
239
+ --page-band-ink: #f5f5f7;
234
240
  }
@@ -199,4 +199,10 @@
199
199
  --z-modal: 1050;
200
200
  --z-popover: 1060;
201
201
  --z-tooltip: 1070;
202
+
203
+ /* page surfaces — derived, see scripts/derive-page-surfaces.mjs */
204
+ --page-hero-bg: #e8ebef;
205
+ --page-hero-ink: #1a1f26;
206
+ --page-band-bg: #eceff2;
207
+ --page-band-ink: #1a1f26;
202
208
  }
@@ -218,4 +218,10 @@
218
218
  --print-muted: #6B6963; /* warm editorial gray */
219
219
  /* stylelint-enable no-invalid-position-declaration */
220
220
  }
221
+
222
+ /* page surfaces — derived, see scripts/derive-page-surfaces.mjs */
223
+ --page-hero-bg: light-dark(#ededea, #1c1c1c);
224
+ --page-hero-ink: light-dark(#0a0a0a, #fafaf7);
225
+ --page-band-bg: light-dark(#f2f2ef, #161616);
226
+ --page-band-ink: light-dark(#0a0a0a, #fafaf7);
221
227
  }
@@ -207,4 +207,10 @@
207
207
  --card-border: 1px solid var(--hair); /* thin rule instead */
208
208
  --input-radius: var(--r-sm); /* 2px */
209
209
  --modal-radius: var(--r-md); /* 3px */
210
+
211
+ /* page surfaces — derived, see scripts/derive-page-surfaces.mjs */
212
+ --page-hero-bg: #1c1c1c;
213
+ --page-hero-ink: #fafaf7;
214
+ --page-band-bg: #161616;
215
+ --page-band-ink: #fafaf7;
210
216
  }
@@ -243,4 +243,10 @@
243
243
  --card-border: 1px solid var(--hair); /* thin ink rule instead */
244
244
  --input-radius: var(--r-sm); /* 2px — text field */
245
245
  --modal-radius: var(--r-md); /* 3px — still tight */
246
+
247
+ /* page surfaces — derived, see scripts/derive-page-surfaces.mjs */
248
+ --page-hero-bg: #ededea;
249
+ --page-hero-ink: #0a0a0a;
250
+ --page-band-bg: #f2f2ef;
251
+ --page-band-ink: #0a0a0a;
246
252
  }
@@ -167,4 +167,10 @@
167
167
  --card-border: 1px solid var(--hair);
168
168
  --input-radius: var(--r-md);
169
169
  --modal-radius: var(--r-lg);
170
+
171
+ /* page surfaces — derived, see scripts/derive-page-surfaces.mjs */
172
+ --page-hero-bg: light-dark(#f2f6fe, #0c1019);
173
+ --page-hero-ink: light-dark(#09090b, #fafafa);
174
+ --page-band-bg: light-dark(#f7fafe, #0b0d13);
175
+ --page-band-ink: light-dark(#09090b, #fafafa);
170
176
  }
@@ -193,4 +193,10 @@
193
193
  --card-border: 1px solid var(--hair);
194
194
  --input-radius: var(--r-md);
195
195
  --modal-radius: var(--r-lg);
196
+
197
+ /* page surfaces — derived, see scripts/derive-page-surfaces.mjs */
198
+ --page-hero-bg: #0c1019;
199
+ --page-hero-ink: #fafafa;
200
+ --page-band-bg: #0b0d13;
201
+ --page-band-ink: #fafafa;
196
202
  }
@@ -196,4 +196,10 @@
196
196
  --card-border: 1px solid var(--hair);
197
197
  --input-radius: var(--r-md);
198
198
  --modal-radius: var(--r-lg);
199
+
200
+ /* page surfaces — derived, see scripts/derive-page-surfaces.mjs */
201
+ --page-hero-bg: #f2f6fe;
202
+ --page-hero-ink: #09090b;
203
+ --page-band-bg: #f7fafe;
204
+ --page-band-ink: #09090b;
199
205
  }
@@ -160,4 +160,10 @@
160
160
  --z-modal: 1050;
161
161
  --z-popover: 1060;
162
162
  --z-tooltip: 1070;
163
+
164
+ /* page surfaces — derived, see scripts/derive-page-surfaces.mjs */
165
+ --page-hero-bg: light-dark(#eae8e2, #1f1f1f);
166
+ --page-hero-ink: light-dark(#2a241e, #f4efe2);
167
+ --page-band-bg: light-dark(#efede5, #1d1c1b);
168
+ --page-band-ink: light-dark(#2a241e, #f4efe2);
163
169
  }