@motion-proto/live-tokens 0.6.2 → 0.8.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 (232) hide show
  1. package/README.md +14 -13
  2. package/dist-plugin/index.cjs +854 -226
  3. package/dist-plugin/index.d.cts +2 -1
  4. package/dist-plugin/index.d.ts +2 -1
  5. package/dist-plugin/index.js +852 -225
  6. package/package.json +26 -40
  7. package/src/{styles → app}/site.css +1 -1
  8. package/src/{component-editor → editor/component-editor}/BadgeEditor.svelte +8 -82
  9. package/src/{component-editor → editor/component-editor}/CalloutEditor.svelte +4 -4
  10. package/src/{component-editor → editor/component-editor}/CardEditor.svelte +28 -76
  11. package/src/{component-editor → editor/component-editor}/CollapsibleSectionEditor.svelte +37 -30
  12. package/src/{component-editor → editor/component-editor}/CornerBadgeEditor.svelte +31 -93
  13. package/src/{component-editor → editor/component-editor}/DialogEditor.svelte +60 -57
  14. package/src/editor/component-editor/ImageEditor.svelte +30 -0
  15. package/src/{component-editor → editor/component-editor}/InlineEditActionsEditor.svelte +6 -4
  16. package/src/editor/component-editor/MenuSelectEditor.svelte +160 -0
  17. package/src/{component-editor → editor/component-editor}/NotificationEditor.svelte +67 -38
  18. package/src/{component-editor → editor/component-editor}/ProgressBarEditor.svelte +5 -4
  19. package/src/{component-editor → editor/component-editor}/RadioButtonEditor.svelte +3 -3
  20. package/src/editor/component-editor/SectionDividerEditor.svelte +565 -0
  21. package/src/{component-editor → editor/component-editor}/SegmentedControlEditor.svelte +2 -2
  22. package/src/{component-editor → editor/component-editor}/StandardButtonsEditor.svelte +29 -21
  23. package/src/{component-editor → editor/component-editor}/TabBarEditor.svelte +9 -14
  24. package/src/{component-editor → editor/component-editor}/TableEditor.svelte +9 -18
  25. package/src/{component-editor → editor/component-editor}/TooltipEditor.svelte +11 -47
  26. package/src/editor/component-editor/editors.d.ts +10 -0
  27. package/src/{component-editor → editor/component-editor}/registry.ts +28 -18
  28. package/src/{component-editor → editor/component-editor}/scaffolding/AngleDial.svelte +54 -15
  29. package/src/{component-editor → editor/component-editor}/scaffolding/ComponentEditorBase.svelte +3 -51
  30. package/src/{component-editor → editor/component-editor}/scaffolding/ComponentFileManager.svelte +151 -424
  31. package/src/{component-editor → editor/component-editor}/scaffolding/ComponentFileMenu.svelte +18 -170
  32. package/src/{component-editor → editor/component-editor}/scaffolding/ComponentsTab.svelte +2 -2
  33. package/src/{component-editor → editor/component-editor}/scaffolding/CopyFromMenu.svelte +44 -4
  34. package/src/{component-editor → editor/component-editor}/scaffolding/FieldsetWrapper.svelte +1 -1
  35. package/src/{component-editor → editor/component-editor}/scaffolding/LinkageChart.svelte +6 -6
  36. package/src/{component-editor → editor/component-editor}/scaffolding/LinkedBlock.svelte +6 -12
  37. package/src/editor/component-editor/scaffolding/NonStylableConfig.svelte +38 -0
  38. package/src/editor/component-editor/scaffolding/RadialShapePad.svelte +483 -0
  39. package/src/{component-editor → editor/component-editor}/scaffolding/SaveAsDialog.svelte +66 -12
  40. package/src/editor/component-editor/scaffolding/ShadowBackdrop.svelte +85 -0
  41. package/src/editor/component-editor/scaffolding/ShadowBackdropControls.svelte +132 -0
  42. package/src/editor/component-editor/scaffolding/StateBlock.svelte +345 -0
  43. package/src/{component-editor → editor/component-editor}/scaffolding/TokenLayout.svelte +17 -12
  44. package/src/{component-editor → editor/component-editor}/scaffolding/TypeEditor.svelte +13 -1
  45. package/src/editor/component-editor/scaffolding/VariantGroup.svelte +858 -0
  46. package/src/{component-editor → editor/component-editor}/scaffolding/buildTypeGroupTokens.ts +1 -0
  47. package/src/{component-editor → editor/component-editor}/scaffolding/editorContext.ts +19 -9
  48. package/src/{component-editor → editor/component-editor}/scaffolding/linkedBlock.ts +2 -2
  49. package/src/{component-editor → editor/component-editor}/scaffolding/types.ts +25 -0
  50. package/src/{lib → editor/core/components}/componentConfigKeys.ts +8 -0
  51. package/src/{lib → editor/core/components}/componentConfigService.ts +3 -3
  52. package/src/{lib → editor/core/components}/componentPersist.ts +11 -9
  53. package/src/editor/core/flashStatus.ts +30 -0
  54. package/src/{lib → editor/core/fonts}/fontLoader.ts +2 -2
  55. package/src/{lib → editor/core/fonts}/fontMigration.ts +4 -4
  56. package/src/{lib → editor/core/fonts}/fontParse.ts +1 -1
  57. package/src/editor/core/manifests/manifestService.ts +171 -0
  58. package/src/editor/core/palettes/familySwap.ts +99 -0
  59. package/src/{lib → editor/core/palettes}/paletteDerivation.ts +71 -2
  60. package/src/{lib → editor/core/palettes}/tokenRegistry.ts +9 -6
  61. package/src/editor/core/productionPulse.ts +37 -0
  62. package/src/{lib → editor/core/routing}/router.ts +1 -1
  63. package/src/{lib/files/versionedFileResource.ts → editor/core/storage/files/versionedFileResourceClient.ts} +8 -1
  64. package/src/{lib → editor/core/store}/editorCore.ts +24 -8
  65. package/src/{lib → editor/core/store}/editorPersistence.ts +3 -3
  66. package/src/{lib → editor/core/store}/editorRenderer.ts +2 -2
  67. package/src/{lib → editor/core/store}/editorStore.ts +222 -28
  68. package/src/{lib → editor/core/store}/editorTypes.ts +56 -13
  69. package/src/editor/core/store/gradientSource.ts +192 -0
  70. package/src/editor/core/themes/migrations/2026-05-19-collapsiblesection-drop-frame-surface.ts +28 -0
  71. package/src/editor/core/themes/migrations/2026-05-19-sectiondivider-rich-gradient.ts +35 -0
  72. package/src/editor/core/themes/migrations/2026-05-20-sectiondivider-slim-variants.ts +82 -0
  73. package/src/editor/core/themes/migrations/2026-05-21-sectiondivider-spacing-to-padding.ts +24 -0
  74. package/src/editor/core/themes/migrations/2026-05-22-sectiondivider-intrinsics-to-css.ts +81 -0
  75. package/src/{lib → editor/core/themes}/migrations/index.ts +10 -0
  76. package/src/{lib → editor/core/themes}/slices/columns.ts +2 -2
  77. package/src/{lib → editor/core/themes}/slices/components.ts +20 -6
  78. package/src/{lib → editor/core/themes}/slices/fonts.ts +1 -1
  79. package/src/{lib → editor/core/themes}/slices/gradients.ts +89 -14
  80. package/src/{lib → editor/core/themes}/slices/overlays.ts +1 -1
  81. package/src/{lib → editor/core/themes}/slices/palettes.ts +1 -1
  82. package/src/{lib → editor/core/themes}/slices/shadows.ts +3 -3
  83. package/src/{lib → editor/core/themes}/themeInit.ts +8 -8
  84. package/src/{lib → editor/core/themes}/themeService.ts +6 -6
  85. package/src/{lib → editor/core/themes}/themeTypes.ts +67 -8
  86. package/src/editor/index.ts +69 -0
  87. package/src/{lib → editor/overlay}/ColumnsOverlay.svelte +0 -1
  88. package/src/{lib → editor/overlay}/LiveEditorOverlay.svelte +80 -129
  89. package/src/{lib → editor/overlay}/columnsOverlay.ts +2 -2
  90. package/src/{pages → editor/pages}/ComponentEditorPage.svelte +12 -12
  91. package/src/{pages → editor/pages}/Editor.svelte +4 -4
  92. package/src/{pages → editor/pages}/EditorShell.svelte +18 -36
  93. package/src/{styles → editor/styles}/ui-editor.css +43 -22
  94. package/src/{styles → editor/styles}/ui-form-controls.css +23 -24
  95. package/src/{ui → editor/ui}/BezierCurveEditor.svelte +119 -68
  96. package/src/{ui → editor/ui}/ColorEditPanel.svelte +13 -13
  97. package/src/{ui → editor/ui}/EditorViewSwitcher.svelte +7 -6
  98. package/src/editor/ui/FileLoadList.svelte +367 -0
  99. package/src/editor/ui/FilePill.svelte +80 -0
  100. package/src/editor/ui/FontStackEditor.svelte +499 -0
  101. package/src/editor/ui/GradientEditor.svelte +690 -0
  102. package/src/{ui → editor/ui}/GradientStopPicker.svelte +12 -4
  103. package/src/editor/ui/ManifestFileManager.svelte +438 -0
  104. package/src/{ui → editor/ui}/PaletteEditor.svelte +180 -673
  105. package/src/editor/ui/ProjectFontsSection.svelte +638 -0
  106. package/src/{ui → editor/ui}/SurfacesTab.svelte +3 -3
  107. package/src/{ui → editor/ui}/TextTab.svelte +3 -3
  108. package/src/editor/ui/ThemeFileManager.svelte +783 -0
  109. package/src/{ui → editor/ui}/UICopyPopover.svelte +4 -4
  110. package/src/{ui → editor/ui}/UIFontFamilySelector.svelte +6 -7
  111. package/src/{ui → editor/ui}/UIFontSizeSelector.svelte +4 -1
  112. package/src/editor/ui/UIInfoPopover.svelte +243 -0
  113. package/src/editor/ui/UILetterSpacingSelector.svelte +65 -0
  114. package/src/{ui → editor/ui}/UILineHeightSelector.svelte +5 -5
  115. package/src/{ui → editor/ui}/UILinkToggle.svelte +2 -2
  116. package/src/{ui → editor/ui}/UIPaddingSelector.svelte +6 -6
  117. package/src/{ui → editor/ui}/UIPaletteSelector.svelte +57 -30
  118. package/src/editor/ui/UIPillButton.svelte +168 -0
  119. package/src/{ui → editor/ui}/UIRadio.svelte +2 -2
  120. package/src/{ui → editor/ui}/UIRelinkConfirmPopover.svelte +4 -4
  121. package/src/editor/ui/UISegmentedControl.svelte +114 -0
  122. package/src/editor/ui/UISquareButton.svelte +172 -0
  123. package/src/{ui → editor/ui}/UITokenSelector.svelte +14 -11
  124. package/src/{ui → editor/ui}/UIVariantSelector.svelte +1 -1
  125. package/src/{ui → editor/ui}/VariablesTab.svelte +46 -17
  126. package/src/{ui → editor/ui}/palette/GradientStopEditor.svelte +13 -13
  127. package/src/{ui → editor/ui}/palette/OverridesPanel.svelte +24 -47
  128. package/src/{ui → editor/ui}/palette/PaletteBase.svelte +11 -8
  129. package/src/{ui → editor/ui}/palette/paletteEditorState.ts +1 -1
  130. package/src/editor/ui/palette/paletteMath.ts +275 -0
  131. package/src/{ui → editor/ui}/sections/ColumnsSection.svelte +137 -18
  132. package/src/{ui → editor/ui}/sections/GradientsSection.svelte +8 -8
  133. package/src/{ui → editor/ui}/sections/OverlaysSection.svelte +18 -18
  134. package/src/{ui → editor/ui}/sections/ShadowsSection.svelte +23 -23
  135. package/src/{ui → editor/ui}/sections/TokenScaleTable.svelte +3 -3
  136. package/src/{components → system/components}/Badge.svelte +0 -36
  137. package/src/{components → system/components}/Button.svelte +2 -2
  138. package/src/{components → system/components}/Card.svelte +34 -60
  139. package/src/{components → system/components}/CollapsibleSection.svelte +25 -2
  140. package/src/{components → system/components}/CornerBadge.svelte +8 -24
  141. package/src/{components → system/components}/Dialog.svelte +1 -1
  142. package/src/system/components/FloatingTokenTags.css +275 -0
  143. package/src/system/components/FloatingTokenTags.svelte +543 -0
  144. package/src/{components → system/components}/InlineEditActions.svelte +6 -4
  145. package/src/system/components/MenuSelect.svelte +229 -0
  146. package/src/{components → system/components}/Notification.svelte +8 -1
  147. package/src/{components → system/components}/ProgressBar.svelte +29 -11
  148. package/src/system/components/SectionDivider.svelte +560 -0
  149. package/src/{components → system/components}/SegmentedControl.svelte +49 -43
  150. package/src/{components → system/components}/TabBar.svelte +81 -65
  151. package/src/{components → system/components}/Table.svelte +17 -3
  152. package/src/{components → system/components}/Tooltip.svelte +6 -4
  153. package/src/system/styles/CONVENTIONS.md +178 -0
  154. package/src/system/styles/fonts.css +20 -0
  155. package/src/system/styles/tokens.css +601 -0
  156. package/src/system/styles/tokens.generated.css +544 -0
  157. package/src/component-editor/ImageEditor.svelte +0 -74
  158. package/src/component-editor/SectionDividerEditor.svelte +0 -265
  159. package/src/component-editor/scaffolding/DividerEditor.svelte +0 -94
  160. package/src/component-editor/scaffolding/GradientCard.svelte +0 -296
  161. package/src/component-editor/scaffolding/NonStylableConfig.svelte +0 -62
  162. package/src/component-editor/scaffolding/ShadowBackdrop.svelte +0 -37
  163. package/src/component-editor/scaffolding/ShadowBackdropControls.svelte +0 -61
  164. package/src/component-editor/scaffolding/StateBlock.svelte +0 -132
  165. package/src/component-editor/scaffolding/VariantGroup.svelte +0 -310
  166. package/src/components/SectionDivider.svelte +0 -483
  167. package/src/data/google-fonts.json +0 -75
  168. package/src/lib/index.ts +0 -68
  169. package/src/lib/presetService.ts +0 -214
  170. package/src/lib/productionPulse.ts +0 -32
  171. package/src/styles/fonts.css +0 -30
  172. package/src/styles/tokens.css +0 -1324
  173. package/src/ui/FontStackEditor.svelte +0 -361
  174. package/src/ui/GradientEditor.svelte +0 -470
  175. package/src/ui/PresetFileManager.svelte +0 -1116
  176. package/src/ui/ProjectFontsSection.svelte +0 -645
  177. package/src/ui/ThemeFileManager.svelte +0 -1020
  178. package/src/ui/UnsavedComponentsDialog.svelte +0 -315
  179. /package/src/{component-editor → editor/component-editor}/index.ts +0 -0
  180. /package/src/{component-editor → editor/component-editor}/scaffolding/DemoHeader.svelte +0 -0
  181. /package/src/{component-editor → editor/component-editor}/scaffolding/componentSectionType.ts +0 -0
  182. /package/src/{component-editor → editor/component-editor}/scaffolding/componentSources.ts +0 -0
  183. /package/src/{component-editor → editor/component-editor}/scaffolding/defaultSections.ts +0 -0
  184. /package/src/{component-editor → editor/component-editor}/scaffolding/siblings.ts +0 -0
  185. /package/src/{lib → editor/core}/cssVarSync.ts +0 -0
  186. /package/src/{lib → editor/core/palettes}/oklch.ts +0 -0
  187. /package/src/{lib → editor/core/routing}/navLinkTypes.ts +0 -0
  188. /package/src/{lib → editor/core/routing}/parentRouteStore.ts +0 -0
  189. /package/src/{lib → editor/core/storage}/storage.ts +0 -0
  190. /package/src/{lib → editor/core/store}/editorConfig.ts +0 -0
  191. /package/src/{lib → editor/core/store}/editorConfigStore.ts +0 -0
  192. /package/src/{lib → editor/core/store}/editorKeybindings.ts +0 -0
  193. /package/src/{lib → editor/core/store}/editorViewStore.ts +0 -0
  194. /package/src/{lib → editor/core/themes}/migrations/2026-04-24-component-prefix-and-suffix-renames.ts +0 -0
  195. /package/src/{lib → editor/core/themes}/migrations/2026-04-24-legacy-keys-and-bg-to-canvas.ts +0 -0
  196. /package/src/{lib → editor/core/themes}/migrations/2026-04-27-segmentedcontrol-disabled-flatten.ts +0 -0
  197. /package/src/{lib → editor/core/themes}/migrations/2026-05-08-collapsiblesection-frame-and-cleanup.ts +0 -0
  198. /package/src/{lib → editor/core/themes}/migrations/2026-05-08-collapsiblesection-variant-namespace.ts +0 -0
  199. /package/src/{lib → editor/core/themes}/migrations/2026-05-10-sectiondivider-gradient-stops.ts +0 -0
  200. /package/src/{lib → editor/core/themes}/migrations/2026-05-13-primary-to-brand.ts +0 -0
  201. /package/src/{lib → editor/core/themes}/parsers/globalRootBlock.ts +0 -0
  202. /package/src/{lib → editor/core/themes}/slices/domainVars.ts +0 -0
  203. /package/src/{lib → editor/overlay}/overlayState.ts +0 -0
  204. /package/src/{pages → editor/pages}/ComponentEditorPage.svelte.d.ts +0 -0
  205. /package/src/{pages → editor/pages}/Editor.svelte.d.ts +0 -0
  206. /package/src/{ui → editor/ui}/Toggle.svelte +0 -0
  207. /package/src/{ui → editor/ui}/UIDialog.svelte +0 -0
  208. /package/src/{ui → editor/ui}/UIFontWeightSelector.svelte +0 -0
  209. /package/src/{ui → editor/ui}/UIOptionItem.svelte +0 -0
  210. /package/src/{ui → editor/ui}/UIOptionList.svelte +0 -0
  211. /package/src/{ui → editor/ui}/UIRadioGroup.svelte +0 -0
  212. /package/src/{lib → editor/ui}/copyPopover.ts +0 -0
  213. /package/src/{ui → editor/ui}/curveEngine.ts +0 -0
  214. /package/src/{ui → editor/ui}/index.ts +0 -0
  215. /package/src/{ui → editor/ui}/keepInViewport.ts +0 -0
  216. /package/src/{ui → editor/ui}/palette/ScaleCurveEditor.svelte +0 -0
  217. /package/src/{lib → editor/ui}/scrollSection.ts +0 -0
  218. /package/src/{ui → editor/ui}/sections/tokenScales.ts +0 -0
  219. /package/src/{ui → editor/ui}/variantScales.ts +0 -0
  220. /package/src/{assets → system/assets}/newspaper.webp +0 -0
  221. /package/src/{assets → system/assets}/offering.webp +0 -0
  222. /package/src/{components → system/components}/Callout.svelte +0 -0
  223. /package/src/{components → system/components}/Image.svelte +0 -0
  224. /package/src/{components → system/components}/RadioButton.svelte +0 -0
  225. /package/src/{components → system/components}/types.ts +0 -0
  226. /package/src/{styles → system/styles}/_padding.scss +0 -0
  227. /package/src/{styles → system/styles}/fonts/Fraunces/Fraunces-italic-latin-ext.woff2 +0 -0
  228. /package/src/{styles → system/styles}/fonts/Fraunces/Fraunces-italic-latin.woff2 +0 -0
  229. /package/src/{styles → system/styles}/fonts/Fraunces/Fraunces-roman-latin-ext.woff2 +0 -0
  230. /package/src/{styles → system/styles}/fonts/Fraunces/Fraunces-roman-latin.woff2 +0 -0
  231. /package/src/{styles → system/styles}/fonts/Manrope/Manrope-latin-ext.woff2 +0 -0
  232. /package/src/{styles → system/styles}/fonts/Manrope/Manrope-latin.woff2 +0 -0
@@ -11,10 +11,8 @@
11
11
  function statePrefix(v: Variant, s: StateName): string {
12
12
  return s === 'default' ? `--button-${v}` : `--button-${v}-${s}`;
13
13
  }
14
- /** Buttons have a single text slot, so its typography props live as peer
15
- * rows in the unified token grid (no TypeEditor fieldset). Default state
16
- * carries the full font shape (family/weight/size/line-height); hover and
17
- * disabled only override text color, so they keep just the color row. */
14
+ // Single text slot: typography props are peer rows, not a TypeEditor fieldset.
15
+ // Default carries the full font shape; hover/disabled only override text color.
18
16
  function variantStateTokens(v: Variant, s: StateName): Token[] {
19
17
  const p = statePrefix(v, s);
20
18
  const tokens: Token[] = [
@@ -39,16 +37,27 @@
39
37
  return tokens;
40
38
  }
41
39
 
40
+ // Outline is the only variant that paints a surface tint on :active; the rest
41
+ // express press feedback through transform/shadow only. Expose just the one
42
+ // tunable property here rather than adding an active state to every variant.
43
+ const outlineActiveTokens: Token[] = [
44
+ { label: 'surface color', groupKey: 'surface', variable: '--button-outline-active-surface' },
45
+ ];
46
+
42
47
  function variantStates(v: Variant): Record<string, Token[]> {
43
- return Object.fromEntries(stateNames.map((s) => [s, variantStateTokens(v, s)]));
48
+ const out: Record<string, Token[]> = {};
49
+ out.default = variantStateTokens(v, 'default');
50
+ out.hover = variantStateTokens(v, 'hover');
51
+ if (v === 'outline') out.active = outlineActiveTokens;
52
+ out.disabled = variantStateTokens(v, 'disabled');
53
+ return out;
44
54
  }
45
55
  export const allTokens: Token[] = variants.flatMap((v) =>
46
56
  Object.values(variantStates(v)).flat(),
47
57
  );
48
58
 
49
- // Linked block:
50
- // - shape props (border-width, radius, padding) link across every variant × state — buttons share one geometry.
51
- // - typography props link across all six variants.
59
+ // Shape props link across every variant × state (buttons share one geometry).
60
+ // Typography props link across all variants.
52
61
  const linkableContexts = new Map<string, string>([
53
62
  ...variants.flatMap((v) => stateNames.flatMap((s) => [
54
63
  [`${statePrefix(v, s)}-border-width`, `${v} ${s}`] as const,
@@ -68,11 +77,11 @@
68
77
  </script>
69
78
 
70
79
  <script lang="ts">
71
- import Button from '../components/Button.svelte';
80
+ import Button from '../../system/components/Button.svelte';
72
81
  import Toggle from '../ui/Toggle.svelte';
73
82
  import VariantGroup from './scaffolding/VariantGroup.svelte';
74
83
  import ComponentEditorBase from './scaffolding/ComponentEditorBase.svelte';
75
- import { editorState, setComponentAlias } from '../lib/editorStore';
84
+ import { editorState, setComponentAlias } from '../core/store/editorStore';
76
85
  import { computeLinkedBlock, withLinkedDisabled } from './scaffolding/linkedBlock';
77
86
 
78
87
  let shimmerRef = $derived($editorState.components.button?.aliases['--button-shimmer']);
@@ -89,15 +98,7 @@
89
98
  ) as Record<string, Token[]>);
90
99
  </script>
91
100
 
92
- <ComponentEditorBase {component} title="Button" description="Reusable button component with multiple variants and sizes. Import from <code>components/Button.svelte</code>" tokens={allTokens} {linked} variants={variantOptions}>
93
- {#snippet config()}
94
-
95
- <label>
96
- <span>Hover shimmer</span>
97
- <Toggle checked={shimmerEnabled} onchange={handleShimmerChange} />
98
- </label>
99
-
100
- {/snippet}
101
+ <ComponentEditorBase {component} title="Button" description="Reusable button component with multiple variants and sizes." tokens={allTokens} {linked} variants={variantOptions}>
101
102
  {#each variants as v}
102
103
  <VariantGroup
103
104
  name={v}
@@ -106,8 +107,15 @@
106
107
  {component}
107
108
  columns={2}
108
109
  siblings={buildSiblings(variants, v, variantStates)}
109
-
110
110
  >
111
+ {#snippet extraPropertyRows(stateName)}
112
+ {#if stateName === 'hover'}
113
+ <div class="property-row">
114
+ <span class="property-label">hover shimmer</span>
115
+ <Toggle checked={shimmerEnabled} onchange={handleShimmerChange} />
116
+ </div>
117
+ {/if}
118
+ {/snippet}
111
119
  {#snippet children({ activeState })}
112
120
  {@const forceClass = activeState === 'hover' ? 'force-hover' : ''}
113
121
  {@const isDisabled = activeState === 'disabled'}
@@ -150,7 +158,7 @@
150
158
 
151
159
  .size-divider {
152
160
  width: 1px;
153
- background: var(--ui-border-faint);
161
+ background: var(--ui-border-low);
154
162
  align-self: stretch;
155
163
  }
156
164
 
@@ -37,7 +37,10 @@
37
37
  { label: 'divider thickness', groupKey: 'bar-divider-thickness', variable: '--tabbar-bar-divider-thickness' },
38
38
  { label: 'indicator thickness', groupKey: 'bar-indicator-thickness', variable: '--tabbar-bar-indicator-thickness' },
39
39
  { label: 'space above', groupKey: 'bar-top-margin', variable: '--tabbar-bar-top-margin' },
40
- { label: 'space below tabs', groupKey: 'bar-bottom-padding', variable: '--tabbar-bar-bottom-padding' },
40
+ // Consumed via `padding-bottom: var(--tabbar-bar-bottom-padding)` — a
41
+ // one-axis read. Splitting would produce top/left/right values that have
42
+ // nowhere to render.
43
+ { label: 'space below tabs', groupKey: 'bar-bottom-padding', variable: '--tabbar-bar-bottom-padding', splittable: false },
41
44
  { label: 'space under divider', groupKey: 'bar-bottom-margin', variable: '--tabbar-bar-bottom-margin' },
42
45
  { label: 'tab gap', groupKey: 'tab-gap', variable: '--tabbar-tab-gap' },
43
46
  ],
@@ -81,10 +84,10 @@
81
84
  </script>
82
85
 
83
86
  <script lang="ts">
84
- import TabBar from '../components/TabBar.svelte';
87
+ import TabBar from '../../system/components/TabBar.svelte';
85
88
  import VariantGroup from './scaffolding/VariantGroup.svelte';
86
89
  import ComponentEditorBase from './scaffolding/ComponentEditorBase.svelte';
87
- import { editorState } from '../lib/editorStore';
90
+ import { editorState } from '../core/store/editorStore';
88
91
  import { computeLinkedBlock, withLinkedDisabled } from './scaffolding/linkedBlock';
89
92
 
90
93
  let selectedDemoTab = $state('overview');
@@ -102,7 +105,7 @@
102
105
  ) as Record<string, Token[]>);
103
106
  </script>
104
107
 
105
- <ComponentEditorBase {component} title="Tab Bar" description="Tab navigation with icon support and disabled state. Import from <code>components/TabBar.svelte</code>" tokens={allTokens} {linked}>
108
+ <ComponentEditorBase {component} title="Tab Bar" description="Tab navigation with icon support and disabled state." tokens={allTokens} {linked}>
106
109
  <VariantGroup
107
110
  name="tabbar"
108
111
  title="Tab Bar"
@@ -115,13 +118,7 @@
115
118
  {@const forceClass = activeState === 'hover tab' ? 'force-hover' : ''}
116
119
  <TabBar tabs={demoTabs} selectedTab={selectedDemoTab} class={forceClass} on:tabChange={(e) => (selectedDemoTab = e.detail)} />
117
120
  <div class="tab-content-demo">
118
- {#if selectedDemoTab === 'overview'}
119
- <p style="margin: 0;">Overview tab content</p>
120
- {:else if selectedDemoTab === 'details'}
121
- <p style="margin: 0;">Details tab content</p>
122
- {:else if selectedDemoTab === 'settings'}
123
- <p style="margin: 0;">Settings tab content</p>
124
- {/if}
121
+ <p style="margin: 0;">placeholder tab content</p>
125
122
  </div>
126
123
  {/snippet}
127
124
  </VariantGroup>
@@ -129,11 +126,9 @@
129
126
 
130
127
  <style>
131
128
  .tab-content-demo {
129
+ width: 100%;
132
130
  padding: var(--space-16);
133
131
  color: var(--ui-text-secondary);
134
132
  background: var(--ui-surface-low);
135
- border: 1px solid var(--ui-border-faint);
136
- border-top: none;
137
- border-radius: 0 0 var(--radius-md) var(--radius-md);
138
133
  }
139
134
  </style>
@@ -3,15 +3,10 @@
3
3
 
4
4
  export const component = 'table';
5
5
 
6
- // Border colors and widths are linkable across wrapper/header/row/column so
7
- // a user can lock "all lines on the table" to the same swatch + weight with
8
- // one move, then break out individual surfaces when needed. Every other
9
- // groupKey is slot-unique so header/cell/stripe stay independent — header bg
10
- // vs zebra stripe, header pad vs cell pad, header text vs cell text all
11
- // serve different visual roles. (Sharing a groupKey would silently declare
12
- // them as siblings without surfacing the link in the LinkedBlock.)
6
+ // Shared border/width groupKeys link all table lines; other groupKeys are slot-unique to keep surfaces independent.
13
7
  const states: Record<string, Token[]> = {
14
8
  wrapper: [
9
+ { label: 'surface color', groupKey: 'wrapper-surface', variable: '--table-default-surface' },
15
10
  { label: 'border color', canBeLinked: true, groupKey: 'border', variable: '--table-default-border' },
16
11
  { label: 'border width', canBeLinked: true, groupKey: 'width', variable: '--table-default-border-width' },
17
12
  { label: 'corner radius', groupKey: 'radius', variable: '--table-default-radius' },
@@ -27,9 +22,10 @@
27
22
  { label: 'padding', groupKey: 'cell-padding', variable: '--table-default-cell-padding' },
28
23
  ],
29
24
  row: [
25
+ { label: 'surface color', groupKey: 'row-surface', variable: '--table-default-row-surface' },
26
+ { label: 'stripe surface', groupKey: 'row-stripe-surface', variable: '--table-default-row-stripe-surface' },
30
27
  { label: 'divider color', canBeLinked: true, groupKey: 'border', variable: '--table-default-row-divider' },
31
28
  { label: 'divider width', canBeLinked: true, groupKey: 'width', variable: '--table-default-row-divider-width' },
32
- { label: 'stripe surface', groupKey: 'row-stripe-surface', variable: '--table-default-row-stripe-surface' },
33
29
  ],
34
30
  column: [
35
31
  { label: 'divider color', canBeLinked: true, groupKey: 'border', variable: '--table-default-column-divider' },
@@ -37,8 +33,7 @@
37
33
  ],
38
34
  };
39
35
 
40
- // State name is the context label so the LinkageChart rows read as
41
- // wrapper/header/row/column for each shared groupKey.
36
+ // State name as context label so LinkageChart rows read wrapper/header/row/column.
42
37
  const linkableContexts = new Map<string, string>(
43
38
  Object.entries(states).flatMap(([state, tokens]) =>
44
39
  tokens
@@ -65,11 +60,7 @@
65
60
  lineHeightVariable: '--table-default-cell-line-height',
66
61
  }],
67
62
  };
68
- // Slot-unique groupKeys keep header text and cell text independent. The
69
- // generic `buildTypeGroupColorTokens` helper isn't used here because it
70
- // derives groupKey from the variable's last-dash suffix, which collapses
71
- // both `--table-...-header-text` and `--table-...-cell-text` onto a shared
72
- // `text` groupKey — phantom-linking the two slots.
63
+ // Hand-rolled (not buildTypeGroupColorTokens) because its suffix-derived groupKey would phantom-link header-text and cell-text.
73
64
  const typeGroupColorTokens: Token[] = [
74
65
  { label: 'color', groupKey: 'header-text', variable: '--table-default-header-text' },
75
66
  { label: 'color', groupKey: 'cell-text', variable: '--table-default-cell-text' },
@@ -93,10 +84,10 @@
93
84
  </script>
94
85
 
95
86
  <script lang="ts">
96
- import Table from '../components/Table.svelte';
87
+ import Table from '../../system/components/Table.svelte';
97
88
  import VariantGroup from './scaffolding/VariantGroup.svelte';
98
89
  import ComponentEditorBase from './scaffolding/ComponentEditorBase.svelte';
99
- import { editorState } from '../lib/editorStore';
90
+ import { editorState } from '../core/store/editorStore';
100
91
  import { computeLinkedBlock, withLinkedDisabled } from './scaffolding/linkedBlock';
101
92
 
102
93
  let linked = $derived(computeLinkedBlock(component, linkableContexts, allTokens, $editorState));
@@ -105,7 +96,7 @@
105
96
  ) as Record<string, Token[]>);
106
97
  </script>
107
98
 
108
- <ComponentEditorBase {component} title="Table" description="Styled wrapper around <code>&lt;table&gt;</code> with horizontal scroll on narrow viewports. Import from <code>components/Table.svelte</code>" tokens={allTokens} {linked}>
99
+ <ComponentEditorBase {component} title="Table" description="Styled wrapper around <code>&lt;table&gt;</code> with horizontal scroll on narrow viewports." tokens={allTokens} {linked}>
109
100
  <VariantGroup name="table" title="Table" states={visibleStates} {typeGroups} {component}>
110
101
  <Table>
111
102
  <table>
@@ -40,35 +40,14 @@
40
40
  </script>
41
41
 
42
42
  <script lang="ts">
43
- import { onMount } from 'svelte';
44
- import Tooltip from '../components/Tooltip.svelte';
43
+ import Tooltip from '../../system/components/Tooltip.svelte';
45
44
  import VariantGroup from './scaffolding/VariantGroup.svelte';
46
45
  import ComponentEditorBase from './scaffolding/ComponentEditorBase.svelte';
47
- import ShadowBackdrop from './scaffolding/ShadowBackdrop.svelte';
48
- import UIPaletteSelector from '../ui/UIPaletteSelector.svelte';
49
- import { setCssVar } from '../lib/cssVarSync';
50
46
 
51
- const bgVar = '--backdrop-tooltip-surface';
52
47
  const hintText = 'Helpful Hint';
53
-
54
- onMount(() => {
55
- if (!document.documentElement.style.getPropertyValue(bgVar)) {
56
- setCssVar(bgVar, 'var(--surface-canvas)');
57
- }
58
- });
59
48
  </script>
60
49
 
61
- <ComponentEditorBase {component} title="Tooltip" description="Hover tooltip with configurable position. Import from <code>components/Tooltip.svelte</code>" tokens={allTokens}>
62
- {#snippet config()}
63
-
64
- <label class="backdrop-config">
65
- <span>Sample background</span>
66
- <div class="picker-slot">
67
- <UIPaletteSelector variable={bgVar} />
68
- </div>
69
- </label>
70
-
71
- {/snippet}
50
+ <ComponentEditorBase {component} title="Tooltip" description="Hover tooltip with configurable position." tokens={allTokens}>
72
51
  <VariantGroup
73
52
  name="tooltip"
74
53
  title="Tooltip"
@@ -76,16 +55,14 @@
76
55
  {typeGroups}
77
56
  {component}
78
57
  >
79
- <ShadowBackdrop mode="color" colorVariable={bgVar}>
80
- <div class="tooltip-demo-row">
81
- <Tooltip text={hintText} open>
82
- <span class="tooltip-demo-target">Helpful Hint</span>
83
- </Tooltip>
84
- <Tooltip text={hintText} position="bottom">
85
- <span class="tooltip-demo-target">Hover me</span>
86
- </Tooltip>
87
- </div>
88
- </ShadowBackdrop>
58
+ <div class="tooltip-demo-row">
59
+ <Tooltip text={hintText} open>
60
+ <span class="tooltip-demo-target">Helpful Hint</span>
61
+ </Tooltip>
62
+ <Tooltip text={hintText} position="bottom">
63
+ <span class="tooltip-demo-target">Hover me</span>
64
+ </Tooltip>
65
+ </div>
89
66
  </VariantGroup>
90
67
  </ComponentEditorBase>
91
68
 
@@ -103,22 +80,9 @@
103
80
  padding: var(--space-8) var(--space-16);
104
81
  color: var(--ui-text-secondary);
105
82
  font-size: var(--font-size-sm);
106
- border: 1px dashed var(--ui-border-subtle);
83
+ border: 1px dashed var(--ui-border-low);
107
84
  border-radius: var(--radius-sm);
108
85
  background: transparent;
109
86
  }
110
87
 
111
- .backdrop-config {
112
- display: inline-flex;
113
- align-items: center;
114
- gap: var(--ui-space-8);
115
- }
116
-
117
- .picker-slot {
118
- min-width: 8rem;
119
- }
120
-
121
- .picker-slot :global(.ui-token-selector) {
122
- width: 100%;
123
- }
124
88
  </style>
@@ -0,0 +1,10 @@
1
+ // Augment the global `*.svelte` ambient module to expose the named
2
+ // `allTokens` export that each `<Name>Editor.svelte` declares in its
3
+ // `<script module>` block. The default ambient (shipped via
4
+ // node_modules/svelte/types/index.d.ts) declares only a default export,
5
+ // so without this augmentation tsc fails on the named imports in
6
+ // registry.ts.
7
+
8
+ declare module '*.svelte' {
9
+ export const allTokens: import('./scaffolding/types').Token[];
10
+ }
@@ -1,6 +1,6 @@
1
1
  import type { Component } from 'svelte';
2
2
  import type { Token } from './scaffolding/types';
3
- import { registerComponentSchema } from '../lib/editorStore';
3
+ import { registerComponentSchema } from '../core/store/editorStore';
4
4
 
5
5
  import BadgeEditor, { allTokens as badgeTokens } from './BadgeEditor.svelte';
6
6
  import CalloutEditor, { allTokens as calloutTokens } from './CalloutEditor.svelte';
@@ -11,6 +11,7 @@ import CollapsibleSectionEditor, { allTokens as collapsibleSectionTokens } from
11
11
  import DialogEditor, { allTokens as dialogTokens } from './DialogEditor.svelte';
12
12
  import ImageEditor, { allTokens as imageTokens } from './ImageEditor.svelte';
13
13
  import InlineEditActionsEditor, { allTokens as inlineEditActionsTokens } from './InlineEditActionsEditor.svelte';
14
+ import MenuSelectEditor, { allTokens as menuSelectTokens } from './MenuSelectEditor.svelte';
14
15
  import NotificationEditor, { allTokens as notificationTokens } from './NotificationEditor.svelte';
15
16
  import ProgressBarEditor, { allTokens as progressBarTokens } from './ProgressBarEditor.svelte';
16
17
  import RadioButtonEditor, { allTokens as radioButtonTokens } from './RadioButtonEditor.svelte';
@@ -32,6 +33,7 @@ export type ComponentId =
32
33
  | 'cornerbadge'
33
34
  | 'image'
34
35
  | 'inlineeditactions'
36
+ | 'menuselect'
35
37
  | 'sectiondivider'
36
38
  | 'collapsiblesection'
37
39
  | 'table'
@@ -71,7 +73,7 @@ export const componentRegistry: Readonly<Record<ComponentId, RegistryEntry>> = O
71
73
  id: 'segmentedcontrol',
72
74
  label: 'Segmented Control',
73
75
  icon: 'fas fa-hand-pointer',
74
- sourceFile: 'src/components/SegmentedControl.svelte',
76
+ sourceFile: 'src/system/components/SegmentedControl.svelte',
75
77
  editorComponent: SegmentedControlEditor,
76
78
  schema: segmentedControlTokens,
77
79
  },
@@ -79,7 +81,7 @@ export const componentRegistry: Readonly<Record<ComponentId, RegistryEntry>> = O
79
81
  id: 'button',
80
82
  label: 'Button',
81
83
  icon: 'fas fa-square',
82
- sourceFile: 'src/components/Button.svelte',
84
+ sourceFile: 'src/system/components/Button.svelte',
83
85
  editorComponent: StandardButtonsEditor,
84
86
  schema: buttonTokens,
85
87
  },
@@ -87,7 +89,7 @@ export const componentRegistry: Readonly<Record<ComponentId, RegistryEntry>> = O
87
89
  id: 'notification',
88
90
  label: 'Notification',
89
91
  icon: 'fas fa-bell',
90
- sourceFile: 'src/components/Notification.svelte',
92
+ sourceFile: 'src/system/components/Notification.svelte',
91
93
  editorComponent: NotificationEditor,
92
94
  schema: notificationTokens,
93
95
  },
@@ -95,7 +97,7 @@ export const componentRegistry: Readonly<Record<ComponentId, RegistryEntry>> = O
95
97
  id: 'dialog',
96
98
  label: 'Dialog',
97
99
  icon: 'fas fa-window-restore',
98
- sourceFile: 'src/components/Dialog.svelte',
100
+ sourceFile: 'src/system/components/Dialog.svelte',
99
101
  editorComponent: DialogEditor,
100
102
  schema: dialogTokens,
101
103
  },
@@ -103,7 +105,7 @@ export const componentRegistry: Readonly<Record<ComponentId, RegistryEntry>> = O
103
105
  id: 'radiobutton',
104
106
  label: 'Radio Button',
105
107
  icon: 'fas fa-dot-circle',
106
- sourceFile: 'src/components/RadioButton.svelte',
108
+ sourceFile: 'src/system/components/RadioButton.svelte',
107
109
  editorComponent: RadioButtonEditor,
108
110
  schema: radioButtonTokens,
109
111
  },
@@ -111,7 +113,7 @@ export const componentRegistry: Readonly<Record<ComponentId, RegistryEntry>> = O
111
113
  id: 'card',
112
114
  label: 'Card',
113
115
  icon: 'fas fa-id-card',
114
- sourceFile: 'src/components/Card.svelte',
116
+ sourceFile: 'src/system/components/Card.svelte',
115
117
  editorComponent: CardEditor,
116
118
  schema: cardTokens,
117
119
  },
@@ -119,7 +121,7 @@ export const componentRegistry: Readonly<Record<ComponentId, RegistryEntry>> = O
119
121
  id: 'badge',
120
122
  label: 'Badge',
121
123
  icon: 'fas fa-tag',
122
- sourceFile: 'src/components/Badge.svelte',
124
+ sourceFile: 'src/system/components/Badge.svelte',
123
125
  editorComponent: BadgeEditor,
124
126
  schema: badgeTokens,
125
127
  },
@@ -127,7 +129,7 @@ export const componentRegistry: Readonly<Record<ComponentId, RegistryEntry>> = O
127
129
  id: 'callout',
128
130
  label: 'Callout',
129
131
  icon: 'fas fa-quote-left',
130
- sourceFile: 'src/components/Callout.svelte',
132
+ sourceFile: 'src/system/components/Callout.svelte',
131
133
  editorComponent: CalloutEditor,
132
134
  schema: calloutTokens,
133
135
  },
@@ -135,7 +137,7 @@ export const componentRegistry: Readonly<Record<ComponentId, RegistryEntry>> = O
135
137
  id: 'cornerbadge',
136
138
  label: 'Corner Badge',
137
139
  icon: 'fas fa-tags',
138
- sourceFile: 'src/components/CornerBadge.svelte',
140
+ sourceFile: 'src/system/components/CornerBadge.svelte',
139
141
  editorComponent: CornerBadgeEditor,
140
142
  schema: cornerBadgeTokens,
141
143
  },
@@ -143,7 +145,7 @@ export const componentRegistry: Readonly<Record<ComponentId, RegistryEntry>> = O
143
145
  id: 'image',
144
146
  label: 'Image',
145
147
  icon: 'fas fa-image',
146
- sourceFile: 'src/components/Image.svelte',
148
+ sourceFile: 'src/system/components/Image.svelte',
147
149
  editorComponent: ImageEditor,
148
150
  schema: imageTokens,
149
151
  },
@@ -151,15 +153,23 @@ export const componentRegistry: Readonly<Record<ComponentId, RegistryEntry>> = O
151
153
  id: 'inlineeditactions',
152
154
  label: 'Inline Edit Actions',
153
155
  icon: 'fas fa-pen',
154
- sourceFile: 'src/components/InlineEditActions.svelte',
156
+ sourceFile: 'src/system/components/InlineEditActions.svelte',
155
157
  editorComponent: InlineEditActionsEditor,
156
158
  schema: inlineEditActionsTokens,
157
159
  },
160
+ menuselect: {
161
+ id: 'menuselect',
162
+ label: 'Menu Select',
163
+ icon: 'fas fa-list',
164
+ sourceFile: 'src/system/components/MenuSelect.svelte',
165
+ editorComponent: MenuSelectEditor,
166
+ schema: menuSelectTokens,
167
+ },
158
168
  sectiondivider: {
159
169
  id: 'sectiondivider',
160
170
  label: 'Section Divider',
161
171
  icon: 'fas fa-minus',
162
- sourceFile: 'src/components/SectionDivider.svelte',
172
+ sourceFile: 'src/system/components/SectionDivider.svelte',
163
173
  editorComponent: SectionDividerEditor,
164
174
  schema: sectionDividerTokens,
165
175
  },
@@ -167,7 +177,7 @@ export const componentRegistry: Readonly<Record<ComponentId, RegistryEntry>> = O
167
177
  id: 'collapsiblesection',
168
178
  label: 'Collapsible Section',
169
179
  icon: 'fas fa-chevron-down',
170
- sourceFile: 'src/components/CollapsibleSection.svelte',
180
+ sourceFile: 'src/system/components/CollapsibleSection.svelte',
171
181
  editorComponent: CollapsibleSectionEditor,
172
182
  schema: collapsibleSectionTokens,
173
183
  },
@@ -175,7 +185,7 @@ export const componentRegistry: Readonly<Record<ComponentId, RegistryEntry>> = O
175
185
  id: 'table',
176
186
  label: 'Table',
177
187
  icon: 'fas fa-table',
178
- sourceFile: 'src/components/Table.svelte',
188
+ sourceFile: 'src/system/components/Table.svelte',
179
189
  editorComponent: TableEditor,
180
190
  schema: tableTokens,
181
191
  },
@@ -183,7 +193,7 @@ export const componentRegistry: Readonly<Record<ComponentId, RegistryEntry>> = O
183
193
  id: 'tabbar',
184
194
  label: 'Tab Bar',
185
195
  icon: 'fas fa-columns',
186
- sourceFile: 'src/components/TabBar.svelte',
196
+ sourceFile: 'src/system/components/TabBar.svelte',
187
197
  editorComponent: TabBarEditor,
188
198
  schema: tabBarTokens,
189
199
  },
@@ -191,7 +201,7 @@ export const componentRegistry: Readonly<Record<ComponentId, RegistryEntry>> = O
191
201
  id: 'tooltip',
192
202
  label: 'Tooltip',
193
203
  icon: 'fas fa-comment-dots',
194
- sourceFile: 'src/components/Tooltip.svelte',
204
+ sourceFile: 'src/system/components/Tooltip.svelte',
195
205
  editorComponent: TooltipEditor,
196
206
  schema: tooltipTokens,
197
207
  },
@@ -199,7 +209,7 @@ export const componentRegistry: Readonly<Record<ComponentId, RegistryEntry>> = O
199
209
  id: 'progressbar',
200
210
  label: 'Progress Bar',
201
211
  icon: 'fas fa-tasks',
202
- sourceFile: 'src/components/ProgressBar.svelte',
212
+ sourceFile: 'src/system/components/ProgressBar.svelte',
203
213
  editorComponent: ProgressBarEditor,
204
214
  schema: progressBarTokens,
205
215
  },
@@ -9,10 +9,14 @@
9
9
  value?: number;
10
10
  label?: string;
11
11
  size?: number;
12
+ /** 'horizontal' (default) lays the label, dial, input, and degree mark on a
13
+ * single line. 'vertical' stacks the dial above the input — used when the
14
+ * dial sits in its own column with a section header providing the label. */
15
+ orientation?: 'horizontal' | 'vertical';
12
16
  onchange?: (payload: { value: number }) => void;
13
17
  }
14
18
 
15
- let { value = $bindable(0), label = 'Angle', size = 44, onchange }: Props = $props();
19
+ let { value = $bindable(0), label = 'Angle', size = 44, orientation = 'horizontal', onchange }: Props = $props();
16
20
 
17
21
  let dialEl: HTMLDivElement | undefined = $state();
18
22
  let dragging = $state(false);
@@ -63,8 +67,8 @@
63
67
  let indicatorTransform = $derived(`rotate(${value}deg)`);
64
68
  </script>
65
69
 
66
- <div class="angle-dial-row">
67
- {#if label}
70
+ <div class="angle-dial-row" class:vertical={orientation === 'vertical'}>
71
+ {#if label && orientation === 'horizontal'}
68
72
  <span class="dial-label">{label}:</span>
69
73
  {/if}
70
74
  <div
@@ -90,16 +94,18 @@
90
94
  <div class="indicator" style="transform: {indicatorTransform}"></div>
91
95
  <div class="hub"></div>
92
96
  </div>
93
- <input
94
- class="num"
95
- type="number"
96
- min="0"
97
- max="360"
98
- step="1"
99
- value={value}
100
- onchange={onInputChange}
101
- />
102
- <span class="suffix">°</span>
97
+ <div class="num-row" style={orientation === 'vertical' ? `width: ${size}px;` : ''}>
98
+ <input
99
+ class="num"
100
+ type="number"
101
+ min="0"
102
+ max="360"
103
+ step="1"
104
+ value={value}
105
+ onchange={onInputChange}
106
+ />
107
+ <span class="suffix">°</span>
108
+ </div>
103
109
  </div>
104
110
 
105
111
  <style>
@@ -110,6 +116,39 @@
110
116
  font-size: var(--ui-font-size-sm);
111
117
  color: var(--ui-text-secondary);
112
118
  }
119
+ /* Stacked layout: dial centered, input + degree mark on the row below. Used
120
+ when the dial sits in its own grid column with an external section label. */
121
+ .angle-dial-row.vertical {
122
+ flex-direction: column;
123
+ align-items: center;
124
+ gap: var(--ui-space-6);
125
+ }
126
+
127
+ .num-row {
128
+ display: inline-flex;
129
+ align-items: center;
130
+ gap: var(--ui-space-8);
131
+ }
132
+
133
+ /* Vertical mode: lock the row to the dial's width and overlay the degree
134
+ mark inside the input's right padding so the input fills the column
135
+ instead of the row growing past the dial. */
136
+ .angle-dial-row.vertical .num-row {
137
+ position: relative;
138
+ display: block;
139
+ }
140
+ .angle-dial-row.vertical .num {
141
+ width: 100%;
142
+ box-sizing: border-box;
143
+ padding-right: 1.25rem;
144
+ }
145
+ .angle-dial-row.vertical .suffix {
146
+ position: absolute;
147
+ right: var(--ui-space-6);
148
+ top: 50%;
149
+ transform: translateY(-50%);
150
+ pointer-events: none;
151
+ }
113
152
 
114
153
  .dial-label {
115
154
  user-select: none;
@@ -119,7 +158,7 @@
119
158
  position: relative;
120
159
  border-radius: 50%;
121
160
  background: var(--ui-surface-lowest);
122
- border: 1px solid var(--ui-border-default);
161
+ border: 1px solid var(--ui-border);
123
162
  cursor: grab;
124
163
  flex: none;
125
164
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.02);
@@ -167,7 +206,7 @@
167
206
  width: 3.25rem;
168
207
  padding: var(--ui-space-2) var(--ui-space-6);
169
208
  background: var(--ui-surface-lowest);
170
- border: 1px solid var(--ui-border-faint);
209
+ border: 1px solid var(--ui-border-low);
171
210
  border-radius: var(--ui-radius-sm);
172
211
  color: var(--ui-text-primary);
173
212
  font-family: var(--ui-font-mono);