@motion-proto/live-tokens 0.1.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 (68) hide show
  1. package/README.md +41 -0
  2. package/dist-plugin/index.cjs +444 -0
  3. package/dist-plugin/index.d.cts +12 -0
  4. package/dist-plugin/index.d.ts +12 -0
  5. package/dist-plugin/index.js +407 -0
  6. package/package.json +86 -0
  7. package/src/components/Badge.svelte +82 -0
  8. package/src/components/Button.svelte +333 -0
  9. package/src/components/Card.svelte +83 -0
  10. package/src/components/CollapsibleSection.svelte +82 -0
  11. package/src/components/DetailNav.svelte +78 -0
  12. package/src/components/Dialog.svelte +269 -0
  13. package/src/components/InlineEditActions.svelte +73 -0
  14. package/src/components/Notification.svelte +308 -0
  15. package/src/components/ProgressBar.svelte +99 -0
  16. package/src/components/RadioButton.svelte +87 -0
  17. package/src/components/SectionDivider.svelte +121 -0
  18. package/src/components/TabBar.svelte +92 -0
  19. package/src/components/Toggle.svelte +86 -0
  20. package/src/components/Tooltip.svelte +64 -0
  21. package/src/lib/ColumnsOverlay.svelte +120 -0
  22. package/src/lib/LiveEditorOverlay.svelte +467 -0
  23. package/src/lib/columnsOverlay.ts +26 -0
  24. package/src/lib/cssVarSync.ts +72 -0
  25. package/src/lib/editorConfig.ts +9 -0
  26. package/src/lib/editorConfigStore.ts +14 -0
  27. package/src/lib/index.ts +51 -0
  28. package/src/lib/oklch.ts +129 -0
  29. package/src/lib/pageSource.ts +6 -0
  30. package/src/lib/tokenInit.ts +29 -0
  31. package/src/lib/tokenService.ts +144 -0
  32. package/src/lib/tokenTypes.ts +45 -0
  33. package/src/pages/Admin.svelte +100 -0
  34. package/src/pages/ShowcasePage.svelte +146 -0
  35. package/src/showcase/BackupBrowser.svelte +617 -0
  36. package/src/showcase/BezierCurveEditor.svelte +648 -0
  37. package/src/showcase/ColorEditPanel.svelte +498 -0
  38. package/src/showcase/ComponentsTab.svelte +107 -0
  39. package/src/showcase/EditorDialog.svelte +137 -0
  40. package/src/showcase/PaletteEditor.svelte +2579 -0
  41. package/src/showcase/PaletteSelector.svelte +627 -0
  42. package/src/showcase/SurfacesTab.svelte +409 -0
  43. package/src/showcase/TextTab.svelte +205 -0
  44. package/src/showcase/TokenFileManager.svelte +683 -0
  45. package/src/showcase/TokenMap.svelte +54 -0
  46. package/src/showcase/VariablesTab.svelte +2657 -0
  47. package/src/showcase/VisualsTab.svelte +233 -0
  48. package/src/showcase/curveEngine.ts +190 -0
  49. package/src/showcase/demos/BadgeDemo.svelte +58 -0
  50. package/src/showcase/demos/CardDemo.svelte +52 -0
  51. package/src/showcase/demos/ChoiceButtonsDemo.svelte +194 -0
  52. package/src/showcase/demos/CollapsibleSectionDemo.svelte +56 -0
  53. package/src/showcase/demos/DialogDemo.svelte +42 -0
  54. package/src/showcase/demos/InlineEditActionsDemo.svelte +27 -0
  55. package/src/showcase/demos/NotificationDemo.svelte +149 -0
  56. package/src/showcase/demos/ProgressBarDemo.svelte +56 -0
  57. package/src/showcase/demos/RadioButtonDemo.svelte +58 -0
  58. package/src/showcase/demos/SectionDividerDemo.svelte +79 -0
  59. package/src/showcase/demos/StandardButtonsDemo.svelte +457 -0
  60. package/src/showcase/demos/TabBarDemo.svelte +60 -0
  61. package/src/showcase/demos/TooltipDemo.svelte +54 -0
  62. package/src/showcase/editor.css +93 -0
  63. package/src/showcase/index.ts +17 -0
  64. package/src/styles/fonts/Domine/Domine-VariableFont_wght.ttf +0 -0
  65. package/src/styles/fonts/Domine/OFL.txt +97 -0
  66. package/src/styles/fonts/Domine/README.txt +66 -0
  67. package/src/styles/fonts.css +18 -0
  68. package/src/styles/form-controls.css +190 -0
@@ -0,0 +1,627 @@
1
+ <script lang="ts">
2
+ import { onMount, onDestroy, createEventDispatcher } from 'svelte';
3
+ import { setCssVar, removeCssVar } from '../lib/cssVarSync';
4
+
5
+ const dispatch = createEventDispatcher();
6
+
7
+ export let variable: string;
8
+ export let label: string;
9
+
10
+ type Category = 'palette' | 'surface' | 'border' | 'text';
11
+
12
+ const families = [
13
+ { name: 'neutral', label: 'Neutral' },
14
+ { name: 'alternate', label: 'Alternate' },
15
+ { name: 'bg', label: 'Background' },
16
+ { name: 'primary', label: 'Primary' },
17
+ { name: 'accent', label: 'Accent' },
18
+ { name: 'special', label: 'Special' },
19
+ { name: 'success', label: 'Success' },
20
+ { name: 'warning', label: 'Warning' },
21
+ { name: 'info', label: 'Info' },
22
+ { name: 'danger', label: 'Danger' },
23
+ ];
24
+
25
+ const familyNames = families.map(f => f.name);
26
+
27
+ const paletteSteps = ['100', '200', '300', '400', '500', '600', '700', '800', '850', '900', '950'];
28
+
29
+ const surfaceSteps = [
30
+ { key: 'lowest', label: 'Lowest' },
31
+ { key: 'lower', label: 'Lower' },
32
+ { key: 'low', label: 'Low' },
33
+ { key: '', label: 'Default' },
34
+ { key: 'high', label: 'High' },
35
+ { key: 'higher', label: 'Higher' },
36
+ { key: 'highest', label: 'Highest' },
37
+ ];
38
+
39
+ const borderSteps = [
40
+ { key: 'faint', label: 'Faint' },
41
+ { key: 'subtle', label: 'Subtle' },
42
+ { key: '', label: 'Default' },
43
+ { key: 'medium', label: 'Medium' },
44
+ { key: 'strong', label: 'Strong' },
45
+ ];
46
+
47
+ const textSteps = [
48
+ { key: 'primary', label: 'Primary' },
49
+ { key: 'secondary', label: 'Secondary' },
50
+ { key: 'tertiary', label: 'Tertiary' },
51
+ { key: 'muted', label: 'Muted' },
52
+ { key: 'disabled', label: 'Disabled' },
53
+ ];
54
+
55
+ const surfaceStepKeys = surfaceSteps.map(s => s.key);
56
+ const borderStepKeys = borderSteps.map(s => s.key);
57
+ const textStepKeys = textSteps.map(s => s.key);
58
+
59
+ const familiesWithText = ['neutral', 'bg', 'primary', 'accent', 'special', 'success', 'warning', 'info', 'danger'];
60
+
61
+ const allCategories: { id: Category; label: string }[] = [
62
+ { id: 'palette', label: 'Palette' },
63
+ { id: 'surface', label: 'Surface' },
64
+ { id: 'border', label: 'Border' },
65
+ { id: 'text', label: 'Text' },
66
+ ];
67
+
68
+ let open = false;
69
+ let selectedFamily: string | null = null;
70
+ let selectedTab: Category = 'palette';
71
+ let container: HTMLElement;
72
+
73
+ let chosenCategory: Category | null = null;
74
+ let chosenFamily: string | null = null;
75
+ let chosenStep: string | null = null;
76
+ let chosenHex: string | null = null;
77
+
78
+ /** Get the CSS variable name for a category/family/step combination */
79
+ function getVarName(category: Category, family: string, stepKey: string): string {
80
+ switch (category) {
81
+ case 'palette':
82
+ return `--color-${family}-${stepKey}`;
83
+ case 'surface':
84
+ return stepKey ? `--surface-${family}-${stepKey}` : `--surface-${family}`;
85
+ case 'border':
86
+ if (!stepKey) {
87
+ return family === 'neutral' ? `--border-${family}-default` : `--border-${family}`;
88
+ }
89
+ return `--border-${family}-${stepKey}`;
90
+ case 'text':
91
+ if (family === 'neutral') return `--text-${stepKey}`;
92
+ if (family === 'primary' && stepKey === 'primary') return '--text-primary-color';
93
+ if (stepKey === 'primary') return `--text-${family}`;
94
+ return `--text-${family}-${stepKey}`;
95
+ }
96
+ }
97
+
98
+ function parseTextVarName(varName: string): { family: string; step: string } | null {
99
+ if (varName === '--text-primary') return { family: 'neutral', step: 'primary' };
100
+ if (varName === '--text-secondary') return { family: 'neutral', step: 'secondary' };
101
+ if (varName === '--text-tertiary') return { family: 'neutral', step: 'tertiary' };
102
+ if (varName === '--text-muted') return { family: 'neutral', step: 'muted' };
103
+ if (varName === '--text-disabled') return { family: 'neutral', step: 'disabled' };
104
+ if (varName === '--text-primary-color') return { family: 'primary', step: 'primary' };
105
+ const m = varName.match(/^--text-([a-z]+)(?:-([a-z]+))?$/);
106
+ if (m) {
107
+ const fam = m[1];
108
+ const hier = m[2] || 'primary';
109
+ if (familiesWithText.includes(fam) && fam !== 'neutral' && textStepKeys.includes(hier)) {
110
+ return { family: fam, step: hier };
111
+ }
112
+ }
113
+ return null;
114
+ }
115
+
116
+ /** Parse a var(...) reference into category/family/step */
117
+ function parseRef(value: string): { category: Category; family: string; step: string } | null {
118
+ const varMatch = value.match(/var\((--[a-z0-9-]+)\)/);
119
+ if (!varMatch) return null;
120
+ const varName = varMatch[1];
121
+
122
+ const paletteM = varName.match(/^--color-([a-z]+)-(\d{3})$/);
123
+ if (paletteM && familyNames.includes(paletteM[1]) && paletteSteps.includes(paletteM[2])) {
124
+ return { category: 'palette', family: paletteM[1], step: paletteM[2] };
125
+ }
126
+
127
+ const surfaceM = varName.match(/^--surface-([a-z]+)(?:-([a-z]+))?$/);
128
+ if (surfaceM && familyNames.includes(surfaceM[1])) {
129
+ const step = surfaceM[2] || '';
130
+ if (surfaceStepKeys.includes(step)) {
131
+ return { category: 'surface', family: surfaceM[1], step };
132
+ }
133
+ }
134
+
135
+ const borderM = varName.match(/^--border-([a-z]+)(?:-([a-z]+))?$/);
136
+ if (borderM && familyNames.includes(borderM[1])) {
137
+ let step = borderM[2] || '';
138
+ if (step === 'default') step = '';
139
+ if (borderStepKeys.includes(step)) {
140
+ return { category: 'border', family: borderM[1], step };
141
+ }
142
+ }
143
+
144
+ const textResult = parseTextVarName(varName);
145
+ if (textResult) {
146
+ return { category: 'text', ...textResult };
147
+ }
148
+
149
+ return null;
150
+ }
151
+
152
+ function initFromCurrent() {
153
+ const raw = document.documentElement.style.getPropertyValue(variable).trim();
154
+ if (!raw) return;
155
+ const parsed = parseRef(raw);
156
+ if (parsed) {
157
+ chosenCategory = parsed.category;
158
+ chosenFamily = parsed.family;
159
+ chosenStep = parsed.step;
160
+ chosenHex = null;
161
+ } else if (/^#[0-9a-f]{3,8}$/i.test(raw)) {
162
+ chosenHex = raw;
163
+ chosenCategory = null;
164
+ chosenFamily = null;
165
+ chosenStep = null;
166
+ }
167
+ }
168
+
169
+ function resetVariable() {
170
+ removeCssVar(variable);
171
+ chosenCategory = null;
172
+ chosenFamily = null;
173
+ chosenStep = null;
174
+ chosenHex = null;
175
+ open = false;
176
+ selectedFamily = null;
177
+ dispatch('change');
178
+ }
179
+
180
+ function toggle() {
181
+ open = !open;
182
+ if (!open) selectedFamily = null;
183
+ }
184
+
185
+ function selectFamily(name: string) {
186
+ selectedFamily = name;
187
+ if (name === chosenFamily && chosenCategory) {
188
+ selectedTab = chosenCategory;
189
+ }
190
+ }
191
+
192
+ function backToFamilies() {
193
+ selectedFamily = null;
194
+ }
195
+
196
+ function selectSwatch(category: Category, step: string) {
197
+ const varName = getVarName(category, selectedFamily!, step);
198
+ if (varName === variable) {
199
+ removeCssVar(variable);
200
+ } else {
201
+ setCssVar(variable, `var(${varName})`);
202
+ }
203
+ chosenCategory = category;
204
+ chosenFamily = selectedFamily;
205
+ chosenStep = step;
206
+ chosenHex = null;
207
+ open = false;
208
+ selectedFamily = null;
209
+ dispatch('change');
210
+ }
211
+
212
+ function handleClickOutside(e: MouseEvent) {
213
+ if (container && !container.contains(e.target as Node)) {
214
+ open = false;
215
+ selectedFamily = null;
216
+ }
217
+ }
218
+
219
+ onMount(() => {
220
+ initFromCurrent();
221
+ document.addEventListener('click', handleClickOutside, true);
222
+ });
223
+
224
+ onDestroy(() => {
225
+ document.removeEventListener('click', handleClickOutside, true);
226
+ });
227
+
228
+ $: displayValue = chosenHex
229
+ ? chosenHex
230
+ : (chosenCategory === 'palette' && chosenFamily && chosenStep)
231
+ ? `${chosenFamily}-${chosenStep}`
232
+ : (chosenCategory && chosenFamily)
233
+ ? getVarName(chosenCategory, chosenFamily, chosenStep || '').replace(/^--/, '')
234
+ : '';
235
+
236
+ $: availableTabs = selectedFamily
237
+ ? allCategories.filter(c => c.id !== 'text' || familiesWithText.includes(selectedFamily!))
238
+ : allCategories;
239
+
240
+ $: if (selectedFamily && !availableTabs.find(t => t.id === selectedTab)) {
241
+ selectedTab = 'palette';
242
+ }
243
+ </script>
244
+
245
+ <div class="palette-selector" bind:this={container}>
246
+ <button class="selector-trigger" on:click={toggle}>
247
+ <div class="trigger-swatch" style="background: var({variable});"></div>
248
+ <div class="trigger-text">
249
+ <span class="trigger-label">{label}</span>
250
+ {#if displayValue}
251
+ <span class="trigger-value">{displayValue}</span>
252
+ {/if}
253
+ </div>
254
+ <i class="fas fa-chevron-down trigger-chevron" class:open></i>
255
+ </button>
256
+
257
+ {#if open}
258
+ <div class="selector-dropdown">
259
+ {#if selectedFamily === null}
260
+ <div class="dropdown-header">
261
+ <code class="variable-name">{variable}</code>
262
+ <button class="reset-btn" on:click={resetVariable} title="Reset to default">
263
+ <i class="fas fa-undo"></i>
264
+ </button>
265
+ </div>
266
+ <div class="family-list">
267
+ {#each families as fam}
268
+ <button class="family-item" class:active={chosenFamily === fam.name} on:click={() => selectFamily(fam.name)}>
269
+ <div class="family-swatches">
270
+ <div class="mini-swatch" style="background: var(--color-{fam.name}-300);"></div>
271
+ <div class="mini-swatch" style="background: var(--color-{fam.name}-500);"></div>
272
+ <div class="mini-swatch" style="background: var(--color-{fam.name}-700);"></div>
273
+ </div>
274
+ <span class="family-label">{fam.label}</span>
275
+ <i class="fas fa-chevron-right family-arrow"></i>
276
+ </button>
277
+ {/each}
278
+ </div>
279
+ {:else}
280
+ <button class="dropdown-back" on:click={backToFamilies}>
281
+ <i class="fas fa-chevron-left"></i>
282
+ <span>{families.find(f => f.name === selectedFamily)?.label}</span>
283
+ </button>
284
+
285
+ <div class="tab-bar">
286
+ {#each availableTabs as tab}
287
+ <button
288
+ class="tab-btn"
289
+ class:selected={selectedTab === tab.id}
290
+ class:assigned={chosenCategory === tab.id && chosenFamily === selectedFamily}
291
+ on:click={() => selectedTab = tab.id}
292
+ >{tab.label}</button>
293
+ {/each}
294
+ </div>
295
+
296
+ {#if selectedTab === 'palette'}
297
+ <div class="step-grid">
298
+ {#each paletteSteps as step}
299
+ <button
300
+ class="step-item"
301
+ class:active={chosenCategory === 'palette' && chosenFamily === selectedFamily && chosenStep === step}
302
+ on:click={() => selectSwatch('palette', step)}
303
+ >
304
+ <div class="step-swatch" style="background: var(--color-{selectedFamily}-{step});"></div>
305
+ <span class="step-label">{step}</span>
306
+ </button>
307
+ {/each}
308
+ </div>
309
+ {:else if selectedTab === 'surface'}
310
+ <div class="step-grid">
311
+ {#each surfaceSteps as step}
312
+ <button
313
+ class="step-item"
314
+ class:active={chosenCategory === 'surface' && chosenFamily === selectedFamily && chosenStep === step.key}
315
+ on:click={() => selectSwatch('surface', step.key)}
316
+ >
317
+ <div class="step-swatch" style="background: var({getVarName('surface', selectedFamily, step.key)});"></div>
318
+ <span class="step-label">{step.label}</span>
319
+ </button>
320
+ {/each}
321
+ </div>
322
+ {:else if selectedTab === 'border'}
323
+ <div class="step-grid">
324
+ {#each borderSteps as step}
325
+ <button
326
+ class="step-item"
327
+ class:active={chosenCategory === 'border' && chosenFamily === selectedFamily && chosenStep === step.key}
328
+ on:click={() => selectSwatch('border', step.key)}
329
+ >
330
+ <div class="step-swatch" style="background: var({getVarName('border', selectedFamily, step.key)});"></div>
331
+ <span class="step-label">{step.label}</span>
332
+ </button>
333
+ {/each}
334
+ </div>
335
+ {:else if selectedTab === 'text'}
336
+ <div class="step-grid">
337
+ {#each textSteps as step}
338
+ <button
339
+ class="step-item"
340
+ class:active={chosenCategory === 'text' && chosenFamily === selectedFamily && chosenStep === step.key}
341
+ on:click={() => selectSwatch('text', step.key)}
342
+ >
343
+ <div class="step-swatch" style="background: var({getVarName('text', selectedFamily, step.key)});"></div>
344
+ <span class="step-label">{step.label}</span>
345
+ </button>
346
+ {/each}
347
+ </div>
348
+ {/if}
349
+ {/if}
350
+ </div>
351
+ {/if}
352
+ </div>
353
+
354
+ <style>
355
+ .palette-selector {
356
+ position: relative;
357
+ }
358
+
359
+ .selector-trigger {
360
+ display: flex;
361
+ align-items: center;
362
+ gap: var(--space-8);
363
+ padding: var(--space-6) var(--space-10);
364
+ background: var(--ui-surface-low);
365
+ border: 1px solid var(--ui-border-default);
366
+ border-radius: var(--radius-md);
367
+ cursor: pointer;
368
+ transition: all var(--transition-fast);
369
+ min-width: 10rem;
370
+ }
371
+
372
+ .selector-trigger:hover {
373
+ border-color: var(--ui-border-strong);
374
+ background: var(--ui-surface-high);
375
+ }
376
+
377
+ .trigger-swatch {
378
+ width: 1.5rem;
379
+ height: 1.5rem;
380
+ border-radius: var(--radius-sm);
381
+ border: 1px solid var(--ui-border-faint);
382
+ flex-shrink: 0;
383
+ }
384
+
385
+ .trigger-text {
386
+ display: flex;
387
+ flex-direction: column;
388
+ gap: 1px;
389
+ flex: 1;
390
+ text-align: left;
391
+ }
392
+
393
+ .trigger-label {
394
+ font-size: var(--font-xs);
395
+ color: var(--ui-text-secondary);
396
+ font-weight: var(--font-weight-medium);
397
+ }
398
+
399
+ .trigger-value {
400
+ font-size: 0.625rem;
401
+ color: var(--ui-text-muted);
402
+ font-family: var(--ui-font-mono);
403
+ }
404
+
405
+ .trigger-chevron {
406
+ font-size: 0.5rem;
407
+ color: var(--ui-text-muted);
408
+ transition: transform var(--transition-fast);
409
+ }
410
+
411
+ .trigger-chevron.open {
412
+ transform: rotate(180deg);
413
+ }
414
+
415
+ .selector-dropdown {
416
+ position: absolute;
417
+ top: calc(100% + var(--space-4));
418
+ left: 0;
419
+ min-width: 14rem;
420
+ max-width: calc(100vw - 2rem);
421
+ background: var(--ui-surface-higher);
422
+ border: 1px solid var(--ui-border-medium);
423
+ border-radius: var(--radius-md);
424
+ box-shadow: var(--shadow-lg);
425
+ z-index: 10;
426
+ overflow: hidden;
427
+ }
428
+
429
+ .dropdown-header {
430
+ display: flex;
431
+ align-items: center;
432
+ gap: var(--space-6);
433
+ padding: var(--space-6) var(--space-8);
434
+ border-bottom: 1px solid var(--ui-border-faint);
435
+ }
436
+
437
+ .variable-name {
438
+ flex: 1;
439
+ font-size: 0.625rem;
440
+ color: var(--ui-text-muted);
441
+ font-family: var(--ui-font-mono);
442
+ overflow: hidden;
443
+ text-overflow: ellipsis;
444
+ white-space: nowrap;
445
+ }
446
+
447
+ .reset-btn {
448
+ display: flex;
449
+ align-items: center;
450
+ justify-content: center;
451
+ width: 1.5rem;
452
+ height: 1.5rem;
453
+ padding: 0;
454
+ background: none;
455
+ border: 1px solid var(--ui-border-default);
456
+ border-radius: var(--radius-sm);
457
+ color: var(--ui-text-secondary);
458
+ font-size: 0.625rem;
459
+ cursor: pointer;
460
+ flex-shrink: 0;
461
+ transition: all var(--transition-fast);
462
+ }
463
+
464
+ .reset-btn:hover {
465
+ background: var(--ui-hover);
466
+ border-color: var(--ui-border-strong);
467
+ color: var(--ui-text-primary);
468
+ }
469
+
470
+ .dropdown-back {
471
+ display: flex;
472
+ align-items: center;
473
+ gap: var(--space-6);
474
+ width: 100%;
475
+ padding: var(--space-8) var(--space-10);
476
+ background: none;
477
+ border: none;
478
+ border-bottom: 1px solid var(--ui-border-faint);
479
+ color: var(--ui-text-secondary);
480
+ font-size: var(--font-sm);
481
+ font-weight: var(--font-weight-medium);
482
+ cursor: pointer;
483
+ transition: background var(--transition-fast);
484
+ }
485
+
486
+ .dropdown-back:hover {
487
+ background: var(--ui-hover);
488
+ }
489
+
490
+ .dropdown-back i {
491
+ font-size: 0.5rem;
492
+ }
493
+
494
+ .tab-bar {
495
+ display: flex;
496
+ border-bottom: 1px solid var(--ui-border-faint);
497
+ }
498
+
499
+ .tab-btn {
500
+ flex: 1;
501
+ padding: var(--space-4) var(--space-4);
502
+ background: none;
503
+ border: 1px solid transparent;
504
+ border-radius: 0;
505
+ color: var(--ui-text-muted);
506
+ font-size: 0.625rem;
507
+ font-weight: var(--font-weight-medium);
508
+ cursor: pointer;
509
+ transition: all var(--transition-fast);
510
+ text-align: center;
511
+ }
512
+
513
+ .tab-btn:hover {
514
+ color: var(--ui-text-secondary);
515
+ background: var(--ui-hover);
516
+ }
517
+
518
+ .tab-btn.assigned {
519
+ border-color: var(--ui-border-default);
520
+ }
521
+
522
+ .tab-btn.selected {
523
+ color: var(--ui-text-primary);
524
+ box-shadow: inset 0 -2px 0 var(--ui-text-accent);
525
+ background: var(--ui-hover);
526
+ }
527
+
528
+ .family-list {
529
+ display: flex;
530
+ flex-direction: column;
531
+ max-height: 20rem;
532
+ overflow-y: auto;
533
+ }
534
+
535
+ .family-item {
536
+ display: flex;
537
+ align-items: center;
538
+ gap: var(--space-8);
539
+ width: 100%;
540
+ padding: var(--space-6) var(--space-10);
541
+ background: none;
542
+ border: none;
543
+ cursor: pointer;
544
+ transition: background var(--transition-fast);
545
+ }
546
+
547
+ .family-item:hover {
548
+ background: var(--ui-hover);
549
+ }
550
+
551
+ .family-item.active {
552
+ background: var(--ui-hover-high);
553
+ box-shadow: inset 3px 0 0 var(--ui-text-accent);
554
+ }
555
+
556
+ .family-item.active .family-label {
557
+ color: var(--ui-text-accent);
558
+ }
559
+
560
+ .family-swatches {
561
+ display: flex;
562
+ gap: 2px;
563
+ }
564
+
565
+ .mini-swatch {
566
+ width: 0.75rem;
567
+ height: 0.75rem;
568
+ border-radius: 2px;
569
+ }
570
+
571
+ .family-label {
572
+ flex: 1;
573
+ font-size: var(--font-sm);
574
+ color: var(--ui-text-primary);
575
+ text-align: left;
576
+ }
577
+
578
+ .family-arrow {
579
+ font-size: 0.5rem;
580
+ color: var(--ui-text-muted);
581
+ }
582
+
583
+ .step-grid {
584
+ display: grid;
585
+ grid-template-columns: repeat(4, 1fr);
586
+ gap: var(--space-4);
587
+ padding: var(--space-8);
588
+ }
589
+
590
+ .step-item {
591
+ display: flex;
592
+ flex-direction: column;
593
+ align-items: center;
594
+ gap: var(--space-2);
595
+ padding: var(--space-4);
596
+ background: none;
597
+ border: 1px solid transparent;
598
+ border-radius: var(--radius-sm);
599
+ cursor: pointer;
600
+ transition: all var(--transition-fast);
601
+ }
602
+
603
+ .step-item:hover {
604
+ background: var(--ui-hover);
605
+ border-color: var(--ui-border-default);
606
+ }
607
+
608
+ .step-item.active {
609
+ border-color: var(--ui-text-accent);
610
+ border-width: 2px;
611
+ background: var(--ui-hover-high);
612
+ padding: 3px;
613
+ }
614
+
615
+ .step-swatch {
616
+ width: 2rem;
617
+ height: 1.5rem;
618
+ border-radius: var(--radius-sm);
619
+ border: 1px solid var(--ui-border-faint);
620
+ }
621
+
622
+ .step-label {
623
+ font-size: 0.5625rem;
624
+ color: var(--ui-text-tertiary);
625
+ font-family: var(--ui-font-mono);
626
+ }
627
+ </style>