@motion-proto/live-tokens 0.68.1 → 0.69.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 (93) hide show
  1. package/.claude/skills/live-tokens-build-page/SKILL.md +16 -5
  2. package/.claude/skills/live-tokens-create-component/SKILL.md +43 -8
  3. package/.claude/skills/live-tokens-create-component/references/token-naming.md +30 -1
  4. package/.claude/skills/live-tokens-fix-findings/SKILL.md +131 -0
  5. package/.claude/skills/live-tokens-pick-component/SKILL.md +2 -1
  6. package/CHANGELOG.md +207 -0
  7. package/README.md +11 -4
  8. package/bin/check-component.mjs +367 -63
  9. package/bin/check-page.mjs +409 -0
  10. package/bin/cli.mjs +57 -8
  11. package/bin/lib/cssValues.mjs +50 -0
  12. package/bin/lib/findings.mjs +106 -0
  13. package/bin/lib/tokenVocabulary.mjs +213 -0
  14. package/dist-plugin/adjust/index.cjs +174 -23
  15. package/dist-plugin/adjust/index.js +68 -23
  16. package/dist-plugin/{chunk-2UX6EVVA.js → chunk-2YNERPXY.js} +1 -1
  17. package/dist-plugin/{chunk-NE6N66EE.js → chunk-GPIBU44G.js} +107 -1
  18. package/dist-plugin/{chunk-ZHPX7ZYQ.js → chunk-RFVYPNRO.js} +39 -1
  19. package/dist-plugin/generateColorsAndType/index.cjs +107 -1
  20. package/dist-plugin/generateColorsAndType/index.js +1 -1
  21. package/dist-plugin/index.cjs +146 -2
  22. package/dist-plugin/index.js +3 -3
  23. package/dist-plugin/migrateData/index.cjs +107 -1
  24. package/dist-plugin/migrateData/index.js +2 -2
  25. package/dist-plugin/tokensCssMigrations/index.cjs +39 -1
  26. package/dist-plugin/tokensCssMigrations/index.js +1 -1
  27. package/package.json +3 -2
  28. package/src/app/site.css +4 -4
  29. package/src/editor/component-editor/ButtonEditor.svelte +71 -4
  30. package/src/editor/component-editor/CardEditor.svelte +6 -6
  31. package/src/editor/component-editor/DialogEditor.svelte +3 -3
  32. package/src/editor/component-editor/IconButtonEditor.svelte +68 -3
  33. package/src/editor/component-editor/ImageEditor.svelte +8 -8
  34. package/src/editor/component-editor/ImageLightboxEditor.svelte +12 -1
  35. package/src/editor/component-editor/MenuSelectEditor.svelte +63 -3
  36. package/src/editor/component-editor/SegmentedControlEditor.svelte +63 -3
  37. package/src/editor/component-editor/SideNavigationEditor.svelte +67 -3
  38. package/src/editor/component-editor/SliderEditor.svelte +186 -0
  39. package/src/editor/component-editor/TabBarEditor.svelte +63 -3
  40. package/src/editor/component-editor/registry.ts +10 -0
  41. package/src/editor/core/components/aliasKinds.ts +51 -28
  42. package/src/editor/core/sketch/sketchLayer.ts +16 -0
  43. package/src/editor/core/store/editorPersistence.ts +44 -1
  44. package/src/editor/core/store/editorRenderer.ts +2 -2
  45. package/src/editor/core/store/editorStore.ts +18 -18
  46. package/src/editor/core/store/editorTypes.ts +7 -6
  47. package/src/editor/core/themes/migrations/2026-09-01-gate-suffix-enabled.ts +31 -0
  48. package/src/editor/core/themes/migrations/2026-09-01-scrim-rename.ts +52 -0
  49. package/src/editor/core/themes/migrations/2026-09-01-tabbar-active-tint.ts +27 -0
  50. package/src/editor/core/themes/migrations/2026-09-01-tint-rename.ts +42 -0
  51. package/src/editor/core/themes/migrations/index.ts +16 -0
  52. package/src/editor/core/themes/slices/domainVars.ts +2 -2
  53. package/src/editor/core/themes/slices/washes.ts +107 -0
  54. package/src/editor/docs/content/editing-tokens.md +5 -3
  55. package/src/editor/docs/content.generated.ts +1 -1
  56. package/src/editor/pages/EditorShell.svelte +1 -1
  57. package/src/editor/ui/SurfacesTab.svelte +3 -3
  58. package/src/editor/ui/UITokenSelector.svelte +1 -0
  59. package/src/editor/ui/VariablesTab.svelte +2 -2
  60. package/src/editor/ui/sections/{OverlaysSection.svelte → WashesSection.svelte} +44 -43
  61. package/src/live-tokens/data/colors-and-type/autumn.json +6 -6
  62. package/src/live-tokens/data/colors-and-type/default.json +6 -6
  63. package/src/live-tokens/data/colors-and-type/halloween.json +6 -6
  64. package/src/live-tokens/data/colors-and-type/midnight-study.json +6 -6
  65. package/src/live-tokens/data/colors-and-type/ocean.json +6 -6
  66. package/src/live-tokens/data/colors-and-type/royal-velvet.json +6 -6
  67. package/src/live-tokens/data/colors-and-type/sketchy.json +6 -6
  68. package/src/live-tokens/data/colors-and-type/spring-meadow.json +6 -6
  69. package/src/live-tokens/data/colors-and-type/sunset.json +6 -6
  70. package/src/live-tokens/data/themes/autumn.json +81 -13
  71. package/src/live-tokens/data/themes/halloween.json +81 -13
  72. package/src/live-tokens/data/themes/midnight-study.json +81 -13
  73. package/src/live-tokens/data/themes/ocean.json +81 -13
  74. package/src/live-tokens/data/themes/royal-velvet.json +81 -13
  75. package/src/live-tokens/data/themes/sketchy.json +81 -13
  76. package/src/live-tokens/data/themes/spring-meadow.json +81 -13
  77. package/src/live-tokens/data/themes/sunset.json +81 -13
  78. package/src/live-tokens/data/tokens.generated.css +6 -6
  79. package/src/system/components/Button.svelte +28 -10
  80. package/src/system/components/Card.svelte +6 -6
  81. package/src/system/components/Dialog.svelte +3 -3
  82. package/src/system/components/IconButton.svelte +23 -7
  83. package/src/system/components/Image.svelte +6 -6
  84. package/src/system/components/MenuSelect.svelte +17 -1
  85. package/src/system/components/SegmentedControl.svelte +20 -2
  86. package/src/system/components/SideNavigation.svelte +22 -2
  87. package/src/system/components/Slider.svelte +348 -0
  88. package/src/system/components/TabBar.svelte +20 -2
  89. package/src/system/styles/CONVENTIONS.md +2 -2
  90. package/src/system/styles/tokens.css +12 -4
  91. package/template/package.json +3 -2
  92. package/template/src/pages/Home.svelte +1 -1
  93. package/src/editor/core/themes/slices/overlays.ts +0 -101
@@ -1,6 +1,6 @@
1
1
  <script module lang="ts">
2
2
  import { buildSiblings } from './scaffolding/siblings';
3
- import type { Token } from './scaffolding/types';
3
+ import type { Token, IntrinsicSpec } from './scaffolding/types';
4
4
 
5
5
  export const component = 'button';
6
6
 
@@ -101,14 +101,34 @@
101
101
  );
102
102
 
103
103
  const variantOptions = variants.map((v) => ({ value: v, label: v.charAt(0).toUpperCase() + v.slice(1) }));
104
+
105
+ // The hover tint is off unless a project turns it on: the gate holds a
106
+ // transparent layer, a no-op, until it holds the tint itself.
107
+ export const intrinsics: IntrinsicSpec[] = [
108
+ {
109
+ key: 'shimmer',
110
+ variants: ['default'],
111
+ variable: () => '--button-shimmer',
112
+ values: ['var(--shimmer-on)', 'var(--shimmer-off)'],
113
+ default: { default: 'var(--shimmer-on)' },
114
+ },
115
+ {
116
+ key: 'hover-tint',
117
+ variants: ['default'],
118
+ variable: () => '--button-hover-tint-enabled',
119
+ values: ['var(--color-transparent)', 'var(--button-hover-tint)'],
120
+ default: { default: 'var(--color-transparent)' },
121
+ },
122
+ ];
104
123
  </script>
105
124
 
106
125
  <script lang="ts">
107
126
  import Button from '../../system/components/Button.svelte';
108
127
  import Toggle from '../ui/Toggle.svelte';
128
+ import UIPaletteSelector from '../ui/UIPaletteSelector.svelte';
109
129
  import VariantGroup from './scaffolding/VariantGroup.svelte';
110
130
  import ComponentEditorBase from './scaffolding/ComponentEditorBase.svelte';
111
- import { editorState, setComponentAlias } from '../core/store/editorStore';
131
+ import { editorState, setComponentAlias, clearComponentAlias } from '../core/store/editorStore';
112
132
  import { computeLinkedBlock, withLinkedDisabled } from './scaffolding/linkedBlock';
113
133
 
114
134
  let shimmerRef = $derived($editorState.components.button?.aliases['--button-shimmer']);
@@ -118,12 +138,49 @@
118
138
  setComponentAlias('button', '--button-shimmer', { kind: 'token', name: checked ? '--shimmer-on' : '--shimmer-off' });
119
139
  }
120
140
 
141
+ // One switch. Turning the tint on also stands each hover surface down to its
142
+ // own base, so hover is the tint alone rather than a swap wearing a wash.
143
+ // Written as aliases rather than gated in CSS: a component token's value is
144
+ // substituted at :root, so it can never name a per-variant private var.
145
+ // Off clears the overrides, returning each surface to its shipped default.
146
+ const HOVER_SWAPS: readonly (readonly [hover: string, base: string])[] = [
147
+ ['--button-primary-hover-surface', '--button-primary-surface'],
148
+ ['--button-secondary-hover-surface', '--button-secondary-surface'],
149
+ ['--button-outline-hover-surface', '--button-outline-surface'],
150
+ ['--button-success-hover-surface', '--button-success-surface'],
151
+ ['--button-danger-hover-surface', '--button-danger-surface'],
152
+ ['--button-warning-hover-surface', '--button-warning-surface'],
153
+ ];
154
+ const TINT_ON = 'var(--button-hover-tint)';
155
+ const TINT_OFF = 'var(--color-transparent)';
156
+
157
+ let tintOn = $derived.by(() => {
158
+ const ref = $editorState.components.button?.aliases['--button-hover-tint-enabled'];
159
+ return (ref?.kind === 'literal' ? ref.value : TINT_OFF) === TINT_ON;
160
+ });
161
+
162
+ function setTintOn(checked: boolean) {
163
+ setComponentAlias('button', '--button-hover-tint-enabled', {
164
+ kind: 'literal',
165
+ value: checked ? TINT_ON : TINT_OFF,
166
+ });
167
+ for (const [hover, base] of HOVER_SWAPS) {
168
+ if (checked) setComponentAlias('button', hover, { kind: 'token', name: base });
169
+ else clearComponentAlias('button', hover);
170
+ }
171
+ }
172
+
121
173
  let previewSize = $state<'default' | 'small'>('default');
122
174
 
123
175
  let linked = $derived(computeLinkedBlock(component, linkableContexts, allTokens, $editorState));
124
176
 
125
177
  let visibleVariantStates = $derived((v: Variant) => Object.fromEntries(
126
- Object.entries(variantStates(v)).map(([name, list]) => [name, withLinkedDisabled(list, linked.varSet)]),
178
+ Object.entries(variantStates(v)).map(([name, list]) => [
179
+ name,
180
+ withLinkedDisabled(list, linked.varSet).map((t) =>
181
+ tintOn && t.variable.endsWith('-hover-surface') ? { ...t, disabled: true } : t,
182
+ ),
183
+ ]),
127
184
  ) as Record<string, Token[]>);
128
185
 
129
186
  let visibleSmallStates = $derived(Object.fromEntries(
@@ -158,7 +215,17 @@
158
215
  >
159
216
  {#snippet extraPropertyRows(stateName)}
160
217
  {#if stateName === 'hover'}
161
- <div class="property-row">
218
+ <div class="property-row" data-token-variables="--button-hover-tint-enabled">
219
+ <span class="property-label">tint layer</span>
220
+ <Toggle checked={tintOn} onchange={setTintOn} />
221
+ </div>
222
+ {#if tintOn}
223
+ <div class="property-row">
224
+ <span class="property-label">tint color</span>
225
+ <UIPaletteSelector variable="--button-hover-tint" {component} showNone={false} />
226
+ </div>
227
+ {/if}
228
+ <div class="property-row" data-token-variables="--button-shimmer">
162
229
  <span class="property-label">hover shimmer</span>
163
230
  <Toggle checked={shimmerEnabled} onchange={handleShimmerChange} />
164
231
  </div>
@@ -92,14 +92,14 @@
92
92
  {
93
93
  key: 'hover-border',
94
94
  variants: ['default'],
95
- variable: () => '--card-hover-border-active',
95
+ variable: () => '--card-hover-border-enabled',
96
96
  values: [HOVER_OFF.border, HOVER_ON.border],
97
97
  default: { default: HOVER_OFF.border },
98
98
  },
99
99
  {
100
100
  key: 'hover-shadow',
101
101
  variants: ['default'],
102
- variable: () => '--card-hover-shadow-active',
102
+ variable: () => '--card-hover-shadow-enabled',
103
103
  values: [HOVER_OFF.shadow, HOVER_ON.shadow],
104
104
  default: { default: HOVER_OFF.shadow },
105
105
  },
@@ -122,15 +122,15 @@
122
122
  ($editorState.components[component]?.aliases ?? {}) as Record<string, import('../core/store/editorTypes').CssVarRef>,
123
123
  );
124
124
  let hoverEnabled = $derived.by(() => {
125
- const ref = aliases['--card-hover-border-active'];
125
+ const ref = aliases['--card-hover-border-enabled'];
126
126
  const raw = ref?.kind === 'literal' ? ref.value : HOVER_OFF.border;
127
127
  return raw === HOVER_ON.border;
128
128
  });
129
129
 
130
130
  function setHoverEnabled(checked: boolean) {
131
131
  const v = checked ? HOVER_ON : HOVER_OFF;
132
- setComponentAlias(component, '--card-hover-border-active', { kind: 'literal', value: v.border });
133
- setComponentAlias(component, '--card-hover-shadow-active', { kind: 'literal', value: v.shadow });
132
+ setComponentAlias(component, '--card-hover-border-enabled', { kind: 'literal', value: v.border });
133
+ setComponentAlias(component, '--card-hover-shadow-enabled', { kind: 'literal', value: v.shadow });
134
134
  }
135
135
  </script>
136
136
 
@@ -146,7 +146,7 @@
146
146
  {#if stateName === 'hover'}
147
147
  <div
148
148
  class="hover-control"
149
- data-token-variables="--card-hover-border-active --card-hover-shadow-active"
149
+ data-token-variables="--card-hover-border-enabled --card-hover-shadow-enabled"
150
150
  >
151
151
  <label class="hover-enable">
152
152
  <input type="checkbox" checked={hoverEnabled} onchange={(e) => setHoverEnabled(e.currentTarget.checked)} />
@@ -12,11 +12,11 @@
12
12
  const DEFAULT_CONFIRM: ButtonVariant = 'primary';
13
13
  const DEFAULT_CANCEL: ButtonVariant = 'outline';
14
14
 
15
- // Frame-level tokens (the dialog box itself + overlay + header + body + footer + close icon).
15
+ // Frame-level tokens (the dialog box itself + scrim + header + body + footer + close icon).
16
16
  // Button styling lives in Button.svelte — the dialog only owns its own chrome.
17
17
  const frameStates: Record<string, Token[]> = {
18
- overlay: [
19
- { label: 'backdrop color', groupKey: 'surface', variable: '--dialog-overlay-surface' },
18
+ scrim: [
19
+ { label: 'scrim color', groupKey: 'surface', variable: '--dialog-scrim-surface' },
20
20
  ],
21
21
  dialog: [
22
22
  { label: 'surface color', variable: '--dialog-surface' },
@@ -1,6 +1,6 @@
1
1
  <script module lang="ts">
2
2
  import { buildSiblings } from './scaffolding/siblings';
3
- import type { Token } from './scaffolding/types';
3
+ import type { Token, IntrinsicSpec } from './scaffolding/types';
4
4
 
5
5
  export const component = 'iconbutton';
6
6
 
@@ -92,21 +92,72 @@
92
92
  danger: 'fas fa-trash',
93
93
  warning: 'fas fa-triangle-exclamation',
94
94
  };
95
+
96
+ // The hover tint is off unless a project turns it on: the gate holds a
97
+ // transparent layer, a no-op, until it holds the tint itself.
98
+ export const intrinsics: IntrinsicSpec[] = [
99
+ {
100
+ key: 'hover-tint',
101
+ variants: ['default'],
102
+ variable: () => '--iconbutton-hover-tint-enabled',
103
+ values: ['var(--color-transparent)', 'var(--iconbutton-hover-tint)'],
104
+ default: { default: 'var(--color-transparent)' },
105
+ },
106
+ ];
95
107
  </script>
96
108
 
97
109
  <script lang="ts">
98
110
  import IconButton from '../../system/components/IconButton.svelte';
99
111
  import VariantGroup from './scaffolding/VariantGroup.svelte';
100
112
  import ComponentEditorBase from './scaffolding/ComponentEditorBase.svelte';
101
- import { editorState } from '../core/store/editorStore';
113
+ import { editorState, setComponentAlias, clearComponentAlias } from '../core/store/editorStore';
102
114
  import { computeLinkedBlock, withLinkedDisabled } from './scaffolding/linkedBlock';
115
+ import Toggle from '../ui/Toggle.svelte';
116
+ import UIPaletteSelector from '../ui/UIPaletteSelector.svelte';
117
+
118
+ // One switch. Turning the tint on also stands each hover surface down to its
119
+ // own base, so hover is the tint alone rather than a swap wearing a wash.
120
+ // Written as aliases rather than gated in CSS: a component token's value is
121
+ // substituted at :root, so it can never name a per-variant private var.
122
+ // Off clears the overrides, returning each surface to its shipped default.
123
+ const HOVER_SWAPS: readonly (readonly [hover: string, base: string])[] = [
124
+ ['--iconbutton-primary-hover-surface', '--iconbutton-primary-surface'],
125
+ ['--iconbutton-secondary-hover-surface', '--iconbutton-secondary-surface'],
126
+ ['--iconbutton-outline-hover-surface', '--iconbutton-outline-surface'],
127
+ ['--iconbutton-success-hover-surface', '--iconbutton-success-surface'],
128
+ ['--iconbutton-danger-hover-surface', '--iconbutton-danger-surface'],
129
+ ['--iconbutton-warning-hover-surface', '--iconbutton-warning-surface'],
130
+ ];
131
+ const TINT_ON = 'var(--iconbutton-hover-tint)';
132
+ const TINT_OFF = 'var(--color-transparent)';
133
+
134
+ let tintOn = $derived.by(() => {
135
+ const ref = $editorState.components.iconbutton?.aliases['--iconbutton-hover-tint-enabled'];
136
+ return (ref?.kind === 'literal' ? ref.value : TINT_OFF) === TINT_ON;
137
+ });
138
+
139
+ function setTintOn(checked: boolean) {
140
+ setComponentAlias('iconbutton', '--iconbutton-hover-tint-enabled', {
141
+ kind: 'literal',
142
+ value: checked ? TINT_ON : TINT_OFF,
143
+ });
144
+ for (const [hover, base] of HOVER_SWAPS) {
145
+ if (checked) setComponentAlias('iconbutton', hover, { kind: 'token', name: base });
146
+ else clearComponentAlias('iconbutton', hover);
147
+ }
148
+ }
103
149
 
104
150
  let previewSize = $state<'default' | 'small'>('default');
105
151
 
106
152
  let linked = $derived(computeLinkedBlock(component, linkableContexts, allTokens, $editorState));
107
153
 
108
154
  let visibleVariantStates = $derived((v: Variant) => Object.fromEntries(
109
- Object.entries(variantStates(v)).map(([name, list]) => [name, withLinkedDisabled(list, linked.varSet)]),
155
+ Object.entries(variantStates(v)).map(([name, list]) => [
156
+ name,
157
+ withLinkedDisabled(list, linked.varSet).map((t) =>
158
+ tintOn && t.variable.endsWith('-hover-surface') ? { ...t, disabled: true } : t,
159
+ ),
160
+ ]),
110
161
  ) as Record<string, Token[]>);
111
162
 
112
163
  let visibleSmallStates = $derived(Object.fromEntries(
@@ -139,6 +190,20 @@
139
190
  siblings={buildSiblings(variants, v, variantStates)}
140
191
  previewActions={sizeAction}
141
192
  >
193
+ {#snippet extraPropertyRows(stateName)}
194
+ {#if stateName === 'hover'}
195
+ <div class="property-row" data-token-variables="--iconbutton-hover-tint-enabled">
196
+ <span class="property-label">tint layer</span>
197
+ <Toggle checked={tintOn} onchange={setTintOn} />
198
+ </div>
199
+ {#if tintOn}
200
+ <div class="property-row">
201
+ <span class="property-label">tint color</span>
202
+ <UIPaletteSelector variable="--iconbutton-hover-tint" {component} showNone={false} />
203
+ </div>
204
+ {/if}
205
+ {/if}
206
+ {/snippet}
142
207
  {#snippet children({ activeState })}
143
208
  {@const forceClass = activeState === 'hover' ? 'force-hover' : activeState === 'active' ? 'force-active' : ''}
144
209
  {@const isDisabled = activeState === 'disabled'}
@@ -16,22 +16,22 @@
16
16
 
17
17
  export const allTokens: Token[] = Object.values(states).flat();
18
18
 
19
- // Global zoom defaults. `none` = off; `scale(...)` = on. `--image-zoom-hover` scales the
20
- // content within the masked frame (overflow scaling); `--image-grow-hover` scales the whole
19
+ // Global zoom defaults. `none` = off; `scale(...)` = on. `--image-zoom-enabled` scales the
20
+ // content within the masked frame (overflow scaling); `--image-grow-enabled` scales the whole
21
21
  // frame so it grows. At most one is ever `scale(...)`.
22
22
  const ZOOM_ON = 'scale(var(--image-zoom-scale))';
23
23
  export const intrinsics: IntrinsicSpec[] = [
24
24
  {
25
25
  key: 'zoom',
26
26
  variants: ['default'],
27
- variable: () => '--image-zoom-hover',
27
+ variable: () => '--image-zoom-enabled',
28
28
  values: ['none', ZOOM_ON],
29
29
  default: { default: 'none' },
30
30
  },
31
31
  {
32
32
  key: 'grow',
33
33
  variants: ['default'],
34
- variable: () => '--image-grow-hover',
34
+ variable: () => '--image-grow-enabled',
35
35
  values: ['none', ZOOM_ON],
36
36
  default: { default: 'none' },
37
37
  },
@@ -52,15 +52,15 @@
52
52
  const ref = aliases[variable];
53
53
  return ref?.kind === 'literal' ? ref.value : fallback;
54
54
  }
55
- let contentOn = $derived(aliasValue('--image-zoom-hover', intrinsics[0].default.default) === ZOOM_ON);
56
- let frameOn = $derived(aliasValue('--image-grow-hover', intrinsics[1].default.default) === ZOOM_ON);
55
+ let contentOn = $derived(aliasValue('--image-zoom-enabled', intrinsics[0].default.default) === ZOOM_ON);
56
+ let frameOn = $derived(aliasValue('--image-grow-enabled', intrinsics[1].default.default) === ZOOM_ON);
57
57
  let useZoom = $derived(contentOn || frameOn);
58
58
  // Overflow scaling = mask the zoom to the frame (the content path). Off = grow the frame.
59
59
  let overflowScaling = $derived(!frameOn);
60
60
 
61
61
  function applyZoom(zoomOn: boolean, masked: boolean) {
62
- setComponentAlias(component, '--image-zoom-hover', { kind: 'literal', value: zoomOn && masked ? ZOOM_ON : 'none' });
63
- setComponentAlias(component, '--image-grow-hover', { kind: 'literal', value: zoomOn && !masked ? ZOOM_ON : 'none' });
62
+ setComponentAlias(component, '--image-zoom-enabled', { kind: 'literal', value: zoomOn && masked ? ZOOM_ON : 'none' });
63
+ setComponentAlias(component, '--image-grow-enabled', { kind: 'literal', value: zoomOn && !masked ? ZOOM_ON : 'none' });
64
64
  }
65
65
  const setUseZoom = (checked: boolean) => applyZoom(checked, overflowScaling);
66
66
  const setOverflowScaling = (checked: boolean) => applyZoom(useZoom, checked);
@@ -1,5 +1,5 @@
1
1
  <script module lang="ts">
2
- import type { Token } from './scaffolding/types';
2
+ import type { Token, IntrinsicSpec } from './scaffolding/types';
3
3
 
4
4
  export const component = 'imagelightbox';
5
5
 
@@ -28,6 +28,17 @@
28
28
  };
29
29
 
30
30
  export const allTokens: Token[] = Object.values(states).flat();
31
+
32
+ // How a tile crops inside its frame: a structural choice, not a themeable value.
33
+ export const intrinsics: IntrinsicSpec[] = [
34
+ {
35
+ key: 'tile-fit',
36
+ variants: ['default'],
37
+ variable: () => '--imagelightbox-tile-object-fit',
38
+ values: ['contain', 'cover'],
39
+ default: { default: 'contain' },
40
+ },
41
+ ];
31
42
  </script>
32
43
 
33
44
  <script lang="ts">
@@ -1,6 +1,6 @@
1
1
  <script module lang="ts">
2
2
  import { buildTypeGroupTokens, buildTypeGroupShareableContexts } from './scaffolding/buildTypeGroupTokens';
3
- import type { Token, TypeGroupConfig } from './scaffolding/types';
3
+ import type { Token, TypeGroupConfig, IntrinsicSpec } from './scaffolding/types';
4
4
 
5
5
  export const component = 'menuselect';
6
6
 
@@ -91,14 +91,55 @@
91
91
  ['--menuselect-selected-icon-size', 'selected item'],
92
92
  ['--menuselect-disabled-icon-size', 'disabled item'],
93
93
  ]);
94
+
95
+ // The hover tint is off unless a project turns it on: the gate holds a
96
+ // transparent layer, a no-op, until it holds the tint itself.
97
+ export const intrinsics: IntrinsicSpec[] = [
98
+ {
99
+ key: 'hover-tint',
100
+ variants: ['default'],
101
+ variable: () => '--menuselect-hover-tint-enabled',
102
+ values: ['var(--color-transparent)', 'var(--menuselect-hover-tint)'],
103
+ default: { default: 'var(--color-transparent)' },
104
+ },
105
+ ];
94
106
  </script>
95
107
 
96
108
  <script lang="ts">
97
109
  import MenuSelect from '../../system/components/MenuSelect.svelte';
98
110
  import VariantGroup from './scaffolding/VariantGroup.svelte';
99
111
  import ComponentEditorBase from './scaffolding/ComponentEditorBase.svelte';
100
- import { editorState } from '../core/store/editorStore';
112
+ import { editorState, setComponentAlias, clearComponentAlias } from '../core/store/editorStore';
101
113
  import { computeLinkedBlock, withLinkedDisabled } from './scaffolding/linkedBlock';
114
+ import Toggle from '../ui/Toggle.svelte';
115
+ import UIPaletteSelector from '../ui/UIPaletteSelector.svelte';
116
+
117
+ // One switch. Turning the tint on also stands each hover surface down to its
118
+ // own base, so hover is the tint alone rather than a swap wearing a wash.
119
+ // Written as aliases rather than gated in CSS: a component token's value is
120
+ // substituted at :root, so it can never name a per-variant private var.
121
+ // Off clears the overrides, returning each surface to its shipped default.
122
+ const HOVER_SWAPS: readonly (readonly [hover: string, base: string])[] = [
123
+ ['--menuselect-hover-surface', '--menuselect-default-surface'],
124
+ ];
125
+ const TINT_ON = 'var(--menuselect-hover-tint)';
126
+ const TINT_OFF = 'var(--color-transparent)';
127
+
128
+ let tintOn = $derived.by(() => {
129
+ const ref = $editorState.components.menuselect?.aliases['--menuselect-hover-tint-enabled'];
130
+ return (ref?.kind === 'literal' ? ref.value : TINT_OFF) === TINT_ON;
131
+ });
132
+
133
+ function setTintOn(checked: boolean) {
134
+ setComponentAlias('menuselect', '--menuselect-hover-tint-enabled', {
135
+ kind: 'literal',
136
+ value: checked ? TINT_ON : TINT_OFF,
137
+ });
138
+ for (const [hover, base] of HOVER_SWAPS) {
139
+ if (checked) setComponentAlias('menuselect', hover, { kind: 'token', name: base });
140
+ else clearComponentAlias('menuselect', hover);
141
+ }
142
+ }
102
143
 
103
144
  type Item = { value: string; label: string; icon?: string; disabled?: boolean };
104
145
  const items: Item[] = [
@@ -114,7 +155,12 @@
114
155
  let linked = $derived(computeLinkedBlock(component, linkableContexts, allTokens, $editorState));
115
156
 
116
157
  let visibleStates = $derived(Object.fromEntries(
117
- Object.entries(states).map(([name, list]) => [name, withLinkedDisabled(list, linked.varSet)]),
158
+ Object.entries(states).map(([name, list]) => [
159
+ name,
160
+ withLinkedDisabled(list, linked.varSet).map((t) =>
161
+ tintOn && t.variable.endsWith('-hover-surface') ? { ...t, disabled: true } : t,
162
+ ),
163
+ ]),
118
164
  ) as Record<string, Token[]>);
119
165
  </script>
120
166
 
@@ -126,6 +172,20 @@
126
172
  {typeGroups}
127
173
  {component}
128
174
  >
175
+ {#snippet extraPropertyRows(stateName)}
176
+ {#if stateName === 'hover item'}
177
+ <div class="property-row" data-token-variables="--menuselect-hover-tint-enabled">
178
+ <span class="property-label">tint layer</span>
179
+ <Toggle checked={tintOn} onchange={setTintOn} />
180
+ </div>
181
+ {#if tintOn}
182
+ <div class="property-row">
183
+ <span class="property-label">tint color</span>
184
+ <UIPaletteSelector variable="--menuselect-hover-tint" {component} showNone={false} />
185
+ </div>
186
+ {/if}
187
+ {/if}
188
+ {/snippet}
129
189
  {#snippet canvasToolbarExtras()}
130
190
  <hr class="canvas-toolbar-divider" />
131
191
  <label class="show-icons-row">
@@ -1,6 +1,6 @@
1
1
  <script module lang="ts">
2
2
  import { buildTypeGroupTokens, buildTypeGroupShareableContexts } from './scaffolding/buildTypeGroupTokens';
3
- import type { Token, TypeGroupConfig } from './scaffolding/types';
3
+ import type { Token, TypeGroupConfig, IntrinsicSpec } from './scaffolding/types';
4
4
 
5
5
  export const component = 'segmentedcontrol';
6
6
 
@@ -123,14 +123,55 @@
123
123
  const linkableContexts = new Map<string, string>(
124
124
  buildTypeGroupShareableContexts(typeGroups),
125
125
  );
126
+
127
+ // The hover tint is off unless a project turns it on: the gate holds a
128
+ // transparent layer, a no-op, until it holds the tint itself.
129
+ export const intrinsics: IntrinsicSpec[] = [
130
+ {
131
+ key: 'hover-tint',
132
+ variants: ['default'],
133
+ variable: () => '--segmentedcontrol-hover-tint-enabled',
134
+ values: ['var(--color-transparent)', 'var(--segmentedcontrol-hover-tint)'],
135
+ default: { default: 'var(--color-transparent)' },
136
+ },
137
+ ];
126
138
  </script>
127
139
 
128
140
  <script lang="ts">
129
141
  import SegmentedControl from '../../system/components/SegmentedControl.svelte';
130
142
  import VariantGroup from './scaffolding/VariantGroup.svelte';
131
143
  import ComponentEditorBase from './scaffolding/ComponentEditorBase.svelte';
132
- import { editorState } from '../core/store/editorStore';
144
+ import { editorState, setComponentAlias, clearComponentAlias } from '../core/store/editorStore';
133
145
  import { computeLinkedBlock, withLinkedDisabled } from './scaffolding/linkedBlock';
146
+ import Toggle from '../ui/Toggle.svelte';
147
+ import UIPaletteSelector from '../ui/UIPaletteSelector.svelte';
148
+
149
+ // One switch. Turning the tint on also stands each hover surface down to its
150
+ // own base, so hover is the tint alone rather than a swap wearing a wash.
151
+ // Written as aliases rather than gated in CSS: a component token's value is
152
+ // substituted at :root, so it can never name a per-variant private var.
153
+ // Off clears the overrides, returning each surface to its shipped default.
154
+ const HOVER_SWAPS: readonly (readonly [hover: string, base: string])[] = [
155
+ ['--segmentedcontrol-option-hover-surface', '--color-transparent'],
156
+ ];
157
+ const TINT_ON = 'var(--segmentedcontrol-hover-tint)';
158
+ const TINT_OFF = 'var(--color-transparent)';
159
+
160
+ let tintOn = $derived.by(() => {
161
+ const ref = $editorState.components.segmentedcontrol?.aliases['--segmentedcontrol-hover-tint-enabled'];
162
+ return (ref?.kind === 'literal' ? ref.value : TINT_OFF) === TINT_ON;
163
+ });
164
+
165
+ function setTintOn(checked: boolean) {
166
+ setComponentAlias('segmentedcontrol', '--segmentedcontrol-hover-tint-enabled', {
167
+ kind: 'literal',
168
+ value: checked ? TINT_ON : TINT_OFF,
169
+ });
170
+ for (const [hover, base] of HOVER_SWAPS) {
171
+ if (checked) setComponentAlias('segmentedcontrol', hover, { kind: 'token', name: base });
172
+ else clearComponentAlias('segmentedcontrol', hover);
173
+ }
174
+ }
134
175
 
135
176
  type Segment = { value: string; label: string; icon?: string; disabled?: boolean };
136
177
  const segments: Segment[] = [
@@ -148,7 +189,12 @@
148
189
  let activeTypeGroups = $derived(previewSize === 'small' ? emptyTypeGroups : typeGroups);
149
190
 
150
191
  let visibleStates = $derived(Object.fromEntries(
151
- Object.entries(activeStates).map(([name, list]) => [name, withLinkedDisabled(list, linked.varSet)]),
192
+ Object.entries(activeStates).map(([name, list]) => [
193
+ name,
194
+ withLinkedDisabled(list, linked.varSet).map((t) =>
195
+ tintOn && t.variable.endsWith('-hover-surface') ? { ...t, disabled: true } : t,
196
+ ),
197
+ ]),
152
198
  ) as Record<string, Token[]>);
153
199
  </script>
154
200
 
@@ -160,6 +206,20 @@
160
206
  typeGroups={activeTypeGroups}
161
207
  {component}
162
208
  >
209
+ {#snippet extraPropertyRows(stateName)}
210
+ {#if stateName === 'hover option'}
211
+ <div class="property-row" data-token-variables="--segmentedcontrol-hover-tint-enabled">
212
+ <span class="property-label">tint layer</span>
213
+ <Toggle checked={tintOn} onchange={setTintOn} />
214
+ </div>
215
+ {#if tintOn}
216
+ <div class="property-row">
217
+ <span class="property-label">tint color</span>
218
+ <UIPaletteSelector variable="--segmentedcontrol-hover-tint" {component} showNone={false} />
219
+ </div>
220
+ {/if}
221
+ {/if}
222
+ {/snippet}
163
223
  {#snippet previewActions()}
164
224
  <label>
165
225
  <span>Size</span>
@@ -1,6 +1,6 @@
1
1
  <script module lang="ts">
2
2
  import { buildTypeGroupColorTokens } from './scaffolding/buildTypeGroupTokens';
3
- import type { Token, TypeGroupConfig } from './scaffolding/types';
3
+ import type { Token, TypeGroupConfig, IntrinsicSpec } from './scaffolding/types';
4
4
 
5
5
  export const component = 'sidenavigation';
6
6
 
@@ -259,14 +259,59 @@
259
259
  ['--sidenavigation-panel-padding', 'panel'],
260
260
  ['--sidenavigation-title-label-border-width', 'title block'],
261
261
  ]);
262
+
263
+ // The hover tint is off unless a project turns it on: the gate holds a
264
+ // transparent layer, a no-op, until it holds the tint itself.
265
+ export const intrinsics: IntrinsicSpec[] = [
266
+ {
267
+ key: 'hover-tint',
268
+ variants: ['default'],
269
+ variable: () => '--sidenavigation-hover-tint-enabled',
270
+ values: ['var(--color-transparent)', 'var(--sidenavigation-hover-tint)'],
271
+ default: { default: 'var(--color-transparent)' },
272
+ },
273
+ ];
262
274
  </script>
263
275
 
264
276
  <script lang="ts">
265
277
  import SideNavigation, { type SideNavSection, type SideNavFooter } from '../../system/components/SideNavigation.svelte';
266
278
  import VariantGroup from './scaffolding/VariantGroup.svelte';
267
279
  import ComponentEditorBase from './scaffolding/ComponentEditorBase.svelte';
268
- import { editorState } from '../core/store/editorStore';
280
+ import { editorState, setComponentAlias, clearComponentAlias } from '../core/store/editorStore';
269
281
  import { computeLinkedBlock, withLinkedDisabled } from './scaffolding/linkedBlock';
282
+ import Toggle from '../ui/Toggle.svelte';
283
+ import UIPaletteSelector from '../ui/UIPaletteSelector.svelte';
284
+
285
+ // One switch. Turning the tint on also stands each hover surface down to its
286
+ // own base, so hover is the tint alone rather than a swap wearing a wash.
287
+ // Written as aliases rather than gated in CSS: a component token's value is
288
+ // substituted at :root, so it can never name a per-variant private var.
289
+ // Off clears the overrides, returning each surface to its shipped default.
290
+ const HOVER_SWAPS: readonly (readonly [hover: string, base: string])[] = [
291
+ ['--sidenavigation-title-hover-surface', '--sidenavigation-title-default-surface'],
292
+ ['--sidenavigation-toggle-hover-surface', '--sidenavigation-toggle-default-surface'],
293
+ ['--sidenavigation-section-hover-surface', '--sidenavigation-section-default-surface'],
294
+ ['--sidenavigation-item-hover-surface', '--sidenavigation-item-default-surface'],
295
+ ['--sidenavigation-footer-hover-surface', '--sidenavigation-footer-default-surface'],
296
+ ];
297
+ const TINT_ON = 'var(--sidenavigation-hover-tint)';
298
+ const TINT_OFF = 'var(--color-transparent)';
299
+
300
+ let tintOn = $derived.by(() => {
301
+ const ref = $editorState.components.sidenavigation?.aliases['--sidenavigation-hover-tint-enabled'];
302
+ return (ref?.kind === 'literal' ? ref.value : TINT_OFF) === TINT_ON;
303
+ });
304
+
305
+ function setTintOn(checked: boolean) {
306
+ setComponentAlias('sidenavigation', '--sidenavigation-hover-tint-enabled', {
307
+ kind: 'literal',
308
+ value: checked ? TINT_ON : TINT_OFF,
309
+ });
310
+ for (const [hover, base] of HOVER_SWAPS) {
311
+ if (checked) setComponentAlias('sidenavigation', hover, { kind: 'token', name: base });
312
+ else clearComponentAlias('sidenavigation', hover);
313
+ }
314
+ }
270
315
 
271
316
  const demoSections: SideNavSection[] = [
272
317
  {
@@ -296,7 +341,12 @@
296
341
  let previewOpen = $state(true);
297
342
 
298
343
  let visibleStates = $derived(Object.fromEntries(
299
- Object.entries(states).map(([name, list]) => [name, withLinkedDisabled(list, linked.varSet)]),
344
+ Object.entries(states).map(([name, list]) => [
345
+ name,
346
+ withLinkedDisabled(list, linked.varSet).map((t) =>
347
+ tintOn && t.variable.endsWith('-hover-surface') ? { ...t, disabled: true } : t,
348
+ ),
349
+ ]),
300
350
  ) as Record<string, Token[]>);
301
351
 
302
352
  // Map the active part/state to the demo's force-hover / force-active hooks
@@ -331,6 +381,20 @@
331
381
  {component}
332
382
  selectorLabel="Part"
333
383
  >
384
+ {#snippet extraPropertyRows(stateName)}
385
+ {#if stateName.endsWith(' / Hover')}
386
+ <div class="property-row" data-token-variables="--sidenavigation-hover-tint-enabled">
387
+ <span class="property-label">tint layer</span>
388
+ <Toggle checked={tintOn} onchange={setTintOn} />
389
+ </div>
390
+ {#if tintOn}
391
+ <div class="property-row">
392
+ <span class="property-label">tint color</span>
393
+ <UIPaletteSelector variable="--sidenavigation-hover-tint" {component} showNone={false} />
394
+ </div>
395
+ {/if}
396
+ {/if}
397
+ {/snippet}
334
398
  {#snippet children({ activeState })}
335
399
  {@const { forceHoverPart, forceActivePart } = deriveForce(activeState)}
336
400
  <div class="sn-preview-frame">