@motion-proto/live-tokens 0.3.9 → 0.5.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 (103) hide show
  1. package/package.json +9 -8
  2. package/src/component-editor/BadgeEditor.svelte +24 -22
  3. package/src/component-editor/CalloutEditor.svelte +3 -3
  4. package/src/component-editor/CardEditor.svelte +25 -21
  5. package/src/component-editor/CollapsibleSectionEditor.svelte +27 -25
  6. package/src/component-editor/CornerBadgeEditor.svelte +37 -35
  7. package/src/component-editor/DialogEditor.svelte +26 -24
  8. package/src/component-editor/ImageEditor.svelte +11 -9
  9. package/src/component-editor/InlineEditActionsEditor.svelte +17 -15
  10. package/src/component-editor/NotificationEditor.svelte +32 -30
  11. package/src/component-editor/ProgressBarEditor.svelte +3 -3
  12. package/src/component-editor/RadioButtonEditor.svelte +31 -29
  13. package/src/component-editor/SectionDividerEditor.svelte +30 -28
  14. package/src/component-editor/SegmentedControlEditor.svelte +29 -25
  15. package/src/component-editor/StandardButtonsEditor.svelte +42 -38
  16. package/src/component-editor/TabBarEditor.svelte +20 -18
  17. package/src/component-editor/TableEditor.svelte +4 -4
  18. package/src/component-editor/TooltipEditor.svelte +11 -9
  19. package/src/component-editor/registry.ts +2 -2
  20. package/src/component-editor/scaffolding/AngleDial.svelte +20 -19
  21. package/src/component-editor/scaffolding/ComponentEditorBase.svelte +44 -20
  22. package/src/component-editor/scaffolding/ComponentFileManager.svelte +260 -37
  23. package/src/component-editor/scaffolding/ComponentFileMenu.svelte +41 -29
  24. package/src/component-editor/scaffolding/ComponentsTab.svelte +7 -3
  25. package/src/component-editor/scaffolding/CopyFromMenu.svelte +21 -12
  26. package/src/component-editor/scaffolding/DemoHeader.svelte +13 -4
  27. package/src/component-editor/scaffolding/DividerEditor.svelte +27 -14
  28. package/src/component-editor/scaffolding/FieldsetWrapper.svelte +10 -4
  29. package/src/component-editor/scaffolding/GradientCard.svelte +25 -20
  30. package/src/component-editor/scaffolding/LinkageChart.svelte +43 -34
  31. package/src/component-editor/scaffolding/LinkedBlock.svelte +24 -21
  32. package/src/component-editor/scaffolding/NonStylableConfig.svelte +6 -1
  33. package/src/component-editor/scaffolding/SaveAsDialog.svelte +39 -35
  34. package/src/component-editor/scaffolding/ShadowBackdrop.svelte +21 -9
  35. package/src/component-editor/scaffolding/ShadowBackdropControls.svelte +8 -3
  36. package/src/component-editor/scaffolding/StateBlock.svelte +30 -13
  37. package/src/component-editor/scaffolding/TokenLayout.svelte +46 -30
  38. package/src/component-editor/scaffolding/TypeEditor.svelte +52 -26
  39. package/src/component-editor/scaffolding/VariantGroup.svelte +81 -48
  40. package/src/component-editor/scaffolding/componentSectionType.ts +2 -2
  41. package/src/components/Badge.svelte +45 -26
  42. package/src/components/Button.svelte +44 -21
  43. package/src/components/Callout.svelte +17 -12
  44. package/src/components/Card.svelte +23 -11
  45. package/src/components/CollapsibleSection.svelte +56 -27
  46. package/src/components/CornerBadge.svelte +32 -18
  47. package/src/components/Dialog.svelte +55 -31
  48. package/src/components/Image.svelte +14 -5
  49. package/src/components/InlineEditActions.svelte +22 -10
  50. package/src/components/Notification.svelte +39 -19
  51. package/src/components/ProgressBar.svelte +27 -17
  52. package/src/components/RadioButton.svelte +27 -10
  53. package/src/components/SectionDivider.svelte +34 -26
  54. package/src/components/SegmentedControl.svelte +23 -9
  55. package/src/components/TabBar.svelte +23 -10
  56. package/src/components/Table.svelte +8 -3
  57. package/src/components/Tooltip.svelte +15 -5
  58. package/src/lib/ColumnsOverlay.svelte +3 -3
  59. package/src/lib/LiveEditorOverlay.svelte +57 -36
  60. package/src/pages/ComponentEditorPage.svelte +17 -13
  61. package/src/pages/EditorShell.svelte +24 -20
  62. package/src/styles/form-controls.css +2 -2
  63. package/src/styles/tokens.css +59 -81
  64. package/src/ui/BezierCurveEditor.svelte +59 -43
  65. package/src/ui/ColorEditPanel.svelte +71 -44
  66. package/src/ui/EditorViewSwitcher.svelte +9 -5
  67. package/src/ui/FontStackEditor.svelte +16 -15
  68. package/src/ui/GradientEditor.svelte +42 -33
  69. package/src/ui/GradientStopPicker.svelte +18 -29
  70. package/src/ui/PaletteEditor.svelte +238 -212
  71. package/src/ui/PresetFileManager.svelte +20 -18
  72. package/src/ui/ProjectFontsSection.svelte +30 -30
  73. package/src/ui/SurfacesTab.svelte +3 -3
  74. package/src/ui/TextTab.svelte +2 -2
  75. package/src/ui/ThemeFileManager.svelte +38 -35
  76. package/src/ui/Toggle.svelte +11 -9
  77. package/src/ui/UICopyPopover.svelte +19 -15
  78. package/src/ui/UIDialog.svelte +48 -30
  79. package/src/ui/UIFontFamilySelector.svelte +104 -78
  80. package/src/ui/UIFontSizeSelector.svelte +38 -20
  81. package/src/ui/UIFontWeightSelector.svelte +33 -13
  82. package/src/ui/UILineHeightSelector.svelte +33 -13
  83. package/src/ui/UILinkToggle.svelte +7 -6
  84. package/src/ui/UIOptionItem.svelte +21 -7
  85. package/src/ui/UIOptionList.svelte +9 -3
  86. package/src/ui/UIPaddingSelector.svelte +108 -82
  87. package/src/ui/UIPaletteSelector.svelte +186 -161
  88. package/src/ui/UIRadio.svelte +23 -8
  89. package/src/ui/UIRadioGroup.svelte +9 -8
  90. package/src/ui/UIRelinkConfirmPopover.svelte +26 -16
  91. package/src/ui/UITokenSelector.svelte +112 -68
  92. package/src/ui/UIVariantSelector.svelte +79 -57
  93. package/src/ui/VariablesTab.svelte +15 -15
  94. package/src/ui/palette/GradientStopEditor.svelte +45 -26
  95. package/src/ui/palette/OverridesPanel.svelte +85 -49
  96. package/src/ui/palette/PaletteBase.svelte +60 -32
  97. package/src/ui/palette/ScaleCurveEditor.svelte +25 -10
  98. package/src/ui/sections/ColumnsSection.svelte +13 -13
  99. package/src/ui/sections/GradientsSection.svelte +12 -9
  100. package/src/ui/sections/OverlaysSection.svelte +50 -47
  101. package/src/ui/sections/ShadowsSection.svelte +110 -104
  102. package/src/ui/sections/TokenScaleTable.svelte +38 -22
  103. package/src/ui/sections/tokenScales.ts +2 -2
@@ -1,5 +1,6 @@
1
1
  <script lang="ts">
2
- import { createEventDispatcher } from 'svelte';
2
+ import { run } from 'svelte/legacy';
3
+
3
4
  import { slide } from 'svelte/transition';
4
5
  import { cubicOut, cubicIn } from 'svelte/easing';
5
6
  import { resolveAliasChain } from '../lib/tokenRegistry';
@@ -29,13 +30,23 @@
29
30
  return name.endsWith('-surface') || name.endsWith('-fill');
30
31
  }
31
32
 
32
- const dispatch = createEventDispatcher();
33
+ interface Props {
34
+ variable: string;
35
+ component?: string | undefined;
36
+ canBeLinked?: boolean;
37
+ disabled?: boolean;
38
+ selectionsLocked?: boolean;
39
+ onchange?: () => void;
40
+ }
33
41
 
34
- export let variable: string;
35
- export let component: string | undefined = undefined;
36
- export let canBeLinked: boolean = false;
37
- export let disabled: boolean = false;
38
- export let selectionsLocked: boolean = false;
42
+ let {
43
+ variable,
44
+ component = undefined,
45
+ canBeLinked = false,
46
+ disabled = false,
47
+ selectionsLocked = false,
48
+ onchange,
49
+ }: Props = $props();
39
50
 
40
51
  type Category = 'palette' | 'surface' | 'border' | 'text';
41
52
 
@@ -95,9 +106,9 @@
95
106
  { id: 'text', label: 'Text' },
96
107
  ];
97
108
 
98
- let selector: UITokenSelector;
99
- let selectedFamily: string | null = null;
100
- let selectedTab: Category = 'palette';
109
+ let selector: UITokenSelector | undefined = $state();
110
+ let selectedFamily: string | null = $state(null);
111
+ let selectedTab: Category = $state('palette');
101
112
 
102
113
  /** Compass-rose layout for the orientation grid. Angles follow the CSS
103
114
  * linear-gradient convention (0° points up, 90° points right). */
@@ -114,24 +125,24 @@
114
125
 
115
126
  /** Numeric input value. Kept in sync with chosenAngle/token-default by the
116
127
  * reactive block below; user edits flow back through `applyOrientation`. */
117
- let angleInput: number = 0;
128
+ let angleInput: number = $state(0);
118
129
 
119
- let chosenCategory: Category | null = null;
120
- let chosenFamily: string | null = null;
121
- let chosenStep: string | null = null;
122
- let chosenNone: boolean = false;
123
- let chosenGradient: string | null = null;
130
+ let chosenCategory = $state<Category | null>(null);
131
+ let chosenFamily = $state<string | null>(null);
132
+ let chosenStep = $state<string | null>(null);
133
+ let chosenNone = $state(false);
134
+ let chosenGradient = $state<string | null>(null);
124
135
  /** Per-slot angle override on the chosen linear gradient. Null means
125
136
  * "no override" — the slot writes `var(--gradient-N)` and inherits the
126
137
  * token's natural angle. Non-null means the slot writes a materialized
127
138
  * `linear-gradient(<angle>, <token's stops>)` so the angle is locally
128
139
  * pinned while stop colors keep flowing from the token's `var()` refs. */
129
- let chosenAngle: number | null = null;
130
- let opacity: number = 100;
140
+ let chosenAngle = $state<number | null>(null);
141
+ let opacity: number = $state(100);
131
142
  let selfDefaultHex: string = '';
132
143
 
133
- $: gradientsAllowed = acceptsGradient(variable);
134
- $: gradientTokens = $editorState.gradients.tokens;
144
+ let gradientsAllowed = $derived(acceptsGradient(variable));
145
+ let gradientTokens = $derived($editorState.gradients.tokens);
135
146
 
136
147
  function captureSelfDefault() {
137
148
  const root = document.documentElement;
@@ -236,8 +247,8 @@
236
247
  opacity = Math.max(0, Math.min(100, Math.round(opacity)));
237
248
  if (chosenCategory === null || chosenFamily === null || chosenStep === null) return;
238
249
  const varName = getVarName(chosenCategory, chosenFamily, chosenStep);
239
- selector.writeOverride(buildValue(varName));
240
- dispatch('change');
250
+ selector?.writeOverride(buildValue(varName));
251
+ onchange?.();
241
252
  }
242
253
 
243
254
  /** Apply (or clear) a per-slot angle override on the chosen linear gradient.
@@ -251,18 +262,18 @@
251
262
  const normalized = ((Math.round(nextAngle) % 360) + 360) % 360;
252
263
  chosenAngle = normalized;
253
264
  if (normalized === token.angle) {
254
- selector.writeOverride(chosenGradient);
265
+ selector?.writeOverride(chosenGradient);
255
266
  } else {
256
- selector.writeOverride(materializeGradient(token, normalized));
267
+ selector?.writeOverride(materializeGradient(token, normalized));
257
268
  }
258
- dispatch('change');
269
+ onchange?.();
259
270
  }
260
271
 
261
272
  function resetOrientation() {
262
273
  if (chosenGradient === null) return;
263
274
  chosenAngle = null;
264
- selector.writeOverride(chosenGradient);
265
- dispatch('change');
275
+ selector?.writeOverride(chosenGradient);
276
+ onchange?.();
266
277
  }
267
278
 
268
279
  function isGradientToken(name: string): boolean {
@@ -386,7 +397,7 @@
386
397
  chosenNone = false;
387
398
  initFromCurrent();
388
399
  selectedFamily = null;
389
- dispatch('change');
400
+ onchange?.();
390
401
  }
391
402
 
392
403
  function handleClose() {
@@ -412,10 +423,10 @@
412
423
  chosenGradient = null;
413
424
  chosenAngle = null;
414
425
  opacity = 100;
415
- selector.writeOverride('transparent');
426
+ selector?.writeOverride('transparent');
416
427
  selectedFamily = null;
417
428
  close();
418
- dispatch('change');
429
+ onchange?.();
419
430
  }
420
431
 
421
432
  function selectSwatch(category: Category, step: string, close: () => void) {
@@ -426,10 +437,10 @@
426
437
  chosenCategory = category;
427
438
  chosenFamily = selectedFamily;
428
439
  chosenStep = step;
429
- selector.writeOverride(buildValue(varName));
440
+ selector?.writeOverride(buildValue(varName));
430
441
  selectedFamily = null;
431
442
  close();
432
- dispatch('change');
443
+ onchange?.();
433
444
  }
434
445
 
435
446
  // Picking any gradient is a fresh start: any prior angle override is
@@ -444,10 +455,10 @@
444
455
  chosenGradient = gradientVar;
445
456
  chosenAngle = null;
446
457
  opacity = 100;
447
- selector.writeOverride(gradientVar);
458
+ selector?.writeOverride(gradientVar);
448
459
  selectedFamily = null;
449
460
  close();
450
- dispatch('change');
461
+ onchange?.();
451
462
  }
452
463
 
453
464
  // Re-derive trigger state when the bound `variable` changes (e.g. when a
@@ -455,35 +466,41 @@
455
466
  // The wrapper UITokenSelector forwards `var-change` only for the currently
456
467
  // bound variable, so prop swaps wouldn't otherwise refresh `chosenCategory`
457
468
  // / `chosenFamily` / `chosenStep` and the meta label drifts from the swatch.
458
- let lastSeenVariable: string | null = null;
459
- $: if (variable !== lastSeenVariable) {
460
- lastSeenVariable = variable;
461
- initFromCurrent();
462
- captureSelfDefault();
463
- }
469
+ let lastSeenVariable: string | null = $state(null);
470
+ run(() => {
471
+ if (variable !== lastSeenVariable) {
472
+ lastSeenVariable = variable;
473
+ initFromCurrent();
474
+ captureSelfDefault();
475
+ }
476
+ });
464
477
 
465
- $: chosenGradientToken = chosenGradient ? getGradientToken(chosenGradient) : undefined;
466
- $: isLinearGradientChosen = !!chosenGradientToken && chosenGradientToken.type === 'linear';
467
- $: effectiveAngle = chosenGradientToken
478
+ let chosenGradientToken = $derived(chosenGradient ? getGradientToken(chosenGradient) : undefined);
479
+ let isLinearGradientChosen = $derived(!!chosenGradientToken && chosenGradientToken.type === 'linear');
480
+ let effectiveAngle = $derived(chosenGradientToken
468
481
  ? (chosenAngle ?? chosenGradientToken.angle)
469
- : 0;
470
- $: angleInput = effectiveAngle;
482
+ : 0);
483
+ run(() => {
484
+ angleInput = effectiveAngle;
485
+ });
471
486
 
472
- $: triggerMeta = chosenNone
487
+ let metaLabel = $derived(chosenNone
473
488
  ? 'none'
474
489
  : chosenGradient
475
490
  ? chosenGradient.replace(/^--/, '') + (chosenAngle !== null ? ` (${effectiveAngle}°)` : '')
476
491
  : (chosenCategory && chosenFamily && chosenStep !== null
477
492
  ? getVarName(chosenCategory, chosenFamily, chosenStep).replace(/^--/, '') + (opacity < 100 ? ` (${opacity}%)` : '')
478
- : '');
493
+ : ''));
479
494
 
480
- $: availableTabs = selectedFamily
495
+ let availableTabs = $derived(selectedFamily
481
496
  ? allCategories.filter(c => c.id !== 'text' || familiesWithText.includes(selectedFamily!))
482
- : allCategories;
497
+ : allCategories);
483
498
 
484
- $: if (selectedFamily && !availableTabs.find(t => t.id === selectedTab)) {
485
- selectedTab = 'palette';
486
- }
499
+ run(() => {
500
+ if (selectedFamily && !availableTabs.find(t => t.id === selectedTab)) {
501
+ selectedTab = 'palette';
502
+ }
503
+ });
487
504
  </script>
488
505
 
489
506
  <UITokenSelector
@@ -496,125 +513,131 @@
496
513
  dropdownMinWidth="14rem"
497
514
  dropdownMaxWidth="calc(100vw - 2rem)"
498
515
  hideDefaultHeader={!!selectedFamily}
499
- on:reset={handleReset}
500
- on:close={handleClose}
501
- on:var-change={initFromCurrent}
516
+ onreset={handleReset}
517
+ onclose={handleClose}
518
+ onvarChange={initFromCurrent}
502
519
  >
503
- <div slot="trigger-preview" class="swatch-wrap">
504
- <div class="swatch" style="background: var({variable});"></div>
505
- </div>
506
- <div slot="subheader" class="opacity-control" class:hidden={chosenGradient !== null}>
507
- <span class="opacity-label">opacity</span>
508
- <input type="range" min="0" max="100" bind:value={opacity} class="opacity-slider" on:input={applyOpacity} />
509
- <input type="number" min="0" max="100" bind:value={opacity} class="opacity-input" on:change={applyOpacity} />
510
- <span class="opacity-unit">%</span>
511
- </div>
512
- <svelte:fragment slot="trigger-meta">{triggerMeta}</svelte:fragment>
513
-
514
- <svelte:fragment let:close>
515
- {#if selectedFamily === null}
516
- <div class="family-list">
517
- <button class="family-item" class:active={chosenNone} on:click={() => selectNone(close)}>
518
- <div class="family-swatches">
519
- <div class="none-swatch"></div>
520
- </div>
521
- <span class="family-label">None</span>
522
- </button>
523
- {#each families as fam}
524
- <button class="family-item" class:active={!chosenNone && chosenFamily === fam.name} on:click={() => selectFamily(fam.name)}>
520
+ {#snippet triggerPreview()}
521
+ <div class="swatch-wrap">
522
+ <div class="swatch" style="background: var({variable});"></div>
523
+ </div>
524
+ {/snippet}
525
+ {#snippet subheader()}
526
+ <div class="opacity-control" class:hidden={chosenGradient !== null}>
527
+ <span class="opacity-label">opacity</span>
528
+ <input type="range" min="0" max="100" bind:value={opacity} class="opacity-slider" oninput={applyOpacity} />
529
+ <input type="number" min="0" max="100" bind:value={opacity} class="opacity-input" onchange={applyOpacity} />
530
+ <span class="opacity-unit">%</span>
531
+ </div>
532
+ {/snippet}
533
+ {#snippet triggerMeta()}{metaLabel}{/snippet}
534
+
535
+ {#snippet children({ close })}
536
+
537
+ {#if selectedFamily === null}
538
+ <div class="family-list">
539
+ <button class="family-item" class:active={chosenNone} onclick={() => selectNone(close)}>
525
540
  <div class="family-swatches">
526
- <div class="mini-swatch" style="background: var(--color-{fam.name}-300);"></div>
527
- <div class="mini-swatch" style="background: var(--color-{fam.name}-500);"></div>
528
- <div class="mini-swatch" style="background: var(--color-{fam.name}-700);"></div>
541
+ <div class="none-swatch"></div>
529
542
  </div>
530
- <span class="family-label">{fam.label}</span>
531
- <i class="fas fa-chevron-right family-arrow"></i>
543
+ <span class="family-label">None</span>
532
544
  </button>
533
- {/each}
534
- {#if gradientsAllowed && gradientTokens.length > 0}
535
- <div class="family-divider">Gradients</div>
536
- {#each gradientTokens as g}
537
- <button class="family-item" class:active={chosenGradient === g.variable} on:click={() => selectGradient(g.variable, close)}>
545
+ {#each families as fam}
546
+ <button class="family-item" class:active={!chosenNone && chosenFamily === fam.name} onclick={() => selectFamily(fam.name)}>
538
547
  <div class="family-swatches">
539
- <div class="gradient-swatch" style="background: var({g.variable});"></div>
548
+ <div class="mini-swatch" style="background: var(--color-{fam.name}-300);"></div>
549
+ <div class="mini-swatch" style="background: var(--color-{fam.name}-500);"></div>
550
+ <div class="mini-swatch" style="background: var(--color-{fam.name}-700);"></div>
540
551
  </div>
541
- <span class="family-label">Gradient {g.variable.replace(/^--gradient-/, '')}</span>
542
- </button>
543
- {/each}
544
- {/if}
545
- </div>
546
- {:else}
547
- <button class="dropdown-back" on:click={backToFamilies}>
548
- <i class="fas fa-chevron-left"></i>
549
- <span>{families.find(f => f.name === selectedFamily)?.label}</span>
550
- </button>
551
-
552
- <div class="tab-bar">
553
- {#each availableTabs as tab}
554
- <button
555
- class="tab-btn"
556
- class:selected={selectedTab === tab.id}
557
- class:assigned={chosenCategory === tab.id && chosenFamily === selectedFamily}
558
- on:click={() => selectedTab = tab.id}
559
- >{tab.label}</button>
560
- {/each}
561
- </div>
562
-
563
- {#if selectedTab === 'palette'}
564
- <div class="step-grid">
565
- {#each paletteSteps as step}
566
- <button
567
- class="step-item"
568
- class:active={chosenCategory === 'palette' && chosenFamily === selectedFamily && chosenStep === step}
569
- on:click={() => selectSwatch('palette', step, close)}
570
- >
571
- <div class="step-swatch" style="background: var(--color-{selectedFamily}-{step});"></div>
572
- <span class="step-label">{step}</span>
552
+ <span class="family-label">{fam.label}</span>
553
+ <i class="fas fa-chevron-right family-arrow"></i>
573
554
  </button>
574
555
  {/each}
556
+ {#if gradientsAllowed && gradientTokens.length > 0}
557
+ <div class="family-divider">Gradients</div>
558
+ {#each gradientTokens as g}
559
+ <button class="family-item" class:active={chosenGradient === g.variable} onclick={() => selectGradient(g.variable, close)}>
560
+ <div class="family-swatches">
561
+ <div class="gradient-swatch" style="background: var({g.variable});"></div>
562
+ </div>
563
+ <span class="family-label">Gradient {g.variable.replace(/^--gradient-/, '')}</span>
564
+ </button>
565
+ {/each}
566
+ {/if}
575
567
  </div>
576
- {:else if selectedTab === 'surface'}
577
- <div class="step-grid">
578
- {#each surfaceSteps as step}
579
- <button
580
- class="step-item"
581
- class:active={chosenCategory === 'surface' && chosenFamily === selectedFamily && chosenStep === step.key}
582
- on:click={() => selectSwatch('surface', step.key, close)}
583
- >
584
- <div class="step-swatch" style="background: {previewBg('surface', selectedFamily, step.key)};"></div>
585
- <span class="step-label">{step.label}</span>
586
- </button>
587
- {/each}
588
- </div>
589
- {:else if selectedTab === 'border'}
590
- <div class="step-grid">
591
- {#each borderSteps as step}
592
- <button
593
- class="step-item"
594
- class:active={chosenCategory === 'border' && chosenFamily === selectedFamily && chosenStep === step.key}
595
- on:click={() => selectSwatch('border', step.key, close)}
596
- >
597
- <div class="step-swatch" style="background: {previewBg('border', selectedFamily, step.key)};"></div>
598
- <span class="step-label">{step.label}</span>
599
- </button>
600
- {/each}
601
- </div>
602
- {:else if selectedTab === 'text'}
603
- <div class="step-grid">
604
- {#each textSteps as step}
568
+ {:else}
569
+ <button class="dropdown-back" onclick={backToFamilies}>
570
+ <i class="fas fa-chevron-left"></i>
571
+ <span>{families.find(f => f.name === selectedFamily)?.label}</span>
572
+ </button>
573
+
574
+ <div class="tab-bar">
575
+ {#each availableTabs as tab}
605
576
  <button
606
- class="step-item"
607
- class:active={chosenCategory === 'text' && chosenFamily === selectedFamily && chosenStep === step.key}
608
- on:click={() => selectSwatch('text', step.key, close)}
609
- >
610
- <div class="step-swatch" style="background: {previewBg('text', selectedFamily, step.key)};"></div>
611
- <span class="step-label">{step.label}</span>
612
- </button>
577
+ class="tab-btn"
578
+ class:selected={selectedTab === tab.id}
579
+ class:assigned={chosenCategory === tab.id && chosenFamily === selectedFamily}
580
+ onclick={() => selectedTab = tab.id}
581
+ >{tab.label}</button>
613
582
  {/each}
614
583
  </div>
584
+
585
+ {#if selectedTab === 'palette'}
586
+ <div class="step-grid">
587
+ {#each paletteSteps as step}
588
+ <button
589
+ class="step-item"
590
+ class:active={chosenCategory === 'palette' && chosenFamily === selectedFamily && chosenStep === step}
591
+ onclick={() => selectSwatch('palette', step, close)}
592
+ >
593
+ <div class="step-swatch" style="background: var(--color-{selectedFamily}-{step});"></div>
594
+ <span class="step-label">{step}</span>
595
+ </button>
596
+ {/each}
597
+ </div>
598
+ {:else if selectedTab === 'surface'}
599
+ <div class="step-grid">
600
+ {#each surfaceSteps as step}
601
+ <button
602
+ class="step-item"
603
+ class:active={chosenCategory === 'surface' && chosenFamily === selectedFamily && chosenStep === step.key}
604
+ onclick={() => selectSwatch('surface', step.key, close)}
605
+ >
606
+ <div class="step-swatch" style="background: {previewBg('surface', selectedFamily, step.key)};"></div>
607
+ <span class="step-label">{step.label}</span>
608
+ </button>
609
+ {/each}
610
+ </div>
611
+ {:else if selectedTab === 'border'}
612
+ <div class="step-grid">
613
+ {#each borderSteps as step}
614
+ <button
615
+ class="step-item"
616
+ class:active={chosenCategory === 'border' && chosenFamily === selectedFamily && chosenStep === step.key}
617
+ onclick={() => selectSwatch('border', step.key, close)}
618
+ >
619
+ <div class="step-swatch" style="background: {previewBg('border', selectedFamily, step.key)};"></div>
620
+ <span class="step-label">{step.label}</span>
621
+ </button>
622
+ {/each}
623
+ </div>
624
+ {:else if selectedTab === 'text'}
625
+ <div class="step-grid">
626
+ {#each textSteps as step}
627
+ <button
628
+ class="step-item"
629
+ class:active={chosenCategory === 'text' && chosenFamily === selectedFamily && chosenStep === step.key}
630
+ onclick={() => selectSwatch('text', step.key, close)}
631
+ >
632
+ <div class="step-swatch" style="background: {previewBg('text', selectedFamily, step.key)};"></div>
633
+ <span class="step-label">{step.label}</span>
634
+ </button>
635
+ {/each}
636
+ </div>
637
+ {/if}
615
638
  {/if}
616
- {/if}
617
- </svelte:fragment>
639
+
640
+ {/snippet}
618
641
  </UITokenSelector>
619
642
 
620
643
  <!--
@@ -640,7 +663,7 @@
640
663
  class="dir-btn"
641
664
  class:active={d.angle === ((effectiveAngle % 360) + 360) % 360}
642
665
  style="grid-column: {d.col}; grid-row: {d.row};"
643
- on:click={() => applyOrientation(d.angle)}
666
+ onclick={() => applyOrientation(d.angle)}
644
667
  title="{d.label} ({d.angle}°)"
645
668
  >{d.glyph}</button>
646
669
  {/each}
@@ -652,7 +675,7 @@
652
675
  max="359"
653
676
  class="angle-input"
654
677
  bind:value={angleInput}
655
- on:change={() => applyOrientation(angleInput)}
678
+ onchange={() => applyOrientation(angleInput)}
656
679
  />
657
680
  <span class="angle-unit">°</span>
658
681
  </div>
@@ -660,7 +683,7 @@
660
683
  type="button"
661
684
  class="orientation-reset"
662
685
  class:active={chosenAngle !== null}
663
- on:click={resetOrientation}
686
+ onclick={resetOrientation}
664
687
  disabled={chosenAngle === null}
665
688
  title="Reset to gradient default"
666
689
  >
@@ -739,6 +762,7 @@
739
762
  font-family: var(--ui-font-mono);
740
763
  text-align: right;
741
764
  -moz-appearance: textfield;
765
+ appearance: textfield;
742
766
  }
743
767
 
744
768
  .opacity-input::-webkit-inner-spin-button,
@@ -1069,6 +1093,7 @@
1069
1093
  font-family: var(--ui-font-mono);
1070
1094
  text-align: right;
1071
1095
  -moz-appearance: textfield;
1096
+ appearance: textfield;
1072
1097
  }
1073
1098
 
1074
1099
  .angle-input::-webkit-inner-spin-button,
@@ -8,13 +8,28 @@
8
8
  <UIRadio bind:group={selected} value="beta" disabled />
9
9
  -->
10
10
  <script lang="ts">
11
- /** Value this radio represents within its group. */
12
- export let value: string;
13
- /** Two-way bound group selection. The radio is checked when `group === value`. */
14
- export let group: string;
15
- /** Optional native name; omit to let Svelte's `bind:group` wire it automatically. */
16
- export let name: string = '';
17
- export let disabled: boolean = false;
11
+ import { createBubbler } from 'svelte/legacy';
12
+
13
+ const bubble = createBubbler();
14
+
15
+
16
+
17
+ interface Props {
18
+ /** Value this radio represents within its group. */
19
+ value: string;
20
+ /** Two-way bound group selection. The radio is checked when `group === value`. */
21
+ group: string;
22
+ /** Optional native name; omit to let Svelte's `bind:group` wire it automatically. */
23
+ name?: string;
24
+ disabled?: boolean;
25
+ }
26
+
27
+ let {
28
+ value,
29
+ group = $bindable(),
30
+ name = '',
31
+ disabled = false
32
+ }: Props = $props();
18
33
  </script>
19
34
 
20
35
  <input
@@ -24,7 +39,7 @@
24
39
  {name}
25
40
  {disabled}
26
41
  bind:group
27
- on:change
42
+ onchange={bubble('change')}
28
43
  />
29
44
 
30
45
  <style>
@@ -1,16 +1,17 @@
1
1
  <script lang="ts">
2
- import { createEventDispatcher } from 'svelte';
3
-
4
- export let value: string;
5
- export let options: ReadonlyArray<{ value: string; label: string }>;
6
- export let name: string;
2
+ interface Props {
3
+ value: string;
4
+ options: ReadonlyArray<{ value: string; label: string }>;
5
+ name: string;
6
+ onchange?: (value: string) => void;
7
+ }
7
8
 
8
- const dispatch = createEventDispatcher<{ change: string }>();
9
+ let { value = $bindable(), options, name, onchange }: Props = $props();
9
10
 
10
11
  function select(v: string) {
11
12
  if (v === value) return;
12
13
  value = v;
13
- dispatch('change', v);
14
+ onchange?.(v);
14
15
  }
15
16
  </script>
16
17
 
@@ -22,7 +23,7 @@
22
23
  {name}
23
24
  value={opt.value}
24
25
  checked={value === opt.value}
25
- on:change={() => select(opt.value)}
26
+ onchange={() => select(opt.value)}
26
27
  />
27
28
  <span>{opt.label}</span>
28
29
  </label>