@motion-proto/live-tokens 0.7.1 → 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 (96) hide show
  1. package/.claude/skills/live-tokens-add-component/SKILL.md +488 -0
  2. package/README.md +34 -0
  3. package/dist-plugin/index.cjs +707 -90
  4. package/dist-plugin/index.d.cts +1 -0
  5. package/dist-plugin/index.d.ts +1 -0
  6. package/dist-plugin/index.js +707 -90
  7. package/package.json +6 -2
  8. package/src/app/site.css +1 -1
  9. package/src/editor/component-editor/CollapsibleSectionEditor.svelte +34 -27
  10. package/src/editor/component-editor/DialogEditor.svelte +4 -4
  11. package/src/editor/component-editor/NotificationEditor.svelte +3 -1
  12. package/src/editor/component-editor/SectionDividerEditor.svelte +439 -112
  13. package/src/editor/component-editor/StandardButtonsEditor.svelte +13 -1
  14. package/src/editor/component-editor/editors.d.ts +10 -0
  15. package/src/editor/component-editor/index.ts +16 -1
  16. package/src/editor/component-editor/registry.ts +103 -26
  17. package/src/editor/component-editor/scaffolding/AngleDial.svelte +52 -13
  18. package/src/editor/component-editor/scaffolding/ComponentFileManager.svelte +10 -11
  19. package/src/editor/component-editor/scaffolding/ComponentsTab.svelte +2 -2
  20. package/src/editor/component-editor/scaffolding/LinkedBlock.svelte +0 -1
  21. package/src/editor/component-editor/scaffolding/RadialShapePad.svelte +483 -0
  22. package/src/editor/component-editor/scaffolding/ShadowBackdrop.svelte +15 -2
  23. package/src/editor/component-editor/scaffolding/StateBlock.svelte +103 -15
  24. package/src/editor/component-editor/scaffolding/TokenLayout.svelte +9 -6
  25. package/src/editor/component-editor/scaffolding/TypeEditor.svelte +13 -1
  26. package/src/editor/component-editor/scaffolding/VariantGroup.svelte +239 -25
  27. package/src/editor/component-editor/scaffolding/buildTypeGroupTokens.ts +1 -0
  28. package/src/editor/component-editor/scaffolding/componentSources.ts +3 -3
  29. package/src/editor/component-editor/scaffolding/defaultSections.ts +15 -10
  30. package/src/editor/component-editor/scaffolding/types.ts +11 -0
  31. package/src/editor/core/components/componentConfigKeys.ts +22 -3
  32. package/src/editor/core/components/componentConfigService.ts +2 -2
  33. package/src/editor/core/components/componentPersist.ts +7 -5
  34. package/src/editor/core/manifests/manifestService.ts +58 -3
  35. package/src/editor/core/palettes/familySwap.ts +99 -0
  36. package/src/editor/core/palettes/paletteDerivation.ts +69 -0
  37. package/src/editor/core/palettes/tokenRegistry.ts +4 -1
  38. package/src/editor/core/store/editorStore.ts +206 -12
  39. package/src/editor/core/store/editorTypes.ts +55 -12
  40. package/src/editor/core/store/gradientSource.ts +192 -0
  41. package/src/editor/core/themes/migrations/2026-05-19-collapsiblesection-drop-frame-surface.ts +28 -0
  42. package/src/editor/core/themes/migrations/2026-05-19-sectiondivider-rich-gradient.ts +35 -0
  43. package/src/editor/core/themes/migrations/2026-05-20-sectiondivider-slim-variants.ts +82 -0
  44. package/src/editor/core/themes/migrations/2026-05-21-sectiondivider-spacing-to-padding.ts +24 -0
  45. package/src/editor/core/themes/migrations/2026-05-22-sectiondivider-intrinsics-to-css.ts +81 -0
  46. package/src/editor/core/themes/migrations/index.ts +10 -0
  47. package/src/editor/core/themes/slices/components.ts +27 -4
  48. package/src/editor/core/themes/slices/gradients.ts +88 -13
  49. package/src/editor/core/themes/themeInit.ts +2 -2
  50. package/src/editor/core/themes/themeTypes.ts +56 -1
  51. package/src/editor/index.ts +10 -1
  52. package/src/editor/overlay/ColumnsOverlay.svelte +0 -1
  53. package/src/editor/overlay/LiveEditorOverlay.svelte +1 -4
  54. package/src/editor/pages/ComponentEditorPage.svelte +53 -3
  55. package/src/editor/pages/EditorShell.svelte +53 -3
  56. package/src/editor/styles/ui-editor.css +1 -0
  57. package/src/editor/styles/ui-form-controls.css +19 -20
  58. package/src/editor/ui/BezierCurveEditor.svelte +114 -63
  59. package/src/editor/ui/EditorViewSwitcher.svelte +0 -1
  60. package/src/editor/ui/FileLoadList.svelte +22 -5
  61. package/src/editor/ui/FontStackEditor.svelte +214 -76
  62. package/src/editor/ui/GradientEditor.svelte +435 -215
  63. package/src/editor/ui/GradientStopPicker.svelte +11 -3
  64. package/src/editor/ui/ManifestFileManager.svelte +71 -4
  65. package/src/editor/ui/PaletteEditor.svelte +52 -79
  66. package/src/editor/ui/ProjectFontsSection.svelte +328 -293
  67. package/src/editor/ui/ThemeFileManager.svelte +0 -4
  68. package/src/editor/ui/UIFontFamilySelector.svelte +0 -1
  69. package/src/editor/ui/UIFontSizeSelector.svelte +3 -0
  70. package/src/editor/ui/UIInfoPopover.svelte +0 -1
  71. package/src/editor/ui/UILetterSpacingSelector.svelte +65 -0
  72. package/src/editor/ui/UIPaletteSelector.svelte +31 -4
  73. package/src/editor/ui/UIPillButton.svelte +33 -3
  74. package/src/editor/ui/UISegmentedControl.svelte +114 -0
  75. package/src/editor/ui/UITokenSelector.svelte +4 -1
  76. package/src/editor/ui/VariablesTab.svelte +41 -35
  77. package/src/editor/ui/palette/OverridesPanel.svelte +14 -37
  78. package/src/editor/ui/palette/PaletteBase.svelte +3 -3
  79. package/src/editor/ui/sections/ColumnsSection.svelte +1 -2
  80. package/src/editor/ui/sections/GradientsSection.svelte +1 -1
  81. package/src/editor/ui/sections/OverlaysSection.svelte +1 -1
  82. package/src/editor/ui/sections/ShadowsSection.svelte +1 -1
  83. package/src/system/components/Button.svelte +2 -2
  84. package/src/system/components/Card.svelte +29 -1
  85. package/src/system/components/CollapsibleSection.svelte +25 -2
  86. package/src/system/components/Dialog.svelte +24 -4
  87. package/src/system/components/FloatingTokenTags.css +43 -24
  88. package/src/system/components/FloatingTokenTags.svelte +88 -137
  89. package/src/system/components/Notification.svelte +8 -1
  90. package/src/system/components/SectionDivider.svelte +532 -381
  91. package/src/system/styles/CONVENTIONS.md +1 -1
  92. package/src/system/styles/fonts.css +3 -16
  93. package/src/system/styles/tokens.css +356 -1199
  94. package/src/system/styles/tokens.generated.css +544 -0
  95. package/src/editor/component-editor/scaffolding/DividerEditor.svelte +0 -94
  96. package/src/editor/component-editor/scaffolding/GradientCard.svelte +0 -296
@@ -457,7 +457,6 @@
457
457
  font-size: var(--ui-font-size-xs);
458
458
  color: var(--ui-text-secondary);
459
459
  text-transform: uppercase;
460
- letter-spacing: 0.05em;
461
460
  }
462
461
 
463
462
  /* Two-card pipeline (Editor → Production) — theme card + production card
@@ -506,7 +505,6 @@
506
505
  font-size: var(--ui-font-size-xs);
507
506
  font-weight: var(--ui-font-weight-semibold);
508
507
  text-transform: uppercase;
509
- letter-spacing: 0.08em;
510
508
  color: var(--ui-text-secondary);
511
509
  line-height: 1.1;
512
510
  }
@@ -516,7 +514,6 @@
516
514
  align-items: center;
517
515
  gap: var(--ui-space-4);
518
516
  font-size: 0.7rem;
519
- letter-spacing: 0.02em;
520
517
  color: var(--ui-text-muted);
521
518
  line-height: 1;
522
519
  }
@@ -607,7 +604,6 @@
607
604
 
608
605
  .tfm-revert-label {
609
606
  font-weight: var(--ui-font-weight-semibold, 600);
610
- letter-spacing: 0.02em;
611
607
  white-space: nowrap;
612
608
  }
613
609
 
@@ -330,6 +330,5 @@
330
330
  font-size: var(--ui-font-size-xs);
331
331
  color: var(--ui-text-tertiary);
332
332
  text-transform: uppercase;
333
- letter-spacing: 0.04em;
334
333
  }
335
334
  </style>
@@ -104,6 +104,9 @@
104
104
  dropdownGridColumns="auto auto auto auto"
105
105
  {onchange}
106
106
  >
107
+ {#snippet triggerMeta({ currentValue, activeOption })}
108
+ {activeOption ? (pxByKey[activeOption.key] ?? '—') : (currentValue || '—')}
109
+ {/snippet}
107
110
  {#snippet option({ opt, active, select })}
108
111
 
109
112
  <button class="font-size-row" class:active onclick={select}>
@@ -181,7 +181,6 @@
181
181
  color: var(--ui-text-primary);
182
182
  font-size: var(--ui-font-size-xs);
183
183
  font-weight: var(--ui-font-weight-semibold);
184
- letter-spacing: 0.02em;
185
184
  line-height: 1.2;
186
185
  }
187
186
 
@@ -0,0 +1,65 @@
1
+ <script lang="ts">
2
+ import UIVariantSelector from './UIVariantSelector.svelte';
3
+ import UIOptionItem from './UIOptionItem.svelte';
4
+
5
+ interface Props {
6
+ variable: string;
7
+ component?: string | undefined;
8
+ canBeLinked?: boolean;
9
+ disabled?: boolean;
10
+ selectionsLocked?: boolean;
11
+ onchange?: () => void;
12
+ }
13
+
14
+ let {
15
+ variable,
16
+ component = undefined,
17
+ canBeLinked = false,
18
+ disabled = false,
19
+ selectionsLocked = false,
20
+ onchange,
21
+ }: Props = $props();
22
+
23
+ const options = [
24
+ { key: 'tighter', label: 'Tighter', value: '-0.04em' },
25
+ { key: 'tight', label: 'Tight', value: '-0.02em' },
26
+ { key: 'normal', label: 'Normal', value: '0' },
27
+ { key: 'wide', label: 'Wide', value: '0.04em' },
28
+ { key: 'wider', label: 'Wider', value: '0.08em' },
29
+ ] as const;
30
+ </script>
31
+
32
+ <UIVariantSelector
33
+ {variable}
34
+ {component}
35
+ {canBeLinked}
36
+ {disabled}
37
+ {selectionsLocked}
38
+ varPrefix="--letter-spacing-"
39
+ {options}
40
+ {onchange}
41
+ >
42
+ {#snippet option({ opt, active, select })}
43
+ <UIOptionItem {active} onclick={select}>
44
+ {#snippet preview()}
45
+ <span class="ls-sample" style="letter-spacing: var(--letter-spacing-{opt.key});">AV</span>
46
+ {/snippet}
47
+ {#snippet label()}
48
+ {opt.label}
49
+ {/snippet}
50
+ {#snippet meta()}
51
+ {opt.value}
52
+ {/snippet}
53
+ </UIOptionItem>
54
+ {/snippet}
55
+ </UIVariantSelector>
56
+
57
+ <style>
58
+ .ls-sample {
59
+ display: inline-block;
60
+ width: 1.75rem;
61
+ text-align: center;
62
+ font-size: var(--ui-font-size-md);
63
+ color: var(--ui-text-primary);
64
+ }
65
+ </style>
@@ -36,6 +36,11 @@
36
36
  canBeLinked?: boolean;
37
37
  disabled?: boolean;
38
38
  selectionsLocked?: boolean;
39
+ /** When set, restrict picker family choices to this family (one of the
40
+ * entries in the `families` list). Other families render but are
41
+ * rendered disabled and not clickable. Out-of-family already-set
42
+ * choices still surface in the trigger meta. */
43
+ familyFilter?: string | null;
39
44
  onchange?: () => void;
40
45
  }
41
46
 
@@ -45,6 +50,7 @@
45
50
  canBeLinked = false,
46
51
  disabled = false,
47
52
  selectionsLocked = false,
53
+ familyFilter = null,
48
54
  onchange,
49
55
  }: Props = $props();
50
56
 
@@ -97,7 +103,7 @@
97
103
  const borderStepKeys = borderSteps.map(s => s.key);
98
104
  const textStepKeys = textSteps.map(s => s.key);
99
105
 
100
- const familiesWithText = ['neutral', 'canvas', 'brand', 'accent', 'special', 'success', 'warning', 'info', 'danger'];
106
+ const familiesWithText = ['neutral', 'alternate', 'canvas', 'brand', 'accent', 'special', 'success', 'warning', 'info', 'danger'];
101
107
 
102
108
  const allCategories: { id: Category; label: string }[] = [
103
109
  { id: 'palette', label: 'Palette' },
@@ -405,6 +411,7 @@
405
411
  }
406
412
 
407
413
  function selectFamily(name: string) {
414
+ if (familyFilter && name !== familyFilter) return;
408
415
  selectedFamily = name;
409
416
  if (name === chosenFamily && chosenCategory) {
410
417
  selectedTab = chosenCategory;
@@ -543,14 +550,23 @@
543
550
  <span class="family-label">None</span>
544
551
  </button>
545
552
  {#each families as fam}
546
- <button class="family-item" class:active={!chosenNone && chosenFamily === fam.name} onclick={() => selectFamily(fam.name)}>
553
+ {@const outOfFamily = familyFilter !== null && fam.name !== familyFilter}
554
+ <button
555
+ class="family-item"
556
+ class:active={!chosenNone && chosenFamily === fam.name}
557
+ class:out-of-family={outOfFamily}
558
+ disabled={outOfFamily}
559
+ onclick={() => selectFamily(fam.name)}
560
+ >
547
561
  <div class="family-swatches">
548
562
  <div class="mini-swatch" style="background: var(--color-{fam.name}-300);"></div>
549
563
  <div class="mini-swatch" style="background: var(--color-{fam.name}-500);"></div>
550
564
  <div class="mini-swatch" style="background: var(--color-{fam.name}-700);"></div>
551
565
  </div>
552
566
  <span class="family-label">{fam.label}</span>
553
- <i class="fas fa-chevron-right family-arrow"></i>
567
+ {#if !outOfFamily}
568
+ <i class="fas fa-chevron-right family-arrow"></i>
569
+ {/if}
554
570
  </button>
555
571
  {/each}
556
572
  {#if gradientsAllowed && gradientTokens.length > 0}
@@ -862,6 +878,18 @@
862
878
  box-shadow: inset 3px 0 0 var(--ui-text-accent);
863
879
  }
864
880
 
881
+ /* Family is filtered out by `familyFilter`. Greyed and unselectable so
882
+ the picker scopes new picks to the variant's family while keeping the
883
+ full palette visible (it isn't gone, it just isn't this gradient's
884
+ business). Pointer cursor is suppressed to match :disabled. */
885
+ .family-item.out-of-family {
886
+ opacity: 0.32;
887
+ cursor: not-allowed;
888
+ }
889
+ .family-item.out-of-family:hover {
890
+ background: none;
891
+ }
892
+
865
893
  .family-item.active .family-label {
866
894
  color: var(--ui-text-accent);
867
895
  }
@@ -900,7 +928,6 @@
900
928
  font-family: var(--ui-font-mono);
901
929
  color: var(--ui-text-tertiary);
902
930
  text-transform: uppercase;
903
- letter-spacing: 0.04em;
904
931
  border-top: 1px solid var(--ui-border-low);
905
932
  }
906
933
 
@@ -2,7 +2,7 @@
2
2
  import type { Snippet } from 'svelte';
3
3
 
4
4
  interface Props {
5
- variant?: 'primary' | 'default' | 'secondary';
5
+ variant?: 'primary' | 'default' | 'secondary' | 'outline';
6
6
  size?: 'default' | 'compact';
7
7
  icon?: string;
8
8
  href?: string;
@@ -39,7 +39,7 @@
39
39
  aria-disabled={disabled ? 'true' : undefined}
40
40
  >
41
41
  {#if icon}<i class="fas {icon}" aria-hidden="true"></i>{/if}
42
- {#if children}{@render children()}{/if}
42
+ {#if children}<span class="ui-pill-label">{@render children()}</span>{/if}
43
43
  </a>
44
44
  {:else}
45
45
  <button
@@ -50,7 +50,7 @@
50
50
  {onclick}
51
51
  >
52
52
  {#if icon}<i class="fas {icon}" aria-hidden="true"></i>{/if}
53
- {#if children}{@render children()}{/if}
53
+ {#if children}<span class="ui-pill-label">{@render children()}</span>{/if}
54
54
  </button>
55
55
  {/if}
56
56
 
@@ -59,6 +59,8 @@
59
59
  display: inline-flex;
60
60
  align-items: center;
61
61
  gap: var(--ui-space-6, 6px);
62
+ max-width: 100%;
63
+ min-width: 0;
62
64
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.15) 0%, rgba(255, 255, 255, 0.25) 100%);
63
65
  border: 1px solid rgba(255, 255, 255, 0.5);
64
66
  color: var(--ui-text-primary, #fff);
@@ -75,11 +77,24 @@
75
77
  border-color var(--ui-transition-fast, 120ms ease);
76
78
  }
77
79
 
80
+ .ui-pill-label {
81
+ min-width: 0;
82
+ overflow: hidden;
83
+ white-space: nowrap;
84
+ text-overflow: ellipsis;
85
+ }
86
+
78
87
  .ui-pill:hover:not(:disabled):not([aria-disabled='true']) {
79
88
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.25) 0%, rgba(255, 255, 255, 0.45) 100%);
80
89
  border-color: rgba(255, 255, 255, 0.75);
81
90
  }
82
91
 
92
+ .ui-pill:focus-visible {
93
+ outline: none;
94
+ border-color: rgba(255, 255, 255, 0.9);
95
+ box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.18);
96
+ }
97
+
83
98
  .ui-pill:disabled,
84
99
  .ui-pill[aria-disabled='true'] {
85
100
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.06) 0%, rgba(255, 255, 255, 0.10) 100%);
@@ -130,6 +145,21 @@
130
145
  border-color: rgba(255, 255, 255, 0.5);
131
146
  }
132
147
 
148
+ /* Variant: outline — transparent fill, just a border; subtle fill on hover */
149
+ .ui-pill-outline {
150
+ background: none;
151
+ border-color: rgba(255, 255, 255, 0.4);
152
+ }
153
+ .ui-pill-outline:hover:not(:disabled):not([aria-disabled='true']) {
154
+ background: linear-gradient(180deg, rgba(255, 255, 255, 0.16) 0%, rgba(255, 255, 255, 0.08) 100%);
155
+ border-color: rgba(255, 255, 255, 0.65);
156
+ }
157
+ .ui-pill-outline:disabled,
158
+ .ui-pill-outline[aria-disabled='true'] {
159
+ background: none;
160
+ border-color: rgba(255, 255, 255, 0.18);
161
+ }
162
+
133
163
  /* Size: compact — for header bars / chrome rails */
134
164
  .ui-pill-compact {
135
165
  font-size: var(--ui-font-size-sm, 14px);
@@ -0,0 +1,114 @@
1
+ <script lang="ts" generics="V extends string">
2
+ interface Option {
3
+ value: V;
4
+ label: string;
5
+ icon?: string;
6
+ title?: string;
7
+ }
8
+
9
+ interface Props {
10
+ value: V;
11
+ options: ReadonlyArray<Option>;
12
+ /** Native radio group name. Auto-generated per instance if omitted. */
13
+ name?: string;
14
+ ariaLabel?: string;
15
+ onchange?: (value: V) => void;
16
+ }
17
+
18
+ let { value = $bindable(), options, name, ariaLabel, onchange }: Props = $props();
19
+
20
+ // Stable per-instance fallback so internal radios group correctly when no
21
+ // `name` is passed. Not reactive: the group identity shouldn't change.
22
+ const fallbackName = `ui-seg-${Math.random().toString(36).slice(2, 9)}`;
23
+ let groupName = $derived(name ?? fallbackName);
24
+
25
+ function select(v: V) {
26
+ if (v === value) return;
27
+ value = v;
28
+ onchange?.(v);
29
+ }
30
+ </script>
31
+
32
+ <div class="ui-seg" role="radiogroup" aria-label={ariaLabel}>
33
+ {#each options as opt (opt.value)}
34
+ <label
35
+ class="ui-seg-item"
36
+ class:active={value === opt.value}
37
+ title={opt.title}
38
+ >
39
+ <input
40
+ type="radio"
41
+ name={groupName}
42
+ value={opt.value}
43
+ checked={value === opt.value}
44
+ onchange={() => select(opt.value)}
45
+ />
46
+ {#if opt.icon}<i class="fas {opt.icon}" aria-hidden="true"></i>{/if}
47
+ <span>{opt.label}</span>
48
+ </label>
49
+ {/each}
50
+ </div>
51
+
52
+ <style>
53
+ .ui-seg {
54
+ display: inline-flex;
55
+ align-self: flex-start;
56
+ align-items: stretch;
57
+ border: 1px solid rgba(255, 255, 255, 0.5);
58
+ border-radius: var(--ui-radius-xl, 8px);
59
+ overflow: hidden;
60
+ }
61
+
62
+ .ui-seg-item {
63
+ position: relative;
64
+ display: inline-flex;
65
+ align-items: center;
66
+ gap: var(--ui-space-6, 6px);
67
+ padding: var(--ui-space-4, 4px) var(--ui-space-12, 12px);
68
+ color: var(--ui-text-secondary, rgba(255, 255, 255, 0.65));
69
+ font-family: inherit;
70
+ font-size: var(--ui-font-size-sm, 14px);
71
+ font-weight: var(--ui-font-weight-medium, 500);
72
+ line-height: 1.5;
73
+ cursor: pointer;
74
+ user-select: none;
75
+ transition:
76
+ background var(--ui-transition-fast, 120ms ease),
77
+ color var(--ui-transition-fast, 120ms ease);
78
+ }
79
+
80
+ .ui-seg-item + .ui-seg-item {
81
+ border-left: 1px solid rgba(255, 255, 255, 0.18);
82
+ }
83
+
84
+ .ui-seg-item input {
85
+ position: absolute;
86
+ opacity: 0;
87
+ pointer-events: none;
88
+ width: 1px;
89
+ height: 1px;
90
+ margin: 0;
91
+ }
92
+
93
+ .ui-seg-item:hover:not(.active) {
94
+ background: rgba(255, 255, 255, 0.06);
95
+ color: var(--ui-text-primary, #fff);
96
+ }
97
+
98
+ .ui-seg-item.active {
99
+ background: linear-gradient(180deg, rgba(255, 255, 255, 0.15) 0%, rgba(255, 255, 255, 0.25) 100%);
100
+ color: var(--ui-text-primary, #fff);
101
+ }
102
+
103
+ .ui-seg-item:has(input:focus-visible) {
104
+ box-shadow: inset 0 0 0 2px rgba(255, 255, 255, 0.55);
105
+ }
106
+
107
+ .ui-seg-item i {
108
+ font-size: var(--ui-font-size-xs, 12px);
109
+ color: rgba(255, 255, 255, 0.65);
110
+ }
111
+ .ui-seg-item.active i {
112
+ color: rgba(255, 255, 255, 0.85);
113
+ }
114
+ </style>
@@ -325,7 +325,10 @@
325
325
  </div>
326
326
 
327
327
  {#if triggerMeta}
328
- <span class="ui-ts-meta-text">{@render triggerMeta()}</span>
328
+ <span
329
+ class="ui-ts-meta-text"
330
+ onmouseenter={(e) => { e.currentTarget.title = e.currentTarget.textContent ?? ''; }}
331
+ >{@render triggerMeta()}</span>
329
332
  {/if}
330
333
  </div>
331
334
 
@@ -8,7 +8,6 @@
8
8
  import OverlaysSection from './sections/OverlaysSection.svelte';
9
9
  import GradientsSection from './sections/GradientsSection.svelte';
10
10
  import ShadowsSection from './sections/ShadowsSection.svelte';
11
- import UIPillButton from './UIPillButton.svelte';
12
11
  import {
13
12
  SPACING_VARS, BORDER_WIDTH_VARS, RADIUS_VARS, FONT_SIZE_VARS,
14
13
  ICON_SIZE_VARS, FONT_WEIGHT_VARS, LINE_HEIGHT_VARS,
@@ -42,8 +41,6 @@
42
41
  setTimeout(() => { copiedVar = null; }, COPIED_FLASH_MS);
43
42
  }
44
43
 
45
- type FontAddMode = 'closed' | 'url' | 'fontface';
46
- let fontAddMode: FontAddMode = $state('closed');
47
44
  </script>
48
45
 
49
46
  <div class="variables-container">
@@ -68,10 +65,16 @@
68
65
  <!-- Spacing & Borders -->
69
66
  <section class="section" id="spacing">
70
67
  <h2 class="section-title">Spacing &amp; Borders</h2>
71
- <h3 class="subsection-title">Spacing</h3>
72
- <TokenScaleTable kind="spacing" vars={SPACING_VARS} {liveVersion} {copiedVar} oncopy={copyVariable} />
73
- <h3 class="subsection-title">Borders</h3>
74
- <TokenScaleTable kind="border" vars={BORDER_WIDTH_VARS} {liveVersion} {copiedVar} oncopy={copyVariable} />
68
+ <div class="spacing-borders-columns">
69
+ <div class="spacing-borders-group">
70
+ <h3 class="subsection-title">Spacing</h3>
71
+ <TokenScaleTable kind="spacing" vars={SPACING_VARS} {liveVersion} {copiedVar} oncopy={copyVariable} />
72
+ </div>
73
+ <div class="spacing-borders-group">
74
+ <h3 class="subsection-title">Borders</h3>
75
+ <TokenScaleTable kind="border" vars={BORDER_WIDTH_VARS} {liveVersion} {copiedVar} oncopy={copyVariable} />
76
+ </div>
77
+ </div>
75
78
  </section>
76
79
 
77
80
  <!-- Columns -->
@@ -85,18 +88,11 @@
85
88
 
86
89
  <!-- Typography -->
87
90
  <section class="section" id="typography">
88
- <div class="typography-header">
89
- <h2 class="section-title">Typography</h2>
90
- <UIPillButton
91
- variant="primary"
92
- icon="fa-plus"
93
- onclick={() => { fontAddMode = fontAddMode === 'closed' ? 'url' : 'closed'; }}
94
- >Add Font</UIPillButton>
95
- </div>
91
+ <h2 class="section-title">Typography</h2>
96
92
 
97
93
  <div class="typography-columns">
98
94
  <div class="typography-group font-families-group">
99
- <ProjectFontsSection bind:addMode={fontAddMode} />
95
+ <ProjectFontsSection />
100
96
  <h3 class="group-title">Font Families</h3>
101
97
  <FontStackEditor />
102
98
  </div>
@@ -161,13 +157,13 @@
161
157
  .variables-container {
162
158
  display: flex;
163
159
  flex-direction: column;
164
- gap: var(--ui-space-32);
160
+ gap: var(--ui-space-48);
165
161
  }
166
162
 
167
163
  .section {
168
164
  display: flex;
169
165
  flex-direction: column;
170
- gap: var(--ui-space-16);
166
+ gap: var(--ui-space-24);
171
167
  }
172
168
 
173
169
  .section-title {
@@ -179,10 +175,12 @@
179
175
  border-bottom: 2px solid var(--ui-border-high);
180
176
  }
181
177
 
178
+ /* Tier-2 group header: xl bold white. No divider — vertical rhythm carries the
179
+ separation, matching the palette section's title style at a smaller size. */
182
180
  .group-title {
183
- font-size: var(--ui-font-size-lg);
184
- font-weight: var(--ui-font-weight-semibold);
185
- color: var(--ui-text-secondary);
181
+ font-size: var(--ui-font-size-xl);
182
+ font-weight: var(--ui-font-weight-bold);
183
+ color: var(--ui-text-primary);
186
184
  margin: 0;
187
185
  }
188
186
 
@@ -193,27 +191,32 @@
193
191
  font-weight: var(--ui-font-weight-semibold);
194
192
  color: var(--ui-text-tertiary);
195
193
  text-transform: uppercase;
196
- letter-spacing: 0.06em;
197
194
  }
198
195
 
199
- .subsection-title:first-child,
200
- .section-title + .subsection-title {
196
+ .subsection-title:first-child {
201
197
  margin-top: 0;
202
198
  }
203
199
 
204
- /* Typography */
205
- .typography-header {
200
+ /* Spacing & Borders columns */
201
+ .spacing-borders-columns {
202
+ display: grid;
203
+ grid-template-columns: repeat(auto-fill, minmax(min(20rem, 100%), 1fr));
204
+ gap: var(--ui-space-24);
205
+ align-items: start;
206
+ }
207
+
208
+ .spacing-borders-group {
206
209
  display: flex;
207
- align-items: center;
208
- justify-content: space-between;
209
- gap: var(--ui-space-12);
210
- padding-bottom: var(--ui-space-8);
211
- border-bottom: 2px solid var(--ui-border-high);
210
+ flex-direction: column;
211
+ gap: var(--ui-space-8);
212
+ min-width: 0;
212
213
  }
213
- .typography-header .section-title {
214
- padding-bottom: 0;
215
- border-bottom: none;
214
+
215
+ .spacing-borders-group .subsection-title {
216
+ margin: 0;
216
217
  }
218
+
219
+ /* Typography */
217
220
  .typography-columns {
218
221
  display: grid;
219
222
  grid-template-columns: repeat(auto-fill, minmax(min(22rem, 100%), 1fr));
@@ -224,12 +227,15 @@
224
227
  .typography-group {
225
228
  display: flex;
226
229
  flex-direction: column;
227
- gap: var(--ui-space-8);
230
+ gap: var(--ui-space-12);
228
231
  min-width: 0;
229
232
  }
230
233
 
231
234
  .font-families-group {
232
235
  grid-column: 1 / -1;
236
+ /* Two logical groups stacked here (Project Fonts + Font Families); space them
237
+ further apart than peers inside a single group. */
238
+ gap: var(--ui-space-32);
233
239
  }
234
240
 
235
241
  /* Utility Tokens */
@@ -1,5 +1,6 @@
1
1
  <script lang="ts">
2
2
  import ScaleCurveEditor from './ScaleCurveEditor.svelte';
3
+ import UIPillButton from '../UIPillButton.svelte';
3
4
  import { type CurveAnchor, lightnessCurveConfig, saturationCurveConfig, textLightnessCurveConfig } from '../curveEngine';
4
5
  import { scaleToCssVar } from '../../core/palettes/paletteDerivation';
5
6
 
@@ -126,19 +127,16 @@
126
127
  <div class="scale-header">
127
128
  <h4 class="scale-title">{scale.title}</h4>
128
129
  {#if supportsSnap}
129
- <button
130
- class="edit-toggle"
131
- class:active={snapped}
132
- type="button"
130
+ <UIPillButton
131
+ size="compact"
132
+ variant={snapped ? 'primary' : 'outline'}
133
133
  onclick={() => onToggleSnap(scale)}
134
- >{snapped ? 'Unsnap' : 'Snap All'}</button>
134
+ >{snapped ? 'Unsnap' : 'Snap All'}</UIPillButton>
135
135
  {/if}
136
- <button class="edit-toggle" type="button" onclick={() => onClearScaleOverrides(scale)}>Clear Overrides</button>
137
- <button
138
- class="edit-toggle"
139
- type="button"
140
- onclick={() => onToggleEditor(scale.title)}
141
- >{editorOpen ? 'Close' : 'Edit'}</button>
136
+ <UIPillButton size="compact" variant="outline" onclick={() => onClearScaleOverrides(scale)}>Clear Overrides</UIPillButton>
137
+ <UIPillButton size="compact" variant="outline" onclick={() => onToggleEditor(scale.title)}>
138
+ {editorOpen ? 'Close' : 'Edit'}
139
+ </UIPillButton>
142
140
  </div>
143
141
  <div class="swatch-grid" style="--swatch-cols: {scale.steps.length}; --swatch-gap: var(--ui-space-8)">
144
142
  {#each scale.steps as step}
@@ -276,36 +274,15 @@
276
274
  display: flex;
277
275
  align-items: center;
278
276
  gap: var(--ui-space-8);
277
+ padding-bottom: 0.5rem;
279
278
  }
280
279
 
281
280
  .scale-title {
282
- font-size: var(--ui-font-size-md);
283
- font-weight: var(--ui-font-weight-semibold);
284
- color: var(--ui-text-tertiary);
285
- margin: 0;
286
- text-transform: uppercase;
287
- letter-spacing: 0.05em;
288
- }
289
-
290
- .edit-toggle {
291
- font-size: var(--ui-font-size-md);
292
- color: var(--ui-text-tertiary);
293
- background: none;
294
- border: 1px solid var(--ui-border-low);
295
- border-radius: var(--ui-radius-sm);
296
- padding: var(--ui-space-2) var(--ui-space-6);
297
- cursor: pointer;
298
- }
299
-
300
- .edit-toggle:hover {
301
- color: var(--ui-text-primary);
302
- border-color: var(--ui-border-high);
303
- }
304
-
305
- .edit-toggle.active {
281
+ font-size: var(--ui-font-size-lg);
282
+ font-weight: var(--ui-font-weight-bold);
306
283
  color: var(--ui-text-primary);
307
- border-color: var(--ui-border-high);
308
- background: var(--ui-surface-high);
284
+ margin: 0;
285
+ padding-right: 1rem;
309
286
  }
310
287
 
311
288
  .swatch-grid {
@@ -153,13 +153,13 @@
153
153
  }
154
154
 
155
155
  .editor-label {
156
- font-size: var(--ui-font-size-lg);
156
+ font-size: var(--ui-font-size-xl);
157
157
  font-weight: var(--ui-font-weight-semibold);
158
158
  color: var(--ui-text-primary);
159
159
  }
160
160
 
161
161
  .base-hex {
162
- font-size: var(--ui-font-size-xs);
162
+ font-size: var(--ui-font-size-md);
163
163
  color: var(--ui-text-secondary);
164
164
  font-family: var(--ui-font-mono);
165
165
  }
@@ -172,7 +172,7 @@
172
172
  padding: var(--ui-space-2) var(--ui-space-4);
173
173
  margin-left: calc(-1 * var(--ui-space-4));
174
174
  border-radius: var(--ui-radius-sm);
175
- font-size: var(--ui-font-size-xs);
175
+ font-size: var(--ui-font-size-md);
176
176
  color: var(--ui-text-secondary);
177
177
  font-family: var(--ui-font-mono);
178
178
  text-align: left;