@motion-proto/live-tokens 0.3.9 → 0.6.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 (107) hide show
  1. package/README.md +47 -4
  2. package/package.json +18 -12
  3. package/src/component-editor/BadgeEditor.svelte +24 -22
  4. package/src/component-editor/CalloutEditor.svelte +3 -3
  5. package/src/component-editor/CardEditor.svelte +25 -21
  6. package/src/component-editor/CollapsibleSectionEditor.svelte +27 -25
  7. package/src/component-editor/CornerBadgeEditor.svelte +37 -35
  8. package/src/component-editor/DialogEditor.svelte +26 -24
  9. package/src/component-editor/ImageEditor.svelte +11 -9
  10. package/src/component-editor/InlineEditActionsEditor.svelte +17 -15
  11. package/src/component-editor/NotificationEditor.svelte +32 -30
  12. package/src/component-editor/ProgressBarEditor.svelte +3 -3
  13. package/src/component-editor/RadioButtonEditor.svelte +31 -29
  14. package/src/component-editor/SectionDividerEditor.svelte +30 -28
  15. package/src/component-editor/SegmentedControlEditor.svelte +29 -25
  16. package/src/component-editor/StandardButtonsEditor.svelte +42 -38
  17. package/src/component-editor/TabBarEditor.svelte +20 -18
  18. package/src/component-editor/TableEditor.svelte +4 -4
  19. package/src/component-editor/TooltipEditor.svelte +11 -9
  20. package/src/component-editor/registry.ts +2 -2
  21. package/src/component-editor/scaffolding/AngleDial.svelte +20 -19
  22. package/src/component-editor/scaffolding/ComponentEditorBase.svelte +44 -20
  23. package/src/component-editor/scaffolding/ComponentFileManager.svelte +262 -38
  24. package/src/component-editor/scaffolding/ComponentFileMenu.svelte +41 -29
  25. package/src/component-editor/scaffolding/ComponentsTab.svelte +7 -3
  26. package/src/component-editor/scaffolding/CopyFromMenu.svelte +21 -12
  27. package/src/component-editor/scaffolding/DemoHeader.svelte +13 -4
  28. package/src/component-editor/scaffolding/DividerEditor.svelte +27 -14
  29. package/src/component-editor/scaffolding/FieldsetWrapper.svelte +10 -4
  30. package/src/component-editor/scaffolding/GradientCard.svelte +25 -20
  31. package/src/component-editor/scaffolding/LinkageChart.svelte +43 -34
  32. package/src/component-editor/scaffolding/LinkedBlock.svelte +24 -21
  33. package/src/component-editor/scaffolding/NonStylableConfig.svelte +6 -1
  34. package/src/component-editor/scaffolding/SaveAsDialog.svelte +39 -35
  35. package/src/component-editor/scaffolding/ShadowBackdrop.svelte +21 -9
  36. package/src/component-editor/scaffolding/ShadowBackdropControls.svelte +8 -3
  37. package/src/component-editor/scaffolding/StateBlock.svelte +30 -13
  38. package/src/component-editor/scaffolding/TokenLayout.svelte +46 -30
  39. package/src/component-editor/scaffolding/TypeEditor.svelte +52 -26
  40. package/src/component-editor/scaffolding/VariantGroup.svelte +81 -48
  41. package/src/component-editor/scaffolding/componentSectionType.ts +2 -2
  42. package/src/components/Badge.svelte +45 -26
  43. package/src/components/Button.svelte +44 -21
  44. package/src/components/Callout.svelte +17 -12
  45. package/src/components/Card.svelte +23 -11
  46. package/src/components/CollapsibleSection.svelte +56 -27
  47. package/src/components/CornerBadge.svelte +32 -18
  48. package/src/components/Dialog.svelte +55 -31
  49. package/src/components/Image.svelte +14 -5
  50. package/src/components/InlineEditActions.svelte +22 -10
  51. package/src/components/Notification.svelte +39 -19
  52. package/src/components/ProgressBar.svelte +27 -17
  53. package/src/components/RadioButton.svelte +27 -10
  54. package/src/components/SectionDivider.svelte +34 -26
  55. package/src/components/SegmentedControl.svelte +23 -9
  56. package/src/components/TabBar.svelte +23 -10
  57. package/src/components/Table.svelte +8 -3
  58. package/src/components/Tooltip.svelte +15 -5
  59. package/src/lib/ColumnsOverlay.svelte +3 -3
  60. package/src/lib/LiveEditorOverlay.svelte +57 -36
  61. package/src/pages/ComponentEditorPage.svelte +25 -14
  62. package/src/pages/Editor.svelte +8 -2
  63. package/src/pages/EditorShell.svelte +24 -20
  64. package/src/styles/site.css +138 -0
  65. package/src/styles/tokens.css +78 -76
  66. package/src/styles/ui-form-controls.css +186 -0
  67. package/src/ui/BezierCurveEditor.svelte +59 -43
  68. package/src/ui/ColorEditPanel.svelte +71 -44
  69. package/src/ui/EditorViewSwitcher.svelte +9 -5
  70. package/src/ui/FontStackEditor.svelte +17 -16
  71. package/src/ui/GradientEditor.svelte +42 -33
  72. package/src/ui/GradientStopPicker.svelte +18 -29
  73. package/src/ui/PaletteEditor.svelte +238 -212
  74. package/src/ui/PresetFileManager.svelte +20 -18
  75. package/src/ui/ProjectFontsSection.svelte +34 -34
  76. package/src/ui/SurfacesTab.svelte +3 -3
  77. package/src/ui/TextTab.svelte +2 -2
  78. package/src/ui/ThemeFileManager.svelte +38 -35
  79. package/src/ui/Toggle.svelte +11 -9
  80. package/src/ui/UICopyPopover.svelte +19 -15
  81. package/src/ui/UIDialog.svelte +48 -30
  82. package/src/ui/UIFontFamilySelector.svelte +104 -78
  83. package/src/ui/UIFontSizeSelector.svelte +38 -20
  84. package/src/ui/UIFontWeightSelector.svelte +33 -13
  85. package/src/ui/UILineHeightSelector.svelte +33 -13
  86. package/src/ui/UILinkToggle.svelte +7 -6
  87. package/src/ui/UIOptionItem.svelte +21 -7
  88. package/src/ui/UIOptionList.svelte +9 -3
  89. package/src/ui/UIPaddingSelector.svelte +108 -82
  90. package/src/ui/UIPaletteSelector.svelte +186 -161
  91. package/src/ui/UIRadio.svelte +23 -8
  92. package/src/ui/UIRadioGroup.svelte +9 -8
  93. package/src/ui/UIRelinkConfirmPopover.svelte +26 -16
  94. package/src/ui/UITokenSelector.svelte +112 -68
  95. package/src/ui/UIVariantSelector.svelte +79 -57
  96. package/src/ui/VariablesTab.svelte +15 -15
  97. package/src/ui/palette/GradientStopEditor.svelte +45 -26
  98. package/src/ui/palette/OverridesPanel.svelte +85 -49
  99. package/src/ui/palette/PaletteBase.svelte +60 -32
  100. package/src/ui/palette/ScaleCurveEditor.svelte +25 -10
  101. package/src/ui/sections/ColumnsSection.svelte +13 -13
  102. package/src/ui/sections/GradientsSection.svelte +12 -9
  103. package/src/ui/sections/OverlaysSection.svelte +50 -47
  104. package/src/ui/sections/ShadowsSection.svelte +110 -104
  105. package/src/ui/sections/TokenScaleTable.svelte +38 -22
  106. package/src/ui/sections/tokenScales.ts +2 -2
  107. package/src/styles/form-controls.css +0 -188
@@ -1,11 +1,21 @@
1
1
  <script lang="ts">
2
- export let text: string = '';
3
- export let position: 'top' | 'bottom' = 'top';
4
- export let open: boolean = false;
2
+ interface Props {
3
+ text?: string;
4
+ position?: 'top' | 'bottom';
5
+ open?: boolean;
6
+ children?: import('svelte').Snippet;
7
+ }
8
+
9
+ let {
10
+ text = '',
11
+ position = 'top',
12
+ open = false,
13
+ children
14
+ }: Props = $props();
5
15
  </script>
6
16
 
7
17
  <div class="tooltip-wrapper" class:open>
8
- <slot />
18
+ {@render children?.()}
9
19
  {#if text}
10
20
  <div class="tooltip" class:bottom={position === 'bottom'}>
11
21
  {text}
@@ -20,7 +30,7 @@
20
30
  --tooltip-text-font-family: var(--font-sans);
21
31
  --tooltip-text-font-size: var(--font-size-sm);
22
32
  --tooltip-text-font-weight: var(--font-weight-normal);
23
- --tooltip-text-line-height: var(--line-height-normal);
33
+ --tooltip-text-line-height: var(--line-height-md);
24
34
  --tooltip-border: var(--border-neutral);
25
35
  --tooltip-border-width: var(--border-width-0);
26
36
  --tooltip-radius: var(--radius-md);
@@ -6,9 +6,9 @@
6
6
  const isInIframe = typeof window !== 'undefined' && window.parent !== window;
7
7
  const enabled = isDev && !isInIframe;
8
8
 
9
- let count = 12;
10
- let gutter = '';
11
- let margin = '';
9
+ let count = $state(12);
10
+ let gutter = $state('');
11
+ let margin = $state('');
12
12
 
13
13
  function readTokens() {
14
14
  const cs = getComputedStyle(document.documentElement);
@@ -1,4 +1,4 @@
1
- <script context="module" lang="ts">
1
+ <script module lang="ts">
2
2
  // __PROJECT_ROOT__ is injected by the themeFileApi Vite plugin as a `define`.
3
3
  // Consumers don't need to configure it themselves. We declare it locally so
4
4
  // this component's type-check passes in consumer projects that haven't added
@@ -12,6 +12,8 @@
12
12
  </script>
13
13
 
14
14
  <script lang="ts">
15
+ import { run } from 'svelte/legacy';
16
+
15
17
  import { onMount, onDestroy } from 'svelte';
16
18
  import { fade } from 'svelte/transition';
17
19
  import { cubicInOut } from 'svelte/easing';
@@ -23,12 +25,23 @@
23
25
  import { postParentRoute } from './parentRouteStore';
24
26
  import type { NavLink } from './navLinkTypes';
25
27
 
26
- export let open: boolean | undefined = undefined;
27
- export let editorPath: string = '/editor';
28
- export let navLinks: NavLink[] = [];
29
- export let pageSources: Record<string, string> = {};
30
- export let hidePageSourceOn: string[] = [];
31
- export let projectRoot: string = INJECTED_PROJECT_ROOT;
28
+ interface Props {
29
+ open?: boolean | undefined;
30
+ editorPath?: string;
31
+ navLinks?: NavLink[];
32
+ pageSources?: Record<string, string>;
33
+ hidePageSourceOn?: string[];
34
+ projectRoot?: string;
35
+ }
36
+
37
+ let {
38
+ open = $bindable(undefined),
39
+ editorPath = '/editor',
40
+ navLinks = [],
41
+ pageSources = {},
42
+ hidePageSourceOn = [],
43
+ projectRoot = INJECTED_PROJECT_ROOT
44
+ }: Props = $props();
32
45
 
33
46
  // Self-gate: only render in dev, and never inside an iframe (the /editor
34
47
  // page embeds this same app in an iframe and would otherwise recursively
@@ -44,25 +57,33 @@
44
57
  if (!consumerControlsOpen) {
45
58
  open = enabled && quietGet(OPEN_KEY) === '1';
46
59
  }
47
- $: if (!consumerControlsOpen && typeof window !== 'undefined') {
48
- quietSet(OPEN_KEY, open ? '1' : '0');
49
- }
50
- $: overlayOpen.set(!!open);
60
+ run(() => {
61
+ if (!consumerControlsOpen && typeof window !== 'undefined') {
62
+ quietSet(OPEN_KEY, open ? '1' : '0');
63
+ }
64
+ });
65
+ run(() => {
66
+ overlayOpen.set(!!open);
67
+ });
51
68
 
52
69
  // Hide the overlay entirely when the user is already on the editor route
53
70
  // (the editor page has its own chrome).
54
- $: onEditorPath = $route === editorPath;
55
- $: sourceFile = pageSources[$route];
56
- $: showSource = !!sourceFile && !!projectRoot && !hidePageSourceOn.includes($route);
71
+ let onEditorPath = $derived($route === editorPath);
72
+ let sourceFile = $derived(pageSources[$route]);
73
+ let showSource = $derived(!!sourceFile && !!projectRoot && !hidePageSourceOn.includes($route));
57
74
 
58
75
  // Mount the iframe the first time the editor is shown, then keep it mounted
59
76
  // across hide/show cycles so editor state (unsaved slider values, scroll
60
77
  // position, expanded sections) survives.
61
- let hasBeenOpen: boolean = !!open;
62
- $: if (open) hasBeenOpen = true;
78
+ let hasBeenOpen: boolean = $state(!!open);
79
+ run(() => {
80
+ if (open) hasBeenOpen = true;
81
+ });
63
82
 
64
- let editorFrame: HTMLIFrameElement | undefined;
65
- $: postParentRoute(editorFrame?.contentWindow, $route);
83
+ let editorFrame: HTMLIFrameElement | undefined = $state();
84
+ run(() => {
85
+ postParentRoute(editorFrame?.contentWindow, $route);
86
+ });
66
87
 
67
88
  type Mode = 'docked' | 'floating';
68
89
 
@@ -109,9 +130,9 @@
109
130
  }
110
131
 
111
132
  const initial = loadState();
112
- let mode: Mode = initial.mode;
113
- let dockedWidth: number = Math.max(MIN_WIDTH, initial.dockedWidth);
114
- let floating = { ...initial.floating };
133
+ let mode: Mode = $state(initial.mode);
134
+ let dockedWidth: number = $state(Math.max(MIN_WIDTH, initial.dockedWidth));
135
+ let floating = $state({ ...initial.floating });
115
136
 
116
137
  // Approximate natural size of the collapsed pill (Editor title + columns toggle).
117
138
  // A few pixels of overshoot is fine — the panel has overflow:hidden.
@@ -125,11 +146,11 @@
125
146
 
126
147
  // Suppress CSS transitions during gestures and mode swaps so dragging doesn't
127
148
  // re-animate every frame, and floating↔docked swaps snap cleanly.
128
- let suppressTransition = false;
149
+ let suppressTransition = $state(false);
129
150
 
130
151
  // Gesture state — a transparent scrim covers the iframe while any gesture is active
131
152
  // so pointer events land on the panel, not on content inside the iframe.
132
- let gesturing: 'drag' | 'resize-left' | 'resize-se' | null = null;
153
+ let gesturing: 'drag' | 'resize-left' | 'resize-se' | null = $state(null);
133
154
 
134
155
  function startDrag(e: PointerEvent) {
135
156
  if (!open || mode !== 'floating') return;
@@ -244,15 +265,15 @@
244
265
  window.removeEventListener('lt-overlay-toggle', handleToggleRequest);
245
266
  });
246
267
 
247
- $: panelStyle = !open
268
+ let panelStyle = $derived(!open
248
269
  ? `position: fixed; top: 12px; right: 12px; width: ${COLLAPSED_WIDTH}px; height: ${COLLAPSED_HEIGHT}px;`
249
270
  : mode === 'docked'
250
271
  ? `position: fixed; top: 0; right: 0; width: ${dockedWidth}px; height: 100vh;`
251
- : `position: fixed; top: ${floating.y}px; left: ${floating.x}px; width: ${floating.width}px; height: ${floating.height}px;`;
272
+ : `position: fixed; top: ${floating.y}px; left: ${floating.x}px; width: ${floating.width}px; height: ${floating.height}px;`);
252
273
  </script>
253
274
 
254
275
  {#if enabled && !onEditorPath}
255
- <!-- svelte-ignore a11y-no-static-element-interactions -->
276
+ <!-- svelte-ignore a11y_no_static_element_interactions -->
256
277
  <div
257
278
  class="lt-overlay"
258
279
  style={panelStyle}
@@ -264,13 +285,13 @@
264
285
  >
265
286
  <div
266
287
  class="header"
267
- on:pointerdown={startDrag}
268
- on:dblclick={handleHeaderDblClick}
288
+ onpointerdown={startDrag}
289
+ ondblclick={handleHeaderDblClick}
269
290
  title={open ? 'Double-click to hide' : 'Double-click to show'}
270
291
  >
271
292
  <button
272
293
  class="hdr-btn text title"
273
- on:click={toggleOpen}
294
+ onclick={toggleOpen}
274
295
  title={open ? 'Hide Editor' : 'Show Editor'}
275
296
  >
276
297
  <i class="fas {open ? 'fa-chevron-right' : 'fa-chevron-left'}"></i>
@@ -280,7 +301,7 @@
280
301
  <button
281
302
  class="hdr-btn icon"
282
303
  class:active={$columnsVisible}
283
- on:click={toggleColumns}
304
+ onclick={toggleColumns}
284
305
  title="{$columnsVisible ? 'Hide' : 'Show'} columns"
285
306
  >
286
307
  <i class="fas fa-grip-lines-vertical"></i>
@@ -290,7 +311,7 @@
290
311
  <button
291
312
  class="hdr-btn icon"
292
313
  title={mode === 'docked' ? 'Float' : 'Dock to right'}
293
- on:click={toggleMode}
314
+ onclick={toggleMode}
294
315
  transition:fade={BTN_FADE}
295
316
  >
296
317
  <i class={mode === 'docked' ? 'fas fa-up-right-from-square' : 'fas fa-thumbtack'}></i>
@@ -313,7 +334,7 @@
313
334
  transition:fade={BTN_FADE}
314
335
  >
315
336
  <i class="fas fa-code"></i>
316
- Show Source
337
+ Show page source
317
338
  </a>
318
339
  {/if}
319
340
 
@@ -329,7 +350,7 @@
329
350
  class:active={$route === link.path}
330
351
  aria-selected={$route === link.path}
331
352
  disabled={link.disabled}
332
- on:click={() => navigate(link.path)}
353
+ onclick={() => navigate(link.path)}
333
354
  >
334
355
  {#if link.icon}<i class="fas {link.icon}"></i>{/if}
335
356
  <span>{link.label}</span>
@@ -347,7 +368,7 @@
347
368
  title="Token editor"
348
369
  class="editor-frame"
349
370
  bind:this={editorFrame}
350
- on:load={() => postParentRoute(editorFrame?.contentWindow, $route)}
371
+ onload={() => postParentRoute(editorFrame?.contentWindow, $route)}
351
372
  ></iframe>
352
373
  {#if gesturing}
353
374
  <div class="gesture-scrim"></div>
@@ -355,9 +376,9 @@
355
376
  </div>
356
377
 
357
378
  {#if mode === 'docked'}
358
- <div class="resize-left" on:pointerdown={startDockedResize}></div>
379
+ <div class="resize-left" onpointerdown={startDockedResize}></div>
359
380
  {:else}
360
- <div class="resize-se" on:pointerdown={startFloatingResize}></div>
381
+ <div class="resize-se" onpointerdown={startFloatingResize}></div>
361
382
  {/if}
362
383
  {/if}
363
384
  </div>
@@ -1,4 +1,6 @@
1
1
  <script lang="ts">
2
+ import { run } from 'svelte/legacy';
3
+
2
4
  import { onMount, onDestroy } from 'svelte';
3
5
  import ComponentsTab from '../component-editor/scaffolding/ComponentsTab.svelte';
4
6
  import PresetFileManager from '../ui/PresetFileManager.svelte';
@@ -6,20 +8,28 @@
6
8
  import { componentRegistryEntries, validateRegistryAgainstServerScan } from '../component-editor/registry';
7
9
  import { listComponents } from '../lib/componentConfigService';
8
10
  import { selectedComponent } from '../lib/editorViewStore';
11
+ // Editor chrome + form controls + icon font must be JS imports (not @import
12
+ // inside the style block) so Vite resolves them via the module graph
13
+ // regardless of how the consumer compiles Svelte CSS (external ?lang.css vs
14
+ // injected); otherwise the @import URLs leak to the browser and 404 against
15
+ // the consumer's root.
16
+ import '../styles/ui-editor.css';
17
+ import '../styles/ui-form-controls.css';
18
+ import '@fortawesome/fontawesome-free/css/all.min.css';
9
19
 
10
- let drawerOpen = true;
20
+ let drawerOpen = $state(true);
11
21
 
12
22
  // Demo page is statically imported from `./Demo.svelte` in App.svelte; the
13
23
  // glob resolves to an empty object if the file has been deleted, in which
14
24
  // case we hide the demo option from the page-switcher.
15
25
  const demoExists = Object.keys(import.meta.glob('./Demo.svelte')).length > 0;
16
26
 
17
- let pageMenuOpen = false;
18
- let pageMenuRoot: HTMLElement;
27
+ let pageMenuOpen = $state(false);
28
+ let pageMenuRoot: HTMLElement | undefined = $state();
19
29
 
20
30
  const HINT_DELAY_MS = 80;
21
- let hintLabel: string | null = null;
22
- let hintTop = 0;
31
+ let hintLabel: string | null = $state(null);
32
+ let hintTop = $state(0);
23
33
  let hintTimer: ReturnType<typeof setTimeout> | null = null;
24
34
 
25
35
  function showHint(label: string, target: HTMLElement) {
@@ -40,7 +50,9 @@
40
50
  hintLabel = null;
41
51
  }
42
52
 
43
- $: if (drawerOpen) hideHint();
53
+ run(() => {
54
+ if (drawerOpen) hideHint();
55
+ });
44
56
 
45
57
  function selectComponent(id: string) {
46
58
  selectedComponent.set(id);
@@ -101,7 +113,7 @@
101
113
  class="rail-toggle"
102
114
  aria-label={drawerOpen ? 'Collapse components menu' : 'Expand components menu'}
103
115
  aria-expanded={drawerOpen}
104
- on:click={() => (drawerOpen = !drawerOpen)}
116
+ onclick={() => (drawerOpen = !drawerOpen)}
105
117
  >
106
118
  <i class="fas {drawerOpen ? 'fa-arrow-left' : 'fa-arrow-right'}"></i>
107
119
  </button>
@@ -112,19 +124,19 @@
112
124
  aria-haspopup="menu"
113
125
  aria-expanded={pageMenuOpen}
114
126
  tabindex={drawerOpen ? 0 : -1}
115
- on:click={() => drawerOpen && (pageMenuOpen = !pageMenuOpen)}
127
+ onclick={() => drawerOpen && (pageMenuOpen = !pageMenuOpen)}
116
128
  >
117
129
  <span class="rail-label">Components</span>
118
130
  <i class="fas fa-chevron-down rail-chevron" class:open={pageMenuOpen}></i>
119
131
  </button>
120
132
  {#if pageMenuOpen && drawerOpen}
121
133
  <div class="page-menu" role="menu">
122
- <button class="page-menu-item" role="menuitem" on:click={() => selectPage('/')}>
134
+ <button class="page-menu-item" role="menuitem" onclick={() => selectPage('/')}>
123
135
  <i class="fas fa-home"></i>
124
136
  <span>Main site</span>
125
137
  </button>
126
138
  {#if demoExists}
127
- <button class="page-menu-item" role="menuitem" on:click={() => selectPage('/demo')}>
139
+ <button class="page-menu-item" role="menuitem" onclick={() => selectPage('/demo')}>
128
140
  <i class="fas fa-box-open"></i>
129
141
  <span>Demo page</span>
130
142
  </button>
@@ -137,9 +149,9 @@
137
149
  <button
138
150
  class="nav-item"
139
151
  class:active={$selectedComponent === item.id}
140
- on:mouseenter={(e) => showHint(item.label, e.currentTarget)}
141
- on:mouseleave={hideHint}
142
- on:click={() => selectComponent(item.id)}
152
+ onmouseenter={(e) => showHint(item.label, e.currentTarget)}
153
+ onmouseleave={hideHint}
154
+ onclick={() => selectComponent(item.id)}
143
155
  >
144
156
  <i class={item.icon}></i>
145
157
  <span class="rail-label">{item.label}</span>
@@ -163,7 +175,6 @@
163
175
  </div>
164
176
 
165
177
  <style>
166
- @import '../styles/ui-editor.css';
167
178
  .components-shell {
168
179
  --rail-w: 48px;
169
180
  display: grid;
@@ -5,6 +5,14 @@
5
5
  import { installEditorKeybindings } from '../lib/editorKeybindings';
6
6
  import { initializeEditorStore } from '../lib/editorStore';
7
7
  import { storageKey } from '../lib/editorConfig';
8
+ // Editor chrome + form controls + icon font must be JS imports (not @import
9
+ // inside the style block) so Vite resolves them via the module graph
10
+ // regardless of how the consumer compiles Svelte CSS (external ?lang.css vs
11
+ // injected); otherwise the @import URLs leak to the browser and 404 against
12
+ // the consumer's root.
13
+ import '../styles/ui-editor.css';
14
+ import '../styles/ui-form-controls.css';
15
+ import '@fortawesome/fontawesome-free/css/all.min.css';
8
16
 
9
17
  const inOverlay = typeof window !== 'undefined' && window.parent !== window;
10
18
 
@@ -46,8 +54,6 @@
46
54
  </div>
47
55
 
48
56
  <style>
49
- @import '../styles/ui-editor.css';
50
-
51
57
  .editor-page {
52
58
  min-height: 100vh;
53
59
  background: black;
@@ -1,4 +1,6 @@
1
1
  <script lang="ts">
2
+ import { run } from 'svelte/legacy';
3
+
2
4
  import { onMount, onDestroy } from 'svelte';
3
5
  import { get } from 'svelte/store';
4
6
  import VariablesTab from '../ui/VariablesTab.svelte';
@@ -28,18 +30,18 @@
28
30
 
29
31
  const componentNavItems = componentRegistryEntries.map(({ id, label, icon }) => ({ id, label, icon }));
30
32
 
31
- let selectedTokenSection: string | null = null;
32
- let saveStatus: 'idle' | 'saving' | 'saved' | 'error' = 'idle';
33
+ let selectedTokenSection: string | null = $state(null);
34
+ let saveStatus: 'idle' | 'saving' | 'saved' | 'error' = $state('idle');
33
35
 
34
- let shellEl: HTMLElement | null = null;
35
- let shellWidth = 1024;
36
+ let shellEl: HTMLElement | null = $state(null);
37
+ let shellWidth = $state(1024);
36
38
  const CONDENSE_BELOW = 520;
37
39
 
38
- $: condensed = $sidebarCondensed === 'auto' ? shellWidth < CONDENSE_BELOW : $sidebarCondensed;
40
+ let condensed = $derived($sidebarCondensed === 'auto' ? shellWidth < CONDENSE_BELOW : $sidebarCondensed);
39
41
 
40
42
  const HINT_DELAY_MS = 80;
41
- let hintLabel: string | null = null;
42
- let hintTop = 0;
43
+ let hintLabel: string | null = $state(null);
44
+ let hintTop = $state(0);
43
45
  let hintTimer: ReturnType<typeof setTimeout> | null = null;
44
46
 
45
47
  function showHint(label: string, target: HTMLElement) {
@@ -60,7 +62,9 @@
60
62
  hintLabel = null;
61
63
  }
62
64
 
63
- $: if (!condensed) hideHint();
65
+ run(() => {
66
+ if (!condensed) hideHint();
67
+ });
64
68
 
65
69
  function scrollToSection(sectionId: string) {
66
70
  selectedTokenSection = sectionId;
@@ -80,8 +84,8 @@
80
84
  });
81
85
  }
82
86
 
83
- async function handleSave(e: CustomEvent<{ fileName: string; displayName: string }>) {
84
- const { fileName, displayName } = e.detail;
87
+ async function handleSave(detail: { fileName: string; displayName: string }) {
88
+ const { fileName, displayName } = detail;
85
89
  saveStatus = 'saving';
86
90
  try {
87
91
  await persistTheme(get(editorState), fileName, displayName);
@@ -93,9 +97,9 @@
93
97
  }
94
98
  }
95
99
 
96
- async function handleLoad(e: CustomEvent<{ fileName: string }>) {
100
+ async function handleLoad(detail: { fileName: string }) {
97
101
  try {
98
- await hydrateTheme(e.detail.fileName);
102
+ await hydrateTheme(detail.fileName);
99
103
  } catch {
100
104
  // silent
101
105
  }
@@ -132,7 +136,7 @@
132
136
  class="rail-toggle"
133
137
  aria-label={condensed ? 'Expand sidebar' : 'Collapse sidebar'}
134
138
  aria-expanded={!condensed}
135
- on:click={toggleCondensed}
139
+ onclick={toggleCondensed}
136
140
  >
137
141
  <i class="fas {condensed ? 'fa-arrow-right' : 'fa-arrow-left'}"></i>
138
142
  </button>
@@ -146,9 +150,9 @@
146
150
  <button
147
151
  class="nav-item"
148
152
  class:active={selectedTokenSection === item.id}
149
- on:mouseenter={(e) => showHint(item.label, e.currentTarget)}
150
- on:mouseleave={hideHint}
151
- on:click={() => scrollToSection(item.id)}
153
+ onmouseenter={(e) => showHint(item.label, e.currentTarget)}
154
+ onmouseleave={hideHint}
155
+ onclick={() => scrollToSection(item.id)}
152
156
  >
153
157
  <i class={item.icon}></i>
154
158
  <span class="nav-label">{item.label}</span>
@@ -157,7 +161,7 @@
157
161
  </div>
158
162
  {#if !condensed}
159
163
  <div class="sidebar-footer">
160
- <ThemeFileManager {saveStatus} on:save={handleSave} on:load={handleLoad} />
164
+ <ThemeFileManager {saveStatus} onsave={handleSave} onload={handleLoad} />
161
165
  </div>
162
166
  {/if}
163
167
  {:else}
@@ -166,9 +170,9 @@
166
170
  <button
167
171
  class="nav-item"
168
172
  class:active={$selectedComponent === item.id}
169
- on:mouseenter={(e) => showHint(item.label, e.currentTarget)}
170
- on:mouseleave={hideHint}
171
- on:click={() => selectComponent(item.id)}
173
+ onmouseenter={(e) => showHint(item.label, e.currentTarget)}
174
+ onmouseleave={hideHint}
175
+ onclick={() => selectComponent(item.id)}
172
176
  >
173
177
  <i class={item.icon}></i>
174
178
  <span class="nav-label">{item.label}</span>
@@ -0,0 +1,138 @@
1
+ /*
2
+ * Site Styles — global typography for the themed pages
3
+ *
4
+ * Unscoped element selectors (h1, h2, p, a, ul li, …) consume the design
5
+ * tokens in tokens.css and recolor with the user's theme. Loaded globally
6
+ * from main.ts.
7
+ *
8
+ * Pair with: ui-editor.css (--ui-* chrome, opposite scope — neutral and
9
+ * theme-immune; only loaded on editor pages).
10
+ */
11
+
12
+ h1 {
13
+ font-family: var(--font-display);
14
+ font-size: var(--font-size-4xl);
15
+ font-weight: var(--font-weight-semibold);
16
+ color: var(--text-primary);
17
+ margin: 0 0 var(--space-12);
18
+ line-height: var(--line-height-sm);
19
+ overflow-wrap: break-word;
20
+ }
21
+
22
+ h2 {
23
+ font-family: var(--font-serif);
24
+ font-size: var(--font-size-2xl);
25
+ font-weight: var(--font-weight-semibold);
26
+ color: var(--text-primary);
27
+ letter-spacing: 0.01em;
28
+ margin: var(--space-32) 0 var(--space-12);
29
+ line-height: var(--line-height-sm);
30
+ overflow-wrap: break-word;
31
+ }
32
+
33
+ h3 {
34
+ font-family: var(--font-serif);
35
+ font-size: var(--font-size-xl);
36
+ font-weight: var(--font-weight-normal);
37
+ color: var(--text-primary);
38
+ margin: var(--space-24) 0 var(--space-8);
39
+ line-height: var(--line-height-sm);
40
+ overflow-wrap: break-word;
41
+ }
42
+
43
+ @media (max-width: 768px) {
44
+ h1 {
45
+ line-height: 1.1;
46
+ margin-bottom: var(--space-8);
47
+ }
48
+
49
+ h2 {
50
+ line-height: 1.15;
51
+ margin-top: var(--space-24);
52
+ }
53
+
54
+ h3 {
55
+ line-height: 1.2;
56
+ margin-top: var(--space-20);
57
+ }
58
+ }
59
+
60
+ p {
61
+ font-family: var(--font-serif);
62
+ font-size: var(--font-size-md);
63
+ color: var(--text-secondary);
64
+ line-height: var(--line-height-md);
65
+ margin: 0 0 14px;
66
+ }
67
+
68
+ p:last-child {
69
+ margin-bottom: 0;
70
+ }
71
+
72
+ a {
73
+ color: var(--text-brand);
74
+ text-decoration: none;
75
+ transition: color var(--duration-150);
76
+ }
77
+
78
+ a:hover {
79
+ color: var(--color-brand-300);
80
+ text-decoration: underline;
81
+ }
82
+
83
+ strong {
84
+ color: var(--text-primary);
85
+ font-weight: var(--font-weight-semibold);
86
+ }
87
+
88
+ ul {
89
+ padding-left: var(--space-24);
90
+ margin: var(--space-12) 0;
91
+ }
92
+
93
+ ul li {
94
+ font-family: var(--font-serif);
95
+ font-size: var(--font-size-md);
96
+ color: var(--text-secondary);
97
+ line-height: 1.75;
98
+ margin-bottom: var(--space-4);
99
+ }
100
+
101
+ ul li::marker {
102
+ color: var(--text-tertiary);
103
+ }
104
+
105
+ ol {
106
+ padding-left: var(--space-24);
107
+ margin: var(--space-12) 0;
108
+ }
109
+
110
+ ol li {
111
+ font-family: var(--font-sans);
112
+ font-size: var(--font-size-md);
113
+ color: var(--text-secondary);
114
+ line-height: 1.6;
115
+ margin-bottom: var(--space-4);
116
+ }
117
+
118
+ ol li::marker {
119
+ color: var(--text-tertiary);
120
+ font-weight: var(--font-weight-semibold);
121
+ }
122
+
123
+ hr {
124
+ border: none;
125
+ border-top: 1px solid var(--border-neutral-faint);
126
+ margin: var(--space-32) 0;
127
+ }
128
+
129
+ blockquote {
130
+ margin: var(--space-16) 0;
131
+ padding: var(--space-12) var(--space-20);
132
+ border: 1px solid var(--color-brand-500);
133
+ border-left: 4px solid var(--color-brand-500);
134
+ background: var(--surface-neutral-high);
135
+ border-radius: 0 var(--radius-md) var(--radius-md) 0;
136
+ color: var(--text-secondary);
137
+ font-style: italic;
138
+ }