@motion-proto/live-tokens 0.67.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 (138) 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 +239 -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/generate-theme.mjs +5 -4
  12. package/bin/lib/cssValues.mjs +50 -0
  13. package/bin/lib/findings.mjs +106 -0
  14. package/bin/lib/tokenVocabulary.mjs +213 -0
  15. package/dist-plugin/adjust/index.cjs +174 -23
  16. package/dist-plugin/adjust/index.d.cts +1 -1
  17. package/dist-plugin/adjust/index.d.ts +1 -1
  18. package/dist-plugin/adjust/index.js +68 -23
  19. package/dist-plugin/{chunk-J2JT4UEA.js → chunk-2YNERPXY.js} +26 -26
  20. package/dist-plugin/{chunk-NE6N66EE.js → chunk-GPIBU44G.js} +107 -1
  21. package/dist-plugin/{chunk-ZHPX7ZYQ.js → chunk-RFVYPNRO.js} +39 -1
  22. package/dist-plugin/{dataPaths-DZUzVv8H.d.ts → dataPaths-bJTCEO4H.d.cts} +1 -1
  23. package/dist-plugin/{dataPaths-DZUzVv8H.d.cts → dataPaths-bJTCEO4H.d.ts} +1 -1
  24. package/dist-plugin/fontPairing/index.d.cts +1 -1
  25. package/dist-plugin/fontPairing/index.d.ts +1 -1
  26. package/dist-plugin/generateColorsAndType/index.cjs +107 -1
  27. package/dist-plugin/generateColorsAndType/index.d.cts +1 -1
  28. package/dist-plugin/generateColorsAndType/index.d.ts +1 -1
  29. package/dist-plugin/generateColorsAndType/index.js +1 -1
  30. package/dist-plugin/index.cjs +173 -29
  31. package/dist-plugin/index.d.cts +1 -1
  32. package/dist-plugin/index.d.ts +1 -1
  33. package/dist-plugin/index.js +7 -7
  34. package/dist-plugin/migrateData/index.cjs +132 -26
  35. package/dist-plugin/migrateData/index.d.cts +1 -1
  36. package/dist-plugin/migrateData/index.d.ts +1 -1
  37. package/dist-plugin/migrateData/index.js +2 -2
  38. package/dist-plugin/tokensCssMigrations/index.cjs +39 -1
  39. package/dist-plugin/tokensCssMigrations/index.d.cts +1 -1
  40. package/dist-plugin/tokensCssMigrations/index.d.ts +1 -1
  41. package/dist-plugin/tokensCssMigrations/index.js +1 -1
  42. package/package.json +6 -5
  43. package/src/app/site.css +4 -4
  44. package/src/editor/bootstrap.ts +6 -6
  45. package/src/editor/component-editor/ButtonEditor.svelte +71 -4
  46. package/src/editor/component-editor/CardEditor.svelte +6 -6
  47. package/src/editor/component-editor/DialogEditor.svelte +3 -3
  48. package/src/editor/component-editor/IconButtonEditor.svelte +68 -3
  49. package/src/editor/component-editor/ImageEditor.svelte +8 -8
  50. package/src/editor/component-editor/ImageLightboxEditor.svelte +13 -2
  51. package/src/editor/component-editor/MenuSelectEditor.svelte +63 -3
  52. package/src/editor/component-editor/SegmentedControlEditor.svelte +63 -3
  53. package/src/editor/component-editor/SideNavigationEditor.svelte +67 -3
  54. package/src/editor/component-editor/SliderEditor.svelte +186 -0
  55. package/src/editor/component-editor/TabBarEditor.svelte +63 -3
  56. package/src/editor/component-editor/registry.ts +10 -0
  57. package/src/editor/component-editor/scaffolding/ComponentFileManager.svelte +4 -4
  58. package/src/editor/core/components/aliasKinds.ts +51 -28
  59. package/src/editor/core/fonts/fontLoader.ts +2 -2
  60. package/src/editor/core/preview/{lookPreview.ts → themePreview.ts} +27 -27
  61. package/src/editor/core/productionPulse.ts +3 -3
  62. package/src/editor/core/sketch/index.ts +52 -44
  63. package/src/editor/core/sketch/maskField.ts +9 -9
  64. package/src/editor/core/sketch/sketchLayer.ts +25 -9
  65. package/src/editor/core/sketch/sketchRegistry.ts +34 -34
  66. package/src/editor/core/sketch/sketchStore.ts +98 -83
  67. package/src/editor/core/sketch/sketchStyleService.ts +4 -4
  68. package/src/editor/core/sketch/sketchStyles.ts +28 -28
  69. package/src/editor/core/store/editorPersistence.ts +44 -1
  70. package/src/editor/core/store/editorRenderer.ts +2 -2
  71. package/src/editor/core/store/editorStore.ts +18 -18
  72. package/src/editor/core/store/editorTypes.ts +7 -6
  73. package/src/editor/core/themes/colorsAndTypeService.ts +1 -1
  74. package/src/editor/core/themes/loadRows.ts +8 -8
  75. package/src/editor/core/themes/migrations/2026-09-01-gate-suffix-enabled.ts +31 -0
  76. package/src/editor/core/themes/migrations/2026-09-01-scrim-rename.ts +52 -0
  77. package/src/editor/core/themes/migrations/2026-09-01-tabbar-active-tint.ts +27 -0
  78. package/src/editor/core/themes/migrations/2026-09-01-tint-rename.ts +42 -0
  79. package/src/editor/core/themes/migrations/index.ts +16 -0
  80. package/src/editor/core/themes/slices/domainVars.ts +2 -2
  81. package/src/editor/core/themes/slices/washes.ts +107 -0
  82. package/src/editor/core/themes/themeDocumentSync.ts +2 -2
  83. package/src/editor/core/themes/themeInit.ts +2 -2
  84. package/src/editor/core/themes/themeService.ts +17 -17
  85. package/src/editor/core/themes/{lookSummary.ts → themeSummary.ts} +9 -9
  86. package/src/editor/core/themes/themeTypes.ts +10 -6
  87. package/src/editor/docs/content/editing-tokens.md +5 -3
  88. package/src/editor/docs/content/getting-started.md +1 -1
  89. package/src/editor/docs/content/sketch-mode.md +22 -17
  90. package/src/editor/docs/content/themes-workflow.md +11 -11
  91. package/src/editor/docs/content/where-themes-live.md +6 -6
  92. package/src/editor/docs/content.generated.ts +5 -5
  93. package/src/editor/index.ts +2 -2
  94. package/src/editor/pages/EditorShell.svelte +1 -1
  95. package/src/editor/ui/SurfacesTab.svelte +3 -3
  96. package/src/editor/ui/ThemePanel.svelte +52 -52
  97. package/src/editor/ui/UITokenSelector.svelte +1 -0
  98. package/src/editor/ui/VariablesTab.svelte +2 -2
  99. package/src/editor/ui/sections/{OverlaysSection.svelte → WashesSection.svelte} +44 -43
  100. package/src/editor/ui/sketch/SketchPreview.svelte +2 -2
  101. package/src/editor/ui/sketch/SketchTab.svelte +64 -38
  102. package/src/live-tokens/data/colors-and-type/autumn.json +6 -6
  103. package/src/live-tokens/data/colors-and-type/default.json +6 -6
  104. package/src/live-tokens/data/colors-and-type/halloween.json +6 -6
  105. package/src/live-tokens/data/colors-and-type/midnight-study.json +6 -6
  106. package/src/live-tokens/data/colors-and-type/ocean.json +6 -6
  107. package/src/live-tokens/data/colors-and-type/royal-velvet.json +6 -6
  108. package/src/live-tokens/data/colors-and-type/sketchy.json +6 -6
  109. package/src/live-tokens/data/colors-and-type/spring-meadow.json +6 -6
  110. package/src/live-tokens/data/colors-and-type/sunset.json +6 -6
  111. package/src/live-tokens/data/sketch-styles/dry.json +5 -5
  112. package/src/live-tokens/data/sketch-styles/hatched.json +9 -9
  113. package/src/live-tokens/data/themes/autumn.json +82 -14
  114. package/src/live-tokens/data/themes/halloween.json +82 -14
  115. package/src/live-tokens/data/themes/midnight-study.json +82 -14
  116. package/src/live-tokens/data/themes/ocean.json +82 -14
  117. package/src/live-tokens/data/themes/royal-velvet.json +82 -14
  118. package/src/live-tokens/data/themes/sketchy.json +82 -14
  119. package/src/live-tokens/data/themes/spring-meadow.json +82 -14
  120. package/src/live-tokens/data/themes/sunset.json +82 -14
  121. package/src/live-tokens/data/tokens.generated.css +6 -6
  122. package/src/system/backdrop/backdrop.ts +1 -1
  123. package/src/system/components/Button.svelte +28 -10
  124. package/src/system/components/Card.svelte +6 -6
  125. package/src/system/components/Dialog.svelte +3 -3
  126. package/src/system/components/IconButton.svelte +23 -7
  127. package/src/system/components/Image.svelte +6 -6
  128. package/src/system/components/MenuSelect.svelte +17 -1
  129. package/src/system/components/SectionDivider.svelte +1 -1
  130. package/src/system/components/SegmentedControl.svelte +20 -2
  131. package/src/system/components/SideNavigation.svelte +22 -2
  132. package/src/system/components/Slider.svelte +348 -0
  133. package/src/system/components/TabBar.svelte +20 -2
  134. package/src/system/styles/CONVENTIONS.md +2 -2
  135. package/src/system/styles/tokens.css +12 -4
  136. package/template/package.json +3 -2
  137. package/template/src/pages/Home.svelte +1 -1
  138. package/src/editor/core/themes/slices/overlays.ts +0 -101
@@ -7,10 +7,10 @@ import {
7
7
  normalizeTheme,
8
8
  planLegacyRenames,
9
9
  versionedFileResourceServer
10
- } from "../chunk-J2JT4UEA.js";
10
+ } from "../chunk-2YNERPXY.js";
11
11
  import {
12
12
  CURRENT_COMPONENT_SCHEMA_VERSION
13
- } from "../chunk-NE6N66EE.js";
13
+ } from "../chunk-GPIBU44G.js";
14
14
  import {
15
15
  readLiveTokensConfig,
16
16
  resolveDataDirs
@@ -603,6 +603,42 @@ var tokensCssMigration_2026_08_27_editorialLargeSteps = {
603
603
  }
604
604
  };
605
605
 
606
+ // vite-plugin/tokensCssMigrations/migrations/2026-09-01-scrim-rename.ts
607
+ var RENAMES2 = [
608
+ ["--overlay-low", "--scrim-low"],
609
+ ["--overlay-high", "--scrim-high"],
610
+ ["--overlay", "--scrim"]
611
+ ];
612
+ var tokensCssMigration_2026_09_01_scrimRename = {
613
+ id: "2026-09-01-scrim-rename",
614
+ kind: "breaking",
615
+ description: "Rename --overlay-{low,base,high} to --scrim-*; a scrim dims what is behind it",
616
+ apply(css) {
617
+ let out = css;
618
+ for (const [oldName, newName] of RENAMES2) out = renameToken(out, oldName, newName);
619
+ return out;
620
+ }
621
+ };
622
+
623
+ // vite-plugin/tokensCssMigrations/migrations/2026-09-01-tint-scale.ts
624
+ var ENTRIES = [
625
+ { name: "--tint-low", value: "rgba(255, 255, 255, 0.05)" },
626
+ { name: "--tint", value: "rgba(255, 255, 255, 0.1)" },
627
+ { name: "--tint-high", value: "rgba(255, 255, 255, 0.15)" }
628
+ ];
629
+ var tokensCssMigration_2026_09_01_tintScale = {
630
+ id: "2026-09-01-tint-scale",
631
+ kind: "additive",
632
+ description: "Add the --tint-* scale; a tint shades the surface it sits on",
633
+ apply(css) {
634
+ return ensureScale(css, {
635
+ entries: ENTRIES,
636
+ sectionComment: "Tints \u2014 translucent washes that shade the surface they sit on",
637
+ anchorPrefixes: ["--scrim", "--overlay"]
638
+ });
639
+ }
640
+ };
641
+
606
642
  // vite-plugin/files/dataPaths.ts
607
643
  var import_fs = __toESM(require("fs"), 1);
608
644
  var import_path = __toESM(require("path"), 1);
@@ -651,7 +687,9 @@ var TOKENS_CSS_MIGRATIONS = [
651
687
  tokensCssMigration_2026_08_26_gradientStops,
652
688
  tokensCssMigration_2026_08_27_backdropPolarity,
653
689
  tokensCssMigration_2026_08_27_editorialSizeSteps,
654
- tokensCssMigration_2026_08_27_editorialLargeSteps
690
+ tokensCssMigration_2026_08_27_editorialLargeSteps,
691
+ tokensCssMigration_2026_09_01_scrimRename,
692
+ tokensCssMigration_2026_09_01_tintScale
655
693
  ];
656
694
  function runTokensCssMigrations(css) {
657
695
  return foldMigrations(css, () => true);
@@ -1,4 +1,4 @@
1
- export { r as readLiveTokensConfig } from '../dataPaths-DZUzVv8H.cjs';
1
+ export { r as readLiveTokensConfig } from '../dataPaths-bJTCEO4H.cjs';
2
2
 
3
3
  /**
4
4
  * A `tokens-css` migration: a pure, idempotent transform on the text of a
@@ -1,4 +1,4 @@
1
- export { r as readLiveTokensConfig } from '../dataPaths-DZUzVv8H.js';
1
+ export { r as readLiveTokensConfig } from '../dataPaths-bJTCEO4H.js';
2
2
 
3
3
  /**
4
4
  * A `tokens-css` migration: a pure, idempotent transform on the text of a
@@ -13,7 +13,7 @@ import {
13
13
  runTokensCssMigrations,
14
14
  semverBumpType,
15
15
  validateTokensCss
16
- } from "../chunk-ZHPX7ZYQ.js";
16
+ } from "../chunk-RFVYPNRO.js";
17
17
  import "../chunk-7TQQTHI6.js";
18
18
  import {
19
19
  readLiveTokensConfig
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@motion-proto/live-tokens",
3
- "version": "0.67.1",
3
+ "version": "0.69.0",
4
4
  "type": "module",
5
5
  "description": "Design token editor with live CSS variable editing. Svelte 5 + Vite 8.",
6
6
  "keywords": [
@@ -123,9 +123,9 @@
123
123
  "./app/fonts.css": "./src/system/styles/fonts.css"
124
124
  },
125
125
  "scripts": {
126
- "dev": "vite",
127
- "build": "vite build",
128
- "preview": "vite preview",
126
+ "dev": "VITE_CONFIG_NATIVE_IGNORE_WARNING=true vite",
127
+ "build": "VITE_CONFIG_NATIVE_IGNORE_WARNING=true vite build",
128
+ "preview": "VITE_CONFIG_NATIVE_IGNORE_WARNING=true vite preview",
129
129
  "check": "svelte-check --tsconfig ./tsconfig.json",
130
130
  "test": "vitest run",
131
131
  "test:watch": "vitest",
@@ -144,6 +144,7 @@
144
144
  "check:overlay-portal": "node scripts/check-overlay-portal.mjs",
145
145
  "check:editor-font-isolation": "node scripts/check-editor-font-isolation.mjs",
146
146
  "check:component-defaults": "node scripts/sync-component-defaults.mjs --check",
147
+ "check:pages": "node bin/cli.mjs check-page --strict",
147
148
  "check:production-is-default": "node scripts/check-production-is-default.mjs",
148
149
  "check:docs-content": "node scripts/sync-docs.mjs --check",
149
150
  "check:token-contract": "node scripts/check-token-contract.mjs",
@@ -157,7 +158,7 @@
157
158
  "collapse:theme": "node scripts/collapse-theme-to-default.mjs",
158
159
  "check:smoke-install": "bash scripts/smoke-install.sh",
159
160
  "check:smoke-create": "bash scripts/smoke-create.sh",
160
- "prepublishOnly": "npm run check:no-style-imports && npm run check:no-tooling-imports && npm run check:slot-prose && npm run check:overlay-portal && npm run check:editor-font-isolation && npm run check:component-defaults && npm run check:production-is-default && npm run check:docs-content && npm run build:lib && npm run check:token-contract && npm run check:preset-themes && npm run check:skills && npm run check:skill-atlas && npm run check:smoke-install && npm run check:smoke-create"
161
+ "prepublishOnly": "npm run check:no-style-imports && npm run check:no-tooling-imports && npm run check:slot-prose && npm run check:overlay-portal && npm run check:editor-font-isolation && npm run check:component-defaults && npm run check:pages && npm run check:production-is-default && npm run check:docs-content && npm run build:lib && npm run check:token-contract && npm run check:preset-themes && npm run check:skills && npm run check:skill-atlas && npm run check:smoke-install && npm run check:smoke-create"
161
162
  },
162
163
  "peerDependencies": {
163
164
  "@sveltejs/vite-plugin-svelte": "^7.0",
package/src/app/site.css CHANGED
@@ -71,7 +71,7 @@ p {
71
71
  font-size: var(--body-md-font-size);
72
72
  color: var(--text-secondary);
73
73
  line-height: var(--body-md-line-height);
74
- margin: 0 0 14px;
74
+ margin: 0 0 var(--space-16);
75
75
  }
76
76
 
77
77
  p:last-child {
@@ -142,15 +142,15 @@ ol li::marker {
142
142
 
143
143
  hr {
144
144
  border: none;
145
- border-top: 1px solid var(--border-neutral-faint);
145
+ border-top: var(--border-width-1) solid var(--border-neutral-faint);
146
146
  margin: var(--space-32) 0;
147
147
  }
148
148
 
149
149
  blockquote {
150
150
  margin: var(--space-16) 0;
151
151
  padding: var(--space-12) var(--space-20);
152
- border: 1px solid var(--color-brand-500);
153
- border-left: 4px solid var(--color-brand-500);
152
+ border: var(--border-width-1) solid var(--color-brand-500);
153
+ border-left: var(--border-width-4) solid var(--color-brand-500);
154
154
  background: var(--surface-neutral-high);
155
155
  border-radius: 0 var(--radius-md) var(--radius-md) 0;
156
156
  color: var(--text-secondary);
@@ -22,16 +22,16 @@ import * as editorStore from './core/store/editorStore';
22
22
  import * as themeDocumentSync from './core/themes/themeDocumentSync';
23
23
  import { initializeTheme } from './core/themes/themeInit';
24
24
  import { registerComponent, type RegisterComponentEntry } from './component-editor/registry';
25
- import { registerSketchLook, type RegisterSketchLookInput } from './core/sketch/sketchRegistry';
25
+ import { registerSketchStyle, type RegisterSketchStyleInput } from './core/sketch/sketchRegistry';
26
26
 
27
27
  export interface BootLiveTokensOptions {
28
28
  /** Consumer-authored components to register with the editor before mount. Dev-only. */
29
29
  components?: RegisterComponentEntry[];
30
- /** Sketchstyles this project ships, joining the shipped looks in every
30
+ /** Sketchstyles this project ships, joining the shipped sketchstyles in every
31
31
  picker. Registered in a build as well as in dev: carrying a customized
32
32
  sketchstyle to a published site is the whole point, so these must not sit
33
33
  behind the DEV guard `components` sits behind. */
34
- sketchLooks?: RegisterSketchLookInput[];
34
+ sketchStyles?: RegisterSketchStyleInput[];
35
35
  }
36
36
 
37
37
  export async function bootLiveTokens(
@@ -44,10 +44,10 @@ export async function bootLiveTokens(
44
44
  columnsOverlay.init();
45
45
  editorStore.init();
46
46
 
47
- // Before the DEV block: `initializeTheme` recovers the open theme's look by
47
+ // Before the DEV block: `initializeTheme` recovers the open theme's style by
48
48
  // name against the pool, so the pool has to be whole by the time it runs.
49
- if (opts.sketchLooks) {
50
- for (const look of opts.sketchLooks) registerSketchLook(look);
49
+ if (opts.sketchStyles) {
50
+ for (const style of opts.sketchStyles) registerSketchStyle(style);
51
51
  }
52
52
 
53
53
  if (import.meta.env.DEV) {
@@ -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,10 +1,10 @@
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
 
6
6
  // Three parts: tile (closed inline + animated modal surface), overlay (scrim),
7
- // chrome (shared toolbar + close-button look). Chrome carries a hover state
7
+ // chrome (shared toolbar + close-button styling). Chrome carries a hover state
8
8
  // for the interactive controls. No second variant.
9
9
  const states: Record<string, Token[]> = {
10
10
  tile: [
@@ -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">