@fractaldesign/fractalstyler 0.0.0-stage → 0.9.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 (46) hide show
  1. package/LICENSE +19 -0
  2. package/README.md +123 -2
  3. package/cli/main.mjs +141 -0
  4. package/cli/scaffold.mjs +406 -0
  5. package/data/recipes.json +61 -0
  6. package/dist/asset.d.ts +3 -0
  7. package/dist/css/fractalstyler.css +2453 -0
  8. package/dist/css/fractalstyler.min.css +2 -0
  9. package/dist/index.d.ts +6 -0
  10. package/dist/index.js +6 -0
  11. package/dist/styles/_00_config.sass +29 -0
  12. package/dist/styles/_00_fonts.sass +58 -0
  13. package/dist/styles/_00_tokens.sass +211 -0
  14. package/dist/styles/_01_base.sass +58 -0
  15. package/dist/styles/_02_dimensions.sass +70 -0
  16. package/dist/styles/_03_typography.sass +170 -0
  17. package/dist/styles/_04_containers.sass +173 -0
  18. package/dist/styles/_05_layouts.sass +104 -0
  19. package/dist/styles/_06_shells.sass +135 -0
  20. package/dist/styles/_07_interactions.sass +212 -0
  21. package/dist/styles/_08_visuals.sass +136 -0
  22. package/dist/styles/_09_own.sass +174 -0
  23. package/dist/styles/colorpacks.sass +119 -0
  24. package/dist/styles/index.sass +14 -0
  25. package/dist/styles/themeplates.sass +159 -0
  26. package/docs/REGISTRY.api.md +477 -0
  27. package/docs/REGISTRY.md +14 -0
  28. package/docs/references/configurations-api.md +220 -0
  29. package/docs/references/configurations.md +258 -0
  30. package/lint/browser.mjs +79 -0
  31. package/lint/cli.mjs +212 -0
  32. package/lint/lib/agent-reporter.mjs +51 -0
  33. package/lint/lib/fuzzy.mjs +45 -0
  34. package/lint/lib/registry.mjs +107 -0
  35. package/lint/lib/sass-linter.mjs +71 -0
  36. package/lint/lib/svelte-linter.mjs +116 -0
  37. package/lint/lib/token-linter.mjs +97 -0
  38. package/package.json +112 -5
  39. package/registry.json +3999 -0
  40. package/scripts/class-vocab.js +197 -0
  41. package/scripts/update-registry.js +934 -0
  42. package/skills/fractal-styler/references/fractals.md +630 -0
  43. package/skills/fractal-styler/references/tokens.md +226 -0
  44. package/skills/fractalstyler/SKILL.md +59 -0
  45. package/skills/fractalstyler/references/fractals.md +630 -0
  46. package/skills/fractalstyler/references/tokens.md +226 -0
@@ -0,0 +1,197 @@
1
+ // =============================================================================
2
+ // class-vocab — turns the fractalstyler registry into a rich class API reference.
3
+ //
4
+ // Decodes class families, scale steps, and provides curated intent descriptions.
5
+ // Used by:
6
+ // - scripts/class-vocab.js -> generates docs/REGISTRY.api.md
7
+ // - lint/browser.mjs -> generates standalone registry browser HTML
8
+ // =============================================================================
9
+
10
+ import fs from 'node:fs';
11
+ import path from 'node:path';
12
+ import { fileURLToPath } from 'node:url';
13
+
14
+ const PKG_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
15
+
16
+ const STEPS = new Set(['xs', 'sm', 'md', 'bs', 'lg', 'xl', '2xl', '3xl']);
17
+ const RADIUS_STEPS = new Set(['xs', 'sm', 'md', 'bs', 'lg', 'full']);
18
+
19
+ export const LAYER_INTROS = {
20
+ L0: 'Tokens and Base resets. L0 defines the CSS custom properties for both color modes (via the =light-theme-tokens / =dark-theme-tokens mixins in _00_tokens.sass), the type/space/radius/motion scales, and native element resets. Never hardcode a raw hex or pixel literal that L0 covers.',
21
+ L1: 'Dimensions. Loop-generated space families — .gap/.rgap/.cgap, .pad/.px/.py/.pt/.pr/.pb/.pl, .mar/.mx/.my/.mt/.mr/.mb/.ml — each across the eight steps (xs sm md bs lg xl 2xl 3xl), all token-routed through var(--space-*). Plus the six radius channels, full/zero sizing, control heights, and viewport heights. No literal ladder; no -mob/-desk bands.',
22
+ L2: 'Containers and alignment universe. .box (flex-col), .row (flex-row), and .grid (css-grid) are the three structural foundations with strict physical axis modifiers (xcenter, ycenter, xbetween, etc.) nested under their base — never standalone. Scroll containers, cards, chips, and form/section wrappers live here too.',
23
+ L3: 'Layouts and responsive grids. .grid-1/.grid-2/.grid-3/.grid-4/.grid-6 with divisor stepping (3→1, 4→2→1, 6→3→2→1), .cspan-* column spans, .card-grid (auto-fit), .prose (reading measure), .frame-* aspect-ratio boxes (from $frame-ratios), and .reel (scroll-snap rail).',
24
+ L4: 'Shells and canonical application markups. The .app-shell canon: .app-header, .app-main, .main-section, the role-bound rails (.sidebar-left drawer, .sidebar-right TOC), .app-footer, and .content-section with its narrow width modes.',
25
+ L5: 'Visuals and Interactions. Typography primitives and roles (_03), inputs, selects, links and the button quartet with its data-variant/data-shape/data-size axes (_07), surface fills, ink, the partition-law border set, elevation shadows, and responsive visibility (_08).',
26
+ Custom: 'Sanctioned extension point (_09_own.sass). Contains project-specific overrides and third-party styling additions that cannot be expressed via canonical layers.'
27
+ };
28
+
29
+ export const GAP_PROPS = { gap: 'gap (both axes)', rgap: 'row-gap', cgap: 'column-gap' };
30
+ export const PAD_PROPS = {
31
+ pad: 'padding (all sides)',
32
+ 'pad-x': 'padding-inline (horizontal)',
33
+ 'pad-y': 'padding-block (vertical)',
34
+ 'pad-top': 'padding-top',
35
+ 'pad-right': 'padding-right',
36
+ 'pad-bottom': 'padding-bottom',
37
+ 'pad-left': 'padding-left'
38
+ };
39
+
40
+ export function renderBrowserHtml(registryEntries, options = {}) {
41
+ const entries = Array.isArray(registryEntries) ? registryEntries : [];
42
+
43
+ return `<!doctype html>
44
+ <html lang="en">
45
+ <head>
46
+ <meta charset="utf-8">
47
+ <meta name="viewport" content="width=device-width, initial-scale=1">
48
+ <title>Fractalstyler — Class Registry Browser</title>
49
+ <style>
50
+ :root { color-scheme: light dark; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, monospace; }
51
+ body { margin: 0; padding: 24px; background: Canvas; color: CanvasText; }
52
+ header { max-width: 1200px; margin: 0 auto 16px; }
53
+ h1 { font-size: 20px; margin: 0 0 6px; letter-spacing: 0.04em; }
54
+ .sub { opacity: .75; font-size: 13px; margin-bottom: 12px; }
55
+ .controls { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 8px; }
56
+ input, select { font: inherit; padding: 6px 12px; border: 1px solid color-mix(in oklab, CanvasText 20%, Canvas); border-radius: 6px; background: Canvas; color: CanvasText; font-size: 13px; }
57
+ input { flex: 1; min-width: 260px; font-family: ui-monospace, Menlo, monospace; }
58
+ table { width: 100%; max-width: 1200px; margin: 0 auto; border-collapse: collapse; font-size: 13px; }
59
+ th, td { text-align: left; padding: 8px 12px; border-bottom: 1px solid color-mix(in oklab, CanvasText 12%, Canvas); vertical-align: top; }
60
+ th { position: sticky; top: 0; background: Canvas; font-weight: 600; }
61
+ code { font-family: ui-monospace, Menlo, monospace; font-size: 13px; font-weight: 600; color: #059669; }
62
+ .layer-badge { display: inline-block; padding: 2px 6px; border-radius: 4px; font-size: 11px; font-weight: 700; background: color-mix(in oklab, CanvasText 10%, Canvas); font-family: ui-monospace, monospace; }
63
+ .desc { opacity: .9; }
64
+ .prop { font-family: ui-monospace, monospace; font-size: 11px; opacity: .7; margin-top: 2px; }
65
+ .empty { opacity: .6; padding: 32px 0; text-align: center; }
66
+ </style>
67
+ </head>
68
+ <body>
69
+ <header>
70
+ <h1>FRACTALSTYLER REGISTRY BROWSER</h1>
71
+ <div class="sub" id="totals">Loading...</div>
72
+ <div class="controls">
73
+ <input id="q" type="search" placeholder="Search class name, description, or property..." autofocus>
74
+ <select id="layerFilter">
75
+ <option value="">All layers</option>
76
+ <option value="L0">L0 (Tokens & Base)</option>
77
+ <option value="L1">L1 (Dimensions)</option>
78
+ <option value="L2">L2 (Containers)</option>
79
+ <option value="L3">L3 (Layouts)</option>
80
+ <option value="L4">L4 (Shells)</option>
81
+ <option value="L5">L5 (Visuals & Interactions)</option>
82
+ <option value="Custom">Custom (_09_own)</option>
83
+ </select>
84
+ </div>
85
+ </header>
86
+ <table>
87
+ <thead>
88
+ <tr>
89
+ <th style="width: 25%">Class</th>
90
+ <th style="width: 12%">Layer</th>
91
+ <th>Description & Property</th>
92
+ </tr>
93
+ </thead>
94
+ <tbody id="rows"></tbody>
95
+ </table>
96
+ <div class="empty" id="empty" hidden>No matching classes found.</div>
97
+
98
+ <script>
99
+ const DATA = ${JSON.stringify(entries)};
100
+ const rows = document.getElementById('rows');
101
+ const empty = document.getElementById('empty');
102
+ const q = document.getElementById('q');
103
+ const layerFilter = document.getElementById('layerFilter');
104
+ const totals = document.getElementById('totals');
105
+
106
+ totals.textContent = \`\${DATA.length} canonical classes indexed from system styling partials\`;
107
+
108
+ function render() {
109
+ const needle = q.value.trim().toLowerCase();
110
+ const layer = layerFilter.value;
111
+ const out = [];
112
+
113
+ for (const item of DATA) {
114
+ if (layer && item.layer !== layer) continue;
115
+ const name = (item.class || '').toLowerCase();
116
+ const desc = (item.description || '').toLowerCase();
117
+ const prop = (item.property || '').toLowerCase();
118
+ if (needle && !(name.includes(needle) || desc.includes(needle) || prop.includes(needle))) continue;
119
+
120
+ out.push(\`<tr>
121
+ <td><code>\${item.class}</code></td>
122
+ <td><span class="layer-badge">\${item.layer}</span></td>
123
+ <td>
124
+ <div class="desc">\${item.description || '—'}</div>
125
+ \${item.property ? \`<div class="prop">\${item.property}</div>\` : ''}
126
+ </td>
127
+ </tr>\`);
128
+ }
129
+
130
+ rows.innerHTML = out.join('');
131
+ empty.hidden = out.length > 0;
132
+ }
133
+
134
+ q.addEventListener('input', render);
135
+ layerFilter.addEventListener('change', render);
136
+ render();
137
+ </script>
138
+ </body>
139
+ </html>
140
+ `;
141
+ }
142
+
143
+ export function renderApiMarkdown(registryEntries) {
144
+ const entries = Array.isArray(registryEntries) ? registryEntries : [];
145
+ const layers = ['L0', 'L1', 'L2', 'L3', 'L4', 'L5', 'Custom'];
146
+
147
+ const lines = [
148
+ '# Fractalstyler — Class API & Vocabulary Reference',
149
+ '',
150
+ '> Comprehensive index of the canonical styling system classes, layers, and intent.',
151
+ '> Generated from `registry.json` via `scripts/class-vocab.js`.',
152
+ '',
153
+ `**${entries.length}** classes indexed across layers.`,
154
+ '',
155
+ 'Every entry is a concrete class — the space families are expanded from the',
156
+ 'family maps in `_00_config.sass` across the eight steps `xs sm md bs lg xl 2xl 3xl`.',
157
+ 'There are no wildcard families, no literal ladders and no `-mob`/`-desk` bands.',
158
+ 'Modifiers nest under their base (`.box.xcenter`), children carry their own name',
159
+ '(`.accordion-content`), and `element[...]` rows are attribute-driven variants.',
160
+ '',
161
+ '---'
162
+ ];
163
+
164
+ for (const layer of layers) {
165
+ const items = entries.filter((e) => e.layer === layer);
166
+ if (items.length === 0) continue;
167
+
168
+ lines.push('', `## Layer ${layer}`, '');
169
+ if (LAYER_INTROS[layer]) {
170
+ lines.push(LAYER_INTROS[layer], '');
171
+ }
172
+
173
+ lines.push('| Class | Property / Expansion | Description |', '|:---|:---|:---|');
174
+ for (const it of items) {
175
+ const prop = it.property ? `\`${it.property.replace(/\|/g, '\\|')}\`` : '—';
176
+ lines.push(`| \`${it.class}\` | ${prop} | ${it.description || '—'} |`);
177
+ }
178
+ }
179
+
180
+ lines.push('');
181
+ return lines.join('\n');
182
+ }
183
+
184
+ // Direct CLI execution — works under either name: the repo's class-vocab.js,
185
+ // or the class-vocab.mjs copy that `fractalstyler eject` places in consumer
186
+ // projects (renamed so it runs as ESM regardless of their package type).
187
+ const invokedAs = path.basename(process.argv[1] || '');
188
+ if (process.argv[1] && /^class-vocab\.(js|mjs)$/.test(invokedAs)) {
189
+ const regPath = path.join(PKG_ROOT, 'registry.json');
190
+ if (fs.existsSync(regPath)) {
191
+ const registry = JSON.parse(fs.readFileSync(regPath, 'utf8'));
192
+ const md = renderApiMarkdown(registry);
193
+ const outDoc = path.join(PKG_ROOT, 'docs', 'REGISTRY.api.md');
194
+ fs.writeFileSync(outDoc, md, 'utf8');
195
+ console.log(`✔ Generated ${outDoc}`);
196
+ }
197
+ }