@motion-proto/live-tokens 0.74.0 → 0.76.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 (171) hide show
  1. package/.claude/skills/live-tokens-check-compliance/SKILL.md +33 -38
  2. package/.claude/skills/live-tokens-create-component/SKILL copy.md +196 -0
  3. package/.claude/skills/live-tokens-create-component/SKILL.md +198 -146
  4. package/.claude/skills/live-tokens-create-component/references/contract-tests.md +95 -52
  5. package/.claude/skills/live-tokens-create-component/references/intrinsics.md +2 -2
  6. package/.claude/skills/live-tokens-create-component/references/linked-siblings.md +2 -2
  7. package/.claude/skills/live-tokens-create-component/references/sketch-mode.md +12 -12
  8. package/.claude/skills/live-tokens-create-component/references/token-naming.md +2 -1
  9. package/.claude/skills/live-tokens-create-page/SKILL.md +187 -0
  10. package/.claude/skills/live-tokens-create-page/references/interaction-sources.md +66 -0
  11. package/.claude/skills/live-tokens-create-page/references/layout-sources.md +87 -0
  12. package/.claude/skills/live-tokens-create-theme/SKILL.md +52 -48
  13. package/.claude/skills/live-tokens-create-theme/references/design-directions.md +1 -1
  14. package/.claude/skills/live-tokens-fix-findings/SKILL.md +69 -60
  15. package/.claude/skills/live-tokens-pick-component/SKILL.md +64 -79
  16. package/.claude/skills/live-tokens-set-colors/SKILL.md +47 -38
  17. package/.claude/skills/live-tokens-set-geometry/SKILL.md +58 -35
  18. package/.claude/skills/live-tokens-set-geometry/references/geometry-anchors.md +5 -3
  19. package/.claude/skills/live-tokens-set-type/SKILL.md +30 -30
  20. package/CHANGELOG.md +138 -0
  21. package/README.md +17 -7
  22. package/bin/check-component.mjs +91 -19
  23. package/bin/check-page.mjs +102 -21
  24. package/bin/cli.mjs +87 -113
  25. package/bin/contractRunner.mjs +945 -0
  26. package/bin/create.mjs +1 -1
  27. package/bin/lib/catalogue.mjs +37 -30
  28. package/bin/lib/findings.mjs +46 -15
  29. package/bin/lib/report.mjs +3 -3
  30. package/bin/lib/tokenVocabulary.mjs +4 -4
  31. package/bin/migrate-routes.mjs +5 -5
  32. package/bin/migrate.mjs +4 -4
  33. package/bin/save-theme.mjs +8 -9
  34. package/bin/set-colors.mjs +9 -11
  35. package/bin/set-geometry.mjs +7 -7
  36. package/bin/set-type.mjs +5 -7
  37. package/bin/setup-claude.mjs +110 -0
  38. package/dist-plugin/{chunk-W6Y4BWFB.js → chunk-6WGFOJXO.js} +22 -0
  39. package/dist-plugin/{chunk-7VRTBGJT.js → chunk-PDNL4NC5.js} +9 -2
  40. package/dist-plugin/{chunk-V3YF6CGT.js → chunk-SWXRVZKT.js} +43 -1
  41. package/dist-plugin/{dataPaths-BhWzd5cL.d.cts → dataPaths-BpIK_Obx.d.cts} +1 -0
  42. package/dist-plugin/{dataPaths-BhWzd5cL.d.ts → dataPaths-BpIK_Obx.d.ts} +1 -0
  43. package/dist-plugin/index.cjs +310 -147
  44. package/dist-plugin/index.d.cts +1 -1
  45. package/dist-plugin/index.d.ts +1 -1
  46. package/dist-plugin/index.js +218 -122
  47. package/dist-plugin/migrateData/index.cjs +66 -2
  48. package/dist-plugin/migrateData/index.d.cts +1 -1
  49. package/dist-plugin/migrateData/index.d.ts +1 -1
  50. package/dist-plugin/migrateData/index.js +3 -3
  51. package/dist-plugin/setColors/index.cjs +61 -4
  52. package/dist-plugin/setColors/index.d.cts +1 -1
  53. package/dist-plugin/setColors/index.d.ts +1 -1
  54. package/dist-plugin/setColors/index.js +5 -5
  55. package/dist-plugin/setGeometry/index.cjs +107 -44
  56. package/dist-plugin/setGeometry/index.d.cts +5 -5
  57. package/dist-plugin/setGeometry/index.d.ts +5 -5
  58. package/dist-plugin/setGeometry/index.js +51 -45
  59. package/dist-plugin/setType/index.cjs +15 -0
  60. package/dist-plugin/setType/index.d.cts +1 -1
  61. package/dist-plugin/setType/index.d.ts +1 -1
  62. package/dist-plugin/setType/index.js +1 -1
  63. package/dist-plugin/tokensCssMigrations/index.d.cts +1 -1
  64. package/dist-plugin/tokensCssMigrations/index.d.ts +1 -1
  65. package/dist-plugin/tokensCssMigrations/index.js +1 -1
  66. package/package.json +35 -8
  67. package/src/app/site.css +19 -9
  68. package/src/editor/bootstrap.ts +2 -0
  69. package/src/editor/component-editor/CollapsibleSectionEditor.svelte +4 -4
  70. package/src/editor/component-editor/DialogEditor.svelte +4 -4
  71. package/src/editor/component-editor/MenuSelectEditor.svelte +4 -1
  72. package/src/editor/component-editor/SegmentedControlEditor.svelte +6 -1
  73. package/src/editor/component-editor/TabBarEditor.svelte +1 -1
  74. package/src/editor/component-editor/TableEditor.svelte +2 -2
  75. package/src/editor/component-editor/scaffolding/TokenLayout.svelte +6 -3
  76. package/src/editor/component-editor/scaffolding/VariantGroup.svelte +41 -20
  77. package/src/editor/core/components/adjustAliases.ts +59 -45
  78. package/src/editor/core/components/aliasKinds.ts +9 -5
  79. package/src/editor/core/preview/themePreview.ts +9 -2
  80. package/src/editor/core/sketch/sketchLayer.ts +22 -0
  81. package/src/editor/core/store/editorStore.ts +10 -1
  82. package/src/editor/core/themes/buildColors.ts +3 -3
  83. package/src/editor/core/themes/liveStateStream.ts +26 -0
  84. package/src/editor/core/themes/migrations/2026-09-07-stroke-role-renames.ts +52 -0
  85. package/src/editor/core/themes/migrations/index.ts +2 -0
  86. package/src/editor/core/themes/themeDocumentSync.ts +22 -11
  87. package/src/editor/core/themes/themeService.ts +9 -2
  88. package/src/editor/pages/ComponentEditorPage.svelte +17 -1
  89. package/src/editor/pages/liveTokensEditorHandle.ts +23 -0
  90. package/src/editor/skill-atlas/SkillAtlas.svelte +75 -573
  91. package/src/editor/skill-atlas/TreeCanvas.svelte +263 -0
  92. package/src/editor/skill-atlas/TreeNodeCard.svelte +136 -53
  93. package/src/editor/skill-atlas/edges.ts +31 -0
  94. package/src/editor/skill-atlas/skillSources.generated.ts +20 -19
  95. package/src/editor/skill-atlas/skillTrees.ts +19 -3840
  96. package/src/editor/skill-atlas/trees/check-compliance.ts +183 -0
  97. package/src/editor/skill-atlas/trees/create-component.ts +275 -0
  98. package/src/editor/skill-atlas/trees/create-page.ts +320 -0
  99. package/src/editor/skill-atlas/trees/create-theme.ts +255 -0
  100. package/src/editor/skill-atlas/trees/fix-findings.ts +469 -0
  101. package/src/editor/skill-atlas/trees/pick-component.ts +300 -0
  102. package/src/editor/skill-atlas/trees/set-colors.ts +148 -0
  103. package/src/editor/skill-atlas/trees/set-geometry.ts +136 -0
  104. package/src/editor/skill-atlas/trees/set-type.ts +142 -0
  105. package/src/editor/skill-atlas/types.ts +3 -4
  106. package/src/editor/skill-atlas/wireLayout.ts +287 -0
  107. package/src/live-tokens/data/themes/autumn.json +15 -15
  108. package/src/live-tokens/data/themes/halloween.json +15 -15
  109. package/src/live-tokens/data/themes/midnight-study.json +15 -15
  110. package/src/live-tokens/data/themes/ocean.json +15 -15
  111. package/src/live-tokens/data/themes/royal-velvet.json +15 -15
  112. package/src/live-tokens/data/themes/sketchy.json +15 -15
  113. package/src/live-tokens/data/themes/spring-meadow.json +15 -15
  114. package/src/live-tokens/data/themes/sunset.json +15 -15
  115. package/src/system/components/Badge.svelte +7 -0
  116. package/src/system/components/Button.svelte +7 -0
  117. package/src/system/components/Callout.svelte +10 -6
  118. package/src/system/components/Card.svelte +23 -4
  119. package/src/system/components/CodeSnippet.svelte +4 -3
  120. package/src/system/components/CollapsibleSection.svelte +23 -8
  121. package/src/system/components/CornerBadge.svelte +6 -0
  122. package/src/system/components/Dialog.svelte +13 -6
  123. package/src/system/components/IconButton.svelte +9 -0
  124. package/src/system/components/Image.svelte +8 -0
  125. package/src/system/components/ImageLightbox.svelte +6 -0
  126. package/src/system/components/InlineEditActions.svelte +7 -0
  127. package/src/system/components/Input.svelte +7 -0
  128. package/src/system/components/MenuSelect.svelte +7 -0
  129. package/src/system/components/Notification.svelte +7 -0
  130. package/src/system/components/Panel.svelte +6 -0
  131. package/src/system/components/ProgressBar.svelte +5 -0
  132. package/src/system/components/RadioButton.svelte +11 -5
  133. package/src/system/components/SectionDivider.svelte +8 -0
  134. package/src/system/components/SegmentedControl.svelte +6 -0
  135. package/src/system/components/SideNavigation.svelte +6 -0
  136. package/src/system/components/Slider.svelte +7 -4
  137. package/src/system/components/TabBar.svelte +15 -9
  138. package/src/system/components/Table.svelte +8 -3
  139. package/src/system/components/Toggle.svelte +4 -4
  140. package/src/system/components/Tooltip.svelte +6 -0
  141. package/src/testing-js/chunk-AO7EZHYV.js +776 -0
  142. package/src/testing-js/chunk-AO7EZHYV.js.map +1 -0
  143. package/src/testing-js/chunk-FAFOAWYL.js +39 -0
  144. package/src/testing-js/chunk-FAFOAWYL.js.map +1 -0
  145. package/src/testing-js/chunk-L73N4NSO.js +23 -0
  146. package/src/testing-js/chunk-L73N4NSO.js.map +1 -0
  147. package/src/testing-js/chunk-LXR3MN6N.js +3063 -0
  148. package/src/testing-js/chunk-LXR3MN6N.js.map +1 -0
  149. package/src/testing-js/chunk-ZMSX6CXR.js +53 -0
  150. package/src/testing-js/chunk-ZMSX6CXR.js.map +1 -0
  151. package/src/testing-js/component-alias.contract.js +81 -0
  152. package/src/testing-js/component-alias.contract.js.map +1 -0
  153. package/src/testing-js/component-editor.contract.js +62 -0
  154. package/src/testing-js/component-editor.contract.js.map +1 -0
  155. package/src/testing-js/component-render.contract.js +568 -0
  156. package/src/testing-js/component-render.contract.js.map +1 -0
  157. package/src/testing-js/index.d.ts +293 -0
  158. package/src/testing-js/index.js +222 -0
  159. package/src/testing-js/index.js.map +1 -0
  160. package/src/testing-js/registry.contract.js +39 -0
  161. package/src/testing-js/registry.contract.js.map +1 -0
  162. package/src/testing-js/vitest-BE6uGF31.d.ts +73 -0
  163. package/src/testing-js/vitest.d.ts +3 -0
  164. package/src/testing-js/vitest.js +13 -0
  165. package/src/testing-js/vitest.js.map +1 -0
  166. package/template/README.md +13 -0
  167. package/template/_gitignore +6 -0
  168. package/template/package.json +3 -1
  169. package/template/src/pages/Home.svelte +4 -18
  170. package/.claude/skills/live-tokens-build-page/SKILL.md +0 -103
  171. package/.claude/skills/live-tokens-build-page/references/layout-sources.md +0 -48
@@ -0,0 +1,3063 @@
1
+ // src/testing/componentContract.ts
2
+ var ContractViolation = class extends Error {
3
+ constructor(rule, component, message) {
4
+ super(`[${rule}] ${component}: ${message}`);
5
+ this.rule = rule;
6
+ this.component = component;
7
+ this.name = "ContractViolation";
8
+ }
9
+ rule;
10
+ component;
11
+ };
12
+ function isInapplicable(value) {
13
+ return typeof value === "object" && value !== null && value.applicable === false;
14
+ }
15
+ var INTERACTIVE_ROLES = /* @__PURE__ */ new Set([
16
+ "button",
17
+ "switch",
18
+ "checkbox",
19
+ "radio",
20
+ "slider",
21
+ "spinbutton",
22
+ "textbox",
23
+ "combobox",
24
+ "listbox",
25
+ "menuitem",
26
+ "tab",
27
+ "link",
28
+ "searchbox",
29
+ "option"
30
+ ]);
31
+ function requiresInteraction(role) {
32
+ return role !== null && INTERACTIVE_ROLES.has(role);
33
+ }
34
+ function partLocator(contract, key) {
35
+ const declared = contract.parts[key];
36
+ if (declared === void 0) {
37
+ throw new ContractViolation("contract-render", contract.id, `no part named "${key}" is declared`);
38
+ }
39
+ return typeof declared === "string" ? { selector: declared } : declared;
40
+ }
41
+
42
+ // src/testing/contracts/index.ts
43
+ import path from "path";
44
+ import { pathToFileURL } from "url";
45
+
46
+ // src/testing/contracts/badge.ts
47
+ var variants = [
48
+ "primary",
49
+ "accent",
50
+ "neutral",
51
+ "alternate",
52
+ "canvas",
53
+ "special",
54
+ "success",
55
+ "warning",
56
+ "danger",
57
+ "info"
58
+ ];
59
+ function basePaints(v) {
60
+ return {
61
+ root: {
62
+ paddingTop: `--badge-${v}-padding`,
63
+ borderRadius: `--badge-${v}-radius`,
64
+ borderTopWidth: `--badge-${v}-border-width`,
65
+ boxShadow: `--badge-${v}-shadow`,
66
+ fontFamily: `--badge-${v}-text-font-family`,
67
+ fontSize: `--badge-${v}-text-font-size`,
68
+ fontWeight: `--badge-${v}-text-font-weight`,
69
+ lineHeight: `--badge-${v}-text-line-height`
70
+ },
71
+ icon: { fontSize: `--badge-${v}-icon-size` }
72
+ };
73
+ }
74
+ function colorPaints(v) {
75
+ return {
76
+ root: {
77
+ backgroundColor: `--badge-${v}-surface`,
78
+ borderTopColor: `--badge-${v}-border`,
79
+ color: `--badge-${v}-text`
80
+ }
81
+ };
82
+ }
83
+ var badgeContract = {
84
+ id: "badge",
85
+ origin: "system",
86
+ view: { variant: "Primary" },
87
+ root: "root",
88
+ parts: {
89
+ root: ".badge",
90
+ icon: ".icon"
91
+ },
92
+ properties: [
93
+ { variant: "Primary", state: "base", paints: basePaints("primary") },
94
+ { variant: "Primary", state: "colors", paints: colorPaints("primary") },
95
+ ...variants.filter((v) => v !== "primary").flatMap((v) => [
96
+ { variant: v.charAt(0).toUpperCase() + v.slice(1), state: "base", paints: basePaints(v) },
97
+ { variant: v.charAt(0).toUpperCase() + v.slice(1), state: "colors", paints: colorPaints(v) }
98
+ ])
99
+ ],
100
+ states: [{ state: "base" }, { state: "colors" }],
101
+ uncovered: {
102
+ "--badge-primary-blur": "consumed via backdrop-filter: blur(), which no probe covers",
103
+ "--badge-accent-blur": "consumed via backdrop-filter: blur(), which no probe covers",
104
+ "--badge-neutral-blur": "consumed via backdrop-filter: blur(), which no probe covers",
105
+ "--badge-alternate-blur": "consumed via backdrop-filter: blur(), which no probe covers",
106
+ "--badge-canvas-blur": "consumed via backdrop-filter: blur(), which no probe covers",
107
+ "--badge-special-blur": "consumed via backdrop-filter: blur(), which no probe covers",
108
+ "--badge-success-blur": "consumed via backdrop-filter: blur(), which no probe covers",
109
+ "--badge-warning-blur": "consumed via backdrop-filter: blur(), which no probe covers",
110
+ "--badge-danger-blur": "consumed via backdrop-filter: blur(), which no probe covers",
111
+ "--badge-info-blur": "consumed via backdrop-filter: blur(), which no probe covers"
112
+ },
113
+ persistence: {
114
+ cases: [
115
+ {
116
+ shape: "token",
117
+ variant: "Primary",
118
+ state: "base",
119
+ variable: "--badge-primary-border-width",
120
+ observe: { part: "root", css: "borderTopWidth" }
121
+ }
122
+ ],
123
+ resetVariable: "--badge-primary-border-width"
124
+ },
125
+ theme: {
126
+ theme: "halloween",
127
+ changed: ["--badge-primary-border-width", "--badge-primary-radius", "--badge-primary-padding"],
128
+ unchanged: ["--badge-primary-shadow", "--badge-primary-icon-size"],
129
+ aliasedTo: {
130
+ "--badge-primary-border-width": "--border-width-3",
131
+ "--badge-primary-radius": "--radius-none"
132
+ },
133
+ observe: { part: "root", css: "borderTopWidth", variable: "--badge-primary-border-width" }
134
+ },
135
+ interaction: {
136
+ applicable: false,
137
+ reason: "a badge is a static label with no interactive role"
138
+ },
139
+ sketch: {
140
+ style: "pencil",
141
+ parts: [{ part: "root", fill: "--badge-primary-surface", stroke: "--badge-primary-border" }]
142
+ }
143
+ };
144
+
145
+ // src/testing/contracts/button.ts
146
+ var variants2 = ["primary", "secondary", "outline", "success", "danger", "warning"];
147
+ var cap = (v) => v.charAt(0).toUpperCase() + v.slice(1);
148
+ function basePaints2(v) {
149
+ return {
150
+ root: {
151
+ paddingTop: `--button-${v}-padding`,
152
+ borderRadius: `--button-${v}-radius`,
153
+ borderTopWidth: `--button-${v}-border-width`,
154
+ fontFamily: `--button-${v}-text-font-family`,
155
+ fontSize: `--button-${v}-text-font-size`,
156
+ fontWeight: `--button-${v}-text-font-weight`,
157
+ lineHeight: `--button-${v}-text-line-height`
158
+ },
159
+ icon: { fontSize: `--button-${v}-icon-size` }
160
+ };
161
+ }
162
+ function statePaints(v, s) {
163
+ const prefix = s === "default" ? `--button-${v}` : `--button-${v}-${s}`;
164
+ const textVar = s === "default" ? `--button-${v}-text` : `--button-${v}-${s}-text`;
165
+ return {
166
+ root: {
167
+ backgroundColor: `${prefix}-surface`,
168
+ borderTopColor: `${prefix}-border`,
169
+ color: textVar
170
+ }
171
+ };
172
+ }
173
+ var otherVariantProperties = variants2.filter((v) => v !== "primary").flatMap((v) => [
174
+ { variant: cap(v), state: "base", paints: basePaints2(v) },
175
+ { variant: cap(v), state: "default", paints: statePaints(v, "default") },
176
+ { variant: cap(v), state: "hover", paints: statePaints(v, "hover") },
177
+ { variant: cap(v), state: "disabled", paints: statePaints(v, "disabled") }
178
+ ]);
179
+ var buttonContract = {
180
+ id: "button",
181
+ origin: "system",
182
+ view: { variant: "Primary" },
183
+ root: "root",
184
+ parts: {
185
+ root: ".button",
186
+ icon: ".button i"
187
+ },
188
+ properties: [
189
+ { variant: "Primary", state: "base", paints: basePaints2("primary") },
190
+ ...otherVariantProperties,
191
+ { variant: "Outline", state: "active", paints: { root: { backgroundColor: "--button-outline-active-surface" } } },
192
+ {
193
+ setup: [{ kind: "control", selector: ".preview-actions select", value: "small" }],
194
+ paints: {
195
+ root: {
196
+ paddingTop: "--button-small-padding",
197
+ fontSize: "--button-small-text-font-size",
198
+ fontWeight: "--button-small-text-font-weight",
199
+ lineHeight: "--button-small-text-line-height"
200
+ },
201
+ icon: { fontSize: "--button-small-icon-size" }
202
+ }
203
+ }
204
+ ],
205
+ states: [
206
+ { state: "base" },
207
+ { state: "default", paints: statePaints("primary", "default") },
208
+ { state: "hover", forceClass: "force-hover", paints: statePaints("primary", "hover") },
209
+ {
210
+ state: "disabled",
211
+ attributes: { root: { disabled: "" } },
212
+ paints: statePaints("primary", "disabled")
213
+ }
214
+ ],
215
+ uncovered: {
216
+ "--button-shimmer": "gates the ::before sweep with a display toggle, which no probe covers",
217
+ "--button-hover-tint": "consumed inside a background-image tint wash, never appearing verbatim in a computed style",
218
+ "--button-hover-tint-enabled": "the gate for the tint wash above, same limitation"
219
+ },
220
+ persistence: {
221
+ cases: [
222
+ {
223
+ shape: "token",
224
+ variant: "Primary",
225
+ state: "base",
226
+ variable: "--button-primary-border-width",
227
+ observe: { part: "root", css: "borderTopWidth" }
228
+ }
229
+ ],
230
+ resetVariable: "--button-primary-border-width"
231
+ },
232
+ theme: {
233
+ theme: "halloween",
234
+ changed: ["--button-primary-border-width", "--button-primary-radius", "--button-primary-padding"],
235
+ unchanged: ["--button-primary-icon-size"],
236
+ aliasedTo: {
237
+ "--button-primary-border-width": "--border-width-3",
238
+ "--button-primary-radius": "--radius-none"
239
+ },
240
+ observe: { part: "root", css: "borderTopWidth", variable: "--button-primary-border-width" }
241
+ },
242
+ interaction: {
243
+ part: "root",
244
+ role: "button",
245
+ cases: [
246
+ {
247
+ name: "clicking a button focuses it",
248
+ action: { kind: "click", part: "root" },
249
+ expect: { kind: "focused", part: "root", value: true }
250
+ },
251
+ {
252
+ name: "a disabled button refuses focus",
253
+ state: "disabled",
254
+ action: { kind: "click", part: "root" },
255
+ expect: { kind: "focused", part: "root", value: false }
256
+ }
257
+ ]
258
+ },
259
+ sketch: {
260
+ style: "pencil",
261
+ parts: [{ part: "root", fill: "--button-primary-surface", stroke: "--button-primary-border" }]
262
+ }
263
+ };
264
+
265
+ // src/testing/contracts/callout.ts
266
+ var variants3 = ["info", "success", "warning", "danger"];
267
+ function paintsFor(v) {
268
+ return {
269
+ root: {
270
+ backgroundColor: `--callout-${v}-surface`,
271
+ borderTopColor: `--callout-${v}-border`,
272
+ borderTopWidth: `--callout-${v}-border-width`,
273
+ borderLeftWidth: `--callout-${v}-accent-width`,
274
+ borderRadius: `--callout-${v}-radius`,
275
+ paddingTop: `--callout-${v}-padding`
276
+ },
277
+ label: {
278
+ color: `--callout-${v}-label`,
279
+ fontFamily: `--callout-${v}-label-font-family`,
280
+ fontSize: `--callout-${v}-label-font-size`,
281
+ fontWeight: `--callout-${v}-label-font-weight`,
282
+ lineHeight: `--callout-${v}-label-line-height`
283
+ },
284
+ message: {
285
+ color: `--callout-${v}-text`,
286
+ fontFamily: `--callout-${v}-text-font-family`,
287
+ fontSize: `--callout-${v}-text-font-size`,
288
+ fontWeight: `--callout-${v}-text-font-weight`,
289
+ lineHeight: `--callout-${v}-text-line-height`
290
+ }
291
+ };
292
+ }
293
+ var calloutContract = {
294
+ id: "callout",
295
+ origin: "system",
296
+ view: { variant: "Info" },
297
+ root: "root",
298
+ parts: {
299
+ root: ".callout",
300
+ label: ".callout-label",
301
+ message: ".callout-message"
302
+ },
303
+ properties: variants3.map((v) => ({ variant: v.charAt(0).toUpperCase() + v.slice(1), paints: paintsFor(v) })),
304
+ states: {
305
+ applicable: false,
306
+ reason: "each variant is one full preset with one state, so the editor renders no state strip"
307
+ },
308
+ persistence: {
309
+ cases: [
310
+ {
311
+ shape: "token",
312
+ variable: "--callout-info-border-width",
313
+ observe: { part: "root", css: "borderTopWidth" }
314
+ }
315
+ ],
316
+ resetVariable: "--callout-info-border-width"
317
+ },
318
+ theme: {
319
+ theme: "halloween",
320
+ changed: ["--callout-info-border-width", "--callout-info-radius", "--callout-info-padding"],
321
+ unchanged: ["--callout-info-label-font-size", "--callout-info-text-font-size"],
322
+ aliasedTo: {
323
+ "--callout-info-border-width": "--border-width-3",
324
+ "--callout-info-radius": "--radius-none"
325
+ },
326
+ observe: { part: "root", css: "borderTopWidth", variable: "--callout-info-border-width" }
327
+ },
328
+ interaction: {
329
+ applicable: false,
330
+ reason: "a callout is a static box with no interactive role"
331
+ },
332
+ sketch: {
333
+ style: "pencil",
334
+ parts: [{ part: "root", fill: "--callout-info-surface", stroke: "--callout-info-border" }]
335
+ }
336
+ };
337
+
338
+ // src/testing/contracts/card.ts
339
+ var cardContract = {
340
+ id: "card",
341
+ origin: "system",
342
+ root: "root",
343
+ parts: {
344
+ root: ".card",
345
+ header: ".card-header",
346
+ icon: ".card-icon",
347
+ title: ".card-title",
348
+ body: ".card-body"
349
+ },
350
+ properties: [
351
+ {
352
+ paints: {
353
+ root: {
354
+ backgroundColor: "--card-default-surface",
355
+ borderTopColor: "--card-default-border",
356
+ borderTopWidth: "--card-default-border-width",
357
+ borderRadius: "--card-default-radius",
358
+ boxShadow: "--card-default-shadow"
359
+ },
360
+ header: {
361
+ backgroundColor: "--card-default-header-surface",
362
+ columnGap: "--card-default-header-gap",
363
+ paddingTop: "--card-default-header-padding-top",
364
+ paddingRight: "--card-default-header-padding-right",
365
+ paddingBottom: "--card-default-header-padding-bottom",
366
+ paddingLeft: "--card-default-header-padding-left"
367
+ },
368
+ icon: { fontSize: "--card-default-icon-size" },
369
+ title: {
370
+ color: "--card-default-title",
371
+ fontFamily: "--card-default-title-font-family",
372
+ fontSize: "--card-default-title-font-size",
373
+ fontWeight: "--card-default-title-font-weight",
374
+ lineHeight: "--card-default-title-line-height"
375
+ },
376
+ body: {
377
+ color: "--card-default-body",
378
+ fontFamily: "--card-default-body-font-family",
379
+ fontSize: "--card-default-body-font-size",
380
+ fontWeight: "--card-default-body-font-weight",
381
+ lineHeight: "--card-default-body-line-height",
382
+ paddingTop: "--card-default-body-padding-top",
383
+ paddingRight: "--card-default-body-padding-right",
384
+ paddingBottom: "--card-default-body-padding-bottom",
385
+ paddingLeft: "--card-default-body-padding-left"
386
+ }
387
+ }
388
+ },
389
+ // `click({force:true})` leaves the pointer resting on `root`, so the
390
+ // browser's real `.card:hover` rule applies afterward, no InteractionCase
391
+ // needed. The editor's `.force-hover` reads these same gated tokens, so
392
+ // the state entry below and this one have to agree.
393
+ {
394
+ setup: [{ kind: "click", part: "root" }],
395
+ paints: {
396
+ root: { borderTopColor: "--card-hover-border-enabled", boxShadow: "--card-hover-shadow-enabled" }
397
+ }
398
+ },
399
+ // The per-side entry above drives `-padding-top` directly, which passes
400
+ // regardless of what it's aliased to. This drives the base token instead,
401
+ // proving the `-padding-top: var(--card-default-*-padding)` link itself.
402
+ {
403
+ paints: {
404
+ header: { paddingTop: "--card-default-header-padding" },
405
+ body: { paddingTop: "--card-default-body-padding" }
406
+ }
407
+ }
408
+ ],
409
+ states: [
410
+ { state: "default" },
411
+ // The gate is off by default, so the hover tab has to show the resting
412
+ // card. Naming the gated tokens is what pins that: `--card-hover-border`
413
+ // resolves to `--border-neutral-strong` and would not match.
414
+ {
415
+ state: "hover",
416
+ forceClass: "force-hover",
417
+ paints: {
418
+ root: { borderTopColor: "--card-hover-border-enabled", boxShadow: "--card-hover-shadow-enabled" }
419
+ }
420
+ }
421
+ ],
422
+ uncovered: {
423
+ "--card-default-blur": "consumed via backdrop-filter: blur(), which no probe covers",
424
+ // `--card-hover-border-enabled` is an alias the editor repoints, so the
425
+ // chain is not live and driving these two moves nothing. The runtime reads
426
+ // them only through the per-instance `hover` prop (Card.svelte:47), which
427
+ // the standardized preview never sets. Before force-hover honoured the
428
+ // gate it read `--card-hover-border` directly and the hover state pinned
429
+ // it; that path was the bug.
430
+ "--card-hover-border": "reachable only through the per-instance hover prop, which the preview does not set",
431
+ "--card-hover-shadow": "reachable only through the per-instance hover prop, which the preview does not set"
432
+ },
433
+ persistence: {
434
+ cases: [
435
+ {
436
+ shape: "token",
437
+ variable: "--card-default-border-width",
438
+ observe: { part: "root", css: "borderTopWidth" }
439
+ }
440
+ ],
441
+ resetVariable: "--card-default-border-width"
442
+ },
443
+ theme: {
444
+ theme: "halloween",
445
+ changed: ["--card-default-border-width", "--card-default-radius"],
446
+ unchanged: ["--card-default-icon-size"],
447
+ aliasedTo: {
448
+ "--card-default-border-width": "--border-width-3",
449
+ "--card-default-radius": "--radius-none"
450
+ },
451
+ observe: { part: "root", css: "borderTopWidth", variable: "--card-default-border-width" }
452
+ },
453
+ interaction: {
454
+ applicable: false,
455
+ reason: "a card is a static container with no interactive role"
456
+ },
457
+ sketch: {
458
+ style: "pencil",
459
+ parts: [
460
+ { part: "root", fill: "--card-default-surface", stroke: "--card-default-border" },
461
+ { part: "header", fill: "--card-default-header-surface" }
462
+ ]
463
+ }
464
+ };
465
+
466
+ // src/testing/contracts/codesnippet.ts
467
+ var codeSnippetContract = {
468
+ id: "codesnippet",
469
+ origin: "system",
470
+ root: "root",
471
+ parts: {
472
+ root: ".codesnippet",
473
+ code: ".code",
474
+ copy: ".copy"
475
+ },
476
+ properties: [
477
+ {
478
+ paints: {
479
+ root: {
480
+ columnGap: "--codesnippet-gap",
481
+ paddingTop: "--codesnippet-padding",
482
+ backgroundColor: "--codesnippet-surface",
483
+ borderTopColor: "--codesnippet-border",
484
+ borderTopWidth: "--codesnippet-border-width",
485
+ borderRadius: "--codesnippet-radius"
486
+ },
487
+ code: {
488
+ color: "--codesnippet-code-text",
489
+ fontFamily: "--codesnippet-code-font-family",
490
+ fontSize: "--codesnippet-code-font-size",
491
+ fontWeight: "--codesnippet-code-font-weight",
492
+ lineHeight: "--codesnippet-code-line-height"
493
+ },
494
+ copy: {
495
+ color: "--codesnippet-icon",
496
+ fontSize: "--codesnippet-icon-size"
497
+ }
498
+ }
499
+ }
500
+ ],
501
+ states: [
502
+ { state: "default" },
503
+ {
504
+ state: "hover",
505
+ forceClass: "force-hover",
506
+ paints: { copy: { color: "--codesnippet-hover-icon" } }
507
+ }
508
+ ],
509
+ uncovered: {
510
+ "--codesnippet-scrollbar-thumb": "drawn via the ::-webkit-scrollbar-thumb pseudo-element and the scrollbar-color property, neither of which a probe can pin to one part",
511
+ "--codesnippet-scrollbar-border-width": "drives ::-webkit-scrollbar height, which a probe cannot read back verbatim"
512
+ },
513
+ persistence: {
514
+ cases: [
515
+ {
516
+ shape: "token",
517
+ variable: "--codesnippet-border-width",
518
+ observe: { part: "root", css: "borderTopWidth" }
519
+ }
520
+ ],
521
+ resetVariable: "--codesnippet-border-width"
522
+ },
523
+ theme: {
524
+ theme: "halloween",
525
+ changed: ["--codesnippet-border-width", "--codesnippet-radius", "--codesnippet-padding", "--codesnippet-scrollbar-border-width"],
526
+ unchanged: ["--codesnippet-icon-size"],
527
+ aliasedTo: {
528
+ "--codesnippet-border-width": "--border-width-3",
529
+ "--codesnippet-radius": "--radius-none"
530
+ },
531
+ observe: { part: "root", css: "borderTopWidth", variable: "--codesnippet-border-width" }
532
+ },
533
+ interaction: {
534
+ part: "copy",
535
+ role: "button",
536
+ cases: [
537
+ {
538
+ name: "clicking copy focuses the button",
539
+ action: { kind: "click", part: "copy" },
540
+ expect: { kind: "focused", part: "copy", value: true }
541
+ }
542
+ ]
543
+ },
544
+ sketch: {
545
+ style: "pencil",
546
+ parts: [{ part: "root", fill: "--codesnippet-surface", stroke: "--codesnippet-border" }]
547
+ }
548
+ };
549
+
550
+ // src/testing/contracts/collapsiblesection.ts
551
+ function clickHover() {
552
+ return [{ kind: "control", selector: '.tabs-selectors.substrip .state-tab-btn:has-text("Hover")' }];
553
+ }
554
+ function headerPaints(v, s) {
555
+ const p = `--collapsiblesection-${v}-${s}`;
556
+ const header = {
557
+ backgroundColor: `${p}-surface`,
558
+ paddingTop: `${p}-padding`
559
+ };
560
+ if (v === "divider") {
561
+ header.borderBottomColor = `${p}-hairline-color`;
562
+ header.borderBottomWidth = `${p}-hairline-thickness`;
563
+ }
564
+ return {
565
+ header,
566
+ label: {
567
+ color: `${p}-label`,
568
+ fontFamily: `${p}-label-font-family`,
569
+ fontSize: `${p}-label-font-size`,
570
+ fontWeight: `${p}-label-font-weight`,
571
+ lineHeight: `${p}-label-line-height`
572
+ },
573
+ icon: { color: `${p}-icon`, fontSize: `${p}-icon-size` }
574
+ };
575
+ }
576
+ var collapsibleSectionContract = {
577
+ id: "collapsiblesection",
578
+ origin: "system",
579
+ view: { variant: "Container", state: "Body" },
580
+ root: "root",
581
+ parts: {
582
+ root: ".es-root",
583
+ header: ".section-header",
584
+ label: ".section-label",
585
+ icon: ".toggle-icon",
586
+ body: ".section-content",
587
+ toggleButton: ".section-toggle-button"
588
+ },
589
+ properties: [
590
+ {
591
+ variant: "Container",
592
+ state: "Container",
593
+ paints: {
594
+ root: {
595
+ borderTopColor: "--collapsiblesection-container-frame-border",
596
+ borderTopWidth: "--collapsiblesection-container-frame-border-width",
597
+ borderRadius: "--collapsiblesection-container-frame-radius"
598
+ }
599
+ }
600
+ },
601
+ { variant: "Container", state: "Header", paints: headerPaints("container", "default") },
602
+ { variant: "Container", state: "Header", setup: clickHover(), paints: headerPaints("container", "hover") },
603
+ {
604
+ variant: "Container",
605
+ state: "Body",
606
+ paints: { body: { backgroundColor: "--collapsiblesection-container-expanded-surface", paddingTop: "--collapsiblesection-container-expanded-padding" } }
607
+ },
608
+ { variant: "Chromeless", state: "Header", paints: headerPaints("chromeless", "default") },
609
+ { variant: "Chromeless", state: "Header", setup: clickHover(), paints: headerPaints("chromeless", "hover") },
610
+ { variant: "Chromeless", state: "Body", paints: { body: { paddingTop: "--collapsiblesection-chromeless-expanded-padding" } } },
611
+ { variant: "With Divider", state: "Header", paints: headerPaints("divider", "default") },
612
+ { variant: "With Divider", state: "Header", setup: clickHover(), paints: headerPaints("divider", "hover") },
613
+ { variant: "With Divider", state: "Body", paints: { body: { paddingTop: "--collapsiblesection-divider-expanded-padding" } } }
614
+ ],
615
+ states: [{ state: "Container" }, { state: "Header" }, { state: "Body" }],
616
+ persistence: {
617
+ cases: [
618
+ {
619
+ shape: "token",
620
+ variant: "Container",
621
+ state: "Container",
622
+ variable: "--collapsiblesection-container-frame-border-width",
623
+ observe: { part: "root", css: "borderTopWidth" }
624
+ }
625
+ ],
626
+ resetVariable: "--collapsiblesection-container-frame-border-width"
627
+ },
628
+ theme: {
629
+ theme: "halloween",
630
+ changed: ["--collapsiblesection-container-frame-border-width", "--collapsiblesection-container-frame-radius"],
631
+ unchanged: ["--collapsiblesection-container-default-icon-size"],
632
+ aliasedTo: {
633
+ "--collapsiblesection-container-frame-border-width": "--border-width-3",
634
+ "--collapsiblesection-container-frame-radius": "--radius-none"
635
+ },
636
+ observe: { part: "root", css: "borderTopWidth", variable: "--collapsiblesection-container-frame-border-width" }
637
+ },
638
+ interaction: {
639
+ part: "toggleButton",
640
+ role: "button",
641
+ // No activation case: CollapsibleSectionEditor's preview drives `expanded`
642
+ // off the active state tab and never wires `ontoggle` back to it, so
643
+ // clicking the real button here can't move `aria-expanded` (same
644
+ // constraint documented on ToggleEditor in toggle.ts).
645
+ cases: [
646
+ {
647
+ name: "clicking the toggle button focuses it",
648
+ action: { kind: "click", part: "toggleButton" },
649
+ expect: { kind: "focused", part: "toggleButton", value: true }
650
+ }
651
+ ]
652
+ },
653
+ sketch: {
654
+ style: "pencil",
655
+ parts: [
656
+ { part: "root", stroke: "--collapsiblesection-container-frame-border" },
657
+ { part: "header", fill: "--collapsiblesection-container-default-surface" },
658
+ { part: "body", fill: "--collapsiblesection-container-expanded-surface" }
659
+ ]
660
+ }
661
+ };
662
+
663
+ // src/testing/contracts/cornerbadge.ts
664
+ var variants4 = [
665
+ "primary",
666
+ "accent",
667
+ "neutral",
668
+ "alternate",
669
+ "canvas",
670
+ "special",
671
+ "success",
672
+ "warning",
673
+ "danger",
674
+ "info"
675
+ ];
676
+ function colorPaints2(v) {
677
+ return {
678
+ badge: {
679
+ backgroundColor: `--corner-badge-${v}-surface`,
680
+ borderTopColor: `--corner-badge-${v}-border`,
681
+ color: `--corner-badge-${v}-text`
682
+ }
683
+ };
684
+ }
685
+ var basePaints3 = {
686
+ root: { bottom: "--corner-badge-margin" },
687
+ badge: {
688
+ borderTopLeftRadius: "--corner-badge-inner-radius",
689
+ borderTopRightRadius: "--corner-badge-v-axis-radius",
690
+ borderBottomRightRadius: "--corner-badge-outer-radius",
691
+ borderBottomLeftRadius: "--corner-badge-h-axis-radius",
692
+ paddingTop: "--corner-badge-padding",
693
+ fontFamily: "--corner-badge-text-font-family",
694
+ fontSize: "--corner-badge-text-font-size",
695
+ fontWeight: "--corner-badge-text-font-weight",
696
+ lineHeight: "--corner-badge-text-line-height"
697
+ }
698
+ };
699
+ var cornerBadgeContract = {
700
+ id: "cornerbadge",
701
+ origin: "system",
702
+ view: { variant: "Primary" },
703
+ root: "root",
704
+ parts: {
705
+ root: ".corner-badge",
706
+ badge: ".corner-badge .badge"
707
+ },
708
+ properties: [
709
+ {
710
+ variant: "Primary",
711
+ paints: {
712
+ root: basePaints3.root,
713
+ badge: { ...basePaints3.badge, ...colorPaints2("primary").badge }
714
+ }
715
+ },
716
+ ...variants4.filter((v) => v !== "primary").map((v) => ({
717
+ variant: v.charAt(0).toUpperCase() + v.slice(1),
718
+ paints: colorPaints2(v)
719
+ }))
720
+ ],
721
+ states: [{ state: "base" }, { state: "colors" }],
722
+ persistence: {
723
+ cases: [
724
+ {
725
+ shape: "token",
726
+ variable: "--corner-badge-outer-radius",
727
+ observe: { part: "badge", css: "borderBottomRightRadius" }
728
+ }
729
+ ],
730
+ resetVariable: "--corner-badge-outer-radius"
731
+ },
732
+ theme: {
733
+ theme: "ocean",
734
+ changed: ["--corner-badge-outer-radius", "--corner-badge-inner-radius", "--corner-badge-margin"],
735
+ unchanged: ["--corner-badge-text-font-size"],
736
+ aliasedTo: {
737
+ "--corner-badge-outer-radius": "--radius-md",
738
+ "--corner-badge-inner-radius": "--radius-md"
739
+ },
740
+ observe: { part: "badge", css: "borderBottomRightRadius", variable: "--corner-badge-outer-radius" }
741
+ },
742
+ interaction: {
743
+ applicable: false,
744
+ reason: "a corner badge is a static marker with no interactive role"
745
+ },
746
+ sketch: {
747
+ style: "pencil",
748
+ parts: [{ part: "badge", fill: "--corner-badge-primary-surface", stroke: "--corner-badge-primary-border" }]
749
+ }
750
+ };
751
+
752
+ // src/testing/contracts/dialog.ts
753
+ var dialogContract = {
754
+ id: "dialog",
755
+ origin: "system",
756
+ root: "root",
757
+ parts: {
758
+ backdrop: ".dialog-backdrop",
759
+ root: ".dialog",
760
+ header: ".dialog-header",
761
+ title: ".dialog-title",
762
+ closeButton: ".dialog-close",
763
+ body: ".dialog-body",
764
+ footer: ".dialog-footer",
765
+ confirmButton: ".dialog-footer-buttons button:last-of-type",
766
+ cancelButton: ".dialog-footer-buttons button:first-of-type"
767
+ },
768
+ properties: [
769
+ {
770
+ paints: {
771
+ backdrop: { backgroundColor: "--dialog-scrim-surface" },
772
+ root: {
773
+ backgroundColor: "--dialog-surface",
774
+ borderTopColor: "--dialog-border",
775
+ borderTopWidth: "--dialog-border-width",
776
+ borderRadius: "--dialog-radius",
777
+ boxShadow: "--dialog-shadow"
778
+ },
779
+ header: {
780
+ backgroundColor: "--dialog-header-surface",
781
+ borderBottomColor: "--dialog-header-divider",
782
+ borderBottomWidth: "--dialog-header-divider-width",
783
+ paddingTop: "--dialog-header-padding"
784
+ },
785
+ title: {
786
+ color: "--dialog-title",
787
+ fontFamily: "--dialog-title-font-family",
788
+ fontSize: "--dialog-title-font-size",
789
+ fontWeight: "--dialog-title-font-weight",
790
+ lineHeight: "--dialog-title-line-height"
791
+ },
792
+ closeButton: {
793
+ color: "--dialog-close-icon",
794
+ fontSize: "--dialog-close-icon-size"
795
+ },
796
+ body: {
797
+ paddingTop: "--dialog-body-padding",
798
+ color: "--dialog-body",
799
+ fontFamily: "--dialog-body-font-family",
800
+ fontSize: "--dialog-body-font-size",
801
+ fontWeight: "--dialog-body-font-weight",
802
+ lineHeight: "--dialog-body-line-height"
803
+ },
804
+ footer: {
805
+ borderTopColor: "--dialog-footer-divider",
806
+ borderTopWidth: "--dialog-footer-divider-width",
807
+ paddingTop: "--dialog-footer-padding"
808
+ }
809
+ }
810
+ }
811
+ ],
812
+ states: [
813
+ { state: "scrim" },
814
+ { state: "dialog" },
815
+ { state: "header" },
816
+ { state: "body" },
817
+ { state: "footer" }
818
+ ],
819
+ uncovered: {
820
+ "--dialog-blur": "consumed via backdrop-filter: blur(), which no probe covers"
821
+ },
822
+ persistence: {
823
+ cases: [
824
+ {
825
+ shape: "token",
826
+ state: "dialog",
827
+ variable: "--dialog-border-width",
828
+ observe: { part: "root", css: "borderTopWidth" }
829
+ },
830
+ {
831
+ shape: "config",
832
+ state: "footer",
833
+ control: '.button-variant-row:has(.property-label:text-is("right button")) select',
834
+ configKey: "--dialog-confirm-variant",
835
+ observe: { part: "confirmButton", css: "backgroundColor" }
836
+ },
837
+ {
838
+ shape: "config",
839
+ state: "footer",
840
+ control: '.button-variant-row:has(.property-label:text-is("left button")) select',
841
+ configKey: "--dialog-cancel-variant",
842
+ observe: { part: "cancelButton", css: "backgroundColor" }
843
+ }
844
+ ],
845
+ resetVariable: "--dialog-border-width"
846
+ },
847
+ theme: {
848
+ theme: "halloween",
849
+ changed: ["--dialog-border-width", "--dialog-radius", "--dialog-header-padding", "--dialog-footer-padding"],
850
+ unchanged: ["--dialog-close-icon-size", "--dialog-title-font-size"],
851
+ aliasedTo: {
852
+ "--dialog-border-width": "--border-width-4",
853
+ "--dialog-radius": "--radius-none"
854
+ },
855
+ observe: { part: "root", css: "borderTopWidth", variable: "--dialog-border-width" }
856
+ },
857
+ interaction: {
858
+ part: "closeButton",
859
+ role: "button",
860
+ cases: [
861
+ {
862
+ name: "clicking the close icon focuses it",
863
+ action: { kind: "click", part: "closeButton" },
864
+ expect: { kind: "focused", part: "closeButton", value: true }
865
+ }
866
+ ]
867
+ },
868
+ sketch: {
869
+ style: "pencil",
870
+ parts: [
871
+ { part: "root", fill: "--dialog-surface", stroke: "--dialog-border" },
872
+ { part: "header", fill: "--dialog-header-surface" }
873
+ ]
874
+ }
875
+ };
876
+
877
+ // src/testing/contracts/iconbutton.ts
878
+ var variants5 = ["primary", "secondary", "outline", "success", "danger", "warning"];
879
+ var cap2 = (v) => v.charAt(0).toUpperCase() + v.slice(1);
880
+ function basePaints4(v) {
881
+ return {
882
+ root: {
883
+ paddingTop: `--iconbutton-${v}-padding`,
884
+ borderRadius: `--iconbutton-${v}-radius`,
885
+ borderTopWidth: `--iconbutton-${v}-border-width`
886
+ },
887
+ icon: { fontSize: `--iconbutton-${v}-icon-size` }
888
+ };
889
+ }
890
+ function statePaints2(v, s) {
891
+ const prefix = s === "default" ? `--iconbutton-${v}` : `--iconbutton-${v}-${s}`;
892
+ const iconVar = s === "default" ? `--iconbutton-${v}-icon` : `--iconbutton-${v}-${s}-icon`;
893
+ return {
894
+ root: { backgroundColor: `${prefix}-surface`, borderTopColor: `${prefix}-border` },
895
+ icon: { color: iconVar }
896
+ };
897
+ }
898
+ var otherVariantProperties2 = variants5.filter((v) => v !== "primary").flatMap((v) => [
899
+ { variant: cap2(v), state: "base", paints: basePaints4(v) },
900
+ { variant: cap2(v), state: "default", paints: statePaints2(v, "default") },
901
+ { variant: cap2(v), state: "hover", paints: statePaints2(v, "hover") },
902
+ { variant: cap2(v), state: "disabled", paints: statePaints2(v, "disabled") }
903
+ ]);
904
+ var iconButtonContract = {
905
+ id: "iconbutton",
906
+ origin: "system",
907
+ view: { variant: "Primary" },
908
+ root: "root",
909
+ parts: {
910
+ root: ".icon-button",
911
+ icon: ".icon-button i"
912
+ },
913
+ properties: [
914
+ { variant: "Primary", state: "base", paints: basePaints4("primary") },
915
+ ...otherVariantProperties2,
916
+ { variant: "Outline", state: "active", paints: { root: { backgroundColor: "--iconbutton-outline-active-surface" } } },
917
+ {
918
+ setup: [{ kind: "control", selector: ".preview-actions select", value: "small" }],
919
+ paints: {
920
+ root: { paddingTop: "--iconbutton-small-padding" },
921
+ icon: { fontSize: "--iconbutton-small-icon-size" }
922
+ }
923
+ }
924
+ ],
925
+ states: [
926
+ { state: "base" },
927
+ { state: "default", paints: statePaints2("primary", "default") },
928
+ { state: "hover", forceClass: "force-hover", paints: statePaints2("primary", "hover") },
929
+ {
930
+ state: "disabled",
931
+ attributes: { root: { disabled: "" } },
932
+ paints: statePaints2("primary", "disabled")
933
+ }
934
+ ],
935
+ uncovered: {
936
+ "--iconbutton-hover-tint": "consumed inside a background-image tint wash, never appearing verbatim in a computed style",
937
+ "--iconbutton-hover-tint-enabled": "the gate for the tint wash above, same limitation"
938
+ },
939
+ persistence: {
940
+ cases: [
941
+ {
942
+ shape: "token",
943
+ variant: "Primary",
944
+ state: "base",
945
+ variable: "--iconbutton-primary-border-width",
946
+ observe: { part: "root", css: "borderTopWidth" }
947
+ }
948
+ ],
949
+ resetVariable: "--iconbutton-primary-border-width"
950
+ },
951
+ theme: {
952
+ theme: "halloween",
953
+ changed: ["--iconbutton-primary-border-width", "--iconbutton-primary-radius", "--iconbutton-primary-padding"],
954
+ unchanged: ["--iconbutton-primary-icon-size"],
955
+ aliasedTo: {
956
+ "--iconbutton-primary-border-width": "--border-width-3",
957
+ "--iconbutton-primary-radius": "--radius-none"
958
+ },
959
+ observe: { part: "root", css: "borderTopWidth", variable: "--iconbutton-primary-border-width" }
960
+ },
961
+ interaction: {
962
+ part: "root",
963
+ role: "button",
964
+ cases: [
965
+ {
966
+ name: "clicking an icon button focuses it",
967
+ action: { kind: "click", part: "root" },
968
+ expect: { kind: "focused", part: "root", value: true }
969
+ }
970
+ ]
971
+ },
972
+ sketch: {
973
+ style: "pencil",
974
+ parts: [{ part: "root", fill: "--iconbutton-primary-surface", stroke: "--iconbutton-primary-border" }]
975
+ }
976
+ };
977
+
978
+ // src/testing/contracts/image.ts
979
+ var imageContract = {
980
+ id: "image",
981
+ origin: "system",
982
+ root: "root",
983
+ parts: {
984
+ root: ".image",
985
+ img: ".image img"
986
+ },
987
+ properties: [
988
+ {
989
+ paints: {
990
+ root: {
991
+ borderTopColor: "--image-default-border",
992
+ borderTopWidth: "--image-default-border-width",
993
+ borderRadius: "--image-default-radius",
994
+ boxShadow: "--image-default-shadow"
995
+ }
996
+ }
997
+ }
998
+ ],
999
+ states: {
1000
+ applicable: false,
1001
+ reason: "the editor renders one token group (image) with no state strip"
1002
+ },
1003
+ uncovered: {
1004
+ "--image-zoom-scale": "consumed inside transform: scale(), never appears verbatim in a computed style"
1005
+ },
1006
+ persistence: {
1007
+ cases: [
1008
+ {
1009
+ shape: "token",
1010
+ variable: "--image-default-border-width",
1011
+ observe: { part: "root", css: "borderTopWidth" }
1012
+ },
1013
+ {
1014
+ shape: "literal",
1015
+ control: 'label.zoom-enable:has-text("Use zoom on hover") input',
1016
+ variable: "--image-zoom-enabled",
1017
+ observe: { part: "img", css: "transform" }
1018
+ }
1019
+ ],
1020
+ resetVariable: "--image-default-border-width"
1021
+ },
1022
+ theme: {
1023
+ theme: "halloween",
1024
+ changed: ["--image-default-border-width", "--image-default-radius"],
1025
+ unchanged: ["--image-default-shadow", "--image-zoom-scale"],
1026
+ aliasedTo: {
1027
+ "--image-default-border-width": "--border-width-3",
1028
+ "--image-default-radius": "--radius-none"
1029
+ },
1030
+ observe: { part: "root", css: "borderTopWidth", variable: "--image-default-border-width" }
1031
+ },
1032
+ interaction: {
1033
+ applicable: false,
1034
+ reason: "a framed picture carries no interactive role; the zoom effect is a hover transform, not an action"
1035
+ },
1036
+ sketch: {
1037
+ style: "pencil",
1038
+ parts: [{ part: "root", stroke: "--image-default-border" }]
1039
+ }
1040
+ };
1041
+
1042
+ // src/testing/contracts/imagelightbox.ts
1043
+ var imageLightboxContract = {
1044
+ id: "imagelightbox",
1045
+ origin: "system",
1046
+ view: { setup: [{ kind: "click", part: "thumb" }] },
1047
+ root: "root",
1048
+ parts: {
1049
+ root: ".image-lightbox-wrapper",
1050
+ thumb: ".image-lightbox-thumb",
1051
+ overlay: { selector: ".image-lightbox-overlay", portal: true },
1052
+ stage: { selector: ".image-lightbox-stage", portal: true },
1053
+ closeButton: { selector: ".image-lightbox-close", portal: true },
1054
+ closeIcon: { selector: ".image-lightbox-close svg", portal: true },
1055
+ prevButton: { selector: ".image-lightbox-nav-prev", portal: true },
1056
+ nextButton: { selector: ".image-lightbox-nav-next", portal: true },
1057
+ counter: { selector: ".image-lightbox-counter", portal: true },
1058
+ toolbar: { selector: ".image-lightbox-toolbar", portal: true }
1059
+ },
1060
+ properties: [
1061
+ {
1062
+ paints: {
1063
+ thumb: {
1064
+ backgroundColor: "--imagelightbox-tile-surface",
1065
+ borderTopColor: "--imagelightbox-tile-border",
1066
+ borderTopWidth: "--imagelightbox-tile-border-width",
1067
+ borderRadius: "--imagelightbox-tile-radius",
1068
+ boxShadow: "--imagelightbox-tile-shadow"
1069
+ },
1070
+ overlay: { backgroundColor: "--imagelightbox-overlay-surface" },
1071
+ closeButton: {
1072
+ backgroundColor: "--imagelightbox-chrome-surface",
1073
+ borderTopColor: "--imagelightbox-chrome-border",
1074
+ borderTopWidth: "--imagelightbox-chrome-border-width",
1075
+ borderRadius: "--imagelightbox-chrome-radius",
1076
+ color: "--imagelightbox-chrome-icon"
1077
+ },
1078
+ closeIcon: { stroke: "--imagelightbox-chrome-icon" }
1079
+ }
1080
+ },
1081
+ // `click({force:true})` leaves the pointer resting on `nextButton`, so the
1082
+ // browser's real `.image-lightbox-nav:hover` rule applies afterward.
1083
+ // Unlike `closeButton`, clicking it does not close the modal.
1084
+ {
1085
+ setup: [{ kind: "click", part: "nextButton" }],
1086
+ paints: { nextButton: { backgroundColor: "--imagelightbox-chrome-hover-surface" } }
1087
+ }
1088
+ ],
1089
+ states: [{ state: "tile" }, { state: "overlay" }, { state: "chrome" }],
1090
+ persistence: {
1091
+ cases: [
1092
+ {
1093
+ // No `state` here: "tile" is the panel's own default tab, and every
1094
+ // `selectView` re-clicks whatever `state` names even when it is
1095
+ // already active. While the modal is open, that click's target sits
1096
+ // under the overlay's full-viewport hit area, so skipping it avoids
1097
+ // an avoidable trip through the same close-on-click behavior `setup`
1098
+ // is about to use on purpose. `setup` closes the modal the
1099
+ // contract-level view opened so the "tile" token control (behind the
1100
+ // still-open overlay otherwise) is reachable, by pressing Escape on
1101
+ // `root` rather than clicking `closeButton`. `assertPersistence`
1102
+ // replays `setup` twice back to back (once directly, once inside
1103
+ // `driveControl`). Measured against a live page: `closeLightbox`'s
1104
+ // `open = false` write happens inside an animation `onfinish`
1105
+ // callback that a `settle()` cycle doesn't durably wait out, so
1106
+ // `closeButton` — a portaled element that unmounts once that callback
1107
+ // fires — can still be gone, still be there, or about to vanish
1108
+ // mid-click, depending on exactly when it lands relative to the
1109
+ // harness's round trips. A second `closeButton` click inherits that
1110
+ // uncertainty. Leading with a `thumb` click to guarantee a target for
1111
+ // the `closeButton` click that follows inherits it from the other
1112
+ // side instead: `openLightbox`'s own `if (open) return` reads the
1113
+ // same not-yet-written flag, so it can no-op rather than reopen, and
1114
+ // nothing then re-drives it — reproduced empirically, a deterministic
1115
+ // dead end. Escape on `root` avoids both: `root` is never portaled,
1116
+ // so the action always has a target, and replaying it before the
1117
+ // first press has finished closing re-enters `closeLightbox` rather
1118
+ // than skipping it, so every replay drives the component strictly
1119
+ // further toward closed. (Escape also collapses the components
1120
+ // editor's own drawer, but nothing else this obligation reads lives
1121
+ // there, and `reopen()` between obligations restores it.)
1122
+ shape: "token",
1123
+ setup: [{ kind: "press", part: "root", key: "Escape" }],
1124
+ variable: "--imagelightbox-tile-border-width",
1125
+ observe: { part: "thumb", css: "borderTopWidth" }
1126
+ }
1127
+ ],
1128
+ resetVariable: "--imagelightbox-tile-border-width"
1129
+ },
1130
+ theme: {
1131
+ // Closes the modal the contract-level setup opened: the Theme Picker
1132
+ // trigger lives in the chrome behind it, and the still-open overlay
1133
+ // blocks clicks the same way it does for the persistence case above.
1134
+ // Unlike that case, `assertThemeProjection` runs `setup` only once, so
1135
+ // the double-replay hazard doesn't apply and a plain `closeButton` click
1136
+ // is enough — and it has to be a click, not the Escape this contract's
1137
+ // persistence case uses, because Escape also collapses the drawer this
1138
+ // obligation's own `.theme-name-trigger` lives in.
1139
+ setup: [{ kind: "click", part: "closeButton" }],
1140
+ theme: "halloween",
1141
+ changed: ["--imagelightbox-tile-border-width", "--imagelightbox-tile-radius", "--imagelightbox-chrome-border-width", "--imagelightbox-chrome-radius"],
1142
+ unchanged: ["--imagelightbox-tile-shadow"],
1143
+ aliasedTo: {
1144
+ "--imagelightbox-tile-border-width": "--border-width-2",
1145
+ "--imagelightbox-tile-radius": "--radius-none"
1146
+ },
1147
+ observe: { part: "thumb", css: "borderTopWidth", variable: "--imagelightbox-tile-border-width" }
1148
+ },
1149
+ interaction: {
1150
+ part: "nextButton",
1151
+ role: "button",
1152
+ cases: [
1153
+ {
1154
+ name: "clicking the next chevron focuses it",
1155
+ action: { kind: "click", part: "nextButton" },
1156
+ expect: { kind: "focused", part: "nextButton", value: true }
1157
+ }
1158
+ ]
1159
+ },
1160
+ // `overlay` and `closeButton` are inside `.image-lightbox-modal`, which
1161
+ // `use:portal` moves to `document.body`. The standardized preview scopes
1162
+ // `data-sketch` to its own `.sketch-scope` wrapper rather than the document
1163
+ // root, so a portaled part sits outside the CSS ancestor match this contract
1164
+ // can observe, even though PART_SPECS draws it on a real host page (where
1165
+ // Sketch mode's scope is the page root). `thumb` never portals, so it is the
1166
+ // part this contract can pin.
1167
+ sketch: {
1168
+ style: "pencil",
1169
+ parts: [
1170
+ { part: "thumb", fill: "--imagelightbox-tile-surface", stroke: "--imagelightbox-tile-border" }
1171
+ ]
1172
+ }
1173
+ };
1174
+
1175
+ // src/testing/contracts/inlineeditactions.ts
1176
+ function statePaints3(btn, s) {
1177
+ const p = `--inlineeditactions-${btn}-${s}`;
1178
+ const button = btn === "save" ? "saveBtn" : "cancelBtn";
1179
+ return {
1180
+ // `-icon-size` lands as `font-size` on the button itself (the icon glyph
1181
+ // inherits it), not on a separate icon element — and a declared "icon"
1182
+ // part here would be the button's own `<i class="fas ...">`, whose native
1183
+ // ::before glyph content is never 'none', which reads as "drawn" to
1184
+ // `assertNoSketchPaint` regardless of sketch mode.
1185
+ [button]: {
1186
+ backgroundColor: `${p}-surface`,
1187
+ borderTopColor: `${p}-border`,
1188
+ borderTopWidth: `${p}-border-width`,
1189
+ borderRadius: `${p}-radius`,
1190
+ paddingTop: `${p}-padding`,
1191
+ color: `${p}-text`,
1192
+ fontSize: `${p}-icon-size`
1193
+ }
1194
+ };
1195
+ }
1196
+ var inlineEditActionsContract = {
1197
+ id: "inlineeditactions",
1198
+ origin: "system",
1199
+ view: { variant: "Save button" },
1200
+ root: "saveBtn",
1201
+ parts: {
1202
+ saveBtn: ".save-btn",
1203
+ cancelBtn: ".cancel-btn"
1204
+ },
1205
+ // Every entry names its own `state` explicitly. VariantGroup mirrors the
1206
+ // last-clicked state tab across sibling variants (for the linked-block
1207
+ // chart), so switching variants without repeating `state` would silently
1208
+ // carry the previous entry's tab forward.
1209
+ properties: [
1210
+ { variant: "Save button", state: "default", paints: statePaints3("save", "default") },
1211
+ { variant: "Save button", state: "hover", paints: statePaints3("save", "hover") },
1212
+ { variant: "Cancel button", state: "default", paints: statePaints3("cancel", "default") },
1213
+ { variant: "Cancel button", state: "hover", paints: statePaints3("cancel", "hover") }
1214
+ ],
1215
+ states: [
1216
+ { state: "default" },
1217
+ { state: "hover" }
1218
+ ],
1219
+ persistence: {
1220
+ cases: [
1221
+ {
1222
+ shape: "token",
1223
+ variable: "--inlineeditactions-save-default-border-width",
1224
+ observe: { part: "saveBtn", css: "borderTopWidth" }
1225
+ }
1226
+ ],
1227
+ resetVariable: "--inlineeditactions-save-default-border-width"
1228
+ },
1229
+ theme: {
1230
+ theme: "halloween",
1231
+ changed: ["--inlineeditactions-save-default-border-width", "--inlineeditactions-save-default-radius"],
1232
+ unchanged: ["--inlineeditactions-save-default-icon-size"],
1233
+ aliasedTo: {
1234
+ "--inlineeditactions-save-default-border-width": "--border-width-2",
1235
+ "--inlineeditactions-save-default-radius": "--radius-none"
1236
+ },
1237
+ observe: { part: "saveBtn", css: "borderTopWidth", variable: "--inlineeditactions-save-default-border-width" }
1238
+ },
1239
+ interaction: {
1240
+ part: "saveBtn",
1241
+ role: "button",
1242
+ cases: [
1243
+ {
1244
+ name: "clicking save focuses the button",
1245
+ action: { kind: "click", part: "saveBtn" },
1246
+ expect: { kind: "focused", part: "saveBtn", value: true }
1247
+ }
1248
+ ]
1249
+ },
1250
+ sketch: {
1251
+ style: "pencil",
1252
+ parts: [
1253
+ { part: "saveBtn", fill: "--inlineeditactions-save-default-surface", stroke: "--inlineeditactions-save-default-border" },
1254
+ { part: "cancelBtn", fill: "--inlineeditactions-cancel-default-surface", stroke: "--inlineeditactions-cancel-default-border" }
1255
+ ]
1256
+ }
1257
+ };
1258
+
1259
+ // src/testing/contracts/input.ts
1260
+ var noError = [
1261
+ { kind: "control", selector: ".canvas-toolbar-select", value: "password" },
1262
+ { kind: "control", selector: '.toolbar-check:has-text("Error") input', check: false }
1263
+ ];
1264
+ var inputContract = {
1265
+ id: "input",
1266
+ origin: "system",
1267
+ view: {
1268
+ setup: [
1269
+ { kind: "control", selector: ".canvas-toolbar-select", value: "password" },
1270
+ { kind: "control", selector: '.toolbar-check:has-text("Error") input', check: true }
1271
+ ]
1272
+ },
1273
+ root: "root",
1274
+ parts: {
1275
+ root: ".input-field",
1276
+ field: ".input-control",
1277
+ label: ".input-label",
1278
+ icon: ".input-icon",
1279
+ toggleButton: ".input-icon-button",
1280
+ hint: ".input-hint",
1281
+ errorMsg: ".input-error",
1282
+ placeholder: { selector: ".input-control", pseudo: "::placeholder" }
1283
+ },
1284
+ properties: [
1285
+ {
1286
+ paints: {
1287
+ root: { rowGap: "--input-gap" },
1288
+ field: {
1289
+ borderRadius: "--input-radius",
1290
+ borderTopWidth: "--input-border-width",
1291
+ paddingTop: "--input-padding",
1292
+ backgroundColor: "--input-default-surface",
1293
+ borderTopColor: "--input-default-border",
1294
+ color: "--input-default-text",
1295
+ fontFamily: "--input-default-text-font-family",
1296
+ fontSize: "--input-default-text-font-size",
1297
+ fontWeight: "--input-default-text-font-weight",
1298
+ lineHeight: "--input-default-text-line-height"
1299
+ },
1300
+ icon: {
1301
+ color: "--input-default-icon",
1302
+ fontSize: "--input-default-icon-size"
1303
+ },
1304
+ label: {
1305
+ color: "--input-label",
1306
+ fontFamily: "--input-label-font-family",
1307
+ fontSize: "--input-label-font-size",
1308
+ fontWeight: "--input-label-font-weight",
1309
+ lineHeight: "--input-label-line-height"
1310
+ },
1311
+ hint: {
1312
+ color: "--input-hint",
1313
+ fontFamily: "--input-hint-font-family",
1314
+ fontSize: "--input-hint-font-size",
1315
+ fontWeight: "--input-hint-font-weight",
1316
+ lineHeight: "--input-hint-line-height"
1317
+ },
1318
+ placeholder: { color: "--input-default-placeholder" }
1319
+ }
1320
+ },
1321
+ // `assertPaintsFromToken` (here) drives a controlled pixel probe, so a
1322
+ // unitless line-height round-trips fine. `assertPaintMap` (states, below)
1323
+ // reads the real, already-resolved value through `normalize()`, which
1324
+ // cannot recompute a unitless multiplier outside the field's own
1325
+ // font-size context, so these three stay out of `states`.
1326
+ {
1327
+ state: "focused",
1328
+ setup: noError,
1329
+ paints: { field: { lineHeight: "--input-focused-text-line-height" } }
1330
+ },
1331
+ {
1332
+ state: "disabled",
1333
+ setup: noError,
1334
+ paints: { field: { lineHeight: "--input-disabled-text-line-height" } }
1335
+ },
1336
+ {
1337
+ state: "error",
1338
+ paints: { errorMsg: { lineHeight: "--input-error-line-height" } }
1339
+ }
1340
+ ],
1341
+ states: [
1342
+ { state: "field" },
1343
+ { state: "default" },
1344
+ {
1345
+ state: "focused",
1346
+ forceClass: "force-focus",
1347
+ setup: noError,
1348
+ paints: {
1349
+ field: {
1350
+ backgroundColor: "--input-focused-surface",
1351
+ outlineColor: "--input-focused-border",
1352
+ outlineWidth: "--input-focused-border-width",
1353
+ color: "--input-focused-text",
1354
+ fontFamily: "--input-focused-text-font-family",
1355
+ fontSize: "--input-focused-text-font-size",
1356
+ fontWeight: "--input-focused-text-font-weight"
1357
+ },
1358
+ icon: { color: "--input-focused-icon", fontSize: "--input-focused-icon-size" }
1359
+ }
1360
+ },
1361
+ {
1362
+ state: "disabled",
1363
+ attributes: { field: { disabled: "" } },
1364
+ paints: {
1365
+ field: {
1366
+ backgroundColor: "--input-disabled-surface",
1367
+ borderTopColor: "--input-disabled-border",
1368
+ color: "--input-disabled-text",
1369
+ fontFamily: "--input-disabled-text-font-family",
1370
+ fontSize: "--input-disabled-text-font-size",
1371
+ fontWeight: "--input-disabled-text-font-weight"
1372
+ },
1373
+ icon: { color: "--input-disabled-icon", fontSize: "--input-disabled-icon-size" }
1374
+ }
1375
+ },
1376
+ { state: "label" },
1377
+ { state: "hint" },
1378
+ {
1379
+ state: "error",
1380
+ paints: {
1381
+ field: { outlineColor: "--input-error-border", outlineWidth: "--input-error-border-width" },
1382
+ errorMsg: {
1383
+ color: "--input-error",
1384
+ fontFamily: "--input-error-font-family",
1385
+ fontSize: "--input-error-font-size",
1386
+ fontWeight: "--input-error-font-weight"
1387
+ }
1388
+ }
1389
+ }
1390
+ ],
1391
+ persistence: {
1392
+ cases: [
1393
+ {
1394
+ shape: "token",
1395
+ variable: "--input-border-width",
1396
+ observe: { part: "field", css: "borderTopWidth" }
1397
+ }
1398
+ ],
1399
+ resetVariable: "--input-border-width"
1400
+ },
1401
+ theme: {
1402
+ theme: "halloween",
1403
+ changed: ["--input-border-width", "--input-radius", "--input-padding"],
1404
+ unchanged: ["--input-label-font-size", "--input-default-icon-size"],
1405
+ aliasedTo: {
1406
+ "--input-border-width": "--border-width-3",
1407
+ "--input-radius": "--radius-none"
1408
+ },
1409
+ observe: { part: "field", css: "borderTopWidth", variable: "--input-border-width" }
1410
+ },
1411
+ interaction: {
1412
+ part: "field",
1413
+ role: "textbox",
1414
+ cases: [
1415
+ {
1416
+ name: "typing writes into the field",
1417
+ action: { kind: "type", part: "field", text: "probe" },
1418
+ expect: { kind: "valueChanges", part: "field" }
1419
+ },
1420
+ {
1421
+ name: "clicking the reveal toggle presses it",
1422
+ action: { kind: "click", part: "toggleButton" },
1423
+ expect: { kind: "attribute", part: "toggleButton", name: "aria-pressed", value: "true" }
1424
+ },
1425
+ {
1426
+ name: "a disabled field refuses focus",
1427
+ state: "disabled",
1428
+ action: { kind: "click", part: "field" },
1429
+ expect: { kind: "focused", part: "field", value: false }
1430
+ }
1431
+ ]
1432
+ },
1433
+ sketch: {
1434
+ style: "pencil",
1435
+ parts: [{ part: "field", fill: "--input-default-surface", stroke: "--input-default-border" }]
1436
+ }
1437
+ };
1438
+
1439
+ // src/testing/contracts/menuselect.ts
1440
+ function itemTextPaints(s, full) {
1441
+ const paints = {
1442
+ color: `--menuselect-${s}-text`,
1443
+ fontFamily: `--menuselect-${s}-text-font-family`,
1444
+ fontSize: `--menuselect-${s}-text-font-size`,
1445
+ fontWeight: `--menuselect-${s}-text-font-weight`
1446
+ };
1447
+ if (full) paints.lineHeight = `--menuselect-${s}-text-line-height`;
1448
+ return paints;
1449
+ }
1450
+ var menuSelectContract = {
1451
+ id: "menuselect",
1452
+ origin: "system",
1453
+ view: { state: "selected item" },
1454
+ root: "root",
1455
+ parts: {
1456
+ root: ".menuselect",
1457
+ item: ".menuselect-item",
1458
+ icon: ".menuselect-item .menuselect-icon",
1459
+ label: ".menuselect-item .menuselect-label",
1460
+ selectedItem: ".menuselect-item.selected",
1461
+ selectedIcon: ".menuselect-item.selected .menuselect-icon",
1462
+ indicator: ".menuselect-indicator",
1463
+ disabledItem: ".menuselect-item:disabled",
1464
+ disabledIcon: ".menuselect-item:disabled .menuselect-icon"
1465
+ },
1466
+ properties: [
1467
+ {
1468
+ state: "menu",
1469
+ paints: {
1470
+ root: {
1471
+ backgroundColor: "--menuselect-menu-surface",
1472
+ borderTopColor: "--menuselect-menu-border",
1473
+ borderTopWidth: "--menuselect-menu-border-width",
1474
+ borderRadius: "--menuselect-menu-radius",
1475
+ boxShadow: "--menuselect-menu-shadow",
1476
+ rowGap: "--menuselect-menu-gap",
1477
+ paddingTop: "--menuselect-menu-padding"
1478
+ },
1479
+ item: {
1480
+ borderRadius: "--menuselect-item-radius",
1481
+ paddingTop: "--menuselect-item-padding"
1482
+ }
1483
+ }
1484
+ },
1485
+ { state: "default item", paints: { item: itemTextPaints("default", true) } },
1486
+ { state: "hover item", paints: { item: itemTextPaints("hover", true) } },
1487
+ { state: "selected item", paints: { selectedItem: itemTextPaints("selected", true) } },
1488
+ { state: "disabled item", paints: { disabledItem: itemTextPaints("disabled", true) } }
1489
+ ],
1490
+ states: [
1491
+ { state: "menu" },
1492
+ {
1493
+ state: "default item",
1494
+ paints: {
1495
+ item: { backgroundColor: "--menuselect-default-surface", ...itemTextPaints("default", false) },
1496
+ icon: { color: "--menuselect-default-icon", fontSize: "--menuselect-default-icon-size" }
1497
+ }
1498
+ },
1499
+ {
1500
+ state: "hover item",
1501
+ paints: {
1502
+ item: { backgroundColor: "--menuselect-hover-surface", ...itemTextPaints("hover", false) },
1503
+ icon: { color: "--menuselect-hover-icon", fontSize: "--menuselect-hover-icon-size" }
1504
+ }
1505
+ },
1506
+ {
1507
+ state: "selected item",
1508
+ paints: {
1509
+ selectedItem: { backgroundColor: "--menuselect-selected-surface", ...itemTextPaints("selected", false) },
1510
+ selectedIcon: { color: "--menuselect-selected-icon", fontSize: "--menuselect-selected-icon-size" },
1511
+ indicator: { color: "--menuselect-selected-indicator" }
1512
+ }
1513
+ },
1514
+ {
1515
+ state: "disabled item",
1516
+ attributes: { disabledItem: { disabled: "" } },
1517
+ paints: {
1518
+ disabledItem: { backgroundColor: "--menuselect-disabled-surface", ...itemTextPaints("disabled", false) },
1519
+ disabledIcon: { color: "--menuselect-disabled-icon", fontSize: "--menuselect-disabled-icon-size" }
1520
+ }
1521
+ }
1522
+ ],
1523
+ uncovered: {
1524
+ "--menuselect-hover-tint": "consumed inside a background-image tint wash, never appearing verbatim in a computed style",
1525
+ "--menuselect-hover-tint-enabled": "the gate for the tint wash above, same limitation"
1526
+ },
1527
+ persistence: {
1528
+ cases: [
1529
+ {
1530
+ shape: "token",
1531
+ state: "menu",
1532
+ variable: "--menuselect-menu-border-width",
1533
+ observe: { part: "root", css: "borderTopWidth" }
1534
+ }
1535
+ ],
1536
+ resetVariable: "--menuselect-menu-border-width"
1537
+ },
1538
+ theme: {
1539
+ theme: "halloween",
1540
+ changed: ["--menuselect-menu-border-width", "--menuselect-menu-radius", "--menuselect-item-padding"],
1541
+ unchanged: ["--menuselect-default-icon-size", "--menuselect-default-text-font-size"],
1542
+ aliasedTo: {
1543
+ "--menuselect-menu-border-width": "--border-width-3",
1544
+ "--menuselect-menu-radius": "--radius-none"
1545
+ },
1546
+ observe: { part: "root", css: "borderTopWidth", variable: "--menuselect-menu-border-width" }
1547
+ },
1548
+ interaction: {
1549
+ part: "item",
1550
+ role: "option",
1551
+ cases: [
1552
+ {
1553
+ name: "clicking an item selects it",
1554
+ action: { kind: "click", part: "item" },
1555
+ expect: { kind: "attribute", part: "item", name: "aria-selected", value: "true" }
1556
+ }
1557
+ ]
1558
+ },
1559
+ sketch: {
1560
+ style: "pencil",
1561
+ parts: [{ part: "root", fill: "--menuselect-menu-surface", stroke: "--menuselect-menu-border" }]
1562
+ }
1563
+ };
1564
+
1565
+ // src/testing/contracts/notification.ts
1566
+ var variants6 = ["info", "success", "warning", "danger"];
1567
+ function paintsFor2(v) {
1568
+ return {
1569
+ root: {
1570
+ borderTopColor: `--notification-${v}-border`,
1571
+ borderTopWidth: `--notification-${v}-border-width`,
1572
+ borderRadius: `--notification-${v}-radius`,
1573
+ color: `--notification-${v}-text`,
1574
+ fontFamily: `--notification-${v}-text-font-family`,
1575
+ fontSize: `--notification-${v}-text-font-size`,
1576
+ fontWeight: `--notification-${v}-text-font-weight`,
1577
+ lineHeight: `--notification-${v}-text-line-height`
1578
+ },
1579
+ header: {
1580
+ backgroundColor: `--notification-${v}-surface`,
1581
+ paddingTop: `--notification-${v}-padding`
1582
+ },
1583
+ icon: {
1584
+ color: `--notification-${v}-icon`,
1585
+ fontSize: `--notification-${v}-icon-size`
1586
+ },
1587
+ title: {
1588
+ color: `--notification-${v}-title`,
1589
+ fontFamily: `--notification-${v}-title-font-family`,
1590
+ fontSize: `--notification-${v}-title-font-size`,
1591
+ fontWeight: `--notification-${v}-title-font-weight`,
1592
+ lineHeight: `--notification-${v}-title-line-height`
1593
+ },
1594
+ actionBackdrop: { backgroundColor: `--notification-${v}-action-surface` }
1595
+ };
1596
+ }
1597
+ var notificationContract = {
1598
+ id: "notification",
1599
+ origin: "system",
1600
+ view: {
1601
+ variant: "Info",
1602
+ setup: [{ kind: "control", selector: '.toolbar-check:has-text("Header button") input', check: true }]
1603
+ },
1604
+ root: "root",
1605
+ parts: {
1606
+ root: ".notification",
1607
+ header: ".notification-header",
1608
+ icon: ".notification-header i",
1609
+ title: ".notification-title",
1610
+ actionBackdrop: ".action-button-backdrop",
1611
+ actionButton: ".action-button-backdrop button"
1612
+ },
1613
+ properties: variants6.map((v) => ({ variant: v.charAt(0).toUpperCase() + v.slice(1), paints: paintsFor2(v) })),
1614
+ states: [{ state: "base" }, { state: "colors" }],
1615
+ persistence: {
1616
+ cases: [
1617
+ {
1618
+ shape: "token",
1619
+ variable: "--notification-info-border-width",
1620
+ observe: { part: "root", css: "borderTopWidth" }
1621
+ }
1622
+ ],
1623
+ resetVariable: "--notification-info-border-width"
1624
+ },
1625
+ theme: {
1626
+ theme: "halloween",
1627
+ changed: ["--notification-info-border-width", "--notification-info-radius", "--notification-info-padding"],
1628
+ unchanged: ["--notification-info-icon-size"],
1629
+ aliasedTo: {
1630
+ "--notification-info-border-width": "--border-width-3",
1631
+ "--notification-info-radius": "--radius-none"
1632
+ },
1633
+ observe: { part: "root", css: "borderTopWidth", variable: "--notification-info-border-width" }
1634
+ },
1635
+ interaction: {
1636
+ part: "actionButton",
1637
+ role: "button",
1638
+ cases: [
1639
+ {
1640
+ name: "clicking the header action focuses it",
1641
+ action: { kind: "click", part: "actionButton" },
1642
+ expect: { kind: "focused", part: "actionButton", value: true }
1643
+ }
1644
+ ]
1645
+ },
1646
+ sketch: {
1647
+ style: "pencil",
1648
+ parts: [
1649
+ { part: "root", stroke: "--notification-info-border" },
1650
+ { part: "header", fill: "--notification-info-surface" }
1651
+ ]
1652
+ }
1653
+ };
1654
+
1655
+ // src/testing/contracts/panel.ts
1656
+ var panelContract = {
1657
+ id: "panel",
1658
+ origin: "system",
1659
+ root: "root",
1660
+ parts: {
1661
+ root: ".panel"
1662
+ },
1663
+ properties: [
1664
+ {
1665
+ paints: {
1666
+ root: {
1667
+ borderTopColor: "--panel-frame-border",
1668
+ borderTopWidth: "--panel-frame-border-width",
1669
+ borderRadius: "--panel-frame-radius",
1670
+ backgroundImage: "--panel-stage-surface",
1671
+ paddingTop: "--panel-stage-padding",
1672
+ paddingLeft: "--panel-stage-inline-padding",
1673
+ columnGap: "--panel-stage-gap"
1674
+ }
1675
+ }
1676
+ }
1677
+ ],
1678
+ states: {
1679
+ applicable: false,
1680
+ reason: "the editor renders one token group (default) with no state strip"
1681
+ },
1682
+ persistence: {
1683
+ cases: [
1684
+ {
1685
+ shape: "token",
1686
+ variable: "--panel-frame-border-width",
1687
+ observe: { part: "root", css: "borderTopWidth" }
1688
+ },
1689
+ {
1690
+ shape: "gradient",
1691
+ variable: "--panel-stage-surface",
1692
+ observe: { part: "root", css: "backgroundColor" }
1693
+ }
1694
+ ],
1695
+ resetVariable: "--panel-frame-border-width"
1696
+ },
1697
+ theme: {
1698
+ theme: "halloween",
1699
+ changed: ["--panel-frame-border-width", "--panel-frame-radius", "--panel-stage-inline-padding", "--panel-stage-padding"],
1700
+ unchanged: ["--panel-stage-gap"],
1701
+ aliasedTo: {
1702
+ "--panel-frame-border-width": "--border-width-3",
1703
+ "--panel-frame-radius": "--radius-none"
1704
+ },
1705
+ observe: { part: "root", css: "borderTopWidth", variable: "--panel-frame-border-width" }
1706
+ },
1707
+ interaction: {
1708
+ applicable: false,
1709
+ reason: "a framed container carries no interactive role"
1710
+ },
1711
+ sketch: {
1712
+ style: "pencil",
1713
+ parts: [{ part: "root", fill: "--panel-stage-surface", stroke: "--panel-frame-border" }]
1714
+ }
1715
+ };
1716
+
1717
+ // src/testing/contracts/progressbar.ts
1718
+ var progressBarContract = {
1719
+ id: "progressbar",
1720
+ origin: "system",
1721
+ root: "root",
1722
+ parts: {
1723
+ root: ".progress",
1724
+ track: ".progress-track",
1725
+ fill: ".progress-fill",
1726
+ label: ".progress-label > span:not(.progress-value)",
1727
+ value: ".progress-value"
1728
+ },
1729
+ properties: [
1730
+ {
1731
+ paints: {
1732
+ root: { rowGap: "--progressbar-label-gap" },
1733
+ track: {
1734
+ backgroundColor: "--progressbar-track-surface",
1735
+ borderTopColor: "--progressbar-track-border",
1736
+ borderTopWidth: "--progressbar-track-border-width",
1737
+ borderRadius: "--progressbar-radius",
1738
+ height: "--progressbar-track-height"
1739
+ },
1740
+ fill: { backgroundColor: "--progressbar-fill" },
1741
+ label: {
1742
+ color: "--progressbar-label",
1743
+ fontFamily: "--progressbar-label-font-family",
1744
+ fontSize: "--progressbar-label-font-size",
1745
+ fontWeight: "--progressbar-label-font-weight",
1746
+ lineHeight: "--progressbar-label-line-height"
1747
+ },
1748
+ value: {
1749
+ color: "--progressbar-value",
1750
+ fontFamily: "--progressbar-value-font-family",
1751
+ fontSize: "--progressbar-value-font-size",
1752
+ fontWeight: "--progressbar-value-font-weight",
1753
+ lineHeight: "--progressbar-value-line-height"
1754
+ }
1755
+ }
1756
+ }
1757
+ ],
1758
+ states: {
1759
+ applicable: false,
1760
+ reason: "the editor renders one token group (default) with no state strip"
1761
+ },
1762
+ persistence: {
1763
+ cases: [
1764
+ {
1765
+ shape: "token",
1766
+ variable: "--progressbar-track-border-width",
1767
+ observe: { part: "track", css: "borderTopWidth" }
1768
+ }
1769
+ ],
1770
+ resetVariable: "--progressbar-track-border-width"
1771
+ },
1772
+ theme: {
1773
+ theme: "halloween",
1774
+ changed: ["--progressbar-radius", "--progressbar-track-border-width"],
1775
+ unchanged: ["--progressbar-track-height"],
1776
+ aliasedTo: {
1777
+ "--progressbar-radius": "--radius-none",
1778
+ "--progressbar-track-border-width": "--border-width-3"
1779
+ },
1780
+ observe: { part: "track", css: "borderTopWidth", variable: "--progressbar-track-border-width" }
1781
+ },
1782
+ interaction: {
1783
+ applicable: false,
1784
+ reason: "a progress readout carries no interactive role"
1785
+ },
1786
+ sketch: {
1787
+ style: "pencil",
1788
+ parts: [{ part: "track", fill: "--progressbar-track-surface", stroke: "--progressbar-track-border" }]
1789
+ }
1790
+ };
1791
+
1792
+ // src/testing/contracts/radiobutton.ts
1793
+ function statePaints4(s, full) {
1794
+ const dotFill = { backgroundColor: `--radiobutton-${s}-dot-fill` };
1795
+ if (full) dotFill.width = `--radiobutton-${s}-dot-size`;
1796
+ const label = {
1797
+ color: `--radiobutton-${s}-label`,
1798
+ fontFamily: `--radiobutton-${s}-label-font-family`,
1799
+ fontSize: `--radiobutton-${s}-label-font-size`,
1800
+ fontWeight: `--radiobutton-${s}-label-font-weight`
1801
+ };
1802
+ if (full) label.lineHeight = `--radiobutton-${s}-label-line-height`;
1803
+ return {
1804
+ dot: {
1805
+ borderTopColor: `--radiobutton-${s}-dot-border-color`,
1806
+ borderTopWidth: `--radiobutton-${s}-dot-border-width`
1807
+ },
1808
+ dotFill,
1809
+ label
1810
+ };
1811
+ }
1812
+ var radioButtonContract = {
1813
+ id: "radiobutton",
1814
+ origin: "system",
1815
+ root: "root",
1816
+ parts: {
1817
+ root: ".radio-button",
1818
+ dot: ".radio-dot",
1819
+ dotFill: { selector: ".radio-dot-fill" },
1820
+ label: ".radio-label"
1821
+ },
1822
+ properties: [
1823
+ { paints: statePaints4("default", true) },
1824
+ { state: "hover", paints: statePaints4("hover", true) },
1825
+ { state: "active", paints: statePaints4("active", true) }
1826
+ ],
1827
+ states: [
1828
+ { state: "default" },
1829
+ {
1830
+ state: "hover",
1831
+ forceClass: "force-hover",
1832
+ paints: statePaints4("hover", false)
1833
+ },
1834
+ {
1835
+ state: "active",
1836
+ forceClass: "active",
1837
+ paints: statePaints4("active", false)
1838
+ }
1839
+ ],
1840
+ persistence: {
1841
+ cases: [
1842
+ {
1843
+ shape: "token",
1844
+ variable: "--radiobutton-default-dot-border-width",
1845
+ observe: { part: "dot", css: "borderTopWidth" }
1846
+ }
1847
+ ],
1848
+ resetVariable: "--radiobutton-default-dot-border-width"
1849
+ },
1850
+ theme: {
1851
+ theme: "halloween",
1852
+ changed: [
1853
+ "--radiobutton-default-dot-border-width",
1854
+ "--radiobutton-hover-dot-border-width",
1855
+ "--radiobutton-active-dot-border-width"
1856
+ ],
1857
+ unchanged: ["--radiobutton-default-label-font-size"],
1858
+ aliasedTo: {
1859
+ "--radiobutton-default-dot-border-width": "--border-width-4"
1860
+ },
1861
+ observe: { part: "dot", css: "borderTopWidth", variable: "--radiobutton-default-dot-border-width" }
1862
+ },
1863
+ interaction: {
1864
+ part: "root",
1865
+ role: "button",
1866
+ cases: [
1867
+ {
1868
+ name: "clicking a radio option focuses it",
1869
+ action: { kind: "click", part: "root" },
1870
+ expect: { kind: "focused", part: "root", value: true }
1871
+ }
1872
+ ]
1873
+ },
1874
+ sketch: {
1875
+ style: "pencil",
1876
+ parts: [{ part: "dot", fill: "--radiobutton-default-dot-fill", stroke: "--radiobutton-default-dot-border-color" }]
1877
+ }
1878
+ };
1879
+
1880
+ // src/testing/contracts/sectiondivider.ts
1881
+ var showOptionalContent = [
1882
+ { kind: "control", selector: 'label.element-show-toggle:has-text("Show description") input', check: true },
1883
+ { kind: "control", selector: 'label.element-show-toggle:has-text("Show eyebrow") input', check: true }
1884
+ ];
1885
+ function variantPaints(v) {
1886
+ return {
1887
+ root: {
1888
+ paddingTop: `--sectiondivider-${v}-padding`,
1889
+ borderRadius: `--sectiondivider-${v}-radius`,
1890
+ borderTopWidth: `--sectiondivider-${v}-border-width`,
1891
+ borderTopColor: `--sectiondivider-${v}-border`,
1892
+ boxShadow: `--sectiondivider-${v}-shadow`,
1893
+ backgroundImage: `--sectiondivider-${v}-background`
1894
+ },
1895
+ titleRow: { paddingTop: `--sectiondivider-${v}-title-padding` },
1896
+ title: {
1897
+ color: `--sectiondivider-${v}-title`,
1898
+ fontFamily: `--sectiondivider-${v}-title-font-family`,
1899
+ fontSize: `--sectiondivider-${v}-title-font-size`,
1900
+ fontWeight: `--sectiondivider-${v}-title-font-weight`,
1901
+ lineHeight: `--sectiondivider-${v}-title-line-height`,
1902
+ letterSpacing: `--sectiondivider-${v}-title-letter-spacing`
1903
+ },
1904
+ descriptionRow: { paddingTop: `--sectiondivider-${v}-description-padding` },
1905
+ description: {
1906
+ color: `--sectiondivider-${v}-description`,
1907
+ fontFamily: `--sectiondivider-${v}-description-font-family`,
1908
+ fontSize: `--sectiondivider-${v}-description-font-size`,
1909
+ fontWeight: `--sectiondivider-${v}-description-font-weight`,
1910
+ lineHeight: `--sectiondivider-${v}-description-line-height`
1911
+ },
1912
+ eyebrow: {
1913
+ paddingTop: `--sectiondivider-${v}-eyebrow-padding`,
1914
+ color: `--sectiondivider-${v}-eyebrow`,
1915
+ fontFamily: `--sectiondivider-${v}-eyebrow-font-family`,
1916
+ fontSize: `--sectiondivider-${v}-eyebrow-font-size`,
1917
+ fontWeight: `--sectiondivider-${v}-eyebrow-font-weight`,
1918
+ letterSpacing: `--sectiondivider-${v}-eyebrow-letter-spacing`
1919
+ },
1920
+ hairline: {
1921
+ backgroundColor: `--sectiondivider-${v}-hairline-color`,
1922
+ height: `--sectiondivider-${v}-hairline-thickness`
1923
+ }
1924
+ };
1925
+ }
1926
+ var sectionDividerContract = {
1927
+ id: "sectiondivider",
1928
+ origin: "system",
1929
+ view: { variant: "Large", setup: showOptionalContent },
1930
+ root: "root",
1931
+ parts: {
1932
+ root: ".section-divider",
1933
+ titleRow: ".title-row",
1934
+ title: ".divider-label",
1935
+ descriptionRow: ".description-row",
1936
+ description: ".divider-description",
1937
+ eyebrow: ".divider-eyebrow",
1938
+ hairline: ".sd-hairline"
1939
+ },
1940
+ properties: [
1941
+ { variant: "Large", setup: showOptionalContent, paints: variantPaints("lg") },
1942
+ { variant: "Medium", setup: showOptionalContent, paints: variantPaints("md") },
1943
+ { variant: "Small", setup: showOptionalContent, paints: variantPaints("sm") }
1944
+ ],
1945
+ states: {
1946
+ applicable: false,
1947
+ reason: "each variant is one full preset with one state, so the editor renders no state strip"
1948
+ },
1949
+ persistence: {
1950
+ cases: [
1951
+ {
1952
+ shape: "token",
1953
+ variable: "--sectiondivider-lg-hairline-color",
1954
+ observe: { part: "hairline", css: "backgroundColor" }
1955
+ },
1956
+ {
1957
+ shape: "gradient",
1958
+ variable: "--sectiondivider-lg-background",
1959
+ observe: { part: "root", css: "backgroundColor" }
1960
+ },
1961
+ {
1962
+ shape: "literal",
1963
+ control: '.sd-intrinsic-row:has(.property-label:text-is("alignment")) select',
1964
+ variable: "--sectiondivider-lg-align",
1965
+ observe: { part: "root", css: "textAlign" }
1966
+ }
1967
+ ],
1968
+ resetVariable: "--sectiondivider-lg-hairline-color"
1969
+ },
1970
+ theme: {
1971
+ theme: "halloween",
1972
+ changed: [
1973
+ "--sectiondivider-lg-radius",
1974
+ "--sectiondivider-lg-border-width",
1975
+ "--sectiondivider-lg-hairline-thickness",
1976
+ "--sectiondivider-lg-hairline-color"
1977
+ ],
1978
+ unchanged: [
1979
+ "--sectiondivider-lg-padding",
1980
+ "--sectiondivider-lg-shadow",
1981
+ "--sectiondivider-lg-title",
1982
+ "--sectiondivider-lg-border",
1983
+ "--sectiondivider-lg-title-font-size"
1984
+ ],
1985
+ aliasedTo: {
1986
+ "--sectiondivider-lg-radius": "--radius-none",
1987
+ "--sectiondivider-lg-border-width": "--border-width-3",
1988
+ "--sectiondivider-lg-hairline-thickness": "--border-width-3"
1989
+ },
1990
+ observe: { part: "root", css: "borderRadius", variable: "--sectiondivider-lg-radius" }
1991
+ },
1992
+ interaction: {
1993
+ applicable: false,
1994
+ reason: "a divider is a static banner: no interactive role, nothing focusable, no pointer behaviour"
1995
+ },
1996
+ sketch: {
1997
+ style: "pencil",
1998
+ parts: [{ part: "hairline", fill: "--sectiondivider-lg-hairline-color" }]
1999
+ }
2000
+ };
2001
+
2002
+ // src/testing/contracts/segmentedcontrol.ts
2003
+ var segmentedControlContract = {
2004
+ id: "segmentedcontrol",
2005
+ origin: "system",
2006
+ view: { state: "selected option" },
2007
+ root: "root",
2008
+ parts: {
2009
+ root: ".segmented-control",
2010
+ segment: ".segment",
2011
+ icon: ".segment i",
2012
+ selectedSegment: ".segment.selected",
2013
+ selectedIcon: ".segment.selected i",
2014
+ divider: ".segment-divider"
2015
+ },
2016
+ properties: [
2017
+ {
2018
+ state: "control bar",
2019
+ paints: {
2020
+ root: {
2021
+ backgroundColor: "--segmentedcontrol-bar-surface",
2022
+ borderTopColor: "--segmentedcontrol-bar-border",
2023
+ borderTopWidth: "--segmentedcontrol-bar-border-width",
2024
+ borderRadius: "--segmentedcontrol-bar-radius",
2025
+ columnGap: "--segmentedcontrol-bar-gap",
2026
+ paddingTop: "--segmentedcontrol-bar-padding"
2027
+ },
2028
+ divider: {
2029
+ backgroundColor: "--segmentedcontrol-divider-color",
2030
+ width: "--segmentedcontrol-divider-thickness",
2031
+ marginTop: "--segmentedcontrol-divider-inset"
2032
+ }
2033
+ }
2034
+ },
2035
+ {
2036
+ state: "option base",
2037
+ paints: {
2038
+ segment: {
2039
+ paddingTop: "--segmentedcontrol-option-padding",
2040
+ columnGap: "--segmentedcontrol-option-gap",
2041
+ borderRadius: "--segmentedcontrol-selected-radius"
2042
+ },
2043
+ icon: { fontSize: "--segmentedcontrol-option-icon-size" }
2044
+ }
2045
+ },
2046
+ {
2047
+ state: "default option",
2048
+ paints: {
2049
+ segment: {
2050
+ lineHeight: "--segmentedcontrol-option-text-line-height"
2051
+ }
2052
+ }
2053
+ },
2054
+ {
2055
+ state: "selected option",
2056
+ paints: { selectedSegment: { lineHeight: "--segmentedcontrol-selected-text-line-height" } }
2057
+ },
2058
+ {
2059
+ state: "disabled option",
2060
+ paints: { segment: { lineHeight: "--segmentedcontrol-disabled-text-line-height" } }
2061
+ },
2062
+ {
2063
+ state: "hover option",
2064
+ paints: { segment: { lineHeight: "--segmentedcontrol-option-hover-text-line-height" } }
2065
+ },
2066
+ {
2067
+ setup: [{ kind: "control", selector: ".preview-actions select", value: "small" }],
2068
+ paints: {
2069
+ root: { borderRadius: "--segmentedcontrol-bar-small-radius", paddingTop: "--segmentedcontrol-bar-small-padding" },
2070
+ segment: {
2071
+ borderRadius: "--segmentedcontrol-selected-small-radius",
2072
+ paddingTop: "--segmentedcontrol-option-small-padding",
2073
+ columnGap: "--segmentedcontrol-option-small-gap",
2074
+ fontSize: "--segmentedcontrol-option-small-text-font-size",
2075
+ lineHeight: "--segmentedcontrol-option-small-text-line-height"
2076
+ },
2077
+ icon: { fontSize: "--segmentedcontrol-option-small-icon-size" },
2078
+ divider: { width: "--segmentedcontrol-small-divider-thickness", marginTop: "--segmentedcontrol-small-divider-inset" }
2079
+ }
2080
+ }
2081
+ ],
2082
+ states: [
2083
+ { state: "control bar" },
2084
+ { state: "option base" },
2085
+ {
2086
+ state: "default option",
2087
+ paints: {
2088
+ icon: { color: "--segmentedcontrol-option-icon" },
2089
+ segment: {
2090
+ color: "--segmentedcontrol-option-text",
2091
+ fontFamily: "--segmentedcontrol-option-text-font-family",
2092
+ fontSize: "--segmentedcontrol-option-text-font-size",
2093
+ fontWeight: "--segmentedcontrol-option-text-font-weight"
2094
+ }
2095
+ }
2096
+ },
2097
+ {
2098
+ state: "selected option",
2099
+ paints: {
2100
+ selectedSegment: {
2101
+ backgroundColor: "--segmentedcontrol-selected-surface",
2102
+ outlineColor: "--segmentedcontrol-selected-border",
2103
+ outlineWidth: "--segmentedcontrol-selected-border-width",
2104
+ color: "--segmentedcontrol-selected-text",
2105
+ fontFamily: "--segmentedcontrol-selected-text-font-family",
2106
+ fontSize: "--segmentedcontrol-selected-text-font-size",
2107
+ fontWeight: "--segmentedcontrol-selected-text-font-weight"
2108
+ },
2109
+ selectedIcon: { color: "--segmentedcontrol-selected-icon" }
2110
+ }
2111
+ },
2112
+ {
2113
+ state: "hover option",
2114
+ paints: {
2115
+ segment: {
2116
+ backgroundColor: "--segmentedcontrol-option-hover-surface",
2117
+ color: "--segmentedcontrol-option-hover-text",
2118
+ fontFamily: "--segmentedcontrol-option-hover-text-font-family",
2119
+ fontSize: "--segmentedcontrol-option-hover-text-font-size",
2120
+ fontWeight: "--segmentedcontrol-option-hover-text-font-weight"
2121
+ },
2122
+ icon: { color: "--segmentedcontrol-option-hover-icon" }
2123
+ }
2124
+ },
2125
+ {
2126
+ state: "disabled option",
2127
+ attributes: { segment: { disabled: "" } },
2128
+ paints: {
2129
+ segment: {
2130
+ backgroundColor: "--segmentedcontrol-disabled-surface",
2131
+ color: "--segmentedcontrol-disabled-text",
2132
+ fontFamily: "--segmentedcontrol-disabled-text-font-family",
2133
+ fontSize: "--segmentedcontrol-disabled-text-font-size",
2134
+ fontWeight: "--segmentedcontrol-disabled-text-font-weight"
2135
+ },
2136
+ icon: { color: "--segmentedcontrol-disabled-icon" }
2137
+ }
2138
+ }
2139
+ ],
2140
+ uncovered: {
2141
+ "--segmentedcontrol-hover-tint": "consumed inside a background-image tint wash, never appearing verbatim in a computed style",
2142
+ "--segmentedcontrol-hover-tint-enabled": "the gate for the tint wash above, same limitation"
2143
+ },
2144
+ persistence: {
2145
+ cases: [
2146
+ {
2147
+ shape: "token",
2148
+ state: "control bar",
2149
+ variable: "--segmentedcontrol-bar-border-width",
2150
+ observe: { part: "root", css: "borderTopWidth" }
2151
+ }
2152
+ ],
2153
+ resetVariable: "--segmentedcontrol-bar-border-width"
2154
+ },
2155
+ theme: {
2156
+ theme: "halloween",
2157
+ changed: ["--segmentedcontrol-bar-border-width", "--segmentedcontrol-bar-radius", "--segmentedcontrol-option-padding"],
2158
+ unchanged: ["--segmentedcontrol-option-icon-size"],
2159
+ aliasedTo: {
2160
+ "--segmentedcontrol-bar-border-width": "--border-width-3",
2161
+ "--segmentedcontrol-bar-radius": "--radius-none"
2162
+ },
2163
+ observe: { part: "root", css: "borderTopWidth", variable: "--segmentedcontrol-bar-border-width" }
2164
+ },
2165
+ interaction: {
2166
+ part: "segment",
2167
+ role: "radio",
2168
+ cases: [
2169
+ {
2170
+ name: "clicking a segment checks it",
2171
+ action: { kind: "click", part: "segment" },
2172
+ expect: { kind: "attribute", part: "segment", name: "aria-checked", value: "true" }
2173
+ },
2174
+ {
2175
+ name: "a disabled control refuses focus",
2176
+ state: "disabled option",
2177
+ action: { kind: "click", part: "segment" },
2178
+ expect: { kind: "focused", part: "segment", value: false }
2179
+ }
2180
+ ]
2181
+ },
2182
+ sketch: {
2183
+ style: "pencil",
2184
+ parts: [
2185
+ { part: "root", fill: "--segmentedcontrol-bar-surface", stroke: "--segmentedcontrol-bar-border" },
2186
+ { part: "selectedSegment", fill: "--segmentedcontrol-selected-surface", stroke: "--segmentedcontrol-selected-border" }
2187
+ ]
2188
+ }
2189
+ };
2190
+
2191
+ // src/testing/contracts/sidenavigation.ts
2192
+ function sub(label) {
2193
+ return [{ kind: "control", selector: `.tabs-selectors.substrip .state-tab-btn:has-text("${label}")` }];
2194
+ }
2195
+ function titlePaints(s) {
2196
+ return {
2197
+ title: {
2198
+ backgroundColor: `--sidenavigation-title-${s}-surface`,
2199
+ borderTopColor: `--sidenavigation-title-${s}-border`,
2200
+ borderTopWidth: `--sidenavigation-title-${s}-border-width`,
2201
+ borderLeftColor: `--sidenavigation-title-${s}-accent`,
2202
+ borderLeftWidth: `--sidenavigation-title-${s}-accent-width`,
2203
+ paddingTop: `--sidenavigation-title-${s}-padding`
2204
+ },
2205
+ titleLabel: {
2206
+ color: `--sidenavigation-title-${s}-label`,
2207
+ fontFamily: `--sidenavigation-title-${s}-label-font-family`,
2208
+ fontSize: `--sidenavigation-title-${s}-label-font-size`,
2209
+ fontWeight: `--sidenavigation-title-${s}-label-font-weight`,
2210
+ lineHeight: `--sidenavigation-title-${s}-label-line-height`
2211
+ }
2212
+ };
2213
+ }
2214
+ function togglePaints(s) {
2215
+ return {
2216
+ toggle: {
2217
+ backgroundColor: `--sidenavigation-toggle-${s}-surface`,
2218
+ borderTopColor: `--sidenavigation-toggle-${s}-border`,
2219
+ borderTopWidth: `--sidenavigation-toggle-${s}-border-width`,
2220
+ borderRadius: `--sidenavigation-toggle-${s}-radius`,
2221
+ paddingTop: `--sidenavigation-toggle-${s}-padding`,
2222
+ color: `--sidenavigation-toggle-${s}-icon`
2223
+ },
2224
+ toggleIcon: { fontSize: `--sidenavigation-toggle-${s}-icon-size` }
2225
+ };
2226
+ }
2227
+ function sectionPaints(part, labelPart, s) {
2228
+ return {
2229
+ [part]: {
2230
+ backgroundColor: `--sidenavigation-section-${s}-surface`,
2231
+ borderLeftColor: `--sidenavigation-section-${s}-accent`,
2232
+ borderLeftWidth: `--sidenavigation-section-${s}-accent-width`
2233
+ },
2234
+ [labelPart]: {
2235
+ color: `--sidenavigation-section-${s}-text`,
2236
+ fontFamily: `--sidenavigation-section-${s}-text-font-family`,
2237
+ fontSize: `--sidenavigation-section-${s}-text-font-size`,
2238
+ fontWeight: `--sidenavigation-section-${s}-text-font-weight`,
2239
+ lineHeight: `--sidenavigation-section-${s}-text-line-height`
2240
+ }
2241
+ };
2242
+ }
2243
+ function itemPaints(part, s) {
2244
+ return {
2245
+ [part]: {
2246
+ backgroundColor: `--sidenavigation-item-${s}-surface`,
2247
+ borderLeftColor: `--sidenavigation-item-${s}-accent`,
2248
+ borderLeftWidth: `--sidenavigation-item-${s}-accent-width`,
2249
+ paddingTop: `--sidenavigation-item-${s}-padding`,
2250
+ color: `--sidenavigation-item-${s}-text`,
2251
+ fontFamily: `--sidenavigation-item-${s}-text-font-family`,
2252
+ fontSize: `--sidenavigation-item-${s}-text-font-size`,
2253
+ fontWeight: `--sidenavigation-item-${s}-text-font-weight`,
2254
+ lineHeight: `--sidenavigation-item-${s}-text-line-height`
2255
+ }
2256
+ };
2257
+ }
2258
+ function footerPaints(s) {
2259
+ return {
2260
+ footer: {
2261
+ backgroundColor: `--sidenavigation-footer-${s}-surface`,
2262
+ borderLeftColor: `--sidenavigation-footer-${s}-accent`,
2263
+ borderLeftWidth: `--sidenavigation-footer-${s}-accent-width`,
2264
+ paddingTop: `--sidenavigation-footer-${s}-padding`,
2265
+ columnGap: `--sidenavigation-footer-${s}-gap`,
2266
+ color: `--sidenavigation-footer-${s}-text`,
2267
+ fontFamily: `--sidenavigation-footer-${s}-text-font-family`,
2268
+ fontSize: `--sidenavigation-footer-${s}-text-font-size`,
2269
+ fontWeight: `--sidenavigation-footer-${s}-text-font-weight`,
2270
+ lineHeight: `--sidenavigation-footer-${s}-text-line-height`
2271
+ },
2272
+ footerIcon: {
2273
+ color: `--sidenavigation-footer-${s}-icon`,
2274
+ fontSize: `--sidenavigation-footer-${s}-icon-size`
2275
+ }
2276
+ };
2277
+ }
2278
+ var sideNavigationContract = {
2279
+ id: "sidenavigation",
2280
+ origin: "system",
2281
+ // Section has no currentPath-driven active state (unlike Item, whose
2282
+ // demo path lands inside section-1): it lights up only through the force
2283
+ // mechanism behind the "Active" sub-tab, so the inventory's one view has
2284
+ // to land there for `sectionActive` to resolve at all.
2285
+ view: { state: "Section", setup: sub("Active") },
2286
+ root: "root",
2287
+ parts: {
2288
+ root: ".sidenavigation",
2289
+ title: ".sn-title",
2290
+ titleLabel: ".sn-title-label",
2291
+ toggle: ".sn-toggle",
2292
+ toggleIcon: ".sn-toggle i",
2293
+ sectionWrap: ".sn-section",
2294
+ section: ".sn-section-header:not(.active)",
2295
+ sectionLabel: ".sn-section-header:not(.active) .section-label",
2296
+ sectionActive: ".sn-section-header.active",
2297
+ sectionLabelActive: ".sn-section-header.active .section-label",
2298
+ item: ".sn-item:not(.active)",
2299
+ itemActive: ".sn-item.active",
2300
+ footer: ".sn-footer",
2301
+ footerIcon: ".sn-footer i"
2302
+ },
2303
+ properties: [
2304
+ {
2305
+ state: "Panel",
2306
+ setup: [],
2307
+ paints: {
2308
+ root: {
2309
+ backgroundColor: "--sidenavigation-panel-surface",
2310
+ borderRightColor: "--sidenavigation-panel-border",
2311
+ borderRightWidth: "--sidenavigation-panel-border-width",
2312
+ paddingTop: "--sidenavigation-panel-padding"
2313
+ },
2314
+ item: { paddingLeft: "--sidenavigation-panel-item-padding" },
2315
+ footer: { marginTop: "--sidenavigation-panel-footer-gap" },
2316
+ sectionWrap: { marginTop: "--sidenavigation-panel-section-gap" }
2317
+ }
2318
+ },
2319
+ {
2320
+ state: "Title Block",
2321
+ setup: [],
2322
+ paints: {
2323
+ title: { columnGap: "--sidenavigation-title-gap", borderRadius: "--sidenavigation-title-radius" },
2324
+ titleLabel: {
2325
+ backgroundColor: "--sidenavigation-title-label-surface",
2326
+ borderTopColor: "--sidenavigation-title-label-border",
2327
+ borderTopWidth: "--sidenavigation-title-label-border-width",
2328
+ borderRadius: "--sidenavigation-title-label-radius",
2329
+ paddingTop: "--sidenavigation-title-label-padding"
2330
+ }
2331
+ }
2332
+ },
2333
+ { state: "Title", setup: [], paints: titlePaints("default") },
2334
+ { state: "Title", setup: sub("Hover"), paints: titlePaints("hover") },
2335
+ { state: "Title", setup: sub("Active"), paints: titlePaints("active") },
2336
+ { state: "Toggle", setup: [], paints: togglePaints("default") },
2337
+ { state: "Toggle", setup: sub("Hover"), paints: togglePaints("hover") },
2338
+ { state: "Section", setup: [], paints: sectionPaints("section", "sectionLabel", "default") },
2339
+ { state: "Section", setup: sub("Hover"), paints: sectionPaints("section", "sectionLabel", "hover") },
2340
+ { state: "Section", setup: sub("Active"), paints: sectionPaints("sectionActive", "sectionLabelActive", "active") },
2341
+ { state: "Item", setup: [], paints: itemPaints("item", "default") },
2342
+ { state: "Item", setup: sub("Hover"), paints: itemPaints("item", "hover") },
2343
+ { state: "Item", setup: [], paints: itemPaints("itemActive", "active") },
2344
+ { state: "Footer", setup: [], paints: footerPaints("default") },
2345
+ { state: "Footer", setup: sub("Hover"), paints: footerPaints("hover") },
2346
+ { state: "Footer", setup: sub("Active"), paints: footerPaints("active") }
2347
+ ],
2348
+ states: [
2349
+ { state: "Panel", setup: [] },
2350
+ { state: "Title", setup: [] },
2351
+ { state: "Title Block", setup: [] },
2352
+ { state: "Toggle", setup: [] },
2353
+ { state: "Section", setup: [] },
2354
+ { state: "Item", setup: [] },
2355
+ { state: "Footer", setup: [] },
2356
+ { state: "Animation", setup: [] }
2357
+ ],
2358
+ uncovered: {
2359
+ "--sidenavigation-open-duration": "consumed by a width transition, no probe covers duration",
2360
+ "--sidenavigation-open-easing": "consumed by a width transition, no probe covers an easing curve",
2361
+ "--sidenavigation-close-duration": "consumed by a width transition, no probe covers duration",
2362
+ "--sidenavigation-close-easing": "consumed by a width transition, no probe covers an easing curve",
2363
+ "--sidenavigation-hover-tint": "consumed inside a background-image tint wash, never appearing verbatim in a computed style",
2364
+ "--sidenavigation-hover-tint-enabled": "the gate for the tint wash above, same limitation"
2365
+ },
2366
+ persistence: {
2367
+ cases: [
2368
+ {
2369
+ shape: "token",
2370
+ state: "Panel",
2371
+ setup: [],
2372
+ variable: "--sidenavigation-panel-border-width",
2373
+ observe: { part: "root", css: "borderRightWidth" }
2374
+ }
2375
+ ],
2376
+ resetVariable: "--sidenavigation-panel-border-width"
2377
+ },
2378
+ theme: {
2379
+ theme: "halloween",
2380
+ changed: ["--sidenavigation-panel-border-width", "--sidenavigation-title-default-border-width"],
2381
+ unchanged: ["--sidenavigation-toggle-default-icon-size"],
2382
+ aliasedTo: {
2383
+ "--sidenavigation-panel-border-width": "--border-width-3",
2384
+ "--sidenavigation-title-default-border-width": "--border-width-3"
2385
+ },
2386
+ observe: { part: "root", css: "borderRightWidth", variable: "--sidenavigation-panel-border-width" }
2387
+ },
2388
+ interaction: {
2389
+ part: "toggle",
2390
+ role: "button",
2391
+ cases: [
2392
+ {
2393
+ name: "clicking the toggle collapses the rail",
2394
+ action: { kind: "click", part: "toggle" },
2395
+ expect: { kind: "attribute", part: "toggle", name: "aria-expanded", value: "false" }
2396
+ }
2397
+ ]
2398
+ },
2399
+ sketch: {
2400
+ style: "pencil",
2401
+ parts: [{ part: "root", fill: "--sidenavigation-panel-surface", stroke: "--sidenavigation-panel-border" }]
2402
+ }
2403
+ };
2404
+
2405
+ // src/testing/contracts/slider.ts
2406
+ function variantPaints2(v) {
2407
+ return {
2408
+ track: {
2409
+ backgroundColor: `--slider-${v}-track-surface`,
2410
+ borderTopColor: `--slider-${v}-track-border`,
2411
+ borderTopWidth: `--slider-${v}-track-border-width`,
2412
+ borderRadius: `--slider-${v}-track-radius`,
2413
+ height: `--slider-${v}-track-height`
2414
+ },
2415
+ fill: { backgroundColor: `--slider-${v}-fill` },
2416
+ thumb: {
2417
+ backgroundColor: `--slider-${v}-thumb-surface`,
2418
+ borderTopColor: `--slider-${v}-thumb-border`,
2419
+ borderTopWidth: `--slider-${v}-thumb-border-width`,
2420
+ borderRadius: `--slider-${v}-thumb-radius`,
2421
+ width: `--slider-${v}-thumb-size`,
2422
+ boxShadow: `--slider-${v}-thumb-shadow`
2423
+ }
2424
+ };
2425
+ }
2426
+ function variantStates(v, variant) {
2427
+ return [
2428
+ {
2429
+ variant,
2430
+ state: "default",
2431
+ attributes: { input: { disabled: null } },
2432
+ paints: { thumb: { backgroundColor: `--slider-${v}-thumb-surface` } }
2433
+ },
2434
+ {
2435
+ variant,
2436
+ state: "hover",
2437
+ forceClass: "force-hover",
2438
+ paints: {
2439
+ thumb: {
2440
+ backgroundColor: `--slider-${v}-hover-thumb-surface`,
2441
+ borderTopColor: `--slider-${v}-hover-thumb-border`
2442
+ }
2443
+ }
2444
+ },
2445
+ {
2446
+ variant,
2447
+ state: "disabled",
2448
+ attributes: { input: { disabled: "" } },
2449
+ paints: {
2450
+ track: { backgroundColor: `--slider-${v}-disabled-track-surface` },
2451
+ fill: { backgroundColor: `--slider-${v}-disabled-fill` },
2452
+ thumb: {
2453
+ backgroundColor: `--slider-${v}-disabled-thumb-surface`,
2454
+ borderTopColor: `--slider-${v}-disabled-thumb-border`
2455
+ }
2456
+ }
2457
+ }
2458
+ ];
2459
+ }
2460
+ var sliderContract = {
2461
+ id: "slider",
2462
+ origin: "system",
2463
+ view: { variant: "Single" },
2464
+ root: "root",
2465
+ parts: {
2466
+ root: ".slider",
2467
+ track: ".slider-track",
2468
+ fill: ".slider-fill",
2469
+ thumb: ".cap",
2470
+ input: "input.thumb",
2471
+ label: ".slider-label > span:not(.slider-value)",
2472
+ value: ".slider-value"
2473
+ },
2474
+ properties: [
2475
+ {
2476
+ variant: "Single",
2477
+ paints: {
2478
+ ...variantPaints2("single"),
2479
+ root: { rowGap: "--slider-label-gap" },
2480
+ label: {
2481
+ color: "--slider-label",
2482
+ fontFamily: "--slider-label-font-family",
2483
+ fontSize: "--slider-label-font-size",
2484
+ fontWeight: "--slider-label-font-weight",
2485
+ lineHeight: "--slider-label-line-height"
2486
+ },
2487
+ value: {
2488
+ color: "--slider-value",
2489
+ fontFamily: "--slider-value-font-family",
2490
+ fontSize: "--slider-value-font-size",
2491
+ fontWeight: "--slider-value-font-weight",
2492
+ lineHeight: "--slider-value-line-height"
2493
+ }
2494
+ }
2495
+ },
2496
+ { variant: "Range", paints: variantPaints2("range") }
2497
+ ],
2498
+ states: [
2499
+ ...variantStates("single", "Single"),
2500
+ ...variantStates("range", "Range")
2501
+ ],
2502
+ persistence: {
2503
+ cases: [
2504
+ {
2505
+ shape: "token",
2506
+ variable: "--slider-single-track-surface",
2507
+ observe: { part: "track", css: "backgroundColor" }
2508
+ },
2509
+ {
2510
+ shape: "opacity",
2511
+ variable: "--slider-single-fill",
2512
+ observe: { part: "fill", css: "backgroundColor" }
2513
+ }
2514
+ ],
2515
+ resetVariable: "--slider-single-track-surface"
2516
+ },
2517
+ theme: {
2518
+ theme: "ocean",
2519
+ changed: [
2520
+ "--slider-single-track-surface",
2521
+ "--slider-single-fill",
2522
+ "--slider-single-thumb-surface"
2523
+ ],
2524
+ unchanged: [
2525
+ "--slider-single-track-height",
2526
+ "--slider-single-track-radius",
2527
+ "--slider-single-thumb-size",
2528
+ "--slider-single-track-border-width"
2529
+ ],
2530
+ aliasedTo: {
2531
+ "--slider-single-track-surface": "--surface-neutral-lowest",
2532
+ "--slider-single-fill": "--surface-brand-high"
2533
+ },
2534
+ observe: { part: "track", css: "backgroundColor", variable: "--slider-single-track-surface" }
2535
+ },
2536
+ interaction: {
2537
+ part: "input",
2538
+ role: "slider",
2539
+ cases: [
2540
+ {
2541
+ name: "arrow right raises the value",
2542
+ action: { kind: "press", part: "input", key: "ArrowRight" },
2543
+ expect: { kind: "valueMoves", part: "input", direction: "up" }
2544
+ },
2545
+ {
2546
+ name: "arrow left lowers the value",
2547
+ action: { kind: "press", part: "input", key: "ArrowLeft" },
2548
+ expect: { kind: "valueMoves", part: "input", direction: "down" }
2549
+ },
2550
+ {
2551
+ name: "Home takes the value to the floor",
2552
+ action: { kind: "press", part: "input", key: "Home" },
2553
+ expect: { kind: "valueBecomes", part: "input", value: "0" }
2554
+ },
2555
+ {
2556
+ name: "dragging the thumb along the track raises the value",
2557
+ action: { kind: "dragTo", part: "thumb", along: "track", fraction: 0.85 },
2558
+ expect: { kind: "valueMoves", part: "input", direction: "up" }
2559
+ },
2560
+ {
2561
+ name: "taking hold of the thumb focuses the slider",
2562
+ action: { kind: "dragTo", part: "thumb", along: "track", fraction: 0.5 },
2563
+ expect: { kind: "focused", part: "input", value: true }
2564
+ },
2565
+ {
2566
+ name: "a disabled slider ignores the arrow keys",
2567
+ state: "disabled",
2568
+ action: { kind: "press", part: "input", key: "ArrowRight" },
2569
+ expect: { kind: "valueHolds", part: "input" }
2570
+ },
2571
+ {
2572
+ name: "a disabled slider refuses focus",
2573
+ state: "disabled",
2574
+ action: { kind: "dragTo", part: "thumb", along: "track", fraction: 0.5 },
2575
+ expect: { kind: "focused", part: "input", value: false }
2576
+ }
2577
+ ]
2578
+ },
2579
+ sketch: {
2580
+ style: "pencil",
2581
+ parts: [
2582
+ {
2583
+ part: "track",
2584
+ fill: "--slider-single-track-surface",
2585
+ stroke: "--slider-single-track-border"
2586
+ },
2587
+ { part: "fill", fill: "--slider-single-fill" }
2588
+ ]
2589
+ }
2590
+ };
2591
+
2592
+ // src/testing/contracts/tabbar.ts
2593
+ function tabPaints(part, iconPart, s, full) {
2594
+ const tab = {
2595
+ backgroundColor: `--tabbar-${s}-surface`,
2596
+ borderTopColor: `--tabbar-${s}-tab-border-color`,
2597
+ borderTopWidth: `--tabbar-${s}-tab-border-width`,
2598
+ borderBottomColor: `--tabbar-${s}-border`,
2599
+ borderBottomWidth: `--tabbar-${s}-indicator-width`,
2600
+ borderTopLeftRadius: `--tabbar-${s}-tab-top-radius`,
2601
+ borderBottomLeftRadius: `--tabbar-${s}-tab-bottom-radius`,
2602
+ paddingTop: `--tabbar-${s}-padding`,
2603
+ color: `--tabbar-${s}-text`,
2604
+ fontFamily: `--tabbar-${s}-text-font-family`,
2605
+ fontSize: `--tabbar-${s}-text-font-size`,
2606
+ fontWeight: `--tabbar-${s}-text-font-weight`
2607
+ };
2608
+ if (full) tab.lineHeight = `--tabbar-${s}-text-line-height`;
2609
+ return {
2610
+ [part]: tab,
2611
+ [iconPart]: { fontSize: `--tabbar-${s}-icon-size` }
2612
+ };
2613
+ }
2614
+ var tabBarContract = {
2615
+ id: "tabbar",
2616
+ origin: "system",
2617
+ root: "root",
2618
+ parts: {
2619
+ root: ".tab-bar",
2620
+ tab: ".tab:not(.active):not(:disabled)",
2621
+ icon: ".tab:not(.active):not(:disabled) i",
2622
+ activeTab: ".tab.active",
2623
+ activeIcon: ".tab.active i",
2624
+ disabledTab: ".tab:disabled",
2625
+ disabledIcon: ".tab:disabled i",
2626
+ // Position-stable: clicking `tab` (".tab:not(.active)") makes that very
2627
+ // tab active, which would invalidate that selector out from under a
2628
+ // focus check re-resolved after the click.
2629
+ secondTab: ".tab:nth-of-type(2)"
2630
+ },
2631
+ properties: [
2632
+ {
2633
+ state: "bar",
2634
+ paints: {
2635
+ root: {
2636
+ borderBottomColor: "--tabbar-bar-divider",
2637
+ borderBottomWidth: "--tabbar-bar-divider-thickness",
2638
+ marginTop: "--tabbar-bar-top-margin",
2639
+ paddingBottom: "--tabbar-bar-bottom-padding",
2640
+ marginBottom: "--tabbar-bar-bottom-margin",
2641
+ columnGap: "--tabbar-tab-gap"
2642
+ }
2643
+ }
2644
+ },
2645
+ { state: "default tab", paints: tabPaints("tab", "icon", "default", true) },
2646
+ { state: "hover tab", paints: tabPaints("tab", "icon", "hover", true) },
2647
+ { state: "active tab", paints: tabPaints("activeTab", "activeIcon", "active", true) },
2648
+ { state: "disabled tab", paints: tabPaints("disabledTab", "disabledIcon", "disabled", true) }
2649
+ ],
2650
+ states: [
2651
+ { state: "bar" },
2652
+ { state: "default tab", paints: tabPaints("tab", "icon", "default", false) },
2653
+ { state: "hover tab", paints: tabPaints("tab", "icon", "hover", false) },
2654
+ { state: "active tab", paints: tabPaints("activeTab", "activeIcon", "active", false) },
2655
+ {
2656
+ state: "disabled tab",
2657
+ attributes: { disabledTab: { disabled: "" } },
2658
+ paints: tabPaints("disabledTab", "disabledIcon", "disabled", false)
2659
+ }
2660
+ ],
2661
+ uncovered: {
2662
+ "--tabbar-hover-tint": "consumed inside a background-image tint wash, never appearing verbatim in a computed style",
2663
+ "--tabbar-hover-tint-enabled": "the gate for the tint wash above, same limitation"
2664
+ },
2665
+ persistence: {
2666
+ cases: [
2667
+ {
2668
+ shape: "token",
2669
+ state: "default tab",
2670
+ variable: "--tabbar-default-tab-border-width",
2671
+ observe: { part: "tab", css: "borderTopWidth" }
2672
+ }
2673
+ ],
2674
+ resetVariable: "--tabbar-default-tab-border-width"
2675
+ },
2676
+ theme: {
2677
+ theme: "halloween",
2678
+ // The indicator width is the canary for re-migration replay: the tabbar
2679
+ // pair (2026-05-29-tabbar-indicator-thickness-to-per-state-width,
2680
+ // 2026-09-07-stroke-role-renames) rewrites it to --border-width-2 if a
2681
+ // reader migrates a theme-embedded config off a stamp of 0 instead of the
2682
+ // theme's componentSchemaVersion.
2683
+ changed: [
2684
+ "--tabbar-default-tab-border-width",
2685
+ "--tabbar-default-padding",
2686
+ "--tabbar-default-indicator-width"
2687
+ ],
2688
+ unchanged: ["--tabbar-default-icon-size"],
2689
+ aliasedTo: {
2690
+ "--tabbar-default-tab-border-width": "--border-width-2",
2691
+ "--tabbar-default-indicator-width": "--border-width-4"
2692
+ },
2693
+ observe: { part: "tab", css: "borderTopWidth", variable: "--tabbar-default-tab-border-width" }
2694
+ },
2695
+ interaction: {
2696
+ part: "secondTab",
2697
+ role: "button",
2698
+ cases: [
2699
+ {
2700
+ name: "clicking a tab focuses it",
2701
+ action: { kind: "click", part: "secondTab" },
2702
+ expect: { kind: "focused", part: "secondTab", value: true }
2703
+ },
2704
+ {
2705
+ name: "a disabled tab refuses focus",
2706
+ state: "disabled tab",
2707
+ action: { kind: "click", part: "disabledTab" },
2708
+ expect: { kind: "focused", part: "disabledTab", value: false }
2709
+ }
2710
+ ]
2711
+ },
2712
+ sketch: {
2713
+ style: "pencil",
2714
+ parts: [
2715
+ { part: "tab", fill: "--tabbar-default-surface", stroke: "--tabbar-default-border" },
2716
+ { part: "activeTab", fill: "--tabbar-active-surface", stroke: "--tabbar-active-border" }
2717
+ ]
2718
+ }
2719
+ };
2720
+
2721
+ // src/testing/contracts/table.ts
2722
+ var tableContract = {
2723
+ id: "table",
2724
+ origin: "system",
2725
+ root: "wrapper",
2726
+ parts: {
2727
+ wrapper: ".table-wrapper",
2728
+ th: ".table-wrapper th",
2729
+ td: ".table-wrapper td"
2730
+ },
2731
+ properties: [
2732
+ {
2733
+ paints: {
2734
+ wrapper: {
2735
+ backgroundColor: "--table-default-surface",
2736
+ borderTopColor: "--table-default-border",
2737
+ borderTopWidth: "--table-default-border-width",
2738
+ borderRadius: "--table-default-radius",
2739
+ boxShadow: "--table-default-shadow"
2740
+ },
2741
+ th: {
2742
+ backgroundColor: "--table-default-header-surface",
2743
+ color: "--table-default-header-text",
2744
+ fontFamily: "--table-default-header-font-family",
2745
+ fontSize: "--table-default-header-font-size",
2746
+ fontWeight: "--table-default-header-font-weight",
2747
+ lineHeight: "--table-default-header-line-height",
2748
+ borderBottomColor: "--table-default-header-divider",
2749
+ borderBottomWidth: "--table-default-header-divider-width",
2750
+ paddingTop: "--table-default-header-padding",
2751
+ borderRightColor: "--table-default-column-divider",
2752
+ borderRightWidth: "--table-default-column-divider-width"
2753
+ },
2754
+ td: {
2755
+ backgroundColor: "--table-default-row-surface",
2756
+ color: "--table-default-cell-text",
2757
+ fontFamily: "--table-default-cell-font-family",
2758
+ fontSize: "--table-default-cell-font-size",
2759
+ fontWeight: "--table-default-cell-font-weight",
2760
+ lineHeight: "--table-default-cell-line-height",
2761
+ paddingTop: "--table-default-cell-padding",
2762
+ borderBottomColor: "--table-default-row-divider",
2763
+ borderBottomWidth: "--table-default-row-divider-width"
2764
+ }
2765
+ }
2766
+ }
2767
+ ],
2768
+ states: [
2769
+ { state: "wrapper" },
2770
+ { state: "header" },
2771
+ { state: "cell" },
2772
+ { state: "row" },
2773
+ { state: "column" }
2774
+ ],
2775
+ uncovered: {
2776
+ "--table-default-row-stripe-surface": "layered as a background-image on every even row; a probe cannot pin a single verbatim computed value to that selector"
2777
+ },
2778
+ persistence: {
2779
+ cases: [
2780
+ {
2781
+ shape: "token",
2782
+ variable: "--table-default-border-width",
2783
+ observe: { part: "wrapper", css: "borderTopWidth" }
2784
+ }
2785
+ ],
2786
+ resetVariable: "--table-default-border-width"
2787
+ },
2788
+ theme: {
2789
+ theme: "halloween",
2790
+ changed: [
2791
+ "--table-default-border-width",
2792
+ "--table-default-radius",
2793
+ "--table-default-header-divider-width",
2794
+ "--table-default-header-padding"
2795
+ ],
2796
+ unchanged: ["--table-default-header-font-size"],
2797
+ aliasedTo: {
2798
+ "--table-default-border-width": "--border-width-4",
2799
+ "--table-default-radius": "--radius-none"
2800
+ },
2801
+ observe: { part: "wrapper", css: "borderTopWidth", variable: "--table-default-border-width" }
2802
+ },
2803
+ interaction: {
2804
+ applicable: false,
2805
+ reason: "a styled table wrapper carries no interactive role"
2806
+ },
2807
+ sketch: {
2808
+ style: "pencil",
2809
+ parts: [{ part: "wrapper", fill: "--table-default-surface", stroke: "--table-default-border" }]
2810
+ }
2811
+ };
2812
+
2813
+ // src/testing/contracts/toggle.ts
2814
+ var defaultPaints = {
2815
+ root: { columnGap: "--toggle-gap" },
2816
+ track: {
2817
+ backgroundColor: "--toggle-track-surface",
2818
+ borderTopColor: "--toggle-track-border",
2819
+ borderTopWidth: "--toggle-track-border-width",
2820
+ borderRadius: "--toggle-track-radius"
2821
+ },
2822
+ thumb: {
2823
+ backgroundColor: "--toggle-thumb-surface",
2824
+ borderTopColor: "--toggle-thumb-border",
2825
+ width: "--toggle-thumb-size",
2826
+ left: "--toggle-track-padding"
2827
+ },
2828
+ label: {
2829
+ color: "--toggle-label-text",
2830
+ fontFamily: "--toggle-label-font-family",
2831
+ fontSize: "--toggle-label-font-size",
2832
+ fontWeight: "--toggle-label-font-weight"
2833
+ }
2834
+ };
2835
+ var toggleContract = {
2836
+ id: "toggle",
2837
+ origin: "system",
2838
+ root: "root",
2839
+ parts: {
2840
+ root: ".toggle",
2841
+ track: ".track",
2842
+ thumb: ".thumb",
2843
+ label: ".label"
2844
+ },
2845
+ properties: [{ paints: defaultPaints }],
2846
+ states: [
2847
+ {
2848
+ state: "default",
2849
+ attributes: { root: { "aria-checked": "false" } }
2850
+ },
2851
+ {
2852
+ state: "hover",
2853
+ forceClass: "force-hover",
2854
+ paints: {
2855
+ track: { backgroundColor: "--toggle-hover-track-surface" },
2856
+ thumb: { backgroundColor: "--toggle-hover-thumb-surface" }
2857
+ }
2858
+ },
2859
+ {
2860
+ state: "on",
2861
+ attributes: { root: { "aria-checked": "true" } },
2862
+ paints: {
2863
+ track: { backgroundColor: "--toggle-on-track-surface", borderTopColor: "--toggle-on-track-border" },
2864
+ thumb: { backgroundColor: "--toggle-on-thumb-surface", borderTopColor: "--toggle-on-thumb-border" }
2865
+ }
2866
+ },
2867
+ {
2868
+ state: "on hover",
2869
+ forceClass: "force-hover",
2870
+ attributes: { root: { "aria-checked": "true" } },
2871
+ paints: {
2872
+ track: { backgroundColor: "--toggle-on-hover-track-surface" },
2873
+ thumb: { backgroundColor: "--toggle-on-hover-thumb-surface" }
2874
+ }
2875
+ },
2876
+ {
2877
+ state: "disabled",
2878
+ attributes: { root: { disabled: "" } },
2879
+ paints: {
2880
+ track: { backgroundColor: "--toggle-disabled-track-surface" },
2881
+ thumb: { backgroundColor: "--toggle-disabled-thumb-surface" },
2882
+ label: { color: "--toggle-disabled-label-text" }
2883
+ }
2884
+ }
2885
+ ],
2886
+ persistence: {
2887
+ cases: [
2888
+ {
2889
+ shape: "token",
2890
+ variable: "--toggle-track-border-width",
2891
+ observe: { part: "track", css: "borderTopWidth" }
2892
+ }
2893
+ ],
2894
+ resetVariable: "--toggle-track-border-width"
2895
+ },
2896
+ theme: {
2897
+ theme: "halloween",
2898
+ changed: ["--toggle-track-border-width", "--toggle-track-radius"],
2899
+ unchanged: ["--toggle-thumb-size", "--toggle-gap"],
2900
+ aliasedTo: {
2901
+ "--toggle-track-border-width": "--border-width-3",
2902
+ "--toggle-track-radius": "--radius-none"
2903
+ },
2904
+ observe: { part: "track", css: "borderTopWidth", variable: "--toggle-track-border-width" }
2905
+ },
2906
+ interaction: {
2907
+ part: "root",
2908
+ role: "switch",
2909
+ // No activation case: Toggle.svelte is controlled (`onclick` only calls
2910
+ // `onchange?.(!checked)`, it never flips `checked` itself), and
2911
+ // ToggleEditor.svelte's preview drives `checked` off the state tab
2912
+ // without wiring `onchange` back to it. Clicking the real control in this
2913
+ // demo has no observable effect on `aria-checked`, so an activation case
2914
+ // would assert a no-op rather than the switch's behavior.
2915
+ cases: [
2916
+ {
2917
+ name: "clicking a toggle focuses it",
2918
+ action: { kind: "click", part: "root" },
2919
+ expect: { kind: "focused", part: "root", value: true }
2920
+ },
2921
+ {
2922
+ name: "a disabled toggle refuses focus",
2923
+ state: "disabled",
2924
+ action: { kind: "click", part: "root" },
2925
+ expect: { kind: "focused", part: "root", value: false }
2926
+ }
2927
+ ]
2928
+ },
2929
+ sketch: {
2930
+ style: "pencil",
2931
+ parts: [{ part: "track", fill: "--toggle-track-surface", stroke: "--toggle-track-border" }]
2932
+ }
2933
+ };
2934
+
2935
+ // src/testing/contracts/tooltip.ts
2936
+ var tooltipContract = {
2937
+ id: "tooltip",
2938
+ origin: "system",
2939
+ root: "root",
2940
+ parts: {
2941
+ root: ".tooltip",
2942
+ arrow: { selector: ".tooltip", pseudo: "::after" }
2943
+ },
2944
+ properties: [
2945
+ {
2946
+ paints: {
2947
+ root: {
2948
+ backgroundColor: "--tooltip-surface",
2949
+ color: "--tooltip-text",
2950
+ borderTopColor: "--tooltip-border",
2951
+ borderTopWidth: "--tooltip-border-width",
2952
+ borderRadius: "--tooltip-radius",
2953
+ paddingTop: "--tooltip-padding",
2954
+ boxShadow: "--tooltip-shadow",
2955
+ fontFamily: "--tooltip-text-font-family",
2956
+ fontSize: "--tooltip-text-font-size",
2957
+ fontWeight: "--tooltip-text-font-weight",
2958
+ lineHeight: "--tooltip-text-line-height"
2959
+ },
2960
+ arrow: {
2961
+ borderRightColor: "--tooltip-border",
2962
+ borderRightWidth: "--tooltip-border-width"
2963
+ }
2964
+ }
2965
+ }
2966
+ ],
2967
+ states: {
2968
+ applicable: false,
2969
+ reason: "the editor renders one token group (tooltip) with no state strip"
2970
+ },
2971
+ persistence: {
2972
+ cases: [
2973
+ {
2974
+ shape: "token",
2975
+ variable: "--tooltip-border",
2976
+ observe: { part: "root", css: "borderTopColor" }
2977
+ }
2978
+ ],
2979
+ resetVariable: "--tooltip-border"
2980
+ },
2981
+ theme: {
2982
+ theme: "halloween",
2983
+ changed: ["--tooltip-border-width", "--tooltip-padding", "--tooltip-radius"],
2984
+ unchanged: ["--tooltip-text-font-size"],
2985
+ aliasedTo: {
2986
+ "--tooltip-border-width": "--border-width-3",
2987
+ "--tooltip-radius": "--radius-none"
2988
+ },
2989
+ observe: { part: "root", css: "borderTopWidth", variable: "--tooltip-border-width" }
2990
+ },
2991
+ interaction: {
2992
+ applicable: false,
2993
+ reason: "a hint carries no interactive role; visibility follows the trigger element's hover or focus, which this component does not own"
2994
+ },
2995
+ sketch: {
2996
+ style: "pencil",
2997
+ parts: [{ part: "root", fill: "--tooltip-surface" }]
2998
+ }
2999
+ };
3000
+
3001
+ // src/testing/contracts/index.ts
3002
+ var shippedContracts = [
3003
+ badgeContract,
3004
+ buttonContract,
3005
+ calloutContract,
3006
+ cardContract,
3007
+ codeSnippetContract,
3008
+ collapsibleSectionContract,
3009
+ cornerBadgeContract,
3010
+ dialogContract,
3011
+ iconButtonContract,
3012
+ imageContract,
3013
+ imageLightboxContract,
3014
+ inlineEditActionsContract,
3015
+ inputContract,
3016
+ menuSelectContract,
3017
+ notificationContract,
3018
+ panelContract,
3019
+ progressBarContract,
3020
+ radioButtonContract,
3021
+ sectionDividerContract,
3022
+ segmentedControlContract,
3023
+ sideNavigationContract,
3024
+ sliderContract,
3025
+ tabBarContract,
3026
+ tableContract,
3027
+ toggleContract,
3028
+ tooltipContract
3029
+ ];
3030
+ var CONTRACTS_MODULE_ENV = "LIVE_TOKENS_CONTRACTS_MODULE";
3031
+ async function loadCustomContracts() {
3032
+ const modulePath = process.env[CONTRACTS_MODULE_ENV];
3033
+ if (!modulePath) return [];
3034
+ const mod = await import(pathToFileURL(path.resolve(modulePath)).href);
3035
+ const value = mod.default ?? mod.contracts;
3036
+ if (!Array.isArray(value)) {
3037
+ throw new Error(
3038
+ `${CONTRACTS_MODULE_ENV}=${modulePath} does not export a contracts array (default export or a named "contracts" export)`
3039
+ );
3040
+ }
3041
+ return value;
3042
+ }
3043
+ async function allContracts(contracts = shippedContracts) {
3044
+ const custom = await loadCustomContracts();
3045
+ return custom.length > 0 ? [...contracts, ...custom] : contracts;
3046
+ }
3047
+ async function selectedContracts(contracts = shippedContracts) {
3048
+ const all = await allContracts(contracts);
3049
+ const requested = process.env.LIVE_TOKENS_COMPONENT;
3050
+ return requested ? all.filter((contract) => contract.id === requested) : all;
3051
+ }
3052
+
3053
+ export {
3054
+ ContractViolation,
3055
+ isInapplicable,
3056
+ requiresInteraction,
3057
+ partLocator,
3058
+ shippedContracts,
3059
+ CONTRACTS_MODULE_ENV,
3060
+ allContracts,
3061
+ selectedContracts
3062
+ };
3063
+ //# sourceMappingURL=chunk-LXR3MN6N.js.map