@decantr/cli 1.5.2 → 1.5.4

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.
@@ -2,6 +2,214 @@
2
2
  import { existsSync, mkdirSync, readFileSync, writeFileSync, appendFileSync } from "fs";
3
3
  import { join, dirname } from "path";
4
4
  import { fileURLToPath } from "url";
5
+ import { computeSpatialTokens } from "@decantr/essence-spec";
6
+
7
+ // src/treatments.ts
8
+ function generateTreatmentCSS(spatialTokens, treatmentOverrides, themeDecorators, themeName) {
9
+ const lines = [];
10
+ lines.push("/* Generated by @decantr/cli \u2014 Visual Treatment System */");
11
+ lines.push("");
12
+ lines.push("/* \u2500\u2500 Layer 1: Base Treatments \u2500\u2500 */");
13
+ lines.push("");
14
+ function emitRule(selector, props) {
15
+ const treatmentName = selector.replace(/^\./, "").replace(/[:[\s].+$/, "");
16
+ const overrides = treatmentOverrides?.[treatmentName];
17
+ const merged = /* @__PURE__ */ new Map();
18
+ for (const [prop, val] of props) {
19
+ merged.set(prop, val);
20
+ }
21
+ if (overrides && selector === `.${treatmentName}`) {
22
+ for (const [prop, val] of Object.entries(overrides)) {
23
+ merged.set(prop, val);
24
+ }
25
+ }
26
+ const body = Array.from(merged.entries()).map(([p, v]) => ` ${p}: ${v};`).join("\n");
27
+ lines.push(`${selector} {`);
28
+ lines.push(body);
29
+ lines.push("}");
30
+ lines.push("");
31
+ }
32
+ emitRule(".d-interactive", [
33
+ ["display", "inline-flex"],
34
+ ["align-items", "center"],
35
+ ["gap", "0.5em"],
36
+ ["padding", "var(--d-interactive-py) var(--d-interactive-px)"],
37
+ ["border", "1px solid var(--d-border)"],
38
+ ["border-radius", "var(--d-radius)"],
39
+ ["background", "transparent"],
40
+ ["color", "var(--d-text)"],
41
+ ["font", "inherit"],
42
+ ["cursor", "pointer"],
43
+ ["text-decoration", "none"],
44
+ ["transition", "background 0.15s ease, border-color 0.15s ease, color 0.15s ease, box-shadow 0.15s ease, opacity 0.15s ease"]
45
+ ]);
46
+ emitRule(".d-interactive:hover", [
47
+ ["border-color", "var(--d-primary-hover)"],
48
+ ["background", "var(--d-surface)"]
49
+ ]);
50
+ emitRule(".d-interactive:focus-visible", [
51
+ ["outline", "2px solid var(--d-primary)"],
52
+ ["outline-offset", "2px"]
53
+ ]);
54
+ emitRule(".d-interactive:disabled", [
55
+ ["opacity", "0.5"],
56
+ ["cursor", "not-allowed"],
57
+ ["pointer-events", "none"]
58
+ ]);
59
+ emitRule('.d-interactive[data-variant="primary"]', [
60
+ ["background", "var(--d-primary)"],
61
+ ["color", "#fff"],
62
+ ["border-color", "var(--d-primary)"]
63
+ ]);
64
+ emitRule('.d-interactive[data-variant="primary"]:hover', [
65
+ ["background", "var(--d-primary-hover)"],
66
+ ["border-color", "var(--d-primary-hover)"]
67
+ ]);
68
+ emitRule('.d-interactive[data-variant="ghost"]', [
69
+ ["border-color", "transparent"],
70
+ ["background", "transparent"]
71
+ ]);
72
+ emitRule('.d-interactive[data-variant="ghost"]:hover', [
73
+ ["background", "var(--d-surface)"]
74
+ ]);
75
+ emitRule('.d-interactive[data-variant="danger"]', [
76
+ ["background", "var(--d-error)"],
77
+ ["color", "#fff"],
78
+ ["border-color", "var(--d-error)"]
79
+ ]);
80
+ emitRule(".d-surface", [
81
+ ["background", "var(--d-surface)"],
82
+ ["border", "1px solid var(--d-border)"],
83
+ ["border-radius", "var(--d-radius)"],
84
+ ["box-shadow", "var(--d-shadow)"],
85
+ ["padding", "var(--d-surface-p)"]
86
+ ]);
87
+ emitRule('.d-surface[data-elevation="raised"]', [
88
+ ["background", "var(--d-surface-raised)"],
89
+ ["box-shadow", "var(--d-shadow-md)"]
90
+ ]);
91
+ emitRule('.d-surface[data-elevation="overlay"]', [
92
+ ["background", "var(--d-surface-raised)"],
93
+ ["box-shadow", "var(--d-shadow-lg)"],
94
+ ["z-index", "50"]
95
+ ]);
96
+ emitRule(".d-surface[data-interactive]:hover", [
97
+ ["border-color", "var(--d-primary-hover, var(--d-border))"],
98
+ ["box-shadow", "var(--d-shadow-md)"],
99
+ ["transition", "border-color 0.15s ease, box-shadow 0.15s ease"]
100
+ ]);
101
+ emitRule(".d-data", [
102
+ ["width", "100%"],
103
+ ["border-collapse", "collapse"],
104
+ ["text-align", "left"],
105
+ ["font-size", "0.875rem"]
106
+ ]);
107
+ emitRule(".d-data-header", [
108
+ ["padding", "var(--d-data-py) var(--d-content-gap)"],
109
+ ["font-weight", "500"],
110
+ ["color", "var(--d-text-muted)"],
111
+ ["border-bottom", "1px solid var(--d-border)"],
112
+ ["font-size", "0.75rem"],
113
+ ["text-transform", "uppercase"],
114
+ ["letter-spacing", "0.05em"]
115
+ ]);
116
+ emitRule(".d-data-row", [
117
+ ["border-bottom", "1px solid var(--d-border)"],
118
+ ["transition", "background 0.1s ease"]
119
+ ]);
120
+ emitRule(".d-data-row:hover", [
121
+ ["background", "var(--d-surface)"]
122
+ ]);
123
+ emitRule(".d-data-cell", [
124
+ ["padding", "var(--d-data-py) var(--d-content-gap)"],
125
+ ["vertical-align", "middle"]
126
+ ]);
127
+ emitRule(".d-control", [
128
+ ["background", "var(--d-surface)"],
129
+ ["color", "var(--d-text)"],
130
+ ["padding", "var(--d-control-py) 0.75rem"],
131
+ ["border-radius", "var(--d-radius)"],
132
+ ["border", "1px solid var(--d-border)"],
133
+ ["width", "100%"],
134
+ ["outline", "none"],
135
+ ["font", "inherit"],
136
+ ["transition", "border-color 0.15s ease, box-shadow 0.15s ease"]
137
+ ]);
138
+ emitRule(".d-control:focus", [
139
+ ["border-color", "var(--d-primary)"],
140
+ ["box-shadow", "0 0 0 3px color-mix(in srgb, var(--d-primary) 25%, transparent)"]
141
+ ]);
142
+ emitRule(".d-control::placeholder", [
143
+ ["color", "var(--d-text-muted)"]
144
+ ]);
145
+ emitRule(".d-control:disabled", [
146
+ ["opacity", "0.5"],
147
+ ["cursor", "not-allowed"]
148
+ ]);
149
+ emitRule(".d-control[aria-invalid]", [
150
+ ["border-color", "var(--d-error)"],
151
+ ["box-shadow", "0 0 0 3px color-mix(in srgb, var(--d-error) 15%, transparent)"]
152
+ ]);
153
+ emitRule(".d-section", [
154
+ ["padding", "var(--d-section-py) 0"]
155
+ ]);
156
+ lines.push(".d-section + .d-section {");
157
+ lines.push(" border-top: 1px solid var(--d-border);");
158
+ lines.push("}");
159
+ lines.push("");
160
+ emitRule(".d-annotation", [
161
+ ["display", "inline-flex"],
162
+ ["align-items", "center"],
163
+ ["gap", "0.25em"],
164
+ ["font-size", "0.75rem"],
165
+ ["font-weight", "500"],
166
+ ["padding", "0.125rem 0.5rem"],
167
+ ["border-radius", "var(--d-radius-full)"],
168
+ ["background", "var(--d-surface)"],
169
+ ["color", "var(--d-text-muted)"],
170
+ ["white-space", "nowrap"]
171
+ ]);
172
+ emitRule('.d-annotation[data-status="success"]', [
173
+ ["background", "color-mix(in srgb, var(--d-success) 15%, transparent)"],
174
+ ["color", "var(--d-success)"]
175
+ ]);
176
+ emitRule('.d-annotation[data-status="error"]', [
177
+ ["background", "color-mix(in srgb, var(--d-error) 15%, transparent)"],
178
+ ["color", "var(--d-error)"]
179
+ ]);
180
+ emitRule('.d-annotation[data-status="warning"]', [
181
+ ["background", "color-mix(in srgb, var(--d-warning) 15%, transparent)"],
182
+ ["color", "var(--d-warning)"]
183
+ ]);
184
+ emitRule('.d-annotation[data-status="info"]', [
185
+ ["background", "color-mix(in srgb, var(--d-info) 15%, transparent)"],
186
+ ["color", "var(--d-info)"]
187
+ ]);
188
+ if (themeDecorators && Object.keys(themeDecorators).length > 0) {
189
+ const label = themeName ? ` (${themeName})` : "";
190
+ lines.push(`/* \u2500\u2500 Layer 3: Theme Decorators${label} \u2500\u2500 */`);
191
+ lines.push("");
192
+ for (const [name, description] of Object.entries(themeDecorators)) {
193
+ const rule = generateDecoratorRule(name, description);
194
+ lines.push(rule);
195
+ lines.push("");
196
+ }
197
+ }
198
+ lines.push("/* \u2500\u2500 Keyframes \u2500\u2500 */");
199
+ lines.push("");
200
+ lines.push("@keyframes decantr-fade-in {");
201
+ lines.push(" from { opacity: 0; transform: translateY(4px); }");
202
+ lines.push(" to { opacity: 1; transform: translateY(0); }");
203
+ lines.push("}");
204
+ lines.push("");
205
+ lines.push("@keyframes decantr-pulse {");
206
+ lines.push(" 0%, 100% { opacity: 1; }");
207
+ lines.push(" 50% { opacity: 0.5; }");
208
+ lines.push("}");
209
+ return lines.join("\n");
210
+ }
211
+
212
+ // src/scaffold.ts
5
213
  var __dirname = dirname(fileURLToPath(import.meta.url));
6
214
  function composeArchetypes(composeEntries, archetypeResults) {
7
215
  if (composeEntries.length === 0) {
@@ -251,8 +459,9 @@ function generateTopologySection(data, personality) {
251
459
  return lines.join("\n");
252
460
  }
253
461
  var CLI_VERSION = "1.0.0";
254
- function generateTokensCSS(themeData, mode) {
462
+ function generateTokensCSS(themeData, mode, spatialTokens) {
255
463
  if (!themeData) {
464
+ const spatialLines2 = spatialTokens ? "\n" + Object.entries(spatialTokens).map(([k, v]) => ` ${k}: ${v};`).join("\n") : "";
256
465
  return `/* No theme data available */
257
466
  :root {
258
467
  --d-primary: #6366f1;
@@ -263,7 +472,7 @@ function generateTokensCSS(themeData, mode) {
263
472
  --d-surface-raised: #27272a;
264
473
  --d-border: #3f3f46;
265
474
  --d-text: #fafafa;
266
- --d-text-muted: #a1a1aa;
475
+ --d-text-muted: #a1a1aa;${spatialLines2}
267
476
  }
268
477
  `;
269
478
  }
@@ -311,10 +520,26 @@ function generateTokensCSS(themeData, mode) {
311
520
  };
312
521
  }
313
522
  const tokens = buildTokens(resolvedMode);
523
+ if (themeData?.typography?.mono) {
524
+ tokens["--d-font-mono"] = themeData.typography.mono;
525
+ }
526
+ if (themeData?.palette?.["accent-glow"]?.[resolvedMode]) {
527
+ tokens["--d-accent-glow"] = themeData.palette["accent-glow"][resolvedMode];
528
+ }
529
+ if (themeData?.motion?.durations?.hover) {
530
+ tokens["--d-duration-hover"] = themeData.motion.durations.hover;
531
+ }
532
+ if (themeData?.motion?.durations?.entrance) {
533
+ tokens["--d-duration-entrance"] = themeData.motion.durations.entrance;
534
+ }
535
+ if (themeData?.motion?.timing) {
536
+ tokens["--d-easing"] = themeData.motion.timing;
537
+ }
314
538
  const lines = Object.entries(tokens).map(([key, value]) => ` ${key}: ${value};`).join("\n");
539
+ const spatialLines = spatialTokens ? "\n" + Object.entries(spatialTokens).map(([k, v]) => ` ${k}: ${v};`).join("\n") : "";
315
540
  let css = `/* Generated by @decantr/cli */
316
541
  :root {
317
- ${lines}
542
+ ${lines}${spatialLines}
318
543
  }
319
544
  `;
320
545
  if (mode === "auto") {
@@ -343,32 +568,6 @@ ${lightLines}
343
568
  }
344
569
  return css;
345
570
  }
346
- function generateDecoratorsCSS(recipeData, themeName) {
347
- if (!recipeData?.decorators) {
348
- return `/* No recipe decorators available */`;
349
- }
350
- const decorators = recipeData.decorators;
351
- const css = [
352
- `/* Generated by @decantr/cli from recipe: ${themeName} */`,
353
- ""
354
- ];
355
- for (const [name, description] of Object.entries(decorators)) {
356
- css.push(generateDecoratorRule(name, description));
357
- css.push("");
358
- }
359
- css.push(`/* Animation keyframes */
360
- @keyframes decantr-fade-in {
361
- from { opacity: 0; transform: translateY(8px); }
362
- to { opacity: 1; transform: translateY(0); }
363
- }
364
-
365
- @keyframes decantr-pulse {
366
- 0%, 100% { opacity: 1; }
367
- 50% { opacity: 0.5; }
368
- }
369
- `);
370
- return css.join("\n");
371
- }
372
571
  function generateGlobalCSS(personality) {
373
572
  const personalityText = personality.join(" ").toLowerCase();
374
573
  let fontBody = "system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif";
@@ -428,34 +627,33 @@ input, button, textarea, select {
428
627
  }
429
628
  `;
430
629
  }
431
- function generateDecoratorsContext(recipeData, recipeName) {
630
+ function generateTreatmentsContext(themeData, themeName) {
432
631
  const lines = [];
433
- lines.push(`# Recipe Decorators: ${recipeName}`);
632
+ lines.push(`# Visual Treatments: ${themeName}`);
633
+ lines.push("");
634
+ lines.push("## Base Treatments");
434
635
  lines.push("");
435
- lines.push("## Available Classes");
636
+ lines.push("d-interactive, d-surface, d-data, d-control, d-section, d-annotation \u2014 see DECANTR.md for usage.");
436
637
  lines.push("");
437
- if (recipeData?.decorators && Object.keys(recipeData.decorators).length > 0) {
438
- lines.push("| Decorator | Description |");
439
- lines.push("|-----------|-------------|");
440
- for (const [name, description] of Object.entries(recipeData.decorators)) {
441
- lines.push(`| ${name} | ${description} |`);
638
+ if (themeData?.decorators && Object.keys(themeData.decorators).length > 0) {
639
+ lines.push(`## Theme Decorators (${themeName}-specific)`);
640
+ lines.push("");
641
+ lines.push("| Class | Use for |");
642
+ lines.push("|-------|---------|");
643
+ for (const [name, description] of Object.entries(themeData.decorators)) {
644
+ const useFor = description.split(".")[0].trim();
645
+ lines.push(`| ${name} | ${useFor} |`);
442
646
  }
443
- } else {
444
- lines.push("No decorators defined.");
647
+ lines.push("");
445
648
  }
649
+ lines.push("## Composition");
446
650
  lines.push("");
447
- lines.push("## Usage");
448
- lines.push("");
449
- lines.push("Decorators are plain CSS class names from `src/styles/decorators.css`. Combine with atoms:");
450
- lines.push("");
651
+ lines.push("Atoms + treatment + theme decorator:");
451
652
  lines.push("```tsx");
452
- lines.push("<div className={css('_flex _col _gap4') + ' " + recipeName + "-card'}>");
453
- lines.push(" <pre className={css('_p3') + ' " + recipeName + "-code'}>{code}</pre>");
454
- lines.push("</div>");
653
+ lines.push(`css('_flex _col _gap4') + ' d-surface'`);
455
654
  lines.push("```");
456
655
  lines.push("");
457
- lines.push("Atoms use `css()` function. Decorators are plain class strings. Combined via string concatenation.");
458
- lines.push("");
656
+ lines.push("Atoms use `css()` function. Treatments and theme decorators are plain class strings.");
459
657
  return lines.join("\n");
460
658
  }
461
659
  function generateDecoratorRule(name, description) {
@@ -693,7 +891,7 @@ function buildEssence(options, archetypeData) {
693
891
  style: options.theme,
694
892
  mode: options.mode,
695
893
  recipe: options.theme,
696
- // Recipe defaults to theme
894
+ // Legacy v2 field — kept for backward compatibility
697
895
  shape: options.shape
698
896
  },
699
897
  personality: options.personality,
@@ -706,6 +904,7 @@ function buildEssence(options, archetypeData) {
706
904
  guard: {
707
905
  enforce_style: true,
708
906
  enforce_recipe: true,
907
+ // Legacy v2 field — kept for backward compatibility
709
908
  mode: options.guard
710
909
  },
711
910
  density: {
@@ -719,7 +918,7 @@ function buildEssence(options, archetypeData) {
719
918
  }
720
919
  return essence;
721
920
  }
722
- function buildEssenceV3(options, archetypeData, themeHints, recipeHints) {
921
+ function buildEssenceV3(options, archetypeData, themeHints) {
723
922
  let pages = [
724
923
  { id: "home", layout: ["hero"] }
725
924
  ];
@@ -756,9 +955,8 @@ function buildEssenceV3(options, archetypeData, themeHints, recipeHints) {
756
955
  };
757
956
  const dna = {
758
957
  theme: {
759
- style: options.theme,
958
+ id: options.theme,
760
959
  mode: options.mode,
761
- recipe: options.theme,
762
960
  shape: options.shape
763
961
  },
764
962
  spacing: {
@@ -768,9 +966,9 @@ function buildEssenceV3(options, archetypeData, themeHints, recipeHints) {
768
966
  content_gap: densityLevelMap[options.density] || "_gap4"
769
967
  },
770
968
  typography: {
771
- scale: themeHints?.typography_hints?.scale || "modular",
772
- heading_weight: themeHints?.typography_hints?.heading_weight || 600,
773
- body_weight: themeHints?.typography_hints?.body_weight || 400
969
+ scale: themeHints?.typography?.scale || "modular",
970
+ heading_weight: themeHints?.typography?.heading_weight || 600,
971
+ body_weight: themeHints?.typography?.body_weight || 400
774
972
  },
775
973
  color: {
776
974
  palette: "semantic",
@@ -778,17 +976,17 @@ function buildEssenceV3(options, archetypeData, themeHints, recipeHints) {
778
976
  cvd_preference: options.accessibility?.cvd_preference || "auto"
779
977
  },
780
978
  radius: {
781
- philosophy: recipeHints?.radius_hints?.philosophy || options.shape,
782
- base: recipeHints?.radius_hints?.base || shapeRadiusMap[options.shape] || 8
979
+ philosophy: themeHints?.radius?.philosophy || options.shape,
980
+ base: themeHints?.radius?.base || shapeRadiusMap[options.shape] || 8
783
981
  },
784
982
  elevation: {
785
983
  system: "layered",
786
984
  max_levels: 3
787
985
  },
788
986
  motion: {
789
- preference: recipeHints?.animation?.preference || themeHints?.motion_hints?.preference || "subtle",
987
+ preference: themeHints?.motion?.preference || "subtle",
790
988
  duration_scale: 1,
791
- reduce_motion: themeHints?.motion_hints?.reduce_motion_default ?? true
989
+ reduce_motion: themeHints?.motion?.reduce_motion ?? true
792
990
  },
793
991
  accessibility: {
794
992
  wcag_level: options.accessibility?.wcag_level || "AA",
@@ -820,219 +1018,84 @@ function buildEssenceV3(options, archetypeData, themeHints, recipeHints) {
820
1018
  }
821
1019
  var CSS_APPROACH_CONTENT = `## CSS Implementation
822
1020
 
823
- This project uses **@decantr/css** for layout atoms and the generated CSS files for theme tokens and recipe decorators.
824
-
825
- ### Setup
1021
+ This project uses **@decantr/css** for layout atoms, **visual treatments** for semantic styling, and **theme decorators** for theme-specific decoration.
826
1022
 
827
- \`\`\`javascript
828
- // 1. Import the atoms runtime
829
- import { css } from '@decantr/css';
1023
+ ### Three File Setup
830
1024
 
831
- // 2. Import generated CSS files (created by decantr init)
832
- import './styles/tokens.css'; // Theme tokens (--d-primary, --d-surface, etc.)
833
- import './styles/decorators.css'; // Recipe decorators
834
1025
  \`\`\`
835
-
836
- ### Using Atoms
837
-
838
- The \`css()\` function processes atom strings and injects CSS at runtime:
839
-
840
- \`\`\`jsx
841
- // Layout atoms
842
- <div className={css('_flex _col _gap4 _p4')}>
843
- <h1 className={css('_heading1')}>Title</h1>
844
- <p className={css('_textsm _fgmuted')}>Description</p>
845
- </div>
846
-
847
- // Responsive prefixes (mobile-first)
848
- <div className={css('_gc1 _sm:gc2 _lg:gc4')}>
849
- {/* 1 col -> 2 cols at 640px -> 4 cols at 1024px */}
850
- </div>
1026
+ src/styles/
1027
+ tokens.css # Design tokens: --d-primary, --d-surface, --d-bg, etc.
1028
+ treatments.css # Visual treatments (d-interactive, d-surface, ...) + theme decorators
1029
+ global.css # Resets, base typography, sr-only
851
1030
  \`\`\`
852
1031
 
853
- ### Atom Reference
854
-
855
- #### Display
856
- | Atom | CSS |
857
- |------|-----|
858
- | \`_flex\` | \`display:flex\` |
859
- | \`_grid\` | \`display:grid\` |
860
- | \`_block\` | \`display:block\` |
861
- | \`_inline\` | \`display:inline\` |
862
- | \`_inlineflex\` | \`display:inline-flex\` |
863
- | \`_none\` | \`display:none\` |
864
- | \`_contents\` | \`display:contents\` |
865
-
866
- #### Flexbox
867
- | Atom | CSS |
868
- |------|-----|
869
- | \`_col\` | \`flex-direction:column\` |
870
- | \`_row\` | \`flex-direction:row\` |
871
- | \`_colrev\` | \`flex-direction:column-reverse\` |
872
- | \`_wrap\` | \`flex-wrap:wrap\` |
873
- | \`_nowrap\` | \`flex-wrap:nowrap\` |
874
- | \`_flex1\` | \`flex:1\` |
875
- | \`_flex0\` | \`flex:none\` |
876
- | \`_flexauto\` | \`flex:auto\` |
877
- | \`_grow\` | \`flex-grow:1\` |
878
- | \`_grow0\` | \`flex-grow:0\` |
879
- | \`_shrink0\` | \`flex-shrink:0\` |
880
-
881
- #### Alignment
882
- | Atom | CSS |
883
- |------|-----|
884
- | \`_aic\` | \`align-items:center\` |
885
- | \`_aifs\` | \`align-items:flex-start\` |
886
- | \`_aife\` | \`align-items:flex-end\` |
887
- | \`_aist\` | \`align-items:stretch\` |
888
- | \`_aibl\` | \`align-items:baseline\` |
889
- | \`_jcc\` | \`justify-content:center\` |
890
- | \`_jcfs\` | \`justify-content:flex-start\` |
891
- | \`_jcfe\` | \`justify-content:flex-end\` |
892
- | \`_jcsb\` | \`justify-content:space-between\` |
893
- | \`_jcsa\` | \`justify-content:space-around\` |
894
- | \`_jcse\` | \`justify-content:space-evenly\` |
895
- | \`_pic\` | \`place-items:center\` |
896
- | \`_pcc\` | \`place-content:center\` |
897
-
898
- #### Spacing (scale: 0, 1, 2, 3, 4, 5, 6, 8, 10, 12, 16, 20, 24, ...)
899
- | Atom | CSS | Notes |
900
- |------|-----|-------|
901
- | \`_gap{n}\` | \`gap:{scale}\` | e.g. \`_gap4\` = \`gap:1rem\` |
902
- | \`_gx{n}\` | \`column-gap:{scale}\` | horizontal gap |
903
- | \`_gy{n}\` | \`row-gap:{scale}\` | vertical gap |
904
- | \`_p{n}\` | \`padding:{scale}\` | all sides |
905
- | \`_pt{n}\`, \`_pr{n}\`, \`_pb{n}\`, \`_pl{n}\` | directional padding | top/right/bottom/left |
906
- | \`_px{n}\` | \`padding-inline:{scale}\` | horizontal |
907
- | \`_py{n}\` | \`padding-block:{scale}\` | vertical |
908
- | \`_m{n}\` | \`margin:{scale}\` | same as padding variants |
909
- | \`_mx{n}\`, \`_my{n}\` | inline/block margin | horizontal/vertical |
910
-
911
- #### Sizing
912
- | Atom | CSS |
913
- |------|-----|
914
- | \`_wfull\` / \`_w100\` | \`width:100%\` |
915
- | \`_hfull\` / \`_h100\` | \`height:100%\` |
916
- | \`_wscreen\` | \`width:100vw\` |
917
- | \`_hscreen\` | \`height:100vh\` |
918
- | \`_wfit\` | \`width:fit-content\` |
919
- | \`_hfit\` | \`height:fit-content\` |
920
- | \`_wauto\` | \`width:auto\` |
921
- | \`_minw0\` | \`min-width:0\` |
922
- | \`_minh0\` | \`min-height:0\` |
923
- | \`_w{n}\`, \`_h{n}\` | width/height from spacing scale |
924
- | \`_minw{n}\`, \`_maxw{n}\` | min/max width from scale |
1032
+ \`\`\`javascript
1033
+ import { css } from '@decantr/css'; // Atoms runtime
1034
+ import './styles/tokens.css'; // Theme tokens
1035
+ import './styles/treatments.css'; // Treatments + theme decorators
1036
+ import './styles/global.css'; // Resets
1037
+ \`\`\`
925
1038
 
926
- #### Text Size
927
- | Atom | Size | Line-height |
928
- |------|------|-------------|
929
- | \`_textxs\` | 0.75rem | 1rem |
930
- | \`_textsm\` | 0.875rem | 1.25rem |
931
- | \`_textbase\` | 1rem | 1.5rem |
932
- | \`_textlg\` | 1.125rem | 1.75rem |
933
- | \`_textxl\` | 1.25rem | 1.75rem |
934
- | \`_text2xl\` | 1.5rem | 2rem |
935
- | \`_text3xl\` | 1.875rem | 2.25rem |
936
- | \`_heading1\`-\`_heading6\` | Heading presets (size + weight) |
1039
+ ### Visual Treatments
937
1040
 
938
- #### Text Style
939
- | Atom | CSS |
940
- |------|-----|
941
- | \`_fontbold\` | \`font-weight:700\` |
942
- | \`_fontsemi\` | \`font-weight:600\` |
943
- | \`_fontmedium\` | \`font-weight:500\` |
944
- | \`_fontlight\` | \`font-weight:300\` |
945
- | \`_italic\` | \`font-style:italic\` |
946
- | \`_underline\` | \`text-decoration:underline\` |
947
- | \`_uppercase\` | \`text-transform:uppercase\` |
948
- | \`_truncate\` | overflow ellipsis + nowrap |
949
- | \`_textl\`, \`_textc\`, \`_textr\` | text-align left/center/right |
1041
+ Six base treatment classes provide semantic styling. Combine with atoms for layout:
950
1042
 
951
- #### Color (theme variable based)
952
- | Atom | CSS |
953
- |------|-----|
954
- | \`_bgprimary\` | \`background:var(--d-primary)\` |
955
- | \`_bgsurface\` | \`background:var(--d-surface)\` |
956
- | \`_bgsurface0\`-\`_bgsurface2\` | surface elevation layers |
957
- | \`_bgmuted\` | \`background:var(--d-muted)\` |
958
- | \`_bgbg\` | \`background:var(--d-bg)\` |
959
- | \`_bgsuccess\`, \`_bgerror\`, \`_bgwarning\`, \`_bginfo\` | status backgrounds |
960
- | \`_fgprimary\` | \`color:var(--d-primary)\` |
961
- | \`_fgtext\` | \`color:var(--d-text)\` |
962
- | \`_fgmuted\` | \`color:var(--d-text-muted)\` |
963
- | \`_fgsuccess\`, \`_fgerror\`, \`_fgwarning\`, \`_fginfo\` | status text |
964
- | \`_bcborder\` | \`border-color:var(--d-border)\` |
1043
+ | Treatment | Class | Variants / States |
1044
+ |-----------|-------|-------------------|
1045
+ | **Interactive Surface** | \`d-interactive\` | \`data-variant="primary\\|ghost\\|danger"\`, hover/focus-visible/disabled states |
1046
+ | **Container Surface** | \`d-surface\` | \`data-variant="raised\\|overlay"\`, optional \`data-interactive\` for hover |
1047
+ | **Data Display** | \`d-data\`, \`d-data-header\`, \`d-data-row\`, \`d-data-cell\` | Row hover highlight |
1048
+ | **Form Control** | \`d-control\` | Focus ring, placeholder, disabled, error via \`aria-invalid\` |
1049
+ | **Section Rhythm** | \`d-section\` | Auto-spacing between adjacent sections, density-aware |
1050
+ | **Inline Annotation** | \`d-annotation\` | \`data-status="success\\|error\\|warning\\|info"\` |
965
1051
 
966
- #### Overflow & Whitespace
967
- | Atom | CSS |
968
- |------|-----|
969
- | \`_overhidden\` | \`overflow:hidden\` |
970
- | \`_overauto\` | \`overflow:auto\` |
971
- | \`_overscroll\` | \`overflow:scroll\` |
972
- | \`_overxauto\`, \`_overyauto\` | axis-specific overflow |
973
- | \`_nowraptext\` | \`white-space:nowrap\` |
974
- | \`_prewrap\` | \`white-space:pre-wrap\` |
975
- | \`_breakword\` | \`overflow-wrap:break-word\` |
1052
+ ### Composition
976
1053
 
977
- #### Cursor & Interaction
978
- | Atom | CSS |
979
- |------|-----|
980
- | \`_pointer\` | \`cursor:pointer\` |
981
- | \`_cursordefault\` | \`cursor:default\` |
982
- | \`_notallowed\` | \`cursor:not-allowed\` |
983
- | \`_grab\` | \`cursor:grab\` |
984
- | \`_selectnone\` | \`user-select:none\` |
985
- | \`_ptrnone\` | \`pointer-events:none\` |
1054
+ Atoms + treatment + theme decorator:
986
1055
 
987
- #### Position & Layout
988
- | Atom | CSS |
989
- |------|-----|
990
- | \`_rel\` | \`position:relative\` |
991
- | \`_abs\` | \`position:absolute\` |
992
- | \`_fixed\` | \`position:fixed\` |
993
- | \`_sticky\` | \`position:sticky\` |
994
- | \`_inset0\` | \`inset:0\` |
995
- | \`_top0\`, \`_right0\`, \`_bottom0\`, \`_left0\` | edge positioning |
996
- | \`_z10\`-\`_z50\` | z-index scale |
1056
+ \`\`\`tsx
1057
+ <button className={css('_px4 _py2') + ' d-interactive'} data-variant="primary">Deploy</button>
1058
+ <div className={css('_flex _col _gap4') + ' d-surface carbon-glass'}>Card</div>
1059
+ <span className="d-annotation" data-status="success">Active</span>
1060
+ \`\`\`
997
1061
 
998
- #### Grid
999
- | Atom | CSS |
1000
- |------|-----|
1001
- | \`_gc1\`-\`_gc12\` | \`grid-template-columns:repeat(N,...)\` |
1002
- | \`_gr1\`-\`_gr6\` | \`grid-template-rows:repeat(N,...)\` |
1003
- | \`_span1\`-\`_span12\`, \`_spanfull\` | column span |
1004
- | \`_rowspan1\`-\`_rowspan6\` | row span |
1062
+ - **Atoms:** \`css('_flex _col _gap4')\` \u2014 processed by @decantr/css runtime
1063
+ - **Treatments:** \`d-interactive\`, \`d-surface\` \u2014 semantic base styles from treatments.css
1064
+ - **Theme decorators:** \`carbon-glass\`, \`carbon-code\` \u2014 theme-specific decoration from treatments.css
1065
+ - **Combined:** \`css('_flex _col') + ' d-surface carbon-card'\`
1005
1066
 
1006
- #### Visual
1007
- | Atom | CSS |
1008
- |------|-----|
1009
- | \`_rounded\` | \`border-radius:var(--d-radius)\` |
1010
- | \`_roundedfull\` | \`border-radius:9999px\` |
1011
- | \`_roundedsm\`, \`_roundedlg\`, \`_roundedxl\` | radius variants |
1012
- | \`_shadow\`, \`_shadowmd\`, \`_shadowlg\` | box-shadow presets |
1013
- | \`_bordernone\` | \`border:none\` |
1014
- | \`_bw{n}\` | \`border-width:{n}px\` |
1015
- | \`_op0\`-\`_op100\` | opacity (0, 25, 50, 75, 100) |
1016
- | \`_trans\` | \`transition:all 0.15s ease\` |
1017
- | \`_visible\`, \`_invisible\` | visibility |
1067
+ ### Atoms Quick Reference
1018
1068
 
1019
- ### Using Recipe Decorators
1069
+ | Category | Examples | Purpose |
1070
+ |----------|----------|---------|
1071
+ | Layout | \`_flex\`, \`_col\`, \`_row\`, \`_wrap\`, \`_grid\` | Flex/grid containers |
1072
+ | Spacing | \`_gap4\`, \`_p4\`, \`_px4\`, \`_py2\`, \`_m0\` | Gaps, padding, margin |
1073
+ | Sizing | \`_w100\`, \`_h100\`, \`_minw0\`, \`_maxwfull\` | Width, height |
1074
+ | Text | \`_textlg\`, \`_text2xl\`, \`_fontbold\`, \`_textc\` | Typography |
1075
+ | Alignment | \`_aic\`, \`_jcc\`, \`_jcsb\`, \`_pic\` | Flex/grid alignment |
1076
+ | Position | \`_rel\`, \`_abs\`, \`_sticky\`, \`_z10\` | Positioning |
1077
+ | Visual | \`_rounded\`, \`_shadow\`, \`_trans\`, \`_op50\` | Decoration |
1078
+ | Color | \`_bgprimary\`, \`_fgtext\`, \`_fgmuted\`, \`_bcborder\` | Theme colors |
1079
+ | Responsive | \`_md:gc2\`, \`_lg:gc4\`, \`_sm:flex\` | Breakpoint prefixes |
1020
1080
 
1021
- Recipe decorators (from \`src/styles/decorators.css\`) are regular CSS class names, NOT atoms. They are applied directly as class names and combined with atoms using string concatenation:
1081
+ Scale: 0, 1, 2, 3, 4, 5, 6, 8, 10, 12, 16, 20, 24. Example: \`_gap4\` = \`gap:1rem\`.
1022
1082
 
1023
- \`\`\`tsx
1024
- // Atoms use css() function, decorators are plain class names
1025
- <div className={css('_flex _col _gap4') + ' carbon-card'}>
1026
- <div className={css('_p4') + ' carbon-glass'}>
1027
- <pre className={css('_p3') + ' carbon-code'}>{code}</pre>
1028
- </div>
1029
- </div>
1030
- \`\`\`
1083
+ ### Design Tokens
1031
1084
 
1032
- **Key difference:**
1033
- - Atoms: \`css('_flex _col _gap4')\` \u2014 processed by @decantr/css runtime
1034
- - Decorators: \`'carbon-card'\`, \`'carbon-glass'\` \u2014 plain CSS classes from decorators.css
1035
- - Combined: \`css('_flex _col') + ' carbon-card'\`
1085
+ | Token | Purpose | Use for |
1086
+ |-------|---------|---------|
1087
+ | \`--d-primary\` | Primary brand color | Buttons, links, focus rings |
1088
+ | \`--d-surface\`, \`--d-surface-raised\` | Surface backgrounds | Cards, panels |
1089
+ | \`--d-bg\` | Page background | Body, main container |
1090
+ | \`--d-border\` | Border color | Dividers, card borders |
1091
+ | \`--d-text\`, \`--d-text-muted\` | Text colors | Body text, secondary text |
1092
+ | \`--d-success\`, \`--d-error\`, \`--d-warning\`, \`--d-info\` | Status colors | Alerts, badges, toasts |
1093
+ | \`--d-shadow\`, \`--d-shadow-lg\` | Elevation shadows | Cards, overlays |
1094
+ | \`--d-radius\`, \`--d-radius-lg\` | Border radii | Buttons, cards |
1095
+ | \`--d-font-mono\` | Monospace font stack | Code, metrics, data |
1096
+ | \`--d-duration-hover\` | Hover transition | Interactive elements |
1097
+ | \`--d-easing\` | Animation easing | All transitions |
1098
+ | \`--d-accent-glow\` | Glow color | Hover effects, focus rings |
1036
1099
 
1037
1100
  ### Routing
1038
1101
 
@@ -1040,28 +1103,7 @@ Check \`decantr.essence.json\` \u2192 \`meta.platform.routing\` for the routing
1040
1103
  - \`"hash"\` \u2192 use \`HashRouter\` (e.g., for static hosting, GitHub Pages)
1041
1104
  - \`"history"\` \u2192 use \`BrowserRouter\` (e.g., for server-rendered apps)
1042
1105
 
1043
- Routes are defined in \`decantr.essence.json\` \u2192 \`blueprint.routes\` and listed in \`.decantr/context/scaffold.md\`.
1044
-
1045
- ### CSS Architecture
1046
-
1047
- The CSS is organized into two parts:
1048
-
1049
- 1. **Atoms (@decantr/css)** - Layout utilities injected at runtime into \`@layer d.atoms\`
1050
- 2. **Generated CSS files** - Theme tokens and recipe decorators created during scaffold
1051
-
1052
- \`\`\`
1053
- src/styles/
1054
- tokens.css # :root { --d-primary: #...; --d-surface: #...; }
1055
- decorators.css # .recipe-card { ... }
1056
- \`\`\`
1057
-
1058
- ### Variable Naming Convention
1059
-
1060
- | Prefix | Purpose | Example |
1061
- |--------|---------|---------|
1062
- | \`--d-\` | Core Decantr tokens | \`--d-primary\`, \`--d-bg\` |
1063
- | \`--d-gap-{n}\` | Spacing tokens | \`--d-gap-4\`, \`--d-gap-8\` |
1064
- | \`--d-radius\` | Border radius | \`--d-radius\`, \`--d-radius-lg\` |`;
1106
+ Routes are defined in \`decantr.essence.json\` \u2192 \`blueprint.routes\` and listed in \`.decantr/context/scaffold.md\`.`;
1065
1107
  function generateDecantrMdV31(guardMode, cssApproach) {
1066
1108
  const template = loadTemplate("DECANTR.md.template");
1067
1109
  return renderTemplate(template, {
@@ -1090,8 +1132,7 @@ function generateProjectJson(detected, options, registrySource) {
1090
1132
  cachedContent: {
1091
1133
  archetypes: [],
1092
1134
  patterns: [],
1093
- themes: [],
1094
- recipes: []
1135
+ themes: []
1095
1136
  }
1096
1137
  },
1097
1138
  initialized: {
@@ -1124,7 +1165,7 @@ function generateTaskContext(templateName, essence) {
1124
1165
  TARGET: essence.target,
1125
1166
  THEME_STYLE: essence.theme.style,
1126
1167
  THEME_MODE: essence.theme.mode,
1127
- THEME_RECIPE: essence.theme.recipe,
1168
+ THEME_RECIPE: essence.theme.style,
1128
1169
  DEFAULT_SHELL: defaultShell,
1129
1170
  GUARD_MODE: essence.guard.mode,
1130
1171
  LAYOUT: layout,
@@ -1148,9 +1189,9 @@ function generateTaskContextV3(templateName, essence) {
1148
1189
  const contentGap = essence.dna.spacing?.content_gap || "_gap4";
1149
1190
  const vars = {
1150
1191
  TARGET: essence.meta.target || "react",
1151
- THEME_STYLE: essence.dna.theme.style,
1192
+ THEME_STYLE: essence.dna.theme.id || essence.dna.theme.style || "",
1152
1193
  THEME_MODE: essence.dna.theme.mode,
1153
- THEME_RECIPE: essence.dna.theme.recipe || essence.dna.theme.style,
1194
+ THEME_RECIPE: essence.dna.theme.id || essence.dna.theme.style || "",
1154
1195
  DEFAULT_SHELL: defaultShell,
1155
1196
  GUARD_MODE: essence.meta.guard.mode,
1156
1197
  LAYOUT: layout,
@@ -1187,9 +1228,9 @@ ${rows.join("\n")}`;
1187
1228
  BLUEPRINT: "",
1188
1229
  PERSONALITY: (essence.dna.personality || []).join(", "),
1189
1230
  TARGET: essence.meta.target ?? "",
1190
- THEME_STYLE: essence.dna.theme.style,
1231
+ THEME_STYLE: essence.dna.theme.id ?? essence.dna.theme.style ?? "",
1191
1232
  THEME_MODE: essence.dna.theme.mode,
1192
- THEME_RECIPE: essence.dna.theme.recipe ?? "",
1233
+ THEME_RECIPE: essence.dna.theme.id ?? essence.dna.theme.style ?? "",
1193
1234
  SHAPE: essence.dna.theme.shape ?? "",
1194
1235
  PAGES_TABLE: pagesTable,
1195
1236
  FEATURES_LIST: featuresList,
@@ -1224,8 +1265,8 @@ ${cacheEntry}
1224
1265
  return true;
1225
1266
  }
1226
1267
  }
1227
- async function scaffoldProject(projectRoot, options, detected, registry, archetypeData, registrySource = "cache", themeData, recipeData, topologyMarkdown, composedSections, routeMap, patternSpecs, blueprintData) {
1228
- const essenceV3 = buildEssenceV3(options, archetypeData, themeData, recipeData);
1268
+ async function scaffoldProject(projectRoot, options, detected, registry, archetypeData, registrySource = "cache", themeData, topologyMarkdown, composedSections, routeMap, patternSpecs, blueprintData) {
1269
+ const essenceV3 = buildEssenceV3(options, archetypeData, themeData);
1229
1270
  const essence = buildEssence(options, archetypeData);
1230
1271
  const decantrDir = join(projectRoot, ".decantr");
1231
1272
  const contextDir = join(decantrDir, "context");
@@ -1261,7 +1302,7 @@ async function scaffoldProject(projectRoot, options, detected, registry, archety
1261
1302
  }
1262
1303
  writeFileSync(essencePath, JSON.stringify(essenceV3, null, 2) + "\n");
1263
1304
  }
1264
- const refreshResult = await refreshDerivedFiles(projectRoot, essenceV3, registry, themeData, recipeData, { isInitialScaffold: true });
1305
+ const refreshResult = await refreshDerivedFiles(projectRoot, essenceV3, registry, themeData, { isInitialScaffold: true });
1265
1306
  contextFiles.push(...refreshResult.contextFiles);
1266
1307
  const gitignoreUpdated = updateGitignore(projectRoot);
1267
1308
  return {
@@ -1276,7 +1317,7 @@ async function scaffoldProject(projectRoot, options, detected, registry, archety
1276
1317
  function scaffoldMinimal(projectRoot) {
1277
1318
  const decantrDir = join(projectRoot, ".decantr");
1278
1319
  const customDir = join(decantrDir, "custom");
1279
- const contentTypes = ["patterns", "recipes", "themes", "blueprints", "archetypes", "shells"];
1320
+ const contentTypes = ["patterns", "themes", "blueprints", "archetypes", "shells"];
1280
1321
  for (const type of contentTypes) {
1281
1322
  mkdirSync(join(customDir, type), { recursive: true });
1282
1323
  }
@@ -1284,9 +1325,8 @@ function scaffoldMinimal(projectRoot) {
1284
1325
  version: "3.0.0",
1285
1326
  dna: {
1286
1327
  theme: {
1287
- style: "default",
1328
+ id: "default",
1288
1329
  mode: "dark",
1289
- recipe: "default",
1290
1330
  shape: "rounded"
1291
1331
  },
1292
1332
  spacing: {
@@ -1368,8 +1408,7 @@ function scaffoldMinimal(projectRoot) {
1368
1408
  cachedContent: {
1369
1409
  archetypes: [],
1370
1410
  patterns: [],
1371
- themes: [],
1372
- recipes: []
1411
+ themes: []
1373
1412
  }
1374
1413
  },
1375
1414
  initialized: {
@@ -1463,12 +1502,11 @@ When available, use these tools:
1463
1502
  gitignoreUpdated
1464
1503
  };
1465
1504
  }
1466
- async function refreshDerivedFiles(projectRoot, essence, registry, prefetchedThemeData, prefetchedRecipeData, options) {
1505
+ async function refreshDerivedFiles(projectRoot, essence, registry, prefetchedThemeData, options) {
1467
1506
  const decantrDir = join(projectRoot, ".decantr");
1468
1507
  const contextDir = join(decantrDir, "context");
1469
1508
  mkdirSync(contextDir, { recursive: true });
1470
- const themeName = essence.dna.theme.style;
1471
- const recipeName = essence.dna.theme.recipe ?? themeName;
1509
+ const themeName = essence.dna.theme.id || essence.dna.theme.style || "default";
1472
1510
  const mode = essence.dna.theme.mode;
1473
1511
  const guardMode = essence.meta.guard.mode;
1474
1512
  const guardConfig = {
@@ -1478,7 +1516,6 @@ async function refreshDerivedFiles(projectRoot, essence, registry, prefetchedThe
1478
1516
  };
1479
1517
  const personality = essence.dna.personality || [];
1480
1518
  let themeData = prefetchedThemeData;
1481
- let recipeData = prefetchedRecipeData;
1482
1519
  if (!themeData) try {
1483
1520
  const themeResult = await registry.fetchTheme(themeName);
1484
1521
  if (themeResult?.data) {
@@ -1489,51 +1526,20 @@ async function refreshDerivedFiles(projectRoot, essence, registry, prefetchedThe
1489
1526
  palette: t.palette,
1490
1527
  cvd_support: t.cvd_support,
1491
1528
  tokens: t.tokens,
1492
- typography_hints: t.typography_hints,
1493
- motion_hints: t.motion_hints
1529
+ typography: t.typography,
1530
+ motion: t.motion,
1531
+ decorators: t.decorators,
1532
+ treatments: t.treatments,
1533
+ spatial: t.spatial,
1534
+ radius: t.radius,
1535
+ shell: t.shell,
1536
+ effects: t.effects,
1537
+ compositions: t.compositions,
1538
+ pattern_preferences: t.pattern_preferences
1494
1539
  };
1495
1540
  }
1496
1541
  } catch {
1497
1542
  }
1498
- if (!recipeData) try {
1499
- const recipeResult = await registry.fetchRecipe(recipeName);
1500
- if (recipeResult?.data) {
1501
- const raw = recipeResult.data;
1502
- const r = raw.data ?? raw;
1503
- recipeData = {
1504
- decorators: r.decorators,
1505
- spatial_hints: r.spatial_hints,
1506
- radius_hints: r.radius_hints
1507
- };
1508
- if (!recipeData.decorators && raw.data) {
1509
- const inner = raw.data;
1510
- if (inner.decorators) {
1511
- recipeData.decorators = inner.decorators;
1512
- recipeData.spatial_hints = inner.spatial_hints;
1513
- recipeData.radius_hints = inner.radius_hints;
1514
- }
1515
- }
1516
- }
1517
- } catch {
1518
- }
1519
- if (!recipeData?.decorators || Object.keys(recipeData.decorators).length === 0) {
1520
- try {
1521
- const apiUrl = registry.apiUrl || "https://api.decantr.ai/v1";
1522
- const resp = await fetch(`${apiUrl}/recipes/@official/${recipeName}`);
1523
- if (resp.ok) {
1524
- const apiData = await resp.json();
1525
- const inner = apiData.data ?? apiData;
1526
- if (inner.decorators && Object.keys(inner.decorators).length > 0) {
1527
- recipeData = {
1528
- decorators: inner.decorators,
1529
- spatial_hints: inner.spatial_hints,
1530
- radius_hints: inner.radius_hints
1531
- };
1532
- }
1533
- }
1534
- } catch {
1535
- }
1536
- }
1537
1543
  if (!themeData?.seed?.primary) {
1538
1544
  try {
1539
1545
  const apiUrl = registry.apiUrl || "https://api.decantr.ai/v1";
@@ -1547,8 +1553,16 @@ async function refreshDerivedFiles(projectRoot, essence, registry, prefetchedThe
1547
1553
  palette: inner.palette,
1548
1554
  cvd_support: inner.cvd_support,
1549
1555
  tokens: inner.tokens,
1550
- typography_hints: inner.typography_hints,
1551
- motion_hints: inner.motion_hints
1556
+ typography: inner.typography,
1557
+ motion: inner.motion,
1558
+ decorators: inner.decorators,
1559
+ treatments: inner.treatments,
1560
+ spatial: inner.spatial,
1561
+ radius: inner.radius,
1562
+ shell: inner.shell,
1563
+ effects: inner.effects,
1564
+ compositions: inner.compositions,
1565
+ pattern_preferences: inner.pattern_preferences
1552
1566
  };
1553
1567
  }
1554
1568
  }
@@ -1557,27 +1571,35 @@ async function refreshDerivedFiles(projectRoot, essence, registry, prefetchedThe
1557
1571
  }
1558
1572
  const stylesDir = join(projectRoot, "src", "styles");
1559
1573
  mkdirSync(stylesDir, { recursive: true });
1574
+ const densityLevel = options.density || "comfortable";
1575
+ const spatialTokens = computeSpatialTokens(densityLevel, themeData?.spatial ? {
1576
+ section_padding: themeData.spatial.section_padding ?? void 0,
1577
+ density_bias: typeof themeData.spatial.density_bias === "number" ? themeData.spatial.density_bias : void 0,
1578
+ content_gap_shift: themeData.spatial.content_gap_shift
1579
+ } : void 0);
1560
1580
  const tokensPath = join(stylesDir, "tokens.css");
1561
1581
  const hasRealThemeData = themeData?.seed?.primary || themeData?.palette?.background;
1562
1582
  if (hasRealThemeData || !existsSync(tokensPath)) {
1563
- writeFileSync(tokensPath, generateTokensCSS(themeData, mode));
1564
- }
1565
- const decoratorsPath = join(stylesDir, "decorators.css");
1566
- const hasRealRecipeData = recipeData?.decorators && Object.keys(recipeData.decorators).length > 0;
1567
- if (hasRealRecipeData || !existsSync(decoratorsPath)) {
1568
- writeFileSync(decoratorsPath, generateDecoratorsCSS(recipeData, themeName));
1569
- }
1583
+ writeFileSync(tokensPath, generateTokensCSS(themeData, mode, spatialTokens));
1584
+ }
1585
+ const treatmentsPath = join(stylesDir, "treatments.css");
1586
+ writeFileSync(treatmentsPath, generateTreatmentCSS(
1587
+ spatialTokens,
1588
+ themeData?.treatments,
1589
+ themeData?.decorators,
1590
+ themeName
1591
+ ));
1570
1592
  const globalPath = join(stylesDir, "global.css");
1571
1593
  if (!existsSync(globalPath)) {
1572
1594
  writeFileSync(globalPath, generateGlobalCSS(personality));
1573
1595
  }
1574
- const cssFiles = [tokensPath, decoratorsPath, globalPath];
1575
- const decoratorsMdPath = join(contextDir, "decorators.md");
1576
- writeFileSync(decoratorsMdPath, generateDecoratorsContext(recipeData, recipeName));
1596
+ const cssFiles = [tokensPath, treatmentsPath, globalPath];
1597
+ const treatmentsMdPath = join(contextDir, "treatments.md");
1598
+ writeFileSync(treatmentsMdPath, generateTreatmentsContext(themeData, themeName));
1577
1599
  const decantrMdPath = join(projectRoot, "DECANTR.md");
1578
1600
  writeFileSync(decantrMdPath, generateDecantrMdV31(guardMode, CSS_APPROACH_CONTENT));
1579
1601
  const hasSections = essence.blueprint.sections && essence.blueprint.sections.length > 0;
1580
- const contextFiles = [decoratorsMdPath];
1602
+ const contextFiles = [treatmentsMdPath];
1581
1603
  if (!hasSections) {
1582
1604
  const summaryPath = join(contextDir, "essence-summary.md");
1583
1605
  writeFileSync(summaryPath, generateEssenceSummaryV3(essence));
@@ -1675,8 +1697,8 @@ async function refreshDerivedFiles(projectRoot, essence, registry, prefetchedThe
1675
1697
  const topologyMarkdown = generateTopologySection(topologyData, personality);
1676
1698
  const themeTokensCss = existsSync(tokensPath) ? readFileSync(tokensPath, "utf-8") : "";
1677
1699
  const decoratorList = [];
1678
- if (recipeData?.decorators) {
1679
- for (const [name, desc] of Object.entries(recipeData.decorators)) {
1700
+ if (themeData?.decorators) {
1701
+ for (const [name, desc] of Object.entries(themeData.decorators)) {
1680
1702
  decoratorList.push({ name, description: desc });
1681
1703
  }
1682
1704
  } else if (existsSync(decoratorsPath)) {
@@ -1744,7 +1766,6 @@ async function refreshDerivedFiles(projectRoot, essence, registry, prefetchedThe
1744
1766
  guardConfig,
1745
1767
  personality,
1746
1768
  themeName,
1747
- recipeName,
1748
1769
  zoneContext,
1749
1770
  patternSpecs: sectionPatterns,
1750
1771
  constraints: essence.dna.constraints,
@@ -1759,7 +1780,6 @@ async function refreshDerivedFiles(projectRoot, essence, registry, prefetchedThe
1759
1780
  appName: essence.meta.archetype || "Application",
1760
1781
  blueprintId: "",
1761
1782
  themeName,
1762
- recipeName,
1763
1783
  personality,
1764
1784
  topologyMarkdown,
1765
1785
  sections,
@@ -1834,8 +1854,8 @@ async function refreshDerivedFiles(projectRoot, essence, registry, prefetchedThe
1834
1854
  }
1835
1855
  const themeTokensCss = existsSync(tokensPath) ? readFileSync(tokensPath, "utf-8") : "";
1836
1856
  const decoratorList = [];
1837
- if (recipeData?.decorators) {
1838
- for (const [name, desc] of Object.entries(recipeData.decorators)) {
1857
+ if (themeData?.decorators) {
1858
+ for (const [name, desc] of Object.entries(themeData.decorators)) {
1839
1859
  decoratorList.push({ name, description: desc });
1840
1860
  }
1841
1861
  } else if (existsSync(decoratorsPath)) {
@@ -1873,7 +1893,6 @@ async function refreshDerivedFiles(projectRoot, essence, registry, prefetchedThe
1873
1893
  guardConfig,
1874
1894
  personality,
1875
1895
  themeName,
1876
- recipeName,
1877
1896
  zoneContext: `This is the primary section (${shell} shell).`,
1878
1897
  patternSpecs,
1879
1898
  constraints: essence.dna.constraints,
@@ -1977,7 +1996,7 @@ function generateSyntheticComponents(patternId, description) {
1977
1996
  return [...new Set(syntheticComponents)];
1978
1997
  }
1979
1998
  function generateSectionContext(input) {
1980
- const { section, decorators, guardConfig, personality, themeName, recipeName, zoneContext, patternSpecs, recipeHints, constraints, shellInfo } = input;
1999
+ const { section, decorators, guardConfig, personality, themeName, zoneContext, patternSpecs, themeHints, constraints, shellInfo } = input;
1981
2000
  const lines = [];
1982
2001
  lines.push(`# Section: ${section.id}`);
1983
2002
  lines.push("");
@@ -2002,23 +2021,21 @@ function generateSectionContext(input) {
2002
2021
  lines.push("");
2003
2022
  lines.push(`**Theme tokens:** see \`src/styles/tokens.css\` \u2014 use \`var(--d-primary)\`, \`var(--d-bg)\`, etc.`);
2004
2023
  lines.push("");
2024
+ lines.push("**Visual Treatments:** All 6 base treatments available (see DECANTR.md for usage).");
2005
2025
  if (decorators.length > 0) {
2006
- const names = decorators.map((d) => `\`${d.name}\``).join(", ");
2007
- lines.push(`**Decorators:** ${names} (see \`src/styles/decorators.css\`)`);
2008
- lines.push("Usage: `className={css('_flex _col') + ' carbon-card'}` \u2014 atoms via css(), decorators as plain class strings.");
2009
- } else {
2010
- lines.push("**Decorators:** none defined.");
2026
+ const names = decorators.map((d) => d.name).join(", ");
2027
+ lines.push(`**Theme decorators:** ${names}`);
2011
2028
  }
2012
2029
  lines.push("");
2013
- if (recipeHints) {
2014
- if (recipeHints.preferred && recipeHints.preferred.length > 0) {
2015
- lines.push(`**Preferred:** ${recipeHints.preferred.join(", ")}`);
2030
+ if (themeHints) {
2031
+ if (themeHints.preferred && themeHints.preferred.length > 0) {
2032
+ lines.push(`**Preferred:** ${themeHints.preferred.join(", ")}`);
2016
2033
  }
2017
- if (recipeHints.compositions) {
2018
- lines.push(`**Compositions:** ${recipeHints.compositions}`);
2034
+ if (themeHints.compositions) {
2035
+ lines.push(`**Compositions:** ${themeHints.compositions}`);
2019
2036
  }
2020
- if (recipeHints.spatialHints) {
2021
- lines.push(`**Spatial hints:** ${recipeHints.spatialHints}`);
2037
+ if (themeHints.spatialHints) {
2038
+ lines.push(`**Spatial hints:** ${themeHints.spatialHints}`);
2022
2039
  }
2023
2040
  lines.push("");
2024
2041
  }
@@ -2103,12 +2120,12 @@ function generateSectionContext(input) {
2103
2120
  return lines.join("\n");
2104
2121
  }
2105
2122
  function generateScaffoldContext(input) {
2106
- const { appName, blueprintId, themeName, recipeName, personality, topologyMarkdown, sections, routes, constraints, seo, navigation } = input;
2123
+ const { appName, blueprintId, themeName, personality, topologyMarkdown, sections, routes, constraints, seo, navigation } = input;
2107
2124
  const lines = [];
2108
2125
  lines.push(`# Scaffold: ${appName}`);
2109
2126
  lines.push("");
2110
2127
  lines.push(`**Blueprint:** ${blueprintId}`);
2111
- lines.push(`**Theme:** ${themeName} | **Recipe:** ${recipeName}`);
2128
+ lines.push(`**Theme:** ${themeName}`);
2112
2129
  lines.push(`**Personality:** ${personality.join(", ")}`);
2113
2130
  lines.push("**Guard mode:** creative (no enforcement during initial scaffolding)");
2114
2131
  lines.push("");
@@ -2203,7 +2220,7 @@ import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as rea
2203
2220
  import { join as join2 } from "path";
2204
2221
  import { RegistryAPIClient } from "@decantr/registry";
2205
2222
  var DEFAULT_API_URL = "https://api.decantr.ai/v1";
2206
- var ALL_CONTENT_TYPES = ["themes", "patterns", "recipes", "blueprints", "archetypes", "shells"];
2223
+ var ALL_CONTENT_TYPES = ["themes", "patterns", "blueprints", "archetypes", "shells"];
2207
2224
  function loadFromCache(cacheDir, contentType, id, namespace) {
2208
2225
  const nsDir = namespace ? join2(cacheDir, namespace) : cacheDir;
2209
2226
  const cachePath = id ? join2(nsDir, contentType, `${id}.json`) : join2(nsDir, contentType, "index.json");
@@ -2367,12 +2384,6 @@ var RegistryClient = class {
2367
2384
  async fetchShell(id) {
2368
2385
  return this.fetchContentItem("shells", id);
2369
2386
  }
2370
- async fetchRecipes() {
2371
- return this.fetchContentList("recipes");
2372
- }
2373
- async fetchRecipe(id) {
2374
- return this.fetchContentItem("recipes", id);
2375
- }
2376
2387
  /**
2377
2388
  * Check if API is available.
2378
2389
  */