@decantr/cli 1.5.2 → 1.5.3

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/dist/bin.js CHANGED
@@ -1,3 +1,3 @@
1
1
  #!/usr/bin/env node
2
- import "./chunk-66RIAQLH.js";
3
- import "./chunk-6RJSFLT4.js";
2
+ import "./chunk-QZVA3PFR.js";
3
+ import "./chunk-B5OX2EXL.js";
@@ -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, recipeDecorators, recipeName) {
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 (recipeDecorators && Object.keys(recipeDecorators).length > 0) {
189
+ const label = recipeName ? ` (${recipeName})` : "";
190
+ lines.push(`/* \u2500\u2500 Layer 3: Recipe Decorators${label} \u2500\u2500 */`);
191
+ lines.push("");
192
+ for (const [name, description] of Object.entries(recipeDecorators)) {
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
  }
@@ -312,9 +521,10 @@ function generateTokensCSS(themeData, mode) {
312
521
  }
313
522
  const tokens = buildTokens(resolvedMode);
314
523
  const lines = Object.entries(tokens).map(([key, value]) => ` ${key}: ${value};`).join("\n");
524
+ const spatialLines = spatialTokens ? "\n" + Object.entries(spatialTokens).map(([k, v]) => ` ${k}: ${v};`).join("\n") : "";
315
525
  let css = `/* Generated by @decantr/cli */
316
526
  :root {
317
- ${lines}
527
+ ${lines}${spatialLines}
318
528
  }
319
529
  `;
320
530
  if (mode === "auto") {
@@ -343,32 +553,6 @@ ${lightLines}
343
553
  }
344
554
  return css;
345
555
  }
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
556
  function generateGlobalCSS(personality) {
373
557
  const personalityText = personality.join(" ").toLowerCase();
374
558
  let fontBody = "system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif";
@@ -428,34 +612,33 @@ input, button, textarea, select {
428
612
  }
429
613
  `;
430
614
  }
431
- function generateDecoratorsContext(recipeData, recipeName) {
615
+ function generateTreatmentsContext(recipeData, recipeName) {
432
616
  const lines = [];
433
- lines.push(`# Recipe Decorators: ${recipeName}`);
617
+ lines.push(`# Visual Treatments: ${recipeName}`);
618
+ lines.push("");
619
+ lines.push("## Base Treatments");
434
620
  lines.push("");
435
- lines.push("## Available Classes");
621
+ lines.push("d-interactive, d-surface, d-data, d-control, d-section, d-annotation \u2014 see DECANTR.md for usage.");
436
622
  lines.push("");
437
623
  if (recipeData?.decorators && Object.keys(recipeData.decorators).length > 0) {
438
- lines.push("| Decorator | Description |");
439
- lines.push("|-----------|-------------|");
624
+ lines.push(`## Recipe Decorators (${recipeName}-specific)`);
625
+ lines.push("");
626
+ lines.push("| Class | Use for |");
627
+ lines.push("|-------|---------|");
440
628
  for (const [name, description] of Object.entries(recipeData.decorators)) {
441
- lines.push(`| ${name} | ${description} |`);
629
+ const useFor = description.split(".")[0].trim();
630
+ lines.push(`| ${name} | ${useFor} |`);
442
631
  }
443
- } else {
444
- lines.push("No decorators defined.");
632
+ lines.push("");
445
633
  }
634
+ lines.push("## Composition");
446
635
  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("");
636
+ lines.push("Atoms + treatment + recipe decorator:");
451
637
  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>");
638
+ lines.push(`css('_flex _col _gap4') + ' d-surface'`);
455
639
  lines.push("```");
456
640
  lines.push("");
457
- lines.push("Atoms use `css()` function. Decorators are plain class strings. Combined via string concatenation.");
458
- lines.push("");
641
+ lines.push("Atoms use `css()` function. Treatments and recipe decorators are plain class strings.");
459
642
  return lines.join("\n");
460
643
  }
461
644
  function generateDecoratorRule(name, description) {
@@ -820,219 +1003,80 @@ function buildEssenceV3(options, archetypeData, themeHints, recipeHints) {
820
1003
  }
821
1004
  var CSS_APPROACH_CONTENT = `## CSS Implementation
822
1005
 
823
- This project uses **@decantr/css** for layout atoms and the generated CSS files for theme tokens and recipe decorators.
824
-
825
- ### Setup
1006
+ This project uses **@decantr/css** for layout atoms, **visual treatments** for semantic styling, and **recipe decorators** for theme-specific decoration.
826
1007
 
827
- \`\`\`javascript
828
- // 1. Import the atoms runtime
829
- import { css } from '@decantr/css';
1008
+ ### Three File Setup
830
1009
 
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
1010
  \`\`\`
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>
1011
+ src/styles/
1012
+ tokens.css # Design tokens: --d-primary, --d-surface, --d-bg, etc.
1013
+ treatments.css # Visual treatments (d-interactive, d-surface, ...) + recipe decorators
1014
+ global.css # Resets, base typography, sr-only
851
1015
  \`\`\`
852
1016
 
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 |
1017
+ \`\`\`javascript
1018
+ import { css } from '@decantr/css'; // Atoms runtime
1019
+ import './styles/tokens.css'; // Theme tokens
1020
+ import './styles/treatments.css'; // Treatments + recipe decorators
1021
+ import './styles/global.css'; // Resets
1022
+ \`\`\`
925
1023
 
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) |
1024
+ ### Visual Treatments
937
1025
 
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 |
1026
+ Six base treatment classes provide semantic styling. Combine with atoms for layout:
950
1027
 
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)\` |
1028
+ | Treatment | Class | Variants / States |
1029
+ |-----------|-------|-------------------|
1030
+ | **Interactive Surface** | \`d-interactive\` | \`data-variant="primary\\|ghost\\|danger"\`, hover/focus-visible/disabled states |
1031
+ | **Container Surface** | \`d-surface\` | \`data-variant="raised\\|overlay"\`, optional \`data-interactive\` for hover |
1032
+ | **Data Display** | \`d-data\`, \`d-data-header\`, \`d-data-row\`, \`d-data-cell\` | Row hover highlight |
1033
+ | **Form Control** | \`d-control\` | Focus ring, placeholder, disabled, error via \`aria-invalid\` |
1034
+ | **Section Rhythm** | \`d-section\` | Auto-spacing between adjacent sections, density-aware |
1035
+ | **Inline Annotation** | \`d-annotation\` | \`data-status="success\\|error\\|warning\\|info"\` |
965
1036
 
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\` |
1037
+ ### Composition
976
1038
 
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\` |
1039
+ Atoms + treatment + recipe decorator:
986
1040
 
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 |
1041
+ \`\`\`tsx
1042
+ <button className={css('_px4 _py2') + ' d-interactive'} data-variant="primary">Deploy</button>
1043
+ <div className={css('_flex _col _gap4') + ' d-surface carbon-glass'}>Card</div>
1044
+ <span className="d-annotation" data-status="success">Active</span>
1045
+ \`\`\`
997
1046
 
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 |
1047
+ - **Atoms:** \`css('_flex _col _gap4')\` \u2014 processed by @decantr/css runtime
1048
+ - **Treatments:** \`d-interactive\`, \`d-surface\` \u2014 semantic base styles from treatments.css
1049
+ - **Recipe decorators:** \`carbon-glass\`, \`carbon-code\` \u2014 theme-specific decoration from treatments.css
1050
+ - **Combined:** \`css('_flex _col') + ' d-surface carbon-card'\`
1005
1051
 
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 |
1052
+ ### Atoms Quick Reference
1018
1053
 
1019
- ### Using Recipe Decorators
1054
+ | Category | Examples | Purpose |
1055
+ |----------|----------|---------|
1056
+ | Layout | \`_flex\`, \`_col\`, \`_row\`, \`_wrap\`, \`_grid\` | Flex/grid containers |
1057
+ | Spacing | \`_gap4\`, \`_p4\`, \`_px4\`, \`_py2\`, \`_m0\` | Gaps, padding, margin |
1058
+ | Sizing | \`_w100\`, \`_h100\`, \`_minw0\`, \`_maxwfull\` | Width, height |
1059
+ | Text | \`_textlg\`, \`_text2xl\`, \`_fontbold\`, \`_textc\` | Typography |
1060
+ | Alignment | \`_aic\`, \`_jcc\`, \`_jcsb\`, \`_pic\` | Flex/grid alignment |
1061
+ | Position | \`_rel\`, \`_abs\`, \`_sticky\`, \`_z10\` | Positioning |
1062
+ | Visual | \`_rounded\`, \`_shadow\`, \`_trans\`, \`_op50\` | Decoration |
1063
+ | Color | \`_bgprimary\`, \`_fgtext\`, \`_fgmuted\`, \`_bcborder\` | Theme colors |
1064
+ | Responsive | \`_md:gc2\`, \`_lg:gc4\`, \`_sm:flex\` | Breakpoint prefixes |
1020
1065
 
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:
1066
+ Scale: 0, 1, 2, 3, 4, 5, 6, 8, 10, 12, 16, 20, 24. Example: \`_gap4\` = \`gap:1rem\`.
1022
1067
 
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
- \`\`\`
1068
+ ### Design Tokens
1031
1069
 
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'\`
1070
+ | Token | Purpose |
1071
+ |-------|---------|
1072
+ | \`--d-primary\` | Primary brand color |
1073
+ | \`--d-surface\`, \`--d-surface-raised\` | Surface backgrounds |
1074
+ | \`--d-bg\` | Page background |
1075
+ | \`--d-border\` | Border color |
1076
+ | \`--d-text\`, \`--d-text-muted\` | Text colors |
1077
+ | \`--d-success\`, \`--d-error\`, \`--d-warning\`, \`--d-info\` | Status colors |
1078
+ | \`--d-shadow\`, \`--d-shadow-lg\` | Elevation shadows |
1079
+ | \`--d-radius\`, \`--d-radius-lg\` | Border radii |
1036
1080
 
1037
1081
  ### Routing
1038
1082
 
@@ -1040,28 +1084,7 @@ Check \`decantr.essence.json\` \u2192 \`meta.platform.routing\` for the routing
1040
1084
  - \`"hash"\` \u2192 use \`HashRouter\` (e.g., for static hosting, GitHub Pages)
1041
1085
  - \`"history"\` \u2192 use \`BrowserRouter\` (e.g., for server-rendered apps)
1042
1086
 
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\` |`;
1087
+ Routes are defined in \`decantr.essence.json\` \u2192 \`blueprint.routes\` and listed in \`.decantr/context/scaffold.md\`.`;
1065
1088
  function generateDecantrMdV31(guardMode, cssApproach) {
1066
1089
  const template = loadTemplate("DECANTR.md.template");
1067
1090
  return renderTemplate(template, {
@@ -1503,7 +1526,8 @@ async function refreshDerivedFiles(projectRoot, essence, registry, prefetchedThe
1503
1526
  recipeData = {
1504
1527
  decorators: r.decorators,
1505
1528
  spatial_hints: r.spatial_hints,
1506
- radius_hints: r.radius_hints
1529
+ radius_hints: r.radius_hints,
1530
+ treatment_overrides: r.treatment_overrides
1507
1531
  };
1508
1532
  if (!recipeData.decorators && raw.data) {
1509
1533
  const inner = raw.data;
@@ -1527,7 +1551,8 @@ async function refreshDerivedFiles(projectRoot, essence, registry, prefetchedThe
1527
1551
  recipeData = {
1528
1552
  decorators: inner.decorators,
1529
1553
  spatial_hints: inner.spatial_hints,
1530
- radius_hints: inner.radius_hints
1554
+ radius_hints: inner.radius_hints,
1555
+ treatment_overrides: inner.treatment_overrides
1531
1556
  };
1532
1557
  }
1533
1558
  }
@@ -1557,27 +1582,35 @@ async function refreshDerivedFiles(projectRoot, essence, registry, prefetchedThe
1557
1582
  }
1558
1583
  const stylesDir = join(projectRoot, "src", "styles");
1559
1584
  mkdirSync(stylesDir, { recursive: true });
1585
+ const densityLevel = options.density || "comfortable";
1586
+ const spatialTokens = computeSpatialTokens(densityLevel, recipeData?.spatial_hints ? {
1587
+ section_padding: recipeData.spatial_hints.section_padding ?? void 0,
1588
+ density_bias: typeof recipeData.spatial_hints.density_bias === "number" ? recipeData.spatial_hints.density_bias : void 0,
1589
+ content_gap_shift: recipeData.spatial_hints.content_gap_shift
1590
+ } : void 0);
1560
1591
  const tokensPath = join(stylesDir, "tokens.css");
1561
1592
  const hasRealThemeData = themeData?.seed?.primary || themeData?.palette?.background;
1562
1593
  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
- }
1594
+ writeFileSync(tokensPath, generateTokensCSS(themeData, mode, spatialTokens));
1595
+ }
1596
+ const treatmentsPath = join(stylesDir, "treatments.css");
1597
+ writeFileSync(treatmentsPath, generateTreatmentCSS(
1598
+ spatialTokens,
1599
+ recipeData?.treatment_overrides,
1600
+ recipeData?.decorators,
1601
+ themeName
1602
+ ));
1570
1603
  const globalPath = join(stylesDir, "global.css");
1571
1604
  if (!existsSync(globalPath)) {
1572
1605
  writeFileSync(globalPath, generateGlobalCSS(personality));
1573
1606
  }
1574
- const cssFiles = [tokensPath, decoratorsPath, globalPath];
1575
- const decoratorsMdPath = join(contextDir, "decorators.md");
1576
- writeFileSync(decoratorsMdPath, generateDecoratorsContext(recipeData, recipeName));
1607
+ const cssFiles = [tokensPath, treatmentsPath, globalPath];
1608
+ const treatmentsMdPath = join(contextDir, "treatments.md");
1609
+ writeFileSync(treatmentsMdPath, generateTreatmentsContext(recipeData, recipeName));
1577
1610
  const decantrMdPath = join(projectRoot, "DECANTR.md");
1578
1611
  writeFileSync(decantrMdPath, generateDecantrMdV31(guardMode, CSS_APPROACH_CONTENT));
1579
1612
  const hasSections = essence.blueprint.sections && essence.blueprint.sections.length > 0;
1580
- const contextFiles = [decoratorsMdPath];
1613
+ const contextFiles = [treatmentsMdPath];
1581
1614
  if (!hasSections) {
1582
1615
  const summaryPath = join(contextDir, "essence-summary.md");
1583
1616
  writeFileSync(summaryPath, generateEssenceSummaryV3(essence));
@@ -2002,12 +2035,10 @@ function generateSectionContext(input) {
2002
2035
  lines.push("");
2003
2036
  lines.push(`**Theme tokens:** see \`src/styles/tokens.css\` \u2014 use \`var(--d-primary)\`, \`var(--d-bg)\`, etc.`);
2004
2037
  lines.push("");
2038
+ lines.push("**Visual Treatments:** All 6 base treatments available (see DECANTR.md for usage).");
2005
2039
  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.");
2040
+ const names = decorators.map((d) => d.name).join(", ");
2041
+ lines.push(`**Recipe decorators:** ${names}`);
2011
2042
  }
2012
2043
  lines.push("");
2013
2044
  if (recipeHints) {
@@ -9,7 +9,7 @@ import {
9
9
  scaffoldMinimal,
10
10
  scaffoldProject,
11
11
  syncRegistry
12
- } from "./chunk-6RJSFLT4.js";
12
+ } from "./chunk-B5OX2EXL.js";
13
13
 
14
14
  // src/index.ts
15
15
  import { readFileSync as readFileSync15, existsSync as existsSync23, readdirSync as readdirSync6 } from "fs";
@@ -2645,7 +2645,8 @@ async function cmdMagic(prompt, projectRoot, options) {
2645
2645
  recipeData = {
2646
2646
  decorators: recipe.decorators || recipeData?.decorators,
2647
2647
  spatial_hints: recipe.spatial_hints,
2648
- radius_hints: recipe.radius_hints
2648
+ radius_hints: recipe.radius_hints,
2649
+ treatment_overrides: recipe.treatment_overrides
2649
2650
  };
2650
2651
  }
2651
2652
  }
@@ -3135,7 +3136,7 @@ import { createRoot } from 'react-dom/client';
3135
3136
  import { BrowserRouter } from 'react-router-dom';
3136
3137
  import { App } from './App';
3137
3138
  import './styles/tokens.css';
3138
- import './styles/decorators.css';
3139
+ import './styles/treatments.css';
3139
3140
  import './styles/global.css';
3140
3141
 
3141
3142
  createRoot(document.getElementById('root')!).render(
@@ -3785,7 +3786,8 @@ ${YELLOW9}You're offline. Scaffolding Decantr default.${RESET13}`);
3785
3786
  recipeData = {
3786
3787
  decorators: recipe.decorators || recipeData?.decorators,
3787
3788
  spatial_hints: recipe.spatial_hints,
3788
- radius_hints: recipe.radius_hints
3789
+ radius_hints: recipe.radius_hints,
3790
+ treatment_overrides: recipe.treatment_overrides
3789
3791
  };
3790
3792
  }
3791
3793
  }
@@ -4297,7 +4299,7 @@ async function main() {
4297
4299
  break;
4298
4300
  }
4299
4301
  case "upgrade": {
4300
- const { cmdUpgrade } = await import("./upgrade-25IURU4X.js");
4302
+ const { cmdUpgrade } = await import("./upgrade-LTLUPBZQ.js");
4301
4303
  const applyFlag = args.includes("--apply");
4302
4304
  await cmdUpgrade(process.cwd(), { apply: applyFlag });
4303
4305
  break;
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
- import "./chunk-66RIAQLH.js";
2
- import "./chunk-6RJSFLT4.js";
1
+ import "./chunk-QZVA3PFR.js";
2
+ import "./chunk-B5OX2EXL.js";
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  RegistryClient,
3
3
  refreshDerivedFiles
4
- } from "./chunk-6RJSFLT4.js";
4
+ } from "./chunk-B5OX2EXL.js";
5
5
 
6
6
  // src/commands/upgrade.ts
7
7
  import { readFileSync, writeFileSync, existsSync } from "fs";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@decantr/cli",
3
- "version": "1.5.2",
3
+ "version": "1.5.3",
4
4
  "description": "Decantr CLI — search the registry, validate essence files, and access design intelligence from the terminal",
5
5
  "license": "MIT",
6
6
  "repository": {