@motion-proto/live-tokens 0.17.0 → 0.18.2

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.
@@ -473,7 +473,17 @@ npx live-tokens check-component <id>
473
473
 
474
474
  It enforces the file layout, the `:global(:root)` block, token-suffix vocabulary, state-before-property rule, theme-token defaults (no raw colour literals), public-imports rule, and the `registerComponent({ id })` call. Exit code 0 means the static contract is met.
475
475
 
476
- Then navigate to `/components` and confirm the runtime behaviours the static check can't see:
476
+ **Then run the registry contract test.** If you're authoring inside the package itself, `src/editor/component-editor/registryContract.test.ts` runs `describe.each(getComponentRegistryEntries())` and verifies, per component:
477
+
478
+ 1. Registration resolves to a real `sourceFile` and a non-empty schema.
479
+ 2. Schema variables are unique within the component.
480
+ 3. Every editable token (excluding `hidden: true`, `kind: 'gradient'`, and padding-side suffixes) is declared in the runtime `<style>` block.
481
+ 4. Every editable token is seeded in the production-pointed config under `src/live-tokens/data/component-configs/<id>/`.
482
+ 5. `setComponentAlias` round-trips the alias through the slice.
483
+
484
+ A new first-party component is auto-covered the moment it lands in `builtInRegistry` — `npm test` will fail if any of the five checks miss. For a consumer-authored component, mirror this pattern in your own test suite if you want the same drift protection (the same test logic works against any `registerComponent` registration; iterate `getComponentRegistryEntries()` after your `main.ts` has run).
485
+
486
+ Finally navigate to `/components` and confirm the runtime behaviours no static check can see:
477
487
 
478
488
  - [ ] The new component appears in the nav rail under the **CUSTOM** group (system entries above, custom below the labeled divider).
479
489
  - [ ] Token rows render. Color pickers, radius selectors, font selectors all work.
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@motion-proto/live-tokens",
3
- "version": "0.17.0",
3
+ "version": "0.18.2",
4
4
  "type": "module",
5
- "description": "Design token editor with live CSS variable editing. Svelte 5 + Vite 6/7.",
5
+ "description": "Design token editor with live CSS variable editing. Svelte 5 + Vite 8.",
6
6
  "keywords": [
7
7
  "svelte",
8
8
  "vite",
@@ -95,12 +95,12 @@
95
95
  "sass": "^1.0",
96
96
  "svelte": "^5",
97
97
  "svelte-preprocess": "^6.0",
98
- "vite": "^6 || ^7"
98
+ "vite": "^8"
99
99
  },
100
100
  "devDependencies": {
101
- "@sveltejs/vite-plugin-svelte": "^6.2.4",
101
+ "@sveltejs/vite-plugin-svelte": "^7.1.2",
102
102
  "@tsconfig/svelte": "^5.0.8",
103
- "@types/node": "^24.12.0",
103
+ "@types/node": "^25.9.1",
104
104
  "happy-dom": "^20.9.0",
105
105
  "highlight.js": "^11.11.1",
106
106
  "marked": "^18.0.4",
@@ -110,8 +110,8 @@
110
110
  "svelte-check": "^4.4.8",
111
111
  "svelte-preprocess": "^6.0.3",
112
112
  "tsup": "^8.5.1",
113
- "typescript": "~5.9.3",
114
- "vite": "^7.3.3",
113
+ "typescript": "~6.0.3",
114
+ "vite": "^8.0.14",
115
115
  "vitest": "^4.1.4"
116
116
  },
117
117
  "dependencies": {
@@ -10,10 +10,6 @@
10
10
  const baseTokens: Token[] = [
11
11
  { label: 'offset from corner', canBeLinked: true, groupKey: 'margin', variable: '--corner-badge-margin', element: 'frame' },
12
12
  { label: 'padding', canBeLinked: true, groupKey: 'padding', variable: '--corner-badge-padding', element: 'frame' },
13
- { label: 'padding-top', canBeLinked: true, groupKey: 'padding-top', variable: '--corner-badge-padding-top', hidden: true, element: 'frame' },
14
- { label: 'padding-right', canBeLinked: true, groupKey: 'padding-right', variable: '--corner-badge-padding-right', hidden: true, element: 'frame' },
15
- { label: 'padding-bottom', canBeLinked: true, groupKey: 'padding-bottom', variable: '--corner-badge-padding-bottom', hidden: true, element: 'frame' },
16
- { label: 'padding-left', canBeLinked: true, groupKey: 'padding-left', variable: '--corner-badge-padding-left', hidden: true, element: 'frame' },
17
13
  { label: 'outer corner radius', canBeLinked: true, groupKey: 'outer-radius', variable: '--corner-badge-outer-radius', element: 'frame' },
18
14
  { label: 'inner corner radius', canBeLinked: true, groupKey: 'inner-radius', variable: '--corner-badge-inner-radius', element: 'frame' },
19
15
  { label: 'horizontal-axis radius', canBeLinked: true, groupKey: 'h-axis-radius', variable: '--corner-badge-h-axis-radius', element: 'frame' },
@@ -12,10 +12,6 @@
12
12
  { label: 'corner radius', groupKey: 'radius', variable: '--input-radius' },
13
13
  { label: 'border width', groupKey: 'border-width', variable: '--input-border-width' },
14
14
  { label: 'padding', variable: '--input-padding', groupKey: 'padding' },
15
- { label: 'padding-top', variable: '--input-padding-top', groupKey: 'padding-top', hidden: true },
16
- { label: 'padding-right', variable: '--input-padding-right', groupKey: 'padding-right', hidden: true },
17
- { label: 'padding-bottom', variable: '--input-padding-bottom', groupKey: 'padding-bottom', hidden: true },
18
- { label: 'padding-left', variable: '--input-padding-left', groupKey: 'padding-left', hidden: true },
19
15
  { label: 'message gap', groupKey: 'gap', variable: '--input-gap' },
20
16
  ],
21
17
  default: [
@@ -12,18 +12,10 @@
12
12
  { label: 'border width', groupKey: 'width', variable: '--menuselect-menu-border-width' },
13
13
  { label: 'corner radius', groupKey: 'menu-radius', variable: '--menuselect-menu-radius' },
14
14
  { label: 'padding', variable: '--menuselect-menu-padding', groupKey: 'menu-padding' },
15
- { label: 'padding-top', variable: '--menuselect-menu-padding-top', groupKey: 'menu-padding-top', hidden: true },
16
- { label: 'padding-right', variable: '--menuselect-menu-padding-right', groupKey: 'menu-padding-right', hidden: true },
17
- { label: 'padding-bottom', variable: '--menuselect-menu-padding-bottom', groupKey: 'menu-padding-bottom', hidden: true },
18
- { label: 'padding-left', variable: '--menuselect-menu-padding-left', groupKey: 'menu-padding-left', hidden: true },
19
15
  { label: 'item gap', groupKey: 'gap', variable: '--menuselect-menu-gap' },
20
16
  { label: 'shadow', groupKey: 'shadow', variable: '--menuselect-menu-shadow' },
21
17
  { label: 'item radius', groupKey: 'item-radius', variable: '--menuselect-item-radius' },
22
18
  { label: 'item padding', variable: '--menuselect-item-padding', groupKey: 'item-padding' },
23
- { label: 'item padding-top', variable: '--menuselect-item-padding-top', groupKey: 'item-padding-top', hidden: true },
24
- { label: 'item padding-right', variable: '--menuselect-item-padding-right', groupKey: 'item-padding-right', hidden: true },
25
- { label: 'item padding-bottom', variable: '--menuselect-item-padding-bottom', groupKey: 'item-padding-bottom', hidden: true },
26
- { label: 'item padding-left', variable: '--menuselect-item-padding-left', groupKey: 'item-padding-left', hidden: true },
27
19
  ],
28
20
  'default item': [
29
21
  { label: 'surface color', groupKey: 'surface', variable: '--menuselect-default-surface' },
@@ -19,17 +19,9 @@
19
19
  { label: 'corner radius', groupKey: 'radius', variable: '--segmentedcontrol-bar-radius' },
20
20
  { label: 'option gap', groupKey: 'gap', variable: '--segmentedcontrol-bar-gap' },
21
21
  { label: 'padding', variable: '--segmentedcontrol-bar-padding', groupKey: 'bar-padding' },
22
- { label: 'padding-top', variable: '--segmentedcontrol-bar-padding-top', groupKey: 'bar-padding-top', hidden: true },
23
- { label: 'padding-right', variable: '--segmentedcontrol-bar-padding-right', groupKey: 'bar-padding-right', hidden: true },
24
- { label: 'padding-bottom', variable: '--segmentedcontrol-bar-padding-bottom', groupKey: 'bar-padding-bottom', hidden: true },
25
- { label: 'padding-left', variable: '--segmentedcontrol-bar-padding-left', groupKey: 'bar-padding-left', hidden: true },
26
22
  ],
27
23
  'option base': [
28
24
  { label: 'padding', variable: '--segmentedcontrol-option-padding', groupKey: 'option-padding' },
29
- { label: 'padding-top', variable: '--segmentedcontrol-option-padding-top', groupKey: 'option-padding-top', hidden: true },
30
- { label: 'padding-right', variable: '--segmentedcontrol-option-padding-right', groupKey: 'option-padding-right', hidden: true },
31
- { label: 'padding-bottom', variable: '--segmentedcontrol-option-padding-bottom', groupKey: 'option-padding-bottom', hidden: true },
32
- { label: 'padding-left', variable: '--segmentedcontrol-option-padding-left', groupKey: 'option-padding-left', hidden: true },
33
25
  { label: 'icon gap', groupKey: 'option-gap', variable: '--segmentedcontrol-option-gap' },
34
26
  { label: 'icon size', groupKey: 'icon-size', variable: '--segmentedcontrol-option-icon-size' },
35
27
  ],
@@ -64,20 +56,12 @@
64
56
  { label: 'divider width', groupKey: 'small-thickness', variable: '--segmentedcontrol-divider-small-thickness' },
65
57
  { label: 'corner radius', groupKey: 'small-radius', variable: '--segmentedcontrol-bar-small-radius' },
66
58
  { label: 'padding', variable: '--segmentedcontrol-bar-small-padding', groupKey: 'bar-small-padding' },
67
- { label: 'padding-top', variable: '--segmentedcontrol-bar-small-padding-top', groupKey: 'bar-small-padding-top', hidden: true },
68
- { label: 'padding-right', variable: '--segmentedcontrol-bar-small-padding-right', groupKey: 'bar-small-padding-right', hidden: true },
69
- { label: 'padding-bottom', variable: '--segmentedcontrol-bar-small-padding-bottom', groupKey: 'bar-small-padding-bottom', hidden: true },
70
- { label: 'padding-left', variable: '--segmentedcontrol-bar-small-padding-left', groupKey: 'bar-small-padding-left', hidden: true },
71
59
  ],
72
60
  'option base': [
73
61
  { label: 'icon size', groupKey: 'small-icon-size', variable: '--segmentedcontrol-option-small-icon-size' },
74
62
  { label: 'font size', groupKey: 'small-text-font-size', variable: '--segmentedcontrol-option-small-text-font-size' },
75
63
  { label: 'line height', groupKey: 'small-text-line-height', variable: '--segmentedcontrol-option-small-text-line-height' },
76
64
  { label: 'padding', variable: '--segmentedcontrol-option-small-padding', groupKey: 'option-small-padding' },
77
- { label: 'padding-top', variable: '--segmentedcontrol-option-small-padding-top', groupKey: 'option-small-padding-top', hidden: true },
78
- { label: 'padding-right', variable: '--segmentedcontrol-option-small-padding-right', groupKey: 'option-small-padding-right', hidden: true },
79
- { label: 'padding-bottom', variable: '--segmentedcontrol-option-small-padding-bottom', groupKey: 'option-small-padding-bottom', hidden: true },
80
- { label: 'padding-left', variable: '--segmentedcontrol-option-small-padding-left', groupKey: 'option-small-padding-left', hidden: true },
81
65
  { label: 'icon gap', groupKey: 'option-small-gap', variable: '--segmentedcontrol-option-small-gap' },
82
66
  ],
83
67
  'selected option': [
@@ -359,6 +359,30 @@
359
359
 
360
360
  /* Component aliases (production configs differing from defaults) */
361
361
  :root:root {
362
+ /* button (my-button) */
363
+ --button-primary-text-font-size: var(--font-size-md);
364
+ --button-primary-text-font-weight: var(--font-weight-semibold);
365
+ --button-primary-radius: var(--radius-xl);
366
+ --button-secondary-text-font-size: var(--font-size-md);
367
+ --button-secondary-text-font-weight: var(--font-weight-semibold);
368
+ --button-secondary-radius: var(--radius-xl);
369
+ --button-outline-text-font-size: var(--font-size-md);
370
+ --button-outline-text-font-weight: var(--font-weight-semibold);
371
+ --button-outline-radius: var(--radius-xl);
372
+ --button-success-text-font-size: var(--font-size-md);
373
+ --button-success-text-font-weight: var(--font-weight-semibold);
374
+ --button-success-border-width: var(--border-width-1);
375
+ --button-success-radius: var(--radius-xl);
376
+ --button-danger-text-font-size: var(--font-size-md);
377
+ --button-danger-text-font-weight: var(--font-weight-semibold);
378
+ --button-danger-border-width: var(--border-width-1);
379
+ --button-danger-radius: var(--radius-xl);
380
+ --button-warning-text-font-size: var(--font-size-md);
381
+ --button-warning-text-font-weight: var(--font-weight-semibold);
382
+ --button-warning-border-width: var(--border-width-1);
383
+ --button-warning-radius: var(--radius-xl);
384
+ --button-small-text-font-size: var(--font-size-sm);
385
+
362
386
  /* card (my-card) */
363
387
  --card-default-surface: color-mix(in srgb, var(--surface-neutral-lower) 70%, transparent);
364
388
  --card-default-header-surface: color-mix(in srgb, var(--surface-neutral-lowest) 80%, transparent);
@@ -76,6 +76,19 @@
76
76
  expandedSections[path] = !expandedSections[path];
77
77
  }
78
78
 
79
+ // Clicking a section label whose route is already current would otherwise be
80
+ // a no-op navigation. Make it a toggle in that case so the user can collapse
81
+ // the section they just opened without having to chase the chevron. The
82
+ // chevron's own onclick is unaffected — its event target isn't inside an <a>.
83
+ function maybeInterceptLabel(e: MouseEvent, section: SideNavSection) {
84
+ if (!section.hasIndexPage || currentPath !== section.path) return;
85
+ if (e.button !== 0 || e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) return;
86
+ const target = e.target as Element | null;
87
+ if (!target?.closest('a')) return;
88
+ e.preventDefault();
89
+ toggleSection(section.path);
90
+ }
91
+
79
92
  function fireToggle() {
80
93
  ontoggle?.();
81
94
  dispatch('toggle');
@@ -164,10 +177,12 @@
164
177
  <div class="sn-menu">
165
178
  {#each sections as section (section.path)}
166
179
  <div class="sn-section">
180
+ <!-- svelte-ignore a11y_click_events_have_key_events, a11y_no_noninteractive_element_interactions, a11y_no_static_element_interactions -->
167
181
  <div
168
182
  class="sn-section-header"
169
183
  class:active={isSectionActive(section)}
170
184
  class:force-hover={isSectionHover(section)}
185
+ onclick={(e) => maybeInterceptLabel(e, section)}
171
186
  >
172
187
  <CollapsibleSection
173
188
  variant="chromeless"