@motion-proto/live-tokens 0.96.0 → 0.98.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.
Files changed (206) hide show
  1. package/.claude/skills/live-tokens-check-compliance/SKILL.md +5 -0
  2. package/.claude/skills/live-tokens-create-component/SKILL.md +4 -3
  3. package/.claude/skills/live-tokens-create-component/references/contract-tests.md +71 -11
  4. package/CHANGELOG.md +309 -0
  5. package/README.md +2 -2
  6. package/bin/check-component.mjs +8 -0
  7. package/bin/check-page.mjs +6 -0
  8. package/bin/cli.mjs +36 -6
  9. package/bin/contractRunner.mjs +142 -17
  10. package/bin/lib/findings.mjs +20 -4
  11. package/bin/rules/a11y.mjs +53 -0
  12. package/bin/set-colors.mjs +1 -0
  13. package/bin/setup-claude.mjs +2 -2
  14. package/dist-plugin/{chunk-A4TBP4MM.js → chunk-SLICH4IH.js} +2 -2
  15. package/dist-plugin/index.cjs +148 -3
  16. package/dist-plugin/index.js +250 -105
  17. package/dist-plugin/setColors/index.cjs +7 -1
  18. package/dist-plugin/setColors/index.d.cts +9 -4
  19. package/dist-plugin/setColors/index.d.ts +9 -4
  20. package/dist-plugin/setColors/index.js +8 -2
  21. package/dist-plugin/setGeometry/index.d.cts +1 -1
  22. package/dist-plugin/setGeometry/index.d.ts +1 -1
  23. package/dist-plugin/setType/index.d.cts +1 -1
  24. package/dist-plugin/setType/index.d.ts +1 -1
  25. package/dist-plugin/{themeTypes-B8_Idrp4.d.cts → themeTypes-BaTQUyac.d.cts} +10 -1
  26. package/dist-plugin/{themeTypes-B8_Idrp4.d.ts → themeTypes-BaTQUyac.d.ts} +10 -1
  27. package/package.json +49 -2
  28. package/src/demo/LabeledSelect.svelte +1 -1
  29. package/src/editor/bootstrap.ts +3 -1
  30. package/src/editor/component-editor/BadgeEditor.svelte +13 -0
  31. package/src/editor/component-editor/ButtonEditor.svelte +33 -2
  32. package/src/editor/component-editor/CalloutEditor.svelte +13 -0
  33. package/src/editor/component-editor/CardEditor.svelte +12 -2
  34. package/src/editor/component-editor/CodeSnippetEditor.svelte +11 -1
  35. package/src/editor/component-editor/CollapsibleSectionEditor.svelte +18 -1
  36. package/src/editor/component-editor/CornerBadgeEditor.svelte +13 -0
  37. package/src/editor/component-editor/DialogEditor.svelte +15 -0
  38. package/src/editor/component-editor/IconButtonEditor.svelte +29 -1
  39. package/src/editor/component-editor/ImageEditor.svelte +20 -9
  40. package/src/editor/component-editor/ImageLightboxEditor.svelte +44 -0
  41. package/src/editor/component-editor/InlineEditActionsEditor.svelte +13 -1
  42. package/src/editor/component-editor/InputEditor.svelte +23 -0
  43. package/src/editor/component-editor/MenuSelectEditor.svelte +27 -1
  44. package/src/editor/component-editor/NotificationEditor.svelte +15 -0
  45. package/src/editor/component-editor/PanelEditor.svelte +1 -1
  46. package/src/editor/component-editor/ProgressBarEditor.svelte +13 -0
  47. package/src/editor/component-editor/RadioButtonEditor.svelte +20 -1
  48. package/src/editor/component-editor/SectionDividerEditor.svelte +41 -13
  49. package/src/editor/component-editor/SegmentedControlEditor.svelte +29 -1
  50. package/src/editor/component-editor/SideNavigationEditor.svelte +40 -1
  51. package/src/editor/component-editor/SlidePagerEditor.svelte +20 -0
  52. package/src/editor/component-editor/SliderEditor.svelte +33 -0
  53. package/src/editor/component-editor/TabBarEditor.svelte +23 -2
  54. package/src/editor/component-editor/TableEditor.svelte +14 -0
  55. package/src/editor/component-editor/ToggleEditor.svelte +17 -0
  56. package/src/editor/component-editor/TooltipEditor.svelte +11 -2
  57. package/src/editor/component-editor/VideoFrameEditor.svelte +8 -0
  58. package/src/editor/component-editor/VideoLightboxEditor.svelte +10 -0
  59. package/src/editor/component-editor/registration.ts +2 -1
  60. package/src/editor/component-editor/registry.ts +69 -27
  61. package/src/editor/component-editor/scaffolding/DemoHeader.svelte +2 -0
  62. package/src/editor/component-editor/scaffolding/StateBlock.svelte +19 -8
  63. package/src/editor/component-editor/scaffolding/TokenLayout.svelte +3 -0
  64. package/src/editor/component-editor/scaffolding/VariantGroup.svelte +70 -10
  65. package/src/editor/component-editor/scaffolding/editorContext.ts +7 -0
  66. package/src/editor/core/a11y/a11yReport.ts +101 -0
  67. package/src/editor/core/a11y/a11yReportTypes.ts +69 -0
  68. package/src/editor/core/a11y/contrastFailures.ts +22 -0
  69. package/src/editor/core/components/contrastPairRegistry.ts +18 -0
  70. package/src/editor/core/palettes/contrast.ts +44 -0
  71. package/src/editor/core/palettes/contrastPairs.ts +79 -0
  72. package/src/editor/core/palettes/resolveColor.ts +77 -0
  73. package/src/editor/core/store/editorPersistence.ts +9 -1
  74. package/src/editor/core/store/editorRenderer.ts +2 -0
  75. package/src/editor/core/store/editorStore.ts +29 -0
  76. package/src/editor/core/store/editorTypes.ts +9 -0
  77. package/src/editor/core/store/editorViewStore.ts +2 -2
  78. package/src/editor/core/store/unsavedEdits.ts +10 -0
  79. package/src/editor/core/themes/buildColors.ts +16 -5
  80. package/src/editor/core/themes/liveStateStream.ts +7 -1
  81. package/src/editor/core/themes/parsers/gradient.ts +5 -2
  82. package/src/editor/core/themes/slices/customColors.ts +226 -0
  83. package/src/editor/core/themes/themeTypes.ts +10 -0
  84. package/src/editor/docs/chapters.ts +1 -0
  85. package/src/editor/docs/content/accessibility.md +362 -0
  86. package/src/editor/docs/content/editing-tokens.md +56 -1
  87. package/src/editor/docs/content.generated.ts +2 -1
  88. package/src/editor/pages/ComponentEditorPage.svelte +10 -1
  89. package/src/editor/pages/EditorShell.svelte +97 -10
  90. package/src/editor/skill-atlas/skillSources.generated.ts +3 -3
  91. package/src/editor/skill-atlas/trees/check-compliance.ts +3 -3
  92. package/src/editor/skill-atlas/trees/create-component.ts +12 -12
  93. package/src/editor/ui/CustomColorReplaceStep.svelte +118 -0
  94. package/src/editor/ui/EditorViewSwitcher.svelte +15 -2
  95. package/src/editor/ui/GradientEditor.svelte +9 -1
  96. package/src/editor/ui/ThemePanel.svelte +4 -4
  97. package/src/editor/ui/UIContrastReadout.svelte +186 -0
  98. package/src/editor/ui/UIInfoPopover.svelte +16 -2
  99. package/src/editor/ui/UIPaletteSelector.svelte +347 -15
  100. package/src/editor/ui/UITokenSelector.svelte +33 -14
  101. package/src/editor/ui/VariablesTab.svelte +3 -0
  102. package/src/editor/ui/a11y/A11yComponentIssues.svelte +103 -0
  103. package/src/editor/ui/a11y/A11yFindingRow.svelte +57 -0
  104. package/src/editor/ui/a11y/A11yIssueRow.svelte +121 -0
  105. package/src/editor/ui/a11y/A11yOverview.svelte +242 -0
  106. package/src/editor/ui/a11y/A11yPanel.svelte +128 -0
  107. package/src/editor/ui/a11y/A11yPropertyMark.svelte +84 -0
  108. package/src/editor/ui/a11y/A11yRailMark.svelte +34 -0
  109. package/src/editor/ui/a11y/AccessibilityTab.svelte +334 -0
  110. package/src/editor/ui/a11y/a11yView.ts +218 -0
  111. package/src/editor/ui/anchorToTrigger.ts +63 -0
  112. package/src/editor/ui/contrastReadout.ts +45 -0
  113. package/src/editor/ui/sections/CustomColorsSection.svelte +254 -0
  114. package/src/live-tokens/data/themes/autumn.json +20 -20
  115. package/src/live-tokens/data/themes/halloween.json +20 -20
  116. package/src/live-tokens/data/themes/midnight-study.json +20 -20
  117. package/src/live-tokens/data/themes/ocean.json +20 -20
  118. package/src/live-tokens/data/themes/royal-velvet.json +20 -20
  119. package/src/live-tokens/data/themes/sketchy.json +20 -20
  120. package/src/live-tokens/data/themes/spring-meadow.json +20 -20
  121. package/src/live-tokens/data/themes/sunset.json +20 -20
  122. package/src/system/components/Badge.svelte +3 -3
  123. package/src/system/components/Button.svelte +9 -3
  124. package/src/system/components/CodeSnippet.svelte +8 -1
  125. package/src/system/components/CornerBadge.svelte +3 -3
  126. package/src/system/components/Dialog.svelte +43 -14
  127. package/src/system/components/IconButton.svelte +1 -1
  128. package/src/system/components/Image.svelte +7 -0
  129. package/src/system/components/ImageLightbox.svelte +26 -26
  130. package/src/system/components/Input.svelte +8 -1
  131. package/src/system/components/MenuSelect.svelte +30 -2
  132. package/src/system/components/Notification.svelte +1 -1
  133. package/src/system/components/ProgressBar.svelte +6 -0
  134. package/src/system/components/RadioButton.svelte +36 -3
  135. package/src/system/components/SegmentedControl.svelte +36 -2
  136. package/src/system/components/SlidePager.svelte +2 -2
  137. package/src/system/components/Slider.svelte +13 -9
  138. package/src/system/components/TabBar.svelte +33 -3
  139. package/src/system/components/Toggle.svelte +2 -2
  140. package/src/system/components/VideoFrame.svelte +1 -1
  141. package/src/system/components/VideoLightbox.svelte +2 -2
  142. package/src/system/internal/focusTrap.ts +35 -0
  143. package/src/testing-js/__aria__/badge.yml +40 -0
  144. package/src/testing-js/__aria__/button.yml +88 -0
  145. package/src/testing-js/__aria__/callout.yml +12 -0
  146. package/src/testing-js/__aria__/card.yml +8 -0
  147. package/src/testing-js/__aria__/codesnippet.yml +21 -0
  148. package/src/testing-js/__aria__/collapsiblesection.yml +23 -0
  149. package/src/testing-js/__aria__/cornerbadge.yml +24 -0
  150. package/src/testing-js/__aria__/dialog.yml +42 -0
  151. package/src/testing-js/__aria__/iconbutton.yml +57 -0
  152. package/src/testing-js/__aria__/image.yml +10 -0
  153. package/src/testing-js/__aria__/imagelightbox.yml +50 -0
  154. package/src/testing-js/__aria__/inlineeditactions.yml +16 -0
  155. package/src/testing-js/__aria__/input.yml +61 -0
  156. package/src/testing-js/__aria__/menuselect.yml +30 -0
  157. package/src/testing-js/__aria__/notification.yml +30 -0
  158. package/src/testing-js/__aria__/panel.yml +2 -0
  159. package/src/testing-js/__aria__/progressbar.yml +2 -0
  160. package/src/testing-js/__aria__/radiobutton.yml +20 -0
  161. package/src/testing-js/__aria__/sectiondivider.yml +9 -0
  162. package/src/testing-js/__aria__/segmentedcontrol.yml +35 -0
  163. package/src/testing-js/__aria__/sidenavigation.yml +304 -0
  164. package/src/testing-js/__aria__/slidepager.yml +72 -0
  165. package/src/testing-js/__aria__/slider.yml +28 -0
  166. package/src/testing-js/__aria__/tabbar.yml +35 -0
  167. package/src/testing-js/__aria__/table.yml +144 -0
  168. package/src/testing-js/__aria__/toggle.yml +12 -0
  169. package/src/testing-js/__aria__/tooltip.yml +2 -0
  170. package/src/testing-js/__aria__/videoframe.yml +6 -0
  171. package/src/testing-js/__aria__/videolightbox.yml +12 -0
  172. package/src/testing-js/{chunk-WIZ6W7UT.js → chunk-3WCLMOM7.js} +2 -2
  173. package/src/testing-js/{chunk-SHBYZUPD.js → chunk-5HNZHIHG.js} +211 -14
  174. package/src/testing-js/chunk-5HNZHIHG.js.map +1 -0
  175. package/src/testing-js/{chunk-NB3NZRBM.js → chunk-7CM5D7PF.js} +5 -1
  176. package/src/testing-js/chunk-7CM5D7PF.js.map +1 -0
  177. package/src/testing-js/chunk-EWSGE27V.js +168 -0
  178. package/src/testing-js/chunk-EWSGE27V.js.map +1 -0
  179. package/src/testing-js/{chunk-GKO27NGN.js → chunk-R54I7YL6.js} +2 -2
  180. package/src/testing-js/{chunk-X6KHEVIG.js → chunk-RWH46IU5.js} +97 -29
  181. package/src/testing-js/chunk-RWH46IU5.js.map +1 -0
  182. package/src/testing-js/chunk-SHLPDNAQ.js +658 -0
  183. package/src/testing-js/chunk-SHLPDNAQ.js.map +1 -0
  184. package/src/testing-js/{chunk-3UKGXCDL.js → chunk-YVVR3GJZ.js} +1 -1
  185. package/src/testing-js/chunk-YVVR3GJZ.js.map +1 -0
  186. package/src/testing-js/component-a11y.contract.js +283 -0
  187. package/src/testing-js/component-a11y.contract.js.map +1 -0
  188. package/src/testing-js/component-alias.contract.js +2 -2
  189. package/src/testing-js/component-behavior.contract.js +2 -2
  190. package/src/testing-js/component-editor.contract.js +6 -5
  191. package/src/testing-js/component-editor.contract.js.map +1 -1
  192. package/src/testing-js/component-render.contract.js +10 -6
  193. package/src/testing-js/component-render.contract.js.map +1 -1
  194. package/src/testing-js/index.d.ts +44 -16
  195. package/src/testing-js/index.js +9 -9
  196. package/src/testing-js/page-compliance.contract.js +163 -18
  197. package/src/testing-js/page-compliance.contract.js.map +1 -1
  198. package/src/testing-js/vitest.js +3 -3
  199. package/template/package.json +9 -0
  200. package/src/editor/ui/keepInViewport.ts +0 -84
  201. package/src/testing-js/chunk-3UKGXCDL.js.map +0 -1
  202. package/src/testing-js/chunk-NB3NZRBM.js.map +0 -1
  203. package/src/testing-js/chunk-SHBYZUPD.js.map +0 -1
  204. package/src/testing-js/chunk-X6KHEVIG.js.map +0 -1
  205. /package/src/testing-js/{chunk-WIZ6W7UT.js.map → chunk-3WCLMOM7.js.map} +0 -0
  206. /package/src/testing-js/{chunk-GKO27NGN.js.map → chunk-R54I7YL6.js.map} +0 -0
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Svelte action: pin a `position: fixed` popover under its trigger so no
3
+ * scrolling or clipping ancestor can cut it off. It aligns to the trigger's
4
+ * left edge, flips above when the space below runs out, and clamps to the
5
+ * viewport. It re-places on scroll, resize, and content reflow.
6
+ */
7
+
8
+ const GAP = 4;
9
+ const MARGIN = 8;
10
+
11
+ export function anchorToTrigger(node: HTMLElement, trigger: HTMLElement) {
12
+ let anchor = trigger;
13
+ let pendingFrame: number | null = null;
14
+
15
+ function place(): void {
16
+ // A container-type or transformed ancestor moves the fixed containing
17
+ // block off the viewport origin. Measuring at 0,0 reads that offset.
18
+ node.style.left = '0px';
19
+ node.style.top = '0px';
20
+ const origin = node.getBoundingClientRect();
21
+ const a = anchor.getBoundingClientRect();
22
+ const vw = window.innerWidth;
23
+ const vh = window.innerHeight;
24
+
25
+ const left = Math.max(MARGIN, Math.min(a.left, vw - MARGIN - origin.width));
26
+ let top = a.bottom + GAP;
27
+ if (top + origin.height > vh - MARGIN) {
28
+ const above = a.top - GAP - origin.height;
29
+ top = above >= MARGIN ? above : Math.max(MARGIN, vh - MARGIN - origin.height);
30
+ }
31
+
32
+ node.style.left = `${left - origin.left}px`;
33
+ node.style.top = `${top - origin.top}px`;
34
+ }
35
+
36
+ function schedule(): void {
37
+ if (pendingFrame !== null) return;
38
+ pendingFrame = requestAnimationFrame(() => {
39
+ pendingFrame = null;
40
+ place();
41
+ });
42
+ }
43
+
44
+ place();
45
+
46
+ const ro = new ResizeObserver(schedule);
47
+ ro.observe(node);
48
+ window.addEventListener('resize', schedule);
49
+ window.addEventListener('scroll', schedule, true);
50
+
51
+ return {
52
+ update(next: HTMLElement) {
53
+ anchor = next;
54
+ schedule();
55
+ },
56
+ destroy() {
57
+ if (pendingFrame !== null) cancelAnimationFrame(pendingFrame);
58
+ ro.disconnect();
59
+ window.removeEventListener('resize', schedule);
60
+ window.removeEventListener('scroll', schedule, true);
61
+ },
62
+ };
63
+ }
@@ -0,0 +1,45 @@
1
+ import { CONTRAST_RULES, LARGE_BOLD_PX, LARGE_TEXT_PX } from '../core/palettes/contrast';
2
+ import { closestToFailing, type PairMeasure } from '../core/palettes/contrastPairs';
3
+
4
+ export type ReadoutState = 'pass' | 'fail' | 'inactive';
5
+
6
+ export interface Readout {
7
+ label: string;
8
+ state: ReadoutState;
9
+ ariaLabel: string;
10
+ measure: PairMeasure;
11
+ }
12
+
13
+ /** Truncation keeps a failing value from displaying as its floor. */
14
+ export function formatRatio(ratio: number): string {
15
+ return `${(Math.floor(ratio * 100 + 1e-9) / 100).toFixed(2)}:1`;
16
+ }
17
+
18
+ export const formatFloor = (floor: number) => `${floor}:1`;
19
+
20
+ export function measureState(measure: PairMeasure): ReadoutState {
21
+ if (measure.pair.inactive) return 'inactive';
22
+ return measure.passes ? 'pass' : 'fail';
23
+ }
24
+
25
+ export function readoutState(measures: PairMeasure[]): Readout {
26
+ const measure = closestToFailing(measures);
27
+ const state = measureState(measure);
28
+ const label = formatRatio(measure.ratio);
29
+ const verdict = state === 'inactive'
30
+ ? 'exempt as a disabled control'
31
+ : `${state === 'pass' ? 'meets' : 'below'} ${formatFloor(measure.floor)}`;
32
+ return { label, state, ariaLabel: `Contrast ${label}, ${verdict}`, measure };
33
+ }
34
+
35
+ export function ruleSentence(measure: PairMeasure): string {
36
+ const rule = CONTRAST_RULES[measure.pair.role];
37
+ let sentence = `WCAG 2.2 success criterion ${rule.criterion}, ${rule.name} (${rule.level}): `
38
+ + `${formatFloor(rule.floor)} against the colors behind it`;
39
+ if (rule.largeFloor !== undefined) {
40
+ sentence += `, or ${formatFloor(rule.largeFloor)} for large text (${LARGE_TEXT_PX}px, or ${LARGE_BOLD_PX}px bold)`;
41
+ }
42
+ sentence += '.';
43
+ if (measure.pair.inactive) sentence += ' Disabled controls are exempt.';
44
+ return sentence;
45
+ }
@@ -0,0 +1,254 @@
1
+ <script lang="ts">
2
+ import { onDestroy } from 'svelte';
3
+ import { get } from 'svelte/store';
4
+ import {
5
+ editorState,
6
+ beginScope,
7
+ commitScope,
8
+ cancelScope,
9
+ beginSliderGesture,
10
+ setCustomColor,
11
+ removeCustomColor,
12
+ replaceAndRemoveCustomColor,
13
+ type Scope,
14
+ } from '../../core/store/editorStore';
15
+ import type { Oklch } from '../../core/palettes/oklch';
16
+ import ColorEditPanel from '../ColorEditPanel.svelte';
17
+ import CustomColorReplaceStep from '../CustomColorReplaceStep.svelte';
18
+
19
+ interface Props {
20
+ copiedVar?: string | null;
21
+ oncopy?: (variable: string) => void;
22
+ }
23
+
24
+ let { copiedVar = null, oncopy }: Props = $props();
25
+
26
+ const PREFIX = '--color-custom-';
27
+ const customNumber = (v: string) => v.slice(PREFIX.length);
28
+
29
+ let tokens = $derived(
30
+ [...$editorState.customColors.tokens]
31
+ .sort((a, b) => Number(customNumber(a.variable)) - Number(customNumber(b.variable))),
32
+ );
33
+
34
+ /** An open edit of one custom colour, or the replace step of its remove. */
35
+ interface Session {
36
+ mode: 'edit' | 'replace';
37
+ variable: string;
38
+ draft: Oklch;
39
+ }
40
+ let session = $state<Session | null>(null);
41
+ // Held outside $state: the store matches an open scope by identity, and a
42
+ // proxied handle would never match.
43
+ let sessionScope: Scope | null = null;
44
+
45
+ function tokenColor(variable: string): Oklch | undefined {
46
+ return get(editorState).customColors.tokens.find((t) => t.variable === variable)?.color;
47
+ }
48
+
49
+ function openEdit(variable: string) {
50
+ const color = tokenColor(variable);
51
+ if (!color) return;
52
+ sessionScope = beginScope({ label: `edit custom colour ${variable}`, collapseToOne: true, clipUndoFloor: true });
53
+ session = { mode: 'edit', variable, draft: { ...color } };
54
+ }
55
+
56
+ function editDraft(h: number, c: number, lPct: number) {
57
+ if (!session) return;
58
+ session.draft = { l: lPct / 100, c, h };
59
+ setCustomColor(session.variable, session.draft);
60
+ }
61
+
62
+ function closeEdit(commit: boolean) {
63
+ if (sessionScope) {
64
+ if (commit) commitScope(sessionScope);
65
+ else cancelScope(sessionScope);
66
+ }
67
+ sessionScope = null;
68
+ session = null;
69
+ }
70
+
71
+ function requestRemove(variable: string) {
72
+ if (removeCustomColor(variable)) return;
73
+ const color = tokenColor(variable);
74
+ if (color) session = { mode: 'replace', variable, draft: { ...color } };
75
+ }
76
+
77
+ function confirmReplace(replacement: string) {
78
+ if (!session) return;
79
+ const { variable } = session;
80
+ session = null;
81
+ replaceAndRemoveCustomColor(variable, replacement);
82
+ }
83
+
84
+ onDestroy(() => {
85
+ if (sessionScope) cancelScope(sessionScope);
86
+ });
87
+ </script>
88
+
89
+ <section class="section" id="custom-colors">
90
+ <h2 class="section-title">Custom Colours</h2>
91
+ <p class="editor-intro">A custom colour holds its own OKLCH value, and every slot that names it follows its edits. Add one from the Custom row of any colour picker. Removing a colour in use first asks which token takes its place.</p>
92
+ {#if tokens.length === 0}
93
+ <p class="custom-empty">This theme has no custom colours.</p>
94
+ {:else}
95
+ <div class="custom-grid">
96
+ {#each tokens as token (token.variable)}
97
+ {@const active = session?.variable === token.variable}
98
+ <div class={['custom-item', { active }]}>
99
+ <div class="custom-box" style="background: var({token.variable});"></div>
100
+ <div class="token-info">
101
+ <button class={['token-variable copyable', { copied: copiedVar === token.variable }]} onclick={() => oncopy?.(token.variable)}>{copiedVar === token.variable ? 'copied!' : token.variable}</button>
102
+ {#if !active}
103
+ <div class="custom-actions">
104
+ <button
105
+ class="custom-btn"
106
+ aria-label="Edit {token.variable}"
107
+ disabled={session !== null}
108
+ onclick={() => openEdit(token.variable)}
109
+ >Edit</button>
110
+ <button
111
+ class="custom-btn"
112
+ aria-label="Remove {token.variable}"
113
+ disabled={session !== null}
114
+ onclick={() => requestRemove(token.variable)}
115
+ >Remove</button>
116
+ </div>
117
+ {/if}
118
+ </div>
119
+ {#if active && session}
120
+ <div class="custom-editor-host">
121
+ {#if session.mode === 'edit'}
122
+ <ColorEditPanel
123
+ title="Custom › {customNumber(session.variable)}"
124
+ hue={session.draft.h}
125
+ chroma={session.draft.c}
126
+ lightness={session.draft.l * 100}
127
+ onHueChromaChange={editDraft}
128
+ onSliderStart={() => beginSliderGesture(`edit ${session?.variable ?? 'custom colour'}`)}
129
+ onConfirm={() => closeEdit(true)}
130
+ onCancel={() => closeEdit(false)}
131
+ />
132
+ {:else}
133
+ <CustomColorReplaceStep
134
+ variable={session.variable}
135
+ color={session.draft}
136
+ onconfirm={confirmReplace}
137
+ oncancel={() => (session = null)}
138
+ />
139
+ {/if}
140
+ </div>
141
+ {/if}
142
+ </div>
143
+ {/each}
144
+ </div>
145
+ {/if}
146
+ </section>
147
+
148
+ <style>
149
+ .section {
150
+ display: flex;
151
+ flex-direction: column;
152
+ gap: var(--ui-space-24);
153
+ }
154
+
155
+ .section-title {
156
+ font-size: var(--ui-font-size-2xl);
157
+ font-weight: var(--ui-font-weight-semibold);
158
+ color: var(--ui-text-primary);
159
+ margin: 0;
160
+ padding-bottom: var(--ui-space-8);
161
+ border-bottom: 2px solid var(--ui-border-high);
162
+ }
163
+
164
+ .editor-intro,
165
+ .custom-empty {
166
+ font-size: var(--ui-font-size-md);
167
+ color: var(--ui-text-tertiary);
168
+ margin: 0;
169
+ }
170
+
171
+ .token-variable.copyable {
172
+ all: unset;
173
+ font-size: var(--ui-font-size-md);
174
+ color: var(--ui-text-tertiary);
175
+ font-family: var(--ui-font-mono);
176
+ cursor: pointer;
177
+ transition: color var(--ui-transition-fast);
178
+ }
179
+
180
+ .token-variable.copyable:hover {
181
+ color: var(--ui-text-accent);
182
+ }
183
+
184
+ .token-variable.copyable.copied {
185
+ color: var(--ui-text-success);
186
+ }
187
+
188
+ .custom-grid {
189
+ display: grid;
190
+ grid-template-columns: repeat(auto-fill, minmax(min(14rem, 100%), 1fr));
191
+ gap: var(--ui-space-16);
192
+ }
193
+
194
+ .custom-item {
195
+ display: flex;
196
+ flex-direction: column;
197
+ gap: var(--ui-space-8);
198
+ padding: var(--ui-space-12);
199
+ border: 1px solid var(--ui-border-low);
200
+ border-radius: var(--ui-radius-lg);
201
+ background: var(--ui-surface-lowest);
202
+ min-width: 0;
203
+ }
204
+
205
+ .custom-item.active {
206
+ grid-column: 1 / -1;
207
+ border-color: var(--ui-text-primary);
208
+ }
209
+
210
+ .custom-box {
211
+ height: 3rem;
212
+ border-radius: var(--ui-radius-md);
213
+ }
214
+
215
+ .token-info {
216
+ display: flex;
217
+ align-items: center;
218
+ justify-content: space-between;
219
+ gap: var(--ui-space-8);
220
+ }
221
+
222
+ .custom-actions {
223
+ display: flex;
224
+ gap: var(--ui-space-4);
225
+ }
226
+
227
+ .custom-btn {
228
+ padding: var(--ui-space-2) var(--ui-space-10);
229
+ background: var(--ui-surface-low);
230
+ border: 1px solid var(--ui-border-low);
231
+ border-radius: var(--ui-radius-sm);
232
+ color: var(--ui-text-secondary);
233
+ font-size: var(--ui-font-size-xs);
234
+ cursor: pointer;
235
+ font-family: inherit;
236
+ }
237
+
238
+ .custom-btn:hover:not(:disabled) {
239
+ color: var(--ui-text-primary);
240
+ border-color: var(--ui-border);
241
+ }
242
+
243
+ .custom-btn:disabled {
244
+ cursor: default;
245
+ opacity: 0.5;
246
+ }
247
+
248
+ .custom-editor-host {
249
+ max-width: 28rem;
250
+ margin-top: var(--ui-space-8);
251
+ padding-top: var(--ui-space-12);
252
+ border-top: 1px dashed var(--ui-border-low);
253
+ }
254
+ </style>
@@ -2480,7 +2480,7 @@
2480
2480
  "createdAt": "2026-05-01T08:28:00.345Z",
2481
2481
  "updatedAt": "2026-08-19T22:39:32.785Z",
2482
2482
  "aliases": {
2483
- "--badge-brand-surface": "--surface-brand",
2483
+ "--badge-brand-surface": "--surface-brand-low",
2484
2484
  "--badge-brand-text": "--text-brand",
2485
2485
  "--badge-brand-border": "--border-brand",
2486
2486
  "--badge-brand-text-font-family": "--font-sans",
@@ -2533,7 +2533,7 @@
2533
2533
  "--badge-alternate-blur": "--blur-none",
2534
2534
  "--badge-alternate-icon-size": "--icon-size-sm",
2535
2535
  "--badge-canvas-surface": "--surface-canvas",
2536
- "--badge-canvas-text": "--text-canvas",
2536
+ "--badge-canvas-text": "--text-secondary",
2537
2537
  "--badge-canvas-border": "--border-canvas",
2538
2538
  "--badge-canvas-text-font-family": "--font-sans",
2539
2539
  "--badge-canvas-text-font-size": "--font-size-md",
@@ -2584,7 +2584,7 @@
2584
2584
  "--badge-warning-shadow": "--shadow-none",
2585
2585
  "--badge-warning-blur": "--blur-none",
2586
2586
  "--badge-warning-icon-size": "--icon-size-sm",
2587
- "--badge-danger-surface": "--surface-danger",
2587
+ "--badge-danger-surface": "--surface-danger-low",
2588
2588
  "--badge-danger-text": "--text-danger",
2589
2589
  "--badge-danger-border": "--border-danger",
2590
2590
  "--badge-danger-text-font-family": "--font-sans",
@@ -2957,7 +2957,7 @@
2957
2957
  "--cornerbadge-text-font-size": "--font-size-sm",
2958
2958
  "--cornerbadge-text-font-weight": "--font-weight-medium",
2959
2959
  "--cornerbadge-text-line-height": "--line-height-none",
2960
- "--cornerbadge-brand-surface": "--surface-brand",
2960
+ "--cornerbadge-brand-surface": "--surface-brand-low",
2961
2961
  "--cornerbadge-brand-border": "--border-brand",
2962
2962
  "--cornerbadge-brand-text": "--text-brand",
2963
2963
  "--cornerbadge-accent-surface": "--surface-accent",
@@ -2971,7 +2971,7 @@
2971
2971
  "--cornerbadge-alternate-text": "--text-alternate",
2972
2972
  "--cornerbadge-canvas-surface": "--surface-canvas",
2973
2973
  "--cornerbadge-canvas-border": "--border-canvas",
2974
- "--cornerbadge-canvas-text": "--text-canvas",
2974
+ "--cornerbadge-canvas-text": "--text-secondary",
2975
2975
  "--cornerbadge-special-surface": "--surface-special",
2976
2976
  "--cornerbadge-special-border": "--border-special",
2977
2977
  "--cornerbadge-special-text": "--text-special",
@@ -2981,7 +2981,7 @@
2981
2981
  "--cornerbadge-warning-surface": "--surface-warning",
2982
2982
  "--cornerbadge-warning-border": "--border-warning",
2983
2983
  "--cornerbadge-warning-text": "--text-warning",
2984
- "--cornerbadge-danger-surface": "--surface-danger",
2984
+ "--cornerbadge-danger-surface": "--surface-danger-low",
2985
2985
  "--cornerbadge-danger-border": "--border-danger",
2986
2986
  "--cornerbadge-danger-text": "--text-danger",
2987
2987
  "--cornerbadge-info-surface": "--surface-info",
@@ -3199,7 +3199,7 @@
3199
3199
  "--input-border-width": "--border-width-1",
3200
3200
  "--input-gap": "--space-8",
3201
3201
  "--input-default-surface": "--surface-neutral-lower",
3202
- "--input-default-border": "--border-neutral",
3202
+ "--input-default-border": "--border-neutral-medium",
3203
3203
  "--input-default-text": "--text-primary",
3204
3204
  "--input-default-text-font-family": "--font-sans",
3205
3205
  "--input-default-text-font-size": "--font-size-sm",
@@ -3358,7 +3358,7 @@
3358
3358
  "--notification-warning-text-font-size": "--font-size-md",
3359
3359
  "--notification-warning-text-font-weight": "--font-weight-normal",
3360
3360
  "--notification-warning-text-line-height": "--line-height-normal",
3361
- "--notification-danger-surface": "--surface-danger",
3361
+ "--notification-danger-surface": "--surface-danger-low",
3362
3362
  "--notification-danger-action-surface": "--surface-neutral-lowest",
3363
3363
  "--notification-danger-border": "--border-danger",
3364
3364
  "--notification-danger-border-width": "--border-width-1",
@@ -3442,7 +3442,7 @@
3442
3442
  "createdAt": "2026-05-10T20:44:44.372Z",
3443
3443
  "updatedAt": "2026-08-19T22:39:32.785Z",
3444
3444
  "aliases": {
3445
- "--radiobutton-default-dot-border-color": "--border-neutral",
3445
+ "--radiobutton-default-dot-border-color": "--border-neutral-medium",
3446
3446
  "--radiobutton-default-dot-border-width": "--border-width-2",
3447
3447
  "--radiobutton-default-dot-fill": "--text-secondary",
3448
3448
  "--radiobutton-default-dot-size": "--dot-size-0",
@@ -3451,7 +3451,7 @@
3451
3451
  "--radiobutton-default-label-font-size": "--font-size-md",
3452
3452
  "--radiobutton-default-label-font-weight": "--font-weight-semibold",
3453
3453
  "--radiobutton-default-label-line-height": "--line-height-none",
3454
- "--radiobutton-hover-dot-border-color": "--border-neutral",
3454
+ "--radiobutton-hover-dot-border-color": "--border-neutral-medium",
3455
3455
  "--radiobutton-hover-dot-border-width": "--border-width-2",
3456
3456
  "--radiobutton-hover-dot-fill": "--text-secondary",
3457
3457
  "--radiobutton-hover-dot-size": "--dot-size-50",
@@ -3460,7 +3460,7 @@
3460
3460
  "--radiobutton-hover-label-font-size": "--font-size-md",
3461
3461
  "--radiobutton-hover-label-font-weight": "--font-weight-semibold",
3462
3462
  "--radiobutton-hover-label-line-height": "--line-height-none",
3463
- "--radiobutton-selected-dot-border-color": "--border-neutral",
3463
+ "--radiobutton-selected-dot-border-color": "--border-neutral-medium",
3464
3464
  "--radiobutton-selected-dot-border-width": "--border-width-2",
3465
3465
  "--radiobutton-selected-dot-fill": "--text-secondary",
3466
3466
  "--radiobutton-selected-dot-size": "--dot-size-50",
@@ -3569,7 +3569,7 @@
3569
3569
  "--segmentedcontrol-hover-tint": "--tint",
3570
3570
  "--segmentedcontrol-hover-tint-enabled": "--color-transparent",
3571
3571
  "--segmentedcontrol-bar-surface": "color-mix(in srgb, var(--surface-neutral-lowest) 75%, transparent)",
3572
- "--segmentedcontrol-bar-border": "--border-neutral",
3572
+ "--segmentedcontrol-bar-border": "--border-neutral-medium",
3573
3573
  "--segmentedcontrol-bar-border-width": "--border-width-1",
3574
3574
  "--segmentedcontrol-bar-radius": "--radius-2xl",
3575
3575
  "--segmentedcontrol-bar-padding": "--space-8",
@@ -3600,7 +3600,7 @@
3600
3600
  "--segmentedcontrol-selected-text-font-weight": "--font-weight-normal",
3601
3601
  "--segmentedcontrol-selected-text-line-height": "--line-height-normal",
3602
3602
  "--segmentedcontrol-selected-icon": "--text-secondary",
3603
- "--segmentedcontrol-selected-border": "--border-brand",
3603
+ "--segmentedcontrol-selected-border": "--border-brand-medium",
3604
3604
  "--segmentedcontrol-selected-border-width": "--border-width-1",
3605
3605
  "--segmentedcontrol-selected-radius": "--radius-xl",
3606
3606
  "--segmentedcontrol-disabled-surface": "--color-transparent",
@@ -3827,7 +3827,7 @@
3827
3827
  "--tabbar-hover-tab-bottom-radius": "--radius-md",
3828
3828
  "--tabbar-hover-indicator-width": "--border-width-2",
3829
3829
  "--tabbar-selected-text": "--text-primary",
3830
- "--tabbar-selected-border": "--color-brand-500",
3830
+ "--tabbar-selected-border": "--border-brand-medium",
3831
3831
  "--tabbar-selected-surface": "--tint-low",
3832
3832
  "--tabbar-selected-text-font-family": "--font-sans",
3833
3833
  "--tabbar-selected-text-font-size": "--font-size-md",
@@ -3897,7 +3897,7 @@
3897
3897
  "updatedAt": "2026-08-19T22:39:32.785Z",
3898
3898
  "aliases": {
3899
3899
  "--toggle-track-surface": "--surface-neutral",
3900
- "--toggle-track-border": "--border-neutral",
3900
+ "--toggle-track-border": "--border-neutral-medium",
3901
3901
  "--toggle-track-border-width": "--border-width-1",
3902
3902
  "--toggle-track-radius": "--radius-full",
3903
3903
  "--toggle-track-padding": "--space-6",
@@ -3912,7 +3912,7 @@
3912
3912
  "--toggle-hover-track-surface": "--surface-neutral-high",
3913
3913
  "--toggle-hover-thumb-surface": "--text-primary",
3914
3914
  "--toggle-on-track-surface": "--surface-brand-high",
3915
- "--toggle-on-track-border": "--border-brand",
3915
+ "--toggle-on-track-border": "--border-brand-medium",
3916
3916
  "--toggle-on-thumb-surface": "--text-primary",
3917
3917
  "--toggle-on-thumb-border": "--border-brand-strong",
3918
3918
  "--toggle-on-hover-track-surface": "--surface-brand-higher",
@@ -3948,13 +3948,13 @@
3948
3948
  "updatedAt": "2026-09-02T10:55:50.831Z",
3949
3949
  "aliases": {
3950
3950
  "--slider-single-track-surface": "--surface-neutral-lowest",
3951
- "--slider-single-track-border": "--border-neutral-subtle",
3951
+ "--slider-single-track-border": "--border-neutral-medium",
3952
3952
  "--slider-single-track-border-width": "--border-width-1",
3953
3953
  "--slider-single-track-height": "--space-8",
3954
3954
  "--slider-single-track-radius": "--radius-full",
3955
3955
  "--slider-single-fill": "--surface-brand-high",
3956
3956
  "--slider-single-thumb-surface": "--surface-neutral-highest",
3957
- "--slider-single-thumb-border": "--border-brand",
3957
+ "--slider-single-thumb-border": "--border-brand-medium",
3958
3958
  "--slider-single-thumb-border-width": "--border-width-2",
3959
3959
  "--slider-single-thumb-size": "--font-size-lg",
3960
3960
  "--slider-single-thumb-radius": "--radius-full",
@@ -3966,13 +3966,13 @@
3966
3966
  "--slider-single-disabled-thumb-surface": "--surface-neutral-lower",
3967
3967
  "--slider-single-disabled-thumb-border": "--border-neutral",
3968
3968
  "--slider-range-track-surface": "--surface-neutral-lowest",
3969
- "--slider-range-track-border": "--border-neutral-subtle",
3969
+ "--slider-range-track-border": "--border-neutral-medium",
3970
3970
  "--slider-range-track-border-width": "--border-width-1",
3971
3971
  "--slider-range-track-height": "--space-8",
3972
3972
  "--slider-range-track-radius": "--radius-full",
3973
3973
  "--slider-range-fill": "--surface-brand-high",
3974
3974
  "--slider-range-thumb-surface": "--surface-neutral-highest",
3975
- "--slider-range-thumb-border": "--border-brand",
3975
+ "--slider-range-thumb-border": "--border-brand-medium",
3976
3976
  "--slider-range-thumb-border-width": "--border-width-2",
3977
3977
  "--slider-range-thumb-size": "--font-size-lg",
3978
3978
  "--slider-range-thumb-radius": "--radius-full",