@motion-proto/live-tokens 0.97.0 → 0.99.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 (191) hide show
  1. package/.claude/skills/live-tokens-check-compliance/SKILL.md +5 -0
  2. package/.claude/skills/live-tokens-create-component/SKILL.md +4 -3
  3. package/.claude/skills/live-tokens-create-component/references/contract-tests.md +71 -11
  4. package/CHANGELOG.md +339 -0
  5. package/README.md +2 -2
  6. package/bin/check-component.mjs +8 -0
  7. package/bin/check-page.mjs +6 -0
  8. package/bin/cli.mjs +36 -6
  9. package/bin/contractRunner.mjs +144 -19
  10. package/bin/lib/findings.mjs +20 -4
  11. package/bin/rules/a11y.mjs +53 -0
  12. package/dist-plugin/{chunk-A4TBP4MM.js → chunk-SLICH4IH.js} +2 -2
  13. package/dist-plugin/index.cjs +204 -3
  14. package/dist-plugin/index.js +306 -105
  15. package/dist-plugin/setColors/index.js +1 -1
  16. package/package.json +47 -2
  17. package/src/demo/LabeledSelect.svelte +1 -1
  18. package/src/editor/bootstrap.ts +5 -1
  19. package/src/editor/component-editor/BadgeEditor.svelte +13 -0
  20. package/src/editor/component-editor/ButtonEditor.svelte +33 -2
  21. package/src/editor/component-editor/CalloutEditor.svelte +13 -0
  22. package/src/editor/component-editor/CardEditor.svelte +12 -2
  23. package/src/editor/component-editor/CodeSnippetEditor.svelte +11 -1
  24. package/src/editor/component-editor/CollapsibleSectionEditor.svelte +18 -1
  25. package/src/editor/component-editor/CornerBadgeEditor.svelte +13 -0
  26. package/src/editor/component-editor/DialogEditor.svelte +15 -0
  27. package/src/editor/component-editor/IconButtonEditor.svelte +29 -1
  28. package/src/editor/component-editor/ImageEditor.svelte +20 -9
  29. package/src/editor/component-editor/ImageLightboxEditor.svelte +44 -0
  30. package/src/editor/component-editor/InlineEditActionsEditor.svelte +13 -1
  31. package/src/editor/component-editor/InputEditor.svelte +23 -0
  32. package/src/editor/component-editor/MenuSelectEditor.svelte +27 -1
  33. package/src/editor/component-editor/NotificationEditor.svelte +15 -0
  34. package/src/editor/component-editor/PanelEditor.svelte +1 -1
  35. package/src/editor/component-editor/ProgressBarEditor.svelte +13 -0
  36. package/src/editor/component-editor/RadioButtonEditor.svelte +20 -1
  37. package/src/editor/component-editor/SectionDividerEditor.svelte +40 -13
  38. package/src/editor/component-editor/SegmentedControlEditor.svelte +29 -1
  39. package/src/editor/component-editor/SideNavigationEditor.svelte +40 -1
  40. package/src/editor/component-editor/SlidePagerEditor.svelte +20 -0
  41. package/src/editor/component-editor/SliderEditor.svelte +33 -0
  42. package/src/editor/component-editor/TabBarEditor.svelte +23 -2
  43. package/src/editor/component-editor/TableEditor.svelte +14 -0
  44. package/src/editor/component-editor/ToggleEditor.svelte +17 -0
  45. package/src/editor/component-editor/TooltipEditor.svelte +11 -2
  46. package/src/editor/component-editor/VideoFrameEditor.svelte +8 -0
  47. package/src/editor/component-editor/VideoLightboxEditor.svelte +10 -0
  48. package/src/editor/component-editor/registration.ts +2 -1
  49. package/src/editor/component-editor/registry.ts +69 -27
  50. package/src/editor/component-editor/scaffolding/DemoHeader.svelte +2 -0
  51. package/src/editor/component-editor/scaffolding/StateBlock.svelte +19 -8
  52. package/src/editor/component-editor/scaffolding/TokenLayout.svelte +3 -0
  53. package/src/editor/component-editor/scaffolding/VariantGroup.svelte +70 -3
  54. package/src/editor/component-editor/scaffolding/editorContext.ts +7 -0
  55. package/src/editor/core/a11y/a11yReport.ts +164 -0
  56. package/src/editor/core/a11y/a11yReportTypes.ts +77 -0
  57. package/src/editor/core/a11y/contrastFailures.ts +22 -0
  58. package/src/editor/core/a11y/contrastFixes.ts +41 -0
  59. package/src/editor/core/components/contrastPairRegistry.ts +18 -0
  60. package/src/editor/core/palettes/contrast.ts +44 -0
  61. package/src/editor/core/palettes/contrastPairs.ts +82 -0
  62. package/src/editor/core/palettes/resolveColor.ts +77 -0
  63. package/src/editor/core/store/editorStore.ts +4 -0
  64. package/src/editor/core/store/editorViewStore.ts +2 -2
  65. package/src/editor/core/store/unsavedEdits.ts +10 -0
  66. package/src/editor/core/themes/liveStateStream.ts +7 -1
  67. package/src/editor/core/themes/parsers/gradient.ts +5 -2
  68. package/src/editor/docs/chapters.ts +1 -0
  69. package/src/editor/docs/content/accessibility.md +411 -0
  70. package/src/editor/docs/content/editing-tokens.md +32 -1
  71. package/src/editor/docs/content.generated.ts +2 -1
  72. package/src/editor/pages/ComponentEditorPage.svelte +11 -1
  73. package/src/editor/pages/EditorShell.svelte +98 -10
  74. package/src/editor/skill-atlas/skillSources.generated.ts +3 -3
  75. package/src/editor/skill-atlas/trees/check-compliance.ts +3 -3
  76. package/src/editor/skill-atlas/trees/create-component.ts +12 -12
  77. package/src/editor/styles/ui-editor.css +2 -0
  78. package/src/editor/ui/EditorViewSwitcher.svelte +15 -2
  79. package/src/editor/ui/GradientEditor.svelte +9 -1
  80. package/src/editor/ui/ThemePanel.svelte +4 -4
  81. package/src/editor/ui/UIContrastReadout.svelte +186 -0
  82. package/src/editor/ui/UIInfoPopover.svelte +16 -2
  83. package/src/editor/ui/UIPaletteSelector.svelte +9 -0
  84. package/src/editor/ui/UITokenSelector.svelte +26 -2
  85. package/src/editor/ui/a11y/A11yByEye.svelte +264 -0
  86. package/src/editor/ui/a11y/A11yComponentIssues.svelte +121 -0
  87. package/src/editor/ui/a11y/A11yFindingRow.svelte +119 -0
  88. package/src/editor/ui/a11y/A11yIssueRow.svelte +152 -0
  89. package/src/editor/ui/a11y/A11yOverview.svelte +246 -0
  90. package/src/editor/ui/a11y/A11yPanel.svelte +153 -0
  91. package/src/editor/ui/a11y/A11yPropertyMark.svelte +84 -0
  92. package/src/editor/ui/a11y/A11yRailMark.svelte +41 -0
  93. package/src/editor/ui/a11y/AccessibilityTab.svelte +321 -0
  94. package/src/editor/ui/a11y/a11yView.ts +257 -0
  95. package/src/editor/ui/contrastReadout.ts +45 -0
  96. package/src/live-tokens/data/colors-and-type/halloween.json +1 -1
  97. package/src/live-tokens/data/themes/autumn.json +35 -35
  98. package/src/live-tokens/data/themes/halloween.json +36 -36
  99. package/src/live-tokens/data/themes/midnight-study.json +35 -35
  100. package/src/live-tokens/data/themes/ocean.json +35 -35
  101. package/src/live-tokens/data/themes/royal-velvet.json +35 -35
  102. package/src/live-tokens/data/themes/sketchy.json +35 -35
  103. package/src/live-tokens/data/themes/spring-meadow.json +35 -35
  104. package/src/live-tokens/data/themes/sunset.json +35 -35
  105. package/src/system/components/Badge.svelte +3 -3
  106. package/src/system/components/Button.svelte +12 -6
  107. package/src/system/components/CodeSnippet.svelte +8 -1
  108. package/src/system/components/CornerBadge.svelte +3 -3
  109. package/src/system/components/Dialog.svelte +43 -14
  110. package/src/system/components/IconButton.svelte +1 -1
  111. package/src/system/components/Image.svelte +7 -0
  112. package/src/system/components/ImageLightbox.svelte +18 -25
  113. package/src/system/components/InlineEditActions.svelte +1 -1
  114. package/src/system/components/Input.svelte +9 -2
  115. package/src/system/components/MenuSelect.svelte +31 -3
  116. package/src/system/components/Notification.svelte +1 -1
  117. package/src/system/components/ProgressBar.svelte +7 -1
  118. package/src/system/components/RadioButton.svelte +36 -3
  119. package/src/system/components/SegmentedControl.svelte +36 -2
  120. package/src/system/components/SlidePager.svelte +3 -3
  121. package/src/system/components/Slider.svelte +19 -15
  122. package/src/system/components/TabBar.svelte +33 -3
  123. package/src/system/components/Toggle.svelte +3 -3
  124. package/src/system/components/VideoFrame.svelte +1 -1
  125. package/src/system/components/VideoLightbox.svelte +2 -2
  126. package/src/system/internal/focusTrap.ts +35 -0
  127. package/src/testing-js/__aria__/badge.yml +40 -0
  128. package/src/testing-js/__aria__/button.yml +88 -0
  129. package/src/testing-js/__aria__/callout.yml +12 -0
  130. package/src/testing-js/__aria__/card.yml +8 -0
  131. package/src/testing-js/__aria__/codesnippet.yml +21 -0
  132. package/src/testing-js/__aria__/collapsiblesection.yml +23 -0
  133. package/src/testing-js/__aria__/cornerbadge.yml +24 -0
  134. package/src/testing-js/__aria__/dialog.yml +42 -0
  135. package/src/testing-js/__aria__/iconbutton.yml +57 -0
  136. package/src/testing-js/__aria__/image.yml +10 -0
  137. package/src/testing-js/__aria__/imagelightbox.yml +50 -0
  138. package/src/testing-js/__aria__/inlineeditactions.yml +16 -0
  139. package/src/testing-js/__aria__/input.yml +61 -0
  140. package/src/testing-js/__aria__/menuselect.yml +30 -0
  141. package/src/testing-js/__aria__/notification.yml +30 -0
  142. package/src/testing-js/__aria__/panel.yml +2 -0
  143. package/src/testing-js/__aria__/progressbar.yml +2 -0
  144. package/src/testing-js/__aria__/radiobutton.yml +20 -0
  145. package/src/testing-js/__aria__/sectiondivider.yml +9 -0
  146. package/src/testing-js/__aria__/segmentedcontrol.yml +35 -0
  147. package/src/testing-js/__aria__/sidenavigation.yml +304 -0
  148. package/src/testing-js/__aria__/slidepager.yml +72 -0
  149. package/src/testing-js/__aria__/slider.yml +28 -0
  150. package/src/testing-js/__aria__/tabbar.yml +35 -0
  151. package/src/testing-js/__aria__/table.yml +144 -0
  152. package/src/testing-js/__aria__/toggle.yml +12 -0
  153. package/src/testing-js/__aria__/tooltip.yml +2 -0
  154. package/src/testing-js/__aria__/videoframe.yml +6 -0
  155. package/src/testing-js/__aria__/videolightbox.yml +12 -0
  156. package/src/testing-js/{chunk-WIZ6W7UT.js → chunk-3WCLMOM7.js} +2 -2
  157. package/src/testing-js/{chunk-NB3NZRBM.js → chunk-7CM5D7PF.js} +5 -1
  158. package/src/testing-js/chunk-7CM5D7PF.js.map +1 -0
  159. package/src/testing-js/{chunk-X6KHEVIG.js → chunk-7QXUJRSO.js} +97 -29
  160. package/src/testing-js/chunk-7QXUJRSO.js.map +1 -0
  161. package/src/testing-js/chunk-GZKUYLDW.js +193 -0
  162. package/src/testing-js/chunk-GZKUYLDW.js.map +1 -0
  163. package/src/testing-js/chunk-RTZQLW3F.js +649 -0
  164. package/src/testing-js/chunk-RTZQLW3F.js.map +1 -0
  165. package/src/testing-js/{chunk-SHBYZUPD.js → chunk-RWOT2XDB.js} +212 -15
  166. package/src/testing-js/chunk-RWOT2XDB.js.map +1 -0
  167. package/src/testing-js/{chunk-GKO27NGN.js → chunk-THWGMSRH.js} +2 -2
  168. package/src/testing-js/{chunk-3UKGXCDL.js → chunk-YVVR3GJZ.js} +1 -1
  169. package/src/testing-js/chunk-YVVR3GJZ.js.map +1 -0
  170. package/src/testing-js/component-a11y.contract.js +313 -0
  171. package/src/testing-js/component-a11y.contract.js.map +1 -0
  172. package/src/testing-js/component-alias.contract.js +2 -2
  173. package/src/testing-js/component-behavior.contract.js +2 -2
  174. package/src/testing-js/component-editor.contract.js +6 -5
  175. package/src/testing-js/component-editor.contract.js.map +1 -1
  176. package/src/testing-js/component-render.contract.js +10 -6
  177. package/src/testing-js/component-render.contract.js.map +1 -1
  178. package/src/testing-js/index.d.ts +44 -16
  179. package/src/testing-js/index.js +11 -11
  180. package/src/testing-js/index.js.map +1 -1
  181. package/src/testing-js/page-compliance.contract.js +132 -13
  182. package/src/testing-js/page-compliance.contract.js.map +1 -1
  183. package/src/testing-js/vitest.js +3 -3
  184. package/template/_gitignore +3 -0
  185. package/template/package.json +9 -0
  186. package/src/testing-js/chunk-3UKGXCDL.js.map +0 -1
  187. package/src/testing-js/chunk-NB3NZRBM.js.map +0 -1
  188. package/src/testing-js/chunk-SHBYZUPD.js.map +0 -1
  189. package/src/testing-js/chunk-X6KHEVIG.js.map +0 -1
  190. /package/src/testing-js/{chunk-WIZ6W7UT.js.map → chunk-3WCLMOM7.js.map} +0 -0
  191. /package/src/testing-js/{chunk-GKO27NGN.js.map → chunk-THWGMSRH.js.map} +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../testing/support/traceProperties.ts","../testing/support/nonTextContrast.ts","../testing/support/a11y.ts"],"sourcesContent":["import type { CDPSession, Page } from '@playwright/test';\n\n/** The CSS properties a result's verdict reads, per element. A name ending in\n * `*` matches every property that starts with the rest; `*` alone matches\n * every property. */\nexport interface TraceAxes {\n target: string[];\n pseudoElements: string[];\n ancestors: string[];\n /** Pseudo-classes forced on the target while its styles are read. The\n * target then keeps only the declarations the force brings in or changes. */\n force?: string[];\n}\n\nconst BOX_SIZE = ['width', 'height', 'min-*', 'inline-size', 'block-size', 'padding*'];\nconst FOCUS = ['focus', 'focus-visible'];\n// Every border property but the radii, which carry no colour or width.\nconst BORDER_PAINT = ['border-block*', 'border-inline*', ...['', '-top', '-right', '-bottom', '-left']\n .flatMap((side) => [`border${side}`, `border${side}-color`, `border${side}-width`, `border${side}-style`])];\n\n// What sets how long a motion lasts; easing and fill shape it without lengthening it.\nconst MOTION = ['animation', 'animation-name', 'animation-duration', 'animation-delay', 'animation-iteration-count',\n 'transition', 'transition-property', 'transition-duration', 'transition-delay'];\n\n/** Decision 2 of docs/plans/a11y-property-marks.md, keyed by engine rule. An\n * engine rule absent here traces nothing. */\nexport const TRACE_AXES: Readonly<Record<string, TraceAxes>> = {\n 'color-contrast': {\n target: ['color', 'background*', 'opacity', 'text-shadow'],\n pseudoElements: ['*'],\n ancestors: ['background*', 'opacity'],\n },\n 'SIA-R65': {\n target: ['outline*', 'box-shadow', 'border*', 'background*', 'color', 'text-decoration*'],\n pseudoElements: [],\n ancestors: [],\n force: FOCUS,\n },\n 'target-size': { target: BOX_SIZE, pseudoElements: BOX_SIZE, ancestors: [] },\n 'focus-indicator-contrast': {\n target: ['outline*', 'box-shadow', ...BORDER_PAINT, 'background*', 'color'],\n pseudoElements: [],\n ancestors: ['background*'],\n force: FOCUS,\n },\n 'control-boundary-contrast': {\n target: [...BORDER_PAINT, 'outline', 'outline-color', 'outline-width', 'outline-style', 'background*'],\n pseudoElements: [],\n ancestors: ['background*'],\n },\n // Every element's line height is 1.5 under the override, so the theme's own cannot cause the overflow.\n 'text-spacing': {\n target: ['height', 'max-height', 'width', 'max-width', 'padding*', 'overflow*', 'white-space'],\n pseudoElements: [],\n ancestors: [],\n },\n // A pseudo-element that does not display does not move, so its display gates the motion, as Button's shimmer does.\n 'prefers-reduced-motion': { target: MOTION, pseudoElements: [...MOTION, 'display'], ancestors: [] },\n};\n\ninterface Declaration {\n name: string;\n value: string;\n text?: string;\n important?: boolean;\n}\n\ninterface Style {\n cssProperties: Declaration[];\n}\n\ninterface RuleMatch {\n rule: {\n origin: string;\n style: Style & { range?: unknown };\n styleSheetId?: string;\n selectorList: { text: string };\n };\n}\n\ninterface ElementStyles {\n inlineStyle?: Style;\n matchedCSSRules?: RuleMatch[];\n}\n\nconst sessions = new WeakMap<Page, Promise<CDPSession>>();\n\n/** One DevTools session per page, with `DOM` and `CSS` enabled. */\nexport function cdpSession(page: Page): Promise<CDPSession> {\n let session = sessions.get(page);\n if (!session) {\n session = (async () => {\n const cdp = await page.context().newCDPSession(page);\n await cdp.send('DOM.enable');\n await cdp.send('CSS.enable');\n return cdp;\n })();\n sessions.set(page, session);\n }\n return session;\n}\n\nfunction onAxis(property: string, axis: string[]): boolean {\n return axis.some((name) => (name.endsWith('*') ? property.startsWith(name.slice(0, -1)) : property === name));\n}\n\n/** Authored declarations only: Chromium also lists each shorthand's longhands\n * without text, which would repeat every reference. */\nfunction authored(style: Style | undefined): Declaration[] {\n return (style?.cssProperties ?? []).filter((declaration) => declaration.text);\n}\n\nfunction regularRules(styles: ElementStyles): RuleMatch['rule'][] {\n return (styles.matchedCSSRules ?? []).filter(({ rule }) => rule.origin === 'regular').map(({ rule }) => rule);\n}\n\nfunction declarations(styles: ElementStyles): Declaration[] {\n return [authored(styles.inlineStyle), ...regularRules(styles).map((rule) => authored(rule.style))].flat();\n}\n\n/** One element's declarations from lowest to highest precedence: its rules\n * in cascade order, then its inline style. */\nfunction cascadeOrder(styles: ElementStyles): Declaration[] {\n return [...regularRules(styles).flatMap((rule) => authored(rule.style)), ...authored(styles.inlineStyle)];\n}\n\n/** The declaration of `name` that wins among `ordered`, or none. */\nfunction winning(ordered: Declaration[], name: string): Declaration | undefined {\n const all = ordered.filter((d) => d.name === name);\n // Chromium flags `important` on standard properties and leaves it in a custom property's value.\n return all.filter((d) => d.important || /!\\s*important\\s*$/i.test(d.value)).at(-1) ?? all.at(-1);\n}\n\nfunction ruleKey(rule: RuleMatch['rule']): string {\n return JSON.stringify([rule.styleSheetId, rule.selectorList.text, rule.style.range]);\n}\n\ntype Longhands = (names: string[]) => Promise<Map<string, string[]>>;\n\n/** The longhands each property sets, read from the page's CSSOM, so that\n * `outline` and `outline-color` count as the same property. */\nfunction longhandsIn(page: Page): Longhands {\n return async (names) => new Map(await page.evaluate((names) => names.map((name): [string, string[]] => {\n if (name.startsWith('--')) return [name, [name]];\n const style = document.createElement('div').style;\n style.setProperty(name, 'initial');\n return [name, style.length ? Array.from({ length: style.length }, (_, i) => style.item(i)) : [name]];\n }), names));\n}\n\n/**\n * The target's declarations under `forced` that the force brings in or\n * changes: every declaration of a rule that matches only under the force,\n * and every declaration of a later rule, or of the inline style, that sets a\n * property one of those rules sets. Rules that match in every state drop, so\n * a field's resting border and surface never name a focus finding.\n */\nasync function forcedDeclarations(plain: ElementStyles, forced: ElementStyles, longhands: Longhands): Promise<Declaration[]> {\n const before = new Set(regularRules(plain).map(ruleKey));\n const cascade = [\n ...regularRules(forced).map((rule) => ({ forcedOnly: !before.has(ruleKey(rule)), declarations: authored(rule.style) })),\n { forcedOnly: false, declarations: authored(forced.inlineStyle) },\n ];\n const expanded = await longhands([...new Set(cascade.flatMap((entry) => entry.declarations.map((d) => d.name)))]);\n const changed = new Set<string>();\n const kept: Declaration[] = [];\n for (const { forcedOnly, declarations } of cascade) {\n for (const d of declarations) {\n const sets = expanded.get(d.name) ?? [d.name];\n if (forcedOnly) sets.forEach((name) => changed.add(name));\n if (forcedOnly || sets.some((name) => changed.has(name))) kept.push(d);\n }\n }\n return kept;\n}\n\nfunction references(value: string): string[] {\n return [...value.matchAll(/var\\(\\s*(--[\\w-]+)/g)].map((match) => match[1]);\n}\n\n/** How many ancestors separate each element `selector` matches from the\n * nearest element `scope` matches, in document order, or -1 outside it. */\nfunction scopeDepths(page: Page, selector: string, scope: string): Promise<number[]> {\n return page.evaluate(({ selector, scope }) => [...document.querySelectorAll(selector)].map((element) => {\n let depth = 0;\n for (let node: Element | null = element; node; node = node.parentElement, depth++) {\n if (node.matches(scope)) return depth;\n }\n return -1;\n }), { selector, scope });\n}\n\nasync function matchedStyles(cdp: CDPSession, nodeId: number, force: string[] = [], forceNodeId = nodeId) {\n const forcePseudoState = (forcedPseudoClasses: string[]) => cdp.send('CSS.forcePseudoState', { nodeId: forceNodeId, forcedPseudoClasses });\n if (force.length === 0) return cdp.send('CSS.getMatchedStylesForNode', { nodeId });\n await forcePseudoState(force);\n return cdp.send('CSS.getMatchedStylesForNode', { nodeId }).finally(() => forcePseudoState([]));\n}\n\ninterface TraceNode {\n nodeId: number;\n depth: number;\n forceNodeId?: number;\n ancestorsFrom: number;\n}\n\nasync function traceNode(\n cdp: CDPSession,\n node: TraceNode,\n axes: TraceAxes,\n known: ReadonlySet<string>,\n longhands: Longhands,\n): Promise<string[]> {\n const matched = await matchedStyles(cdp, node.nodeId, axes.force, node.forceNodeId);\n const own = axes.force\n ? await forcedDeclarations(await matchedStyles(cdp, node.nodeId), matched, longhands)\n : declarations(matched);\n const pseudo = (matched.pseudoElements ?? []).flatMap(({ matches }) => declarations({ matchedCSSRules: matches }));\n const chain = (matched.inherited ?? []).slice(0, node.depth);\n\n // A reference resolves on the element whose declaration holds it: level 0\n // is the target, level n its nth ancestor, and PSEUDO its pseudo-elements.\n // Only the winning declaration of a private property counts there, so a\n // selected tab traces its selected binding and leaves the default one out.\n const PSEUDO = -1;\n const levels = [cascadeOrder(matched), ...chain.map(cascadeOrder)];\n const pseudoCustom = pseudo.filter((d) => d.name.startsWith('--'));\n const from = Math.max(node.ancestorsFrom - 1, 0);\n const pending: { name: string; level: number }[] = [\n ...own.filter((d) => onAxis(d.name, axes.target)).map((d) => ({ d, level: 0 })),\n ...pseudo.filter((d) => onAxis(d.name, axes.pseudoElements)).map((d) => ({ d, level: PSEUDO })),\n ...chain.slice(from).flatMap((entry, i) => declarations(entry)\n .filter((d) => onAxis(d.name, axes.ancestors))\n .map((d) => ({ d, level: from + i + 1 }))),\n ].flatMap(({ d, level }) => references(d.value).map((name) => ({ name, level })));\n\n const resolve = (name: string, level: number): { declarations: Declaration[]; level: number } => {\n if (level === PSEUDO) {\n const own = pseudoCustom.filter((d) => d.name === name);\n if (own.length > 0) return { declarations: own, level: PSEUDO };\n }\n for (let at = Math.max(level, 0); at < levels.length; at++) {\n const won = winning(levels[at], name);\n if (won) return { declarations: [won], level: at };\n }\n return { declarations: [], level };\n };\n\n const found = new Set<string>();\n const followed = new Set<string>();\n for (let next = pending.pop(); next !== undefined; next = pending.pop()) {\n const { name, level } = next;\n const key = `${name}@${level}`;\n if (known.has(name)) found.add(name);\n else if (!followed.has(key)) {\n followed.add(key);\n const resolved = resolve(name, level);\n for (const d of resolved.declarations) pending.push(...references(d.value).map((ref) => ({ name: ref, level: resolved.level })));\n }\n }\n return [...found];\n}\n\n/** The element a trace reads, inside `scope`. `forceOn`, when set, names the\n * element `axes.force` applies to, for a focus style drawn on another\n * element. `ancestorsFrom` is the nearest ancestor whose `axes.ancestors`\n * declarations count, 1 being the parent and the default. */\nexport interface TraceTarget {\n selector: string;\n scope: string;\n forceOn?: string;\n ancestorsFrom?: number;\n}\n\n/**\n * The component properties `axes` reads on the element `target.selector`\n * names inside `target.scope` (decisions 1 to 3): each matched declaration on\n * an axis, narrowed under `axes.force` to the ones the force brings in or\n * changes, its `var()` references followed through the winning declaration\n * of each private custom property on the target or an in-scope ancestor,\n * kept where a name is in `known`. Sorted and unique.\n */\nexport async function traceProperties(\n page: Page,\n target: TraceTarget,\n axes: TraceAxes,\n known: ReadonlySet<string>,\n): Promise<string[]> {\n let depths: number[];\n let forceDepths: number[] = [];\n try {\n depths = await scopeDepths(page, target.selector, target.scope);\n if (target.forceOn) forceDepths = await scopeDepths(page, target.forceOn, target.scope);\n } catch {\n // An Alfa label or an axe frame path need not parse as one selector.\n return [];\n }\n const cdp = await cdpSession(page);\n const { root } = await cdp.send('DOM.getDocument', { depth: 0 });\n const query = async (selector: string) => (await cdp.send('DOM.querySelectorAll', { nodeId: root.nodeId, selector })).nodeIds;\n const nodeIds = await query(target.selector);\n const forceNodeId = target.forceOn\n ? (await query(target.forceOn)).find((_, index) => forceDepths[index] >= 0)\n : undefined;\n if (target.forceOn && forceNodeId === undefined) return [];\n const longhands = longhandsIn(page);\n const found = new Set<string>();\n for (const [index, nodeId] of nodeIds.entries()) {\n if (depths[index] < 0) continue;\n const node = { nodeId, depth: depths[index], forceNodeId, ancestorsFrom: target.ancestorsFrom ?? 1 };\n for (const name of await traceNode(cdp, node, axes, known, longhands)) found.add(name);\n }\n return [...found].sort();\n}\n","import type { Page } from '@playwright/test';\nimport type { A11yResult } from './a11y';\nimport { cdpSession } from './traceProperties';\n\nexport const FOCUS_INDICATOR_RULE = 'focus-indicator-contrast';\nexport const BOUNDARY_RULE = 'control-boundary-contrast';\n\nconst NON_TEXT_URL = 'https://www.w3.org/WAI/WCAG22/Understanding/non-text-contrast.html';\nconst FOCUS_ATTR = 'data-lt-focus-probe';\nconst NO_TRANSITION_ID = 'lt-focus-probe-no-transition';\nconst FORCE = ['focus', 'focus-visible'];\n/** Marks a focusable whose focus ring the probe found on a sibling, which\n * SIA-R65 cannot see. `scanFocusVisible` reads it. */\nexport const SIBLING_FOCUS_ATTR = 'data-lt-focus-sibling';\n\n/** Where the trace reads a result's properties: `selector` names the element\n * that paints the colour, `forceOn` the element focus is forced on, and\n * `ancestorsFrom` the nearest ancestor (1 is the parent, 0 the element\n * itself) whose surface lies behind the measured edge. */\nexport interface TraceRequest {\n selector: string;\n forceOn?: string;\n ancestorsFrom?: number;\n}\n\nexport interface CheckedResult {\n result: A11yResult;\n trace: TraceRequest;\n}\n\n/** A part whose border, outline or surface draws a control's visible edge.\n * `when` narrows it to the elements that also match, such as a state class. */\nexport interface BoundaryPart {\n selector: string;\n label: string;\n when?: string;\n}\n\ntype Snapshot = string[];\n\ntype FocusOutcome =\n | { kind: 'none' | 'browser' | 'pass' }\n | {\n kind: 'fail' | 'review';\n channel: string;\n ratio?: number;\n target: string;\n indicator: string;\n html: string;\n ancestorsFrom: number;\n };\n\ntype BoundaryOutcome = {\n kind: 'fail' | 'review';\n label: string;\n target: string;\n html: string;\n edge: { channel: 'border' | 'outline'; ratio: number } | null;\n surface: number | null;\n ancestorsFrom: number;\n};\n\ntype PageRequest =\n | { kind: 'focusables'; scope: string; exclude: string[]; attr: string; styleId: string; marker: string }\n | { kind: 'snapshot'; attr: string; index: number; scope: string; exclude: string[] }\n | { kind: 'measure'; attr: string; index: number; scope: string; exclude: string[]; before: Snapshot; marker: string }\n | { kind: 'cleanup'; attr: string; styleId: string }\n | { kind: 'boundaries'; scope: string; exclude: string[]; parts: BoundaryPart[]; root: string }\n | { kind: 'describe'; scope: string; exclude: string[]; attr: string; values: string[] };\n\n/**\n * Runs in the page, so it carries every helper it needs. Colours resolve and\n * composite on a 1px canvas, which reads any CSS colour syntax the computed\n * style returns and blends alpha the way the page paints it.\n */\nfunction inPage(request: PageRequest): unknown {\n const scopes = [...document.querySelectorAll<HTMLElement>(('scope' in request && request.scope) || ':not(*)')];\n const excluded = (el: Element) => 'exclude' in request && request.exclude.some((selector) => el.closest(selector));\n const inScope = (el: Element) => scopes.some((scope) => scope.contains(el)) && !excluded(el);\n\n const canvas = document.createElement('canvas');\n canvas.width = canvas.height = 1;\n const ctx = canvas.getContext('2d', { willReadFrequently: true })!;\n const paint = (layers: string[]): number[] => {\n ctx.clearRect(0, 0, 1, 1);\n for (const layer of ['#fff', ...layers]) {\n ctx.fillStyle = 'transparent';\n ctx.fillStyle = layer;\n ctx.fillRect(0, 0, 1, 1);\n }\n return [...ctx.getImageData(0, 0, 1, 1).data.slice(0, 3)];\n };\n const alpha = (color: string): number => {\n ctx.clearRect(0, 0, 1, 1);\n ctx.fillStyle = 'transparent';\n ctx.fillStyle = color;\n ctx.fillRect(0, 0, 1, 1);\n return ctx.getImageData(0, 0, 1, 1).data[3] / 255;\n };\n const rgb = (channels: number[]) => `rgb(${channels.join(' ')})`;\n const luminance = (channels: number[]) => {\n const [r, g, b] = channels.map((c) => {\n const s = c / 255;\n return s <= 0.04045 ? s / 12.92 : ((s + 0.055) / 1.055) ** 2.4;\n });\n return 0.2126 * r + 0.7152 * g + 0.0722 * b;\n };\n const contrast = (a: number[], b: number[]) => {\n const [hi, lo] = [luminance(a), luminance(b)].sort((x, y) => y - x);\n return (hi + 0.05) / (lo + 0.05);\n };\n\n const selectorFor = (el: Element): string => {\n const count = (selector: string) => scopes.reduce((n, scope) => n + (scope.matches(selector) ? 1 : 0) + scope.querySelectorAll(selector).length, 0);\n const own = (e: Element) => e.tagName.toLowerCase()\n + [...e.classList].filter((name) => !name.startsWith('svelte-') && /^[\\w-]+$/.test(name)).map((name) => `.${name}`).join('');\n const path: string[] = [];\n for (let e: Element | null = el; e; e = e.parentElement) {\n const step = own(e);\n path.unshift(step);\n if (count(path.join(' > ')) === 1) break;\n const same = [...(e.parentElement?.children ?? [])].filter((c) => c.tagName === e!.tagName);\n if (same.length > 1) path[0] = `${step}:nth-of-type(${same.indexOf(e) + 1})`;\n if (count(path.join(' > ')) === 1 || scopes.includes(e as HTMLElement)) break;\n }\n return path.join(' > ');\n };\n\n /** The surfaces behind `ring`, from the nearest box that contains it up to\n * the first opaque one, composited over white. A gradient or image among\n * them leaves the colour unknown. Past a scope lie the editor's own\n * surfaces, so the walk ends there on `--page-bg`, which a real page puts\n * behind the component. A part that fills its preview edge to edge spills\n * its ring past the scope. */\n const backdrop = (from: Element, ring: DOMRect) => {\n const layers: string[] = [];\n let level = 0;\n let first = -1;\n for (let e: Element | null = from; e; e = e.parentElement, level++) {\n const box = e.getBoundingClientRect();\n const isScope = scopes.includes(e as HTMLElement);\n const contains = isScope || (box.left <= ring.left + 0.5 && box.top <= ring.top + 0.5\n && box.right >= ring.right - 0.5 && box.bottom >= ring.bottom - 0.5);\n if (!contains) continue;\n if (first < 0) first = level;\n const style = getComputedStyle(e);\n if (style.backgroundImage !== 'none') return { color: null, first };\n const a = alpha(style.backgroundColor);\n if (a > 0) layers.unshift(style.backgroundColor);\n if (a >= 1) break;\n if (isScope) {\n const page = getComputedStyle(document.documentElement).getPropertyValue('--page-bg').trim();\n if (/gradient|url\\(/.test(page)) return { color: null, first };\n if (page) layers.unshift(page);\n break;\n }\n }\n return { color: paint(layers), first: Math.max(first, 0) };\n };\n const inflate = (box: DOMRect, by: number) => new DOMRect(box.x - by, box.y - by, box.width + 2 * by, box.height + 2 * by);\n const px = (value: string) => parseFloat(value) || 0;\n const html = (el: Element) => el.outerHTML.replace(/\\s+/g, ' ').slice(0, 300);\n\n const SIDES = ['Top', 'Right', 'Bottom', 'Left'] as const;\n const visibleSide = (style: CSSStyleDeclaration, side: string) => {\n const s = style as unknown as Record<string, string>;\n return !['none', 'hidden'].includes(s[`border${side}Style`]) && px(s[`border${side}Width`]) >= 0.99\n ? s[`border${side}Color`] : null;\n };\n\n const candidates = (target: Element): Element[] => {\n const list = [target, ...target.querySelectorAll('*')];\n for (const sibling of target.parentElement?.children ?? []) {\n if (sibling !== target) list.push(sibling, ...sibling.querySelectorAll('*'));\n }\n return list.filter(inScope);\n };\n const signature = (el: Element) => {\n const s = getComputedStyle(el);\n return JSON.stringify([\n [s.outlineStyle, s.outlineWidth, s.outlineColor, s.outlineOffset],\n s.boxShadow,\n SIDES.map((side) => visibleSide(s, side)),\n ]);\n };\n\n /** The colour focus brings in on `el`, by decision 8's order: an outline,\n * else the first outer box shadow, else a border. A channel focus leaves\n * alone, or paints fully transparent, is no indicator. */\n const indicatorOf = (el: Element, before: string) => {\n const [outlineBefore, shadowBefore, bordersBefore] = JSON.parse(before);\n const s = getComputedStyle(el);\n const outline = [s.outlineStyle, s.outlineWidth, s.outlineColor, s.outlineOffset];\n if (JSON.stringify(outline) !== JSON.stringify(outlineBefore) && s.outlineStyle !== 'none' && px(s.outlineWidth) > 0) {\n if (s.outlineStyle === 'auto') return { channel: 'browser', color: '', extent: 0 };\n if (alpha(s.outlineColor) > 0) return { channel: 'outline', color: s.outlineColor, extent: px(s.outlineOffset) + px(s.outlineWidth) };\n }\n if (s.boxShadow !== shadowBefore && s.boxShadow !== 'none') {\n const shadows = s.boxShadow.split(/,(?![^(]*\\))/).map((part) => part.trim());\n for (const shadow of shadows) {\n if (/\\binset\\b/.test(shadow)) continue;\n const color = /^(?:[a-z-]+\\([^)]*\\)|#[\\da-f]+|[a-z]+)/i.exec(shadow)?.[0] ?? '';\n if (!color || alpha(color) === 0) continue;\n const [x = 0, y = 0, blur = 0, spread = 0] = (shadow.slice(color.length).match(/-?[\\d.]+px/g) ?? []).map(px);\n return { channel: 'shadow', color, extent: Math.max(Math.abs(x), Math.abs(y)) + blur + spread };\n }\n }\n const borders = SIDES.map((side) => visibleSide(s, side));\n for (const [i, color] of borders.entries()) {\n if (color && color !== bordersBefore[i] && alpha(color) > 0) return { channel: 'border', color, extent: 0 };\n }\n return null;\n };\n\n switch (request.kind) {\n case 'focusables': {\n // A focus transition would leave the computed colour at its start.\n const style = document.createElement('style');\n style.id = request.styleId;\n style.textContent = '*, *::before, *::after { transition: none !important; }';\n document.head.append(style);\n for (const el of document.querySelectorAll(`[${request.marker}]`)) el.removeAttribute(request.marker);\n const focusable = 'a[href], area[href], button, input:not([type=\"hidden\"]), select, textarea, summary, iframe, [tabindex], [contenteditable]:not([contenteditable=\"false\"])';\n const found = scopes.flatMap((scope) => [scope, ...scope.querySelectorAll(focusable)])\n .filter((el, i, all) => all.indexOf(el) === i && el.matches(focusable) && inScope(el))\n .filter((el) => (el as HTMLElement).tabIndex >= 0 && !el.matches(':disabled') && el.checkVisibility({ visibilityProperty: true }));\n found.forEach((el, i) => el.setAttribute(request.attr, String(i)));\n return found.length;\n }\n case 'snapshot': {\n const target = document.querySelector(`[${request.attr}=\"${request.index}\"]`)!;\n return candidates(target).map(signature);\n }\n case 'measure': {\n const target = document.querySelector(`[${request.attr}=\"${request.index}\"]`)!;\n const list = candidates(target);\n for (const [i, el] of list.entries()) {\n if (request.before[i] === undefined || request.before[i] === signature(el)) continue;\n const found = indicatorOf(el, request.before[i]);\n if (!found) continue;\n if (el !== target && !target.contains(el)) target.setAttribute(request.marker, '');\n if (found.channel === 'browser') return { kind: 'browser' };\n const ring = inflate(el.getBoundingClientRect(), Math.max(found.extent, 0));\n const behind = backdrop(found.channel === 'border' ? el.parentElement! : el, found.extent < 0 ? el.getBoundingClientRect() : ring);\n const offset = found.channel === 'border' ? 1 : 0;\n const common = {\n channel: found.channel,\n target: selectorFor(target),\n indicator: selectorFor(el),\n html: html(target),\n ancestorsFrom: behind.first + offset,\n };\n if (!behind.color) return { kind: 'review', ...common };\n const ratio = contrast(paint([rgb(behind.color), found.color]), behind.color);\n return ratio < 3 ? { kind: 'fail', ratio, ...common } : { kind: 'pass' };\n }\n return { kind: 'none' };\n }\n case 'cleanup': {\n document.getElementById(request.styleId)?.remove();\n for (const el of document.querySelectorAll(`[${request.attr}]`)) el.removeAttribute(request.attr);\n return null;\n }\n case 'boundaries': {\n const outcomes: BoundaryOutcome[] = [];\n const control = 'button, input, select, textarea, [aria-disabled]';\n const inactive = (el: Element) => {\n const root = el.closest(request.root) ?? el;\n const controls = [root, ...root.querySelectorAll(control)].filter((c) => c.matches(control));\n return controls.length > 0 && controls.every((c) => c.matches(':disabled') || c.getAttribute('aria-disabled') === 'true');\n };\n for (const part of request.parts) {\n const elements = scopes.flatMap((scope) => [scope, ...scope.querySelectorAll(part.selector)])\n .filter((el, i, all) => all.indexOf(el) === i && el.matches(part.selector) && inScope(el))\n .filter((el) => !part.when || el.matches(part.when))\n .filter((el) => el.checkVisibility({ visibilityProperty: true }) && el.getBoundingClientRect().width > 0 && !inactive(el));\n for (const el of elements) {\n const style = getComputedStyle(el);\n const behind = backdrop(el.parentElement!, el.getBoundingClientRect());\n const common = { label: part.label, target: selectorFor(el), html: html(el), ancestorsFrom: behind.first + 1 };\n if (!behind.color) {\n outcomes.push({ kind: 'review', edge: null, surface: null, ...common });\n continue;\n }\n const surfaceColor = paint([rgb(behind.color), style.backgroundColor]);\n const surface = style.backgroundImage === 'none' ? contrast(surfaceColor, behind.color) : null;\n // A state indicator may draw one side, as TabBar's underline does, or\n // an inset outline, as SegmentedControl's selected option does.\n const edges: { channel: 'border' | 'outline'; color: string; under: number[] }[] = [];\n const borderUnder = style.backgroundClip === 'border-box' ? surfaceColor : behind.color;\n for (const side of SIDES) {\n const color = visibleSide(style, side);\n if (color) edges.push({ channel: 'border', color, under: borderUnder });\n }\n if (!['none', 'auto'].includes(style.outlineStyle) && px(style.outlineWidth) >= 0.99) {\n edges.push({ channel: 'outline', color: style.outlineColor, under: px(style.outlineOffset) < 0 ? surfaceColor : behind.color });\n }\n const edge = edges\n .map(({ channel, color, under }) => ({ channel, ratio: contrast(paint([rgb(under), color]), behind.color) }))\n .reduce<BoundaryOutcome['edge']>((best, next) => (best && best.ratio >= next.ratio ? best : next), null);\n if ((edge?.ratio ?? 0) >= 3 || (surface ?? 0) >= 3) continue;\n outcomes.push({ kind: surface === null ? 'review' : 'fail', edge, surface, ...common });\n }\n }\n return outcomes;\n }\n case 'describe': {\n return request.values.map((value) => {\n const el = document.querySelector(`[${request.attr}=\"${value}\"]`)!;\n return { target: selectorFor(el), html: html(el) };\n });\n }\n }\n}\n\nfunction run<T>(page: Page, request: PageRequest): Promise<T> {\n return page.evaluate(inPage, request) as Promise<T>;\n}\n\n/** The selector and markup a result names for each element in `scope` whose\n * `attr` holds one of `values`, in that order. The selector is unique in\n * `scope`, which the trace needs. */\nexport function describeTagged(\n page: Page,\n scope: string,\n exclude: string[],\n attr: string,\n values: string[],\n): Promise<{ target: string; html: string }[]> {\n return run(page, { kind: 'describe', scope, exclude, attr, values });\n}\n\n/** Below 3:1 fails; WCAG does not round, so neither does the reading. */\nfunction ratioText(ratio: number): string {\n return `${Math.floor(ratio * 100) / 100}:1`;\n}\n\n/**\n * Decision 8: focus forced on each focusable element in `scope`, the colour\n * it brings in measured against the surfaces behind it. The indicator is the\n * first of the element, its descendants and its siblings whose outline, box\n * shadow or border focus changes, so a ring drawn on a sibling, as Slider\n * draws it, counts. Chromium's own `auto` ring pairs a dark and a light band,\n * one of which contrasts with any backdrop, so it passes. An element focus\n * leaves unchanged is SIA-R65's finding and adds none here. A ring found on\n * a sibling marks the focused element with `SIBLING_FOCUS_ATTR` until the\n * next scan, which settles SIA-R65's \"can't tell\" on it.\n */\nexport async function scanFocusIndicators(page: Page, scope: string, exclude: string[]): Promise<CheckedResult[]> {\n const cdp = await cdpSession(page);\n const count = await run<number>(page, { kind: 'focusables', scope, exclude, attr: FOCUS_ATTR, styleId: NO_TRANSITION_ID, marker: SIBLING_FOCUS_ATTR });\n const checked: CheckedResult[] = [];\n try {\n const { root } = await cdp.send('DOM.getDocument', { depth: 0 });\n for (let index = 0; index < count; index++) {\n const { nodeId } = await cdp.send('DOM.querySelector', { nodeId: root.nodeId, selector: `[${FOCUS_ATTR}=\"${index}\"]` });\n const before = await run<Snapshot>(page, { kind: 'snapshot', attr: FOCUS_ATTR, index, scope, exclude });\n await cdp.send('CSS.forcePseudoState', { nodeId, forcedPseudoClasses: FORCE });\n let outcome: FocusOutcome;\n try {\n outcome = await run<FocusOutcome>(page, { kind: 'measure', attr: FOCUS_ATTR, index, scope, exclude, before, marker: SIBLING_FOCUS_ATTR });\n } finally {\n await cdp.send('CSS.forcePseudoState', { nodeId, forcedPseudoClasses: [] });\n }\n if (outcome.kind !== 'fail' && outcome.kind !== 'review') continue;\n const where = outcome.indicator === outcome.target ? '' : ` on ${outcome.indicator}`;\n checked.push({\n result: {\n rule: outcome.kind === 'fail' ? 'a11y-component' : 'a11y-review',\n engineRule: FOCUS_INDICATOR_RULE,\n impact: null,\n target: outcome.target,\n html: outcome.html,\n help: outcome.kind === 'fail'\n ? `Focus ${outcome.channel}${where} ${ratioText(outcome.ratio!)} against the backdrop, needs 3:1`\n : `Focus ${outcome.channel}${where} sits over a gradient or image. Check by eye that it contrasts 3:1 with the backdrop`,\n helpUrl: NON_TEXT_URL,\n },\n trace: { selector: outcome.indicator, forceOn: outcome.target, ancestorsFrom: outcome.ancestorsFrom },\n });\n }\n } finally {\n await run(page, { kind: 'cleanup', attr: FOCUS_ATTR, styleId: NO_TRANSITION_ID });\n }\n return checked;\n}\n\n/**\n * Decision 9: each visible element of each boundary part in `scope` passes\n * when its best border side or outline, 1px or wider, or its surface\n * contrasts 3:1 with the surfaces behind it. A part of a control whose every\n * form control is disabled is inactive, which SC 1.4.11 exempts. `root` names\n * the component's root part, the extent of the control.\n */\nexport async function scanBoundaries(\n page: Page,\n scope: string,\n exclude: string[],\n parts: BoundaryPart[],\n root: string,\n): Promise<CheckedResult[]> {\n if (parts.length === 0) return [];\n const outcomes = await run<BoundaryOutcome[]>(page, { kind: 'boundaries', scope, exclude, parts, root });\n return outcomes.map((outcome) => {\n const edge = outcome.edge === null ? 'no border' : `${outcome.edge.channel} ${ratioText(outcome.edge.ratio)}`;\n return {\n result: {\n rule: outcome.kind === 'fail' ? 'a11y-component' : 'a11y-review',\n engineRule: BOUNDARY_RULE,\n impact: null,\n target: outcome.target,\n html: outcome.html,\n help: outcome.kind === 'fail'\n ? `${outcome.label} ${edge} and surface ${ratioText(outcome.surface!)} against the backdrop, needs 3:1`\n : `${outcome.label} sits on or carries a gradient or image. Check by eye that its edge contrasts 3:1 with the backdrop`,\n helpUrl: NON_TEXT_URL,\n },\n trace: { selector: outcome.target, ancestorsFrom: outcome.ancestorsFrom },\n };\n });\n}\n","import { test, type Page } from '@playwright/test';\nimport { SIBLING_FOCUS_ATTR } from './nonTextContrast';\n\nexport type A11yRule =\n | 'a11y-component'\n | 'a11y-page'\n | 'a11y-review'\n | 'a11y-focus-visible'\n | 'a11y-text-spacing'\n | 'a11y-reduced-motion'\n | 'a11y-keyboard';\n\n/** One element one engine flagged. `line` is the page file line, which only\n * the page suite can resolve; a component finding anchors on its runtime.\n * `view` names the variants, states, and themes the element failed in.\n * `variables` names the component properties the result traced to. */\nexport interface A11yResult {\n rule: A11yRule;\n engineRule: string;\n impact: string | null;\n target: string;\n html: string;\n help: string;\n helpUrl: string;\n line?: number;\n view?: string;\n variables?: string[];\n}\n\nconst A11Y_ANNOTATION = 'a11y';\n\nexport type A11yEngine = 'axe' | 'alfa';\n\nconst AXE_TAGS = ['wcag2a', 'wcag2aa', 'wcag21aa', 'wcag22aa'];\nconst SCOPE_ATTR = 'data-lt-a11y-scope';\nconst EXCLUDE_ATTR = 'data-lt-a11y-exclude';\nconst HTML_LIMIT = 300;\n\nclass EngineMissing extends Error {}\n\nexport async function importEngine<T>(packageName: string, load: () => Promise<T>): Promise<T> {\n try {\n return await load();\n } catch (error) {\n if ((error as { code?: string }).code === 'ERR_MODULE_NOT_FOUND') throw new EngineMissing(`${packageName} is not installed`);\n throw error;\n }\n}\n\nlet axeModules: ReturnType<typeof importAxe> | null = null;\nasync function importAxe() {\n await importEngine('axe-core', () => import('axe-core'));\n const { AxeBuilder } = await importEngine('@axe-core/playwright', () => import('@axe-core/playwright'));\n return { AxeBuilder };\n}\nconst loadAxe = () => (axeModules ??= importAxe());\n\nlet alfaModules: ReturnType<typeof importAlfa> | null = null;\nasync function importAlfa() {\n const { Audit, Outcome } = await importEngine('@siteimprove/alfa-act', () => import('@siteimprove/alfa-act'));\n const { Rules } = await importEngine('@siteimprove/alfa-rules', () => import('@siteimprove/alfa-rules'));\n const { Node } = await importEngine('@siteimprove/alfa-dom', () => import('@siteimprove/alfa-dom'));\n const { Native: DomNative } = await importEngine('@siteimprove/alfa-dom', () => import('@siteimprove/alfa-dom/native'));\n const { Device } = await importEngine('@siteimprove/alfa-device', () => import('@siteimprove/alfa-device'));\n const { Native: DeviceNative } = await importEngine('@siteimprove/alfa-device', () => import('@siteimprove/alfa-device/native'));\n const { Page: AlfaPage } = await importEngine('@siteimprove/alfa-web', () => import('@siteimprove/alfa-web'));\n const http = await importEngine('@siteimprove/alfa-http', () => import('@siteimprove/alfa-http'));\n const { URL } = await importEngine('@siteimprove/alfa-url', () => import('@siteimprove/alfa-url'));\n return {\n Audit, Outcome, R65: Rules.get('R65').getUnsafe(), Node, DomNative, Device, DeviceNative, AlfaPage, http, URL,\n };\n}\nconst loadAlfa = () => (alfaModules ??= importAlfa());\n\n/** The reason to annotate a test `inapplicable` with when one of `engines`\n * cannot load, or null when all of them load. */\nexport async function missingEngine(engines: A11yEngine[]): Promise<string | null> {\n try {\n for (const name of engines) await (name === 'axe' ? loadAxe() : loadAlfa());\n return null;\n } catch (error) {\n if (error instanceof EngineMissing) return error.message;\n throw error;\n }\n}\n\ninterface AxeNode {\n target: unknown[];\n html: string;\n impact?: string | null;\n any: { message: string }[];\n all: { message: string }[];\n none: { message: string }[];\n}\n\ninterface AxeRuleResult {\n id: string;\n impact?: string | null;\n help: string;\n helpUrl: string;\n nodes: AxeNode[];\n}\n\nfunction axeResults(rule: A11yRule, results: AxeRuleResult[], withReasons: boolean): A11yResult[] {\n return results.flatMap((result) =>\n result.nodes.map((node) => {\n const reasons = withReasons\n ? [...new Set([...node.any, ...node.all, ...node.none].map((check) => check.message).filter(Boolean))]\n : [];\n return {\n rule,\n engineRule: result.id,\n impact: node.impact ?? result.impact ?? null,\n target: node.target.map((part) => (Array.isArray(part) ? part.join(' >>> ') : String(part))).join(' | '),\n html: node.html.slice(0, HTML_LIMIT),\n help: [result.help, ...reasons].join('. '),\n helpUrl: result.helpUrl,\n };\n }),\n );\n}\n\n/** axe over `scope` (a selector), minus `exclude`. Violations report under\n * `rule`, and the results axe cannot decide under `a11y-review`. A page scan\n * leaves contrast to `page-contrast` (decision 8). `only` narrows the scan\n * from the WCAG tags to the axe rules it names. */\nexport async function scanAxe(\n page: Page,\n scope: string,\n exclude: string[],\n rule: 'a11y-component' | 'a11y-page',\n only?: string[],\n): Promise<A11yResult[]> {\n const { AxeBuilder } = await loadAxe();\n let builder = new AxeBuilder({ page }).include(scope);\n builder = only ? builder.withRules(only) : builder.withTags(AXE_TAGS);\n for (const selector of exclude) builder = builder.exclude(selector);\n if (rule === 'a11y-page') builder = builder.disableRules(['color-contrast']);\n const result = await builder.analyze();\n return [\n ...axeResults(rule, result.violations as AxeRuleResult[], false),\n ...axeResults('a11y-review', result.incomplete as AxeRuleResult[], true),\n ];\n}\n\ninterface SerialNode {\n type: string;\n name?: string;\n attributes?: { name: string }[];\n children?: SerialNode[];\n}\n\n/** Keeps the head, every scope's ancestor chain, and each scope's subtree\n * whole. The style sheets travel on the document node, so the cascade stays\n * intact, and SIA-R65 skips the editor's own controls: 0.3 to 0.8 s a scan\n * against 2.5 s for the whole document, with the same outcomes. */\nfunction pruneToScope(node: SerialNode): boolean {\n if (node.type === 'element' && node.attributes?.some((a) => a.name === SCOPE_ATTR)) return true;\n if (!node.children) return false;\n if (node.type === 'element' && node.name === 'head') return true;\n node.children = node.children.filter((child) => child.type === 'type' || pruneToScope(child));\n return node.type === 'document' || node.children.some((child) => child.type === 'element');\n}\n\nconst FOCUS_HELP = {\n failed: 'Focusing the element changes nothing visible',\n cantTell: 'Focusing the element changes no style on it, its descendants, or its ancestors',\n};\n\n/** Alfa's SIA-R65 over `scope` (a selector), minus `exclude`. A cantTell\n * outcome means Alfa found no focus style at all, so it reports beside a\n * failure under `a11y-focus-visible`, unless `scanFocusIndicators` found the\n * element's ring on a sibling, where Alfa does not look. */\nexport async function scanFocusVisible(page: Page, scope: string, exclude: string[]): Promise<A11yResult[]> {\n const alfa = await loadAlfa();\n await page.evaluate(({ scope, exclude, scopeAttr, excludeAttr }) => {\n for (const element of document.querySelectorAll(scope)) element.setAttribute(scopeAttr, '');\n for (const selector of exclude) {\n for (const element of document.querySelectorAll(selector)) element.setAttribute(excludeAttr, '');\n }\n }, { scope, exclude, scopeAttr: SCOPE_ATTR, excludeAttr: EXCLUDE_ATTR });\n let json: SerialNode;\n try {\n const handle = await page.evaluateHandle(() => document);\n json = (await handle.evaluate(alfa.DomNative.fromNode as (node: Document) => Promise<unknown>)) as SerialNode;\n await handle.dispose();\n } finally {\n await page.evaluate((attrs) => {\n for (const attr of attrs) for (const element of document.querySelectorAll(`[${attr}]`)) element.removeAttribute(attr);\n }, [SCOPE_ATTR, EXCLUDE_ATTR]);\n }\n pruneToScope(json);\n\n const fromWindow = alfa.DeviceNative.fromWindow as () => ReturnType<typeof alfa.DeviceNative.fromWindow>;\n const device = alfa.Device.from(await page.evaluate(fromWindow));\n const url = alfa.URL.parse(page.url()).getUnsafe();\n const { Request, Response, Headers, Header } = alfa.http;\n const alfaPage = alfa.AlfaPage.of(\n Request.of('GET', url),\n Response.of(url, 200, Headers.of([Header.of('Content-Type', 'text/html')])),\n alfa.Node.from(json as Parameters<typeof alfa.Node.from>[0], device) as Parameters<typeof alfa.AlfaPage.of>[2],\n device,\n );\n\n const results: A11yResult[] = [];\n for (const outcome of await alfa.Audit.of(alfaPage, [alfa.R65]).evaluate()) {\n const kind = alfa.Outcome.isFailed(outcome) ? 'failed' : alfa.Outcome.isCantTell(outcome) ? 'cantTell' : null;\n if (!kind) continue;\n const target = outcome.target as unknown as AlfaElement;\n if (!inScope(target)) continue;\n if (kind === 'cantTell' && target.attribute?.(SIBLING_FOCUS_ATTR).isSome()) continue;\n results.push({\n rule: 'a11y-focus-visible',\n engineRule: 'SIA-R65',\n impact: null,\n target: elementLabel(target),\n html: target.toString().replace(/\\s+/g, ' ').slice(0, HTML_LIMIT),\n help: FOCUS_HELP[kind],\n helpUrl: alfa.R65.uri,\n });\n }\n return results;\n}\n\ninterface AlfaElement {\n name: string;\n classes: Iterable<string>;\n id: { isSome(): boolean; get(): string };\n attribute?: (name: string) => { isSome(): boolean };\n inclusiveAncestors(): Iterable<AlfaElement>;\n toString(): string;\n}\n\nfunction inScope(target: AlfaElement): boolean {\n for (const node of target.inclusiveAncestors()) {\n if (typeof node.attribute !== 'function') continue;\n if (node.attribute(EXCLUDE_ATTR).isSome()) return false;\n if (node.attribute(SCOPE_ATTR).isSome()) return true;\n }\n return false;\n}\n\nfunction elementLabel(target: AlfaElement): string {\n const id = target.id.isSome() ? `#${target.id.get()}` : '';\n return `${target.name}${id}${[...target.classes].map((name) => `.${name}`).join('')}`;\n}\n\n/** Records `results` on the running test as the annotation the runner turns\n * into findings (decision 3). The test itself passes. */\nexport function recordA11y(results: A11yResult[]): void {\n if (results.length === 0) return;\n test.info().annotations.push({ type: A11Y_ANNOTATION, description: JSON.stringify(results) });\n}\n"],"mappings":"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","names":["names","declarations","selector","scope","own","inScope","scope","exclude"]}
@@ -211,6 +211,18 @@ var buttonContract = {
211
211
  action: { kind: "click", part: "root" },
212
212
  expect: { kind: "focused", part: "root", value: true }
213
213
  },
214
+ {
215
+ name: "Enter activates a button",
216
+ rule: "a11y-keyboard",
217
+ action: { kind: "press", part: "root", key: "Enter" },
218
+ expect: { kind: "activated", part: "root" }
219
+ },
220
+ {
221
+ name: "Space activates a button",
222
+ rule: "a11y-keyboard",
223
+ action: { kind: "press", part: "root", key: "Space" },
224
+ expect: { kind: "activated", part: "root" }
225
+ },
214
226
  {
215
227
  name: "a disabled button refuses focus",
216
228
  state: "disabled",
@@ -628,15 +640,27 @@ var collapsibleSectionContract = {
628
640
  interaction: {
629
641
  part: "toggleButton",
630
642
  role: "button",
631
- // No activation case: CollapsibleSectionEditor's preview drives `open`
632
- // off the active state tab and never wires `ontoggle` back to it, so
633
- // clicking the real button here can't move `aria-expanded` (same
643
+ // Activation reads as a click on the button: CollapsibleSectionEditor's
644
+ // preview drives `open` off the active state tab and never wires
645
+ // `ontoggle` back to it, so `aria-expanded` never moves here (same
634
646
  // constraint documented on ToggleEditor in toggle.ts).
635
647
  cases: [
636
648
  {
637
649
  name: "clicking the toggle button focuses it",
638
650
  action: { kind: "click", part: "toggleButton" },
639
651
  expect: { kind: "focused", part: "toggleButton", value: true }
652
+ },
653
+ {
654
+ name: "Enter activates the toggle button",
655
+ rule: "a11y-keyboard",
656
+ action: { kind: "press", part: "toggleButton", key: "Enter" },
657
+ expect: { kind: "activated", part: "toggleButton" }
658
+ },
659
+ {
660
+ name: "Space activates the toggle button",
661
+ rule: "a11y-keyboard",
662
+ action: { kind: "press", part: "toggleButton", key: "Space" },
663
+ expect: { kind: "activated", part: "toggleButton" }
640
664
  }
641
665
  ]
642
666
  },
@@ -877,6 +901,12 @@ var dialogContract = {
877
901
  name: "clicking the close icon focuses it",
878
902
  action: { kind: "click", part: "closeButton" },
879
903
  expect: { kind: "focused", part: "closeButton", value: true }
904
+ },
905
+ {
906
+ name: "Enter activates the close icon",
907
+ rule: "a11y-keyboard",
908
+ action: { kind: "press", part: "closeButton", key: "Enter" },
909
+ expect: { kind: "activated", part: "closeButton" }
880
910
  }
881
911
  ]
882
912
  },
@@ -888,6 +918,12 @@ var dialogContract = {
888
918
  action: { kind: "click", part: "closeButton" },
889
919
  expect: { kind: "callback", prop: "onclose", args: [] }
890
920
  },
921
+ {
922
+ name: "Escape asks to close",
923
+ props: { open: true, inline: true, title: "Confirm" },
924
+ action: { kind: "keydown", part: "closeButton", key: "Escape" },
925
+ expect: { kind: "callback", prop: "onclose", args: [] }
926
+ },
891
927
  {
892
928
  name: "title drives the heading",
893
929
  props: { open: true, inline: true, title: "Confirm" },
@@ -996,6 +1032,18 @@ var iconButtonContract = {
996
1032
  name: "clicking an icon button focuses it",
997
1033
  action: { kind: "click", part: "root" },
998
1034
  expect: { kind: "focused", part: "root", value: true }
1035
+ },
1036
+ {
1037
+ name: "Enter activates an icon button",
1038
+ rule: "a11y-keyboard",
1039
+ action: { kind: "press", part: "root", key: "Enter" },
1040
+ expect: { kind: "activated", part: "root" }
1041
+ },
1042
+ {
1043
+ name: "Space activates an icon button",
1044
+ rule: "a11y-keyboard",
1045
+ action: { kind: "press", part: "root", key: "Space" },
1046
+ expect: { kind: "activated", part: "root" }
999
1047
  }
1000
1048
  ]
1001
1049
  },
@@ -1207,6 +1255,22 @@ var imageLightboxContract = {
1207
1255
  name: "clicking the next chevron focuses it",
1208
1256
  action: { kind: "click", part: "nextButton" },
1209
1257
  expect: { kind: "focused", part: "nextButton", value: true }
1258
+ },
1259
+ // `setup: []` replaces the view's thumb click, which would land on the
1260
+ // open overlay and close the lightbox before the key does.
1261
+ {
1262
+ name: "Escape closes the lightbox",
1263
+ rule: "a11y-keyboard",
1264
+ setup: [],
1265
+ action: { kind: "press", part: "closeButton", key: "Escape" },
1266
+ expect: { kind: "attribute", part: "thumb", name: "aria-expanded", value: "false" }
1267
+ },
1268
+ {
1269
+ name: "Enter on the tile opens the lightbox",
1270
+ rule: "a11y-keyboard",
1271
+ setup: [],
1272
+ action: { kind: "press", part: "thumb", key: "Enter" },
1273
+ expect: { kind: "attribute", part: "thumb", name: "aria-expanded", value: "true" }
1210
1274
  }
1211
1275
  ]
1212
1276
  },
@@ -1485,6 +1549,7 @@ var inputContract = {
1485
1549
  },
1486
1550
  observe: { part: "field", css: "borderTopWidth", variable: "--input-border-width" }
1487
1551
  },
1552
+ boundaries: [{ part: "field", label: "Field" }],
1488
1553
  interaction: {
1489
1554
  part: "field",
1490
1555
  role: "textbox",
@@ -1499,6 +1564,12 @@ var inputContract = {
1499
1564
  action: { kind: "click", part: "toggleButton" },
1500
1565
  expect: { kind: "attribute", part: "toggleButton", name: "aria-pressed", value: "true" }
1501
1566
  },
1567
+ {
1568
+ name: "Space activates the reveal toggle",
1569
+ rule: "a11y-keyboard",
1570
+ action: { kind: "press", part: "toggleButton", key: "Space" },
1571
+ expect: { kind: "activated", part: "toggleButton" }
1572
+ },
1502
1573
  {
1503
1574
  name: "a disabled field refuses focus",
1504
1575
  state: "disabled",
@@ -1551,6 +1622,7 @@ var menuSelectContract = {
1551
1622
  parts: {
1552
1623
  root: ".menuselect",
1553
1624
  item: ".menuselect-item",
1625
+ secondItem: ".menuselect > li:nth-of-type(2) > .menuselect-item",
1554
1626
  icon: ".menuselect-item .menuselect-icon",
1555
1627
  label: ".menuselect-item .menuselect-label",
1556
1628
  selectedItem: ".menuselect-item.selected",
@@ -1649,6 +1721,18 @@ var menuSelectContract = {
1649
1721
  name: "clicking an item selects it",
1650
1722
  action: { kind: "click", part: "item" },
1651
1723
  expect: { kind: "attribute", part: "item", name: "aria-selected", value: "true" }
1724
+ },
1725
+ {
1726
+ name: "Enter selects an item",
1727
+ rule: "a11y-keyboard",
1728
+ action: { kind: "press", part: "item", key: "Enter" },
1729
+ expect: { kind: "activated", part: "item" }
1730
+ },
1731
+ {
1732
+ name: "arrow down moves focus to the next item",
1733
+ rule: "a11y-keyboard",
1734
+ action: { kind: "press", part: "item", key: "ArrowDown" },
1735
+ expect: { kind: "focused", part: "secondItem", value: true }
1652
1736
  }
1653
1737
  ]
1654
1738
  },
@@ -1971,7 +2055,8 @@ var radioButtonContract = {
1971
2055
  root: ".radio-button",
1972
2056
  dot: ".radio-dot",
1973
2057
  dotFill: { selector: ".radio-dot-fill" },
1974
- label: ".radio-label"
2058
+ label: ".radio-label",
2059
+ secondOption: ".radio-button:nth-of-type(2)"
1975
2060
  },
1976
2061
  properties: [
1977
2062
  { paints: statePaints4("default", true) },
@@ -2014,14 +2099,27 @@ var radioButtonContract = {
2014
2099
  },
2015
2100
  observe: { part: "dot", css: "borderTopWidth", variable: "--radiobutton-default-dot-border-width" }
2016
2101
  },
2102
+ boundaries: [{ part: "dot", label: "Circle" }],
2017
2103
  interaction: {
2018
2104
  part: "root",
2019
- role: "button",
2105
+ role: "radio",
2020
2106
  cases: [
2021
2107
  {
2022
2108
  name: "clicking a radio option focuses it",
2023
2109
  action: { kind: "click", part: "root" },
2024
2110
  expect: { kind: "focused", part: "root", value: true }
2111
+ },
2112
+ {
2113
+ name: "Space selects a radio option",
2114
+ rule: "a11y-keyboard",
2115
+ action: { kind: "press", part: "root", key: "Space" },
2116
+ expect: { kind: "activated", part: "root" }
2117
+ },
2118
+ {
2119
+ name: "arrow down moves focus to the next radio option",
2120
+ rule: "a11y-keyboard",
2121
+ action: { kind: "press", part: "root", key: "ArrowDown" },
2122
+ expect: { kind: "focused", part: "secondOption", value: true }
2025
2123
  }
2026
2124
  ]
2027
2125
  },
@@ -2186,6 +2284,7 @@ var segmentedControlContract = {
2186
2284
  root: ".segmented-control",
2187
2285
  segment: ".segment",
2188
2286
  icon: ".segment i",
2287
+ secondSegment: ".segment:nth-of-type(2)",
2189
2288
  selectedSegment: ".segment.selected",
2190
2289
  selectedIcon: ".segment.selected i",
2191
2290
  hairline: ".segment-hairline"
@@ -2339,6 +2438,10 @@ var segmentedControlContract = {
2339
2438
  },
2340
2439
  observe: { part: "root", css: "borderTopWidth", variable: "--segmentedcontrol-bar-border-width" }
2341
2440
  },
2441
+ boundaries: [
2442
+ { part: "root", label: "Bar" },
2443
+ { part: "selectedSegment", label: "Selected option" }
2444
+ ],
2342
2445
  interaction: {
2343
2446
  part: "segment",
2344
2447
  role: "radio",
@@ -2348,6 +2451,18 @@ var segmentedControlContract = {
2348
2451
  action: { kind: "click", part: "segment" },
2349
2452
  expect: { kind: "attribute", part: "segment", name: "aria-checked", value: "true" }
2350
2453
  },
2454
+ {
2455
+ name: "Space checks a segment",
2456
+ rule: "a11y-keyboard",
2457
+ action: { kind: "press", part: "segment", key: "Space" },
2458
+ expect: { kind: "activated", part: "segment" }
2459
+ },
2460
+ {
2461
+ name: "arrow right moves focus to the next segment",
2462
+ rule: "a11y-keyboard",
2463
+ action: { kind: "press", part: "segment", key: "ArrowRight" },
2464
+ expect: { kind: "focused", part: "secondSegment", value: true }
2465
+ },
2351
2466
  {
2352
2467
  name: "a disabled control refuses focus",
2353
2468
  state: "disabled option",
@@ -2583,6 +2698,12 @@ var sideNavigationContract = {
2583
2698
  },
2584
2699
  observe: { part: "root", css: "borderRightWidth", variable: "--sidenavigation-panel-border-width" }
2585
2700
  },
2701
+ boundaries: [
2702
+ { part: "title", label: "Selected title", when: ".selected" },
2703
+ { part: "sectionSelected", label: "Selected section" },
2704
+ { part: "itemSelected", label: "Selected item" },
2705
+ { part: "footer", label: "Selected footer", when: ".selected, .force-footer-selected .sn-footer" }
2706
+ ],
2586
2707
  interaction: {
2587
2708
  part: "toggle",
2588
2709
  role: "button",
@@ -2591,6 +2712,12 @@ var sideNavigationContract = {
2591
2712
  name: "clicking the toggle collapses the rail",
2592
2713
  action: { kind: "click", part: "toggle" },
2593
2714
  expect: { kind: "attribute", part: "toggle", name: "aria-expanded", value: "false" }
2715
+ },
2716
+ {
2717
+ name: "Enter activates the toggle",
2718
+ rule: "a11y-keyboard",
2719
+ action: { kind: "press", part: "toggle", key: "Enter" },
2720
+ expect: { kind: "activated", part: "toggle" }
2594
2721
  }
2595
2722
  ]
2596
2723
  },
@@ -2748,10 +2875,14 @@ var sliderContract = {
2748
2875
  ],
2749
2876
  aliasedTo: {
2750
2877
  "--slider-single-track-surface": "--surface-neutral-lowest",
2751
- "--slider-single-fill": "--surface-brand-high"
2878
+ "--slider-single-fill": "--border-brand-medium"
2752
2879
  },
2753
2880
  observe: { part: "track", css: "backgroundColor", variable: "--slider-single-track-surface" }
2754
2881
  },
2882
+ boundaries: [
2883
+ { part: "track", label: "Track" },
2884
+ { part: "thumb", label: "Thumb" }
2885
+ ],
2755
2886
  interaction: {
2756
2887
  part: "input",
2757
2888
  role: "slider",
@@ -2781,6 +2912,18 @@ var sliderContract = {
2781
2912
  action: { kind: "dragTo", part: "thumb", along: "track", fraction: 0.5 },
2782
2913
  expect: { kind: "focused", part: "input", value: true }
2783
2914
  },
2915
+ {
2916
+ name: "Page Up raises the value",
2917
+ rule: "a11y-keyboard",
2918
+ action: { kind: "press", part: "input", key: "PageUp" },
2919
+ expect: { kind: "valueMoves", part: "input", direction: "up" }
2920
+ },
2921
+ {
2922
+ name: "End takes the value to the ceiling",
2923
+ rule: "a11y-keyboard",
2924
+ action: { kind: "press", part: "input", key: "End" },
2925
+ expect: { kind: "valueBecomes", part: "input", value: "100" }
2926
+ },
2784
2927
  {
2785
2928
  name: "a disabled slider ignores the arrow keys",
2786
2929
  state: "disabled",
@@ -2873,7 +3016,8 @@ var tabBarContract = {
2873
3016
  // Position-stable: clicking `tab` (".tab:not(.selected)") makes that very
2874
3017
  // tab selected, which would invalidate that selector out from under a
2875
3018
  // focus check re-resolved after the click.
2876
- secondTab: ".tab:nth-of-type(2)"
3019
+ secondTab: ".tab:nth-of-type(2)",
3020
+ firstTab: ".tab:nth-of-type(1)"
2877
3021
  },
2878
3022
  properties: [
2879
3023
  {
@@ -2939,15 +3083,28 @@ var tabBarContract = {
2939
3083
  },
2940
3084
  observe: { part: "tab", css: "borderTopWidth", variable: "--tabbar-default-tab-border-width" }
2941
3085
  },
3086
+ boundaries: [{ part: "tabSelected", label: "Selected tab" }],
2942
3087
  interaction: {
2943
3088
  part: "secondTab",
2944
- role: "button",
3089
+ role: "tab",
2945
3090
  cases: [
2946
3091
  {
2947
3092
  name: "clicking a tab focuses it",
2948
3093
  action: { kind: "click", part: "secondTab" },
2949
3094
  expect: { kind: "focused", part: "secondTab", value: true }
2950
3095
  },
3096
+ {
3097
+ name: "Enter activates a tab",
3098
+ rule: "a11y-keyboard",
3099
+ action: { kind: "press", part: "secondTab", key: "Enter" },
3100
+ expect: { kind: "activated", part: "secondTab" }
3101
+ },
3102
+ {
3103
+ name: "arrow right moves focus to the next tab",
3104
+ rule: "a11y-keyboard",
3105
+ action: { kind: "press", part: "firstTab", key: "ArrowRight" },
3106
+ expect: { kind: "focused", part: "secondTab", value: true }
3107
+ },
2951
3108
  {
2952
3109
  name: "a disabled tab refuses focus",
2953
3110
  state: "disabled tab",
@@ -2975,6 +3132,11 @@ var tabBarContract = {
2975
3132
  props: { tabs: [behaviorTabs[0], { ...behaviorTabs[1], disabled: true }], value: "one" },
2976
3133
  action: { kind: "click", part: "secondTab" },
2977
3134
  expect: { kind: "no-callback", prop: "onchange" }
3135
+ },
3136
+ {
3137
+ name: "an icon-only tab takes its label as its name",
3138
+ props: { tabs: behaviorTabs, value: "one", iconOnly: true },
3139
+ expect: { kind: "attribute", part: "secondTab", name: "aria-label", value: "Two" }
2978
3140
  }
2979
3141
  ]
2980
3142
  },
@@ -3176,21 +3338,30 @@ var toggleContract = {
3176
3338
  },
3177
3339
  observe: { part: "track", css: "borderTopWidth", variable: "--toggle-track-border-width" }
3178
3340
  },
3341
+ boundaries: [
3342
+ { part: "track", label: "Track" },
3343
+ { part: "thumb", label: "Thumb" }
3344
+ ],
3179
3345
  interaction: {
3180
3346
  part: "root",
3181
3347
  role: "switch",
3182
- // No activation case: Toggle.svelte is controlled (`onclick` only calls
3183
- // `onchange?.(!checked)`, it never flips `checked` itself), and
3184
- // ToggleEditor.svelte's preview drives `checked` off the state tab
3185
- // without wiring `onchange` back to it. Clicking the real control in this
3186
- // demo has no observable effect on `aria-checked`, so an activation case
3187
- // would assert a no-op rather than the switch's behavior.
3348
+ // Activation reads as a click on the switch: Toggle.svelte is controlled
3349
+ // (`onclick` only calls `onchange?.(!checked)`, it never flips `checked`
3350
+ // itself), and ToggleEditor.svelte's preview drives `checked` off the
3351
+ // state tab without wiring `onchange` back to it, so `aria-checked` never
3352
+ // moves in this demo.
3188
3353
  cases: [
3189
3354
  {
3190
3355
  name: "clicking a toggle focuses it",
3191
3356
  action: { kind: "click", part: "root" },
3192
3357
  expect: { kind: "focused", part: "root", value: true }
3193
3358
  },
3359
+ {
3360
+ name: "Space activates a toggle",
3361
+ rule: "a11y-keyboard",
3362
+ action: { kind: "press", part: "root", key: "Space" },
3363
+ expect: { kind: "activated", part: "root" }
3364
+ },
3194
3365
  {
3195
3366
  name: "a disabled toggle refuses focus",
3196
3367
  state: "disabled",
@@ -3418,6 +3589,19 @@ var videoLightboxContract = {
3418
3589
  name: "clicking the still opens the lightbox",
3419
3590
  action: { kind: "click", part: "trigger" },
3420
3591
  expect: { kind: "attribute", part: "trigger", name: "aria-expanded", value: "true" }
3592
+ },
3593
+ // The click case above leaves the lightbox open with focus inside it.
3594
+ {
3595
+ name: "Escape closes the lightbox",
3596
+ rule: "a11y-keyboard",
3597
+ action: { kind: "press", key: "Escape" },
3598
+ expect: { kind: "attribute", part: "trigger", name: "aria-expanded", value: "false" }
3599
+ },
3600
+ {
3601
+ name: "Enter on the still opens the lightbox",
3602
+ rule: "a11y-keyboard",
3603
+ action: { kind: "press", part: "trigger", key: "Enter" },
3604
+ expect: { kind: "attribute", part: "trigger", name: "aria-expanded", value: "true" }
3421
3605
  }
3422
3606
  ]
3423
3607
  },
@@ -3644,6 +3828,19 @@ var slidePagerContract = {
3644
3828
  name: "the next chevron moves the frame to the next slide",
3645
3829
  action: { kind: "click", part: "nextControl" },
3646
3830
  expect: { kind: "attribute", part: "stage", name: "aria-label", value: "Open slide 3 full screen" }
3831
+ },
3832
+ {
3833
+ name: "Escape closes the full-screen slide",
3834
+ rule: "a11y-keyboard",
3835
+ setup: [{ kind: "click", part: "stage" }],
3836
+ action: { kind: "press", key: "Escape" },
3837
+ expect: { kind: "attribute", part: "stage", name: "aria-expanded", value: "false" }
3838
+ },
3839
+ {
3840
+ name: "Enter on the frame opens the full-screen slide",
3841
+ rule: "a11y-keyboard",
3842
+ action: { kind: "press", part: "stage", key: "Enter" },
3843
+ expect: { kind: "attribute", part: "stage", name: "aria-expanded", value: "true" }
3647
3844
  }
3648
3845
  ]
3649
3846
  },
@@ -3735,4 +3932,4 @@ export {
3735
3932
  allContracts,
3736
3933
  selectedContracts
3737
3934
  };
3738
- //# sourceMappingURL=chunk-SHBYZUPD.js.map
3935
+ //# sourceMappingURL=chunk-RWOT2XDB.js.map