@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
@@ -26,6 +26,7 @@ Both checkers are static. They read the project's source, never open a browser,
26
26
  - the fixes the checkers applied, each with its count and any visible shift
27
27
  - the remaining changes by rule, each with its count and any visible shift
28
28
  - the findings left, each with its reason and any config entry the user chose
29
+ - the `a11y-*` alerts, each with its element and guidance, for the user to review
29
30
  - both checker commands with their exit codes
30
31
 
31
32
  `check-page <path>` scopes a run to one page. `check-component <id>` scopes a run to one component: its runtime, its editor, and its registration. The checkers read tokens.css from its default location.
@@ -50,6 +51,10 @@ Every finding's `repair` says what moving it costs.
50
51
  - **`choice`.** A role or an ambiguous value determines the fix, such as a color literal and the role it plays, or a spacing literal tied between two steps. `details` lists the candidates, and `guidance` says how to pick one.
51
52
  - **`authored`.** New code is the fix: a runtime that has to start behaving, an editor schema that has to name a token, a route that has to move. `guidance` names what to write. No candidate list applies.
52
53
 
54
+ ## Accessibility alerts
55
+
56
+ A `--tests` run adds the `a11y-*` rules, which test the rendered page or component against WCAG 2 A and AA with axe, Siteimprove Alfa, the contract's keyboard cases, and the suite's own focus indicator, control boundary, text spacing, and reduced motion checks. Each reports at `warn` and blocks only under `--strict`. Every `a11y-*` finding is an alert the user reviews: list it in the reply, and repair it from its guidance when the repair scope includes warnings. An `a11y-review` finding marks a check axe could not decide, such as text over a gradient, and the user judges it by eye. Inside the live-tokens repository, an `a11y-component` finding with engine rule `aria-snapshot` marks an accessibility tree that changed since it was recorded; after a deliberate markup change, `check-component <id> --tests --update-aria` records it anew.
57
+
53
58
  ## Scope
54
59
 
55
60
  - Add no token to `tokens.css`. Map a literal with no matching token to the nearest existing token by role. When no token fits, leave the finding and say so.
@@ -161,11 +161,12 @@ The excerpt shows the chain for part of the property map. Every editable value r
161
161
 
162
162
  ## Component editor
163
163
 
164
- Create `src/system/components/StatCardEditor.svelte` beside the runtime file. The editor has three parts.
164
+ Create `src/system/components/StatCardEditor.svelte` beside the runtime file. The editor has four parts.
165
165
 
166
166
  1. A `<script module>` block exports `component`, the id, and `allTokens`, one row per property in the map. A row is `{ label, variable, element? }`; `element` groups rows in the panel by part, and `label` names the property in the row.
167
167
  2. The instance script imports the runtime component and the editor primitives from the package's public paths, and maps the state being edited to preview props.
168
168
  3. The markup mounts `ComponentEditorBase` with one `VariantGroup` per variant, each rendering a preview.
169
+ 4. The same `<script module>` block exports `contrastPairs`, one pair for each foreground color and each backdrop it sits on, and the registration passes it as `contrastPairs`. Every `allTokens` color with a text or icon suffix (`-text`, `-label`, `-icon`, `-title`, `-body`, `-value`, and the other text roles in `references/token-naming.md`) is the foreground of at least one pair. A pair is `{ foreground, backdrop, role, fontSize?, fontWeight?, inactive?, context? }`. `backdrop` lists the properties the runtime paints behind the foreground, top layer first, and leaves out `--page-bg`, which the editor puts under the last layer. `role` is `'text'`, or `'non-text'` for an icon or another mark. A text pair names its `-font-size` property as `fontSize`, the smaller one when the text renders at two sizes, and its `-font-weight` property as `fontWeight`. A disabled state's pair sets `inactive: true`, because WCAG exempts disabled controls. A foreground over several backdrops takes one pair per backdrop, each with its own `context`. A pair names only properties in `allTokens` and intrinsic variables, and no two pairs share a foreground, a backdrop, and a context.
169
170
 
170
171
  ```svelte
171
172
  <script module lang="ts">
@@ -224,7 +225,7 @@ registerComponent({
224
225
  });
225
226
  ```
226
227
 
227
- Import the module from `src/main.ts`, before `bootLiveTokens` or `mount`, and name it as `registrySetup` in `live-tokens.testing.ts`. One module then serves the running app and `check-component --tests`, which imports it to see the registration without mounting the app. The component's contract, one `ComponentContract` in the module `contractsModule` names, is what the eight component suites run; without it `--tests` reports `contract-missing`. Read `references/contract-tests.md` for both. A component that declares intrinsics adds `intrinsics` to the entry. `check-component` finds the registration by the id literal inside the call.
228
+ Import the module from `src/main.ts`, before `bootLiveTokens` or `mount`, and name it as `registrySetup` in `live-tokens.testing.ts`. One module then serves the running app and `check-component --tests`, which imports it to see the registration without mounting the app. The component's contract, one `ComponentContract` in the module `contractsModule` names, is what the eight component suites run; without it `--tests` reports `contract-missing`. Read `references/contract-tests.md` for both. An interactive component's contract declares a keyboard case for each key its WAI-ARIA pattern handles: Enter or Space activates, an arrow key moves where the pattern moves, and Escape closes an overlay. A component that declares intrinsics adds `intrinsics` to the entry. `check-component` finds the registration by the id literal inside the call.
228
229
 
229
230
  At boot the plugin reads the `:global(:root)` block and writes `component-configs/<id>/default.json`, one token per property. An edit in the editor writes `_working.json`; Save As writes a named config. The assignments stay token references through that flow.
230
231
 
@@ -239,7 +240,7 @@ A fixed overlay portals to `<body>`: read `references/fixed-overlays.md`. A cont
239
240
  ## Verification
240
241
 
241
242
  1. Run `npx live-tokens check-component <id> --tests --strict --json`. Inside the live-tokens repository, run `node bin/cli.mjs check-component <id> --tests --strict --json`. It applies every `auto` repair, runs the registry contract and, for a component with a contract in the module `contractsModule` names, the component contract suites, and returns the fixes it applied, the findings that remain, and coverage by rule. `--off=<rule>` silences a rule for one run, and `--no-fix` reports without editing.
242
- 2. Each remaining finding carries a rule id, a line, and its `guidance`. Make each repair from its guidance, and run the command again until it exits 0 with complete applicable coverage and no disabled checks.
243
+ 2. Each remaining finding carries a rule id, a line, and its `guidance`. Make each repair from its guidance, and run the command again until it exits 0 with complete applicable coverage and no disabled checks. The `a11y-*` findings report accessibility at `warn`, and `--strict` makes them block with the rest. An `a11y-review` finding marks a check axe could not decide, such as text over a gradient: judge it by eye against its help page, and repair it when it fails. Name the ones that pass in the reply, and add `--off=a11y-review` to the final run, the one disabled check this step allows. Inside the live-tokens repository, an `aria-snapshot` finding marks an accessibility tree that changed since it was recorded; after a deliberate markup change, rerun with `--update-aria`.
243
244
  3. Run the project's Svelte check and its build.
244
245
  4. Reply with the files, the component id, the props, and the result of each check, naming any check the environment prevented.
245
246
 
@@ -119,10 +119,10 @@ export const contracts: ComponentContract[] = [
119
119
  },
120
120
  theme: {
121
121
  theme: 'brand',
122
- changed: ['--statcard-radius'],
123
- unchanged: ['--statcard-padding'],
124
- aliasedTo: { '--statcard-radius': '--radius-none' },
125
- observe: { part: 'root', css: 'borderRadius', variable: '--statcard-radius' },
122
+ changed: ['--statcard-surface'],
123
+ unchanged: ['--statcard-radius', '--statcard-padding'],
124
+ aliasedTo: { '--statcard-surface': '--surface-neutral' },
125
+ observe: { part: 'root', css: 'backgroundColor', variable: '--statcard-surface' },
126
126
  },
127
127
  interaction: { applicable: false, reason: 'a stat card carries no interactive role' },
128
128
  sketch: {
@@ -145,18 +145,78 @@ Each obligation and what it needs from the project:
145
145
  forces. `applicable: false` when the editor renders no state strip.
146
146
  - `persistence`: an edit the run makes through the editor, saves, reloads, and
147
147
  reads back. `resetVariable` is a property one of the cases moves.
148
- - `theme`: a theme in the project's data tree whose block for the component
149
- reassigns a property. Save one with the editor before the first run.
150
- `changed` names the properties the theme moves, `unchanged` the ones it
151
- leaves, and `aliasedTo` the design token each reassigned property resolves
152
- to under it.
153
- - `interaction`: the interactive part's role and its cases. `applicable:
154
- false` for a component with no interactive role.
148
+ - `theme`: a saved theme in the project's data tree, previewed as it stands.
149
+ The run checks that the component follows the theme's design tokens.
150
+ `changed` names the properties the theme moves through its design tokens,
151
+ such as colors, shadows, and fonts, `unchanged` the ones it leaves, and `aliasedTo` the design token each
152
+ changed property resolves to under it. A theme assigns geometry steps only
153
+ to the components it holds a block for, so a new component's radius,
154
+ padding, and border width belong in `unchanged`.
155
+ - `interaction`: the interactive part's role and its cases, keyboard cases
156
+ among them. See Keyboard cases below. `applicable: false` for a component
157
+ with no interactive role.
158
+ - `boundaries`: optional. The parts that draw a control's visible edge,
159
+ such as a text field or a switch track, each with a label:
160
+ `[{ part: 'field', label: 'Field' }]`. The accessibility suite measures
161
+ each part's border and surface against the backdrop behind it at 3:1, per
162
+ WCAG 2.2 SC 1.4.11. A component with no control edge leaves it out.
155
163
  - `behavior`: what a callback prop does. See Behavior below.
156
164
  - `sketch`: a sketch style id and the fill and stroke each reserved-class part
157
165
  resolves to under it. `applicable: false` only for a component
158
166
  `references/sketch-mode.md` exempts.
159
167
 
168
+ ## Keyboard cases
169
+
170
+ An interactive component declares one `press` case for each key its WAI-ARIA
171
+ pattern handles: Enter or Space activates, an arrow key moves focus or value
172
+ where the pattern moves it, and Escape closes an overlay. Each sets
173
+ `rule: 'a11y-keyboard'`. A failed keyboard case reports `a11y-keyboard` at
174
+ `warn` and lets the test pass; a case without `rule` fails as
175
+ `contract-interaction`, an error.
176
+
177
+ ```ts
178
+ interaction: {
179
+ part: 'secondTab',
180
+ role: 'button',
181
+ cases: [
182
+ {
183
+ name: 'Enter activates a tab',
184
+ rule: 'a11y-keyboard',
185
+ action: { kind: 'press', part: 'secondTab', key: 'Enter' },
186
+ expect: { kind: 'activated', part: 'secondTab' },
187
+ },
188
+ {
189
+ name: 'arrow right moves focus to the next tab',
190
+ rule: 'a11y-keyboard',
191
+ action: { kind: 'press', part: 'firstTab', key: 'ArrowRight' },
192
+ expect: { kind: 'focused', part: 'secondTab', value: true },
193
+ },
194
+ ],
195
+ },
196
+ ```
197
+
198
+ - `press` focuses `part` and sends `key`. Without `part`, the key goes to the
199
+ element that holds focus, such as an overlay that focused itself as it
200
+ opened.
201
+ - `activated` passes when the key fired a click on the part, as a native
202
+ button does for Enter and Space. The preview never feeds a controlled
203
+ component's callback back into its props, so a click is the activation it
204
+ shows.
205
+ - `focused` with `value: true` names the part an arrow key moves focus to.
206
+ Arrow cases need parts that keep their position as the selection moves,
207
+ such as `firstTab` and `secondTab`.
208
+ - `present` with `value: false` passes when the part has left the document,
209
+ as a dialog's root does when Escape closes it:
210
+ `{ kind: 'present', part: 'root', value: false }`.
211
+
212
+ The accessibility suite reads the same contract. It scans every view that
213
+ `properties` and `states` name with axe and Siteimprove Alfa's rule SIA-R65,
214
+ measures focus indicators and the `boundaries` parts at 3:1, checks text
215
+ spacing and reduced motion, and reports each failure at `warn` under an
216
+ `a11y-*` rule. Mark placeholder
217
+ content the editor adds to its preview, such as a demo label, with
218
+ `data-preview-demo`, and the scans skip it.
219
+
160
220
  ## Behavior
161
221
 
162
222
  `behavior` runs under Vitest with `mount` from Svelte and happy-dom, not in
package/CHANGELOG.md CHANGED
@@ -1,5 +1,344 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.99.0 — Accessibility reports by theme
4
+
5
+ ### Changed
6
+
7
+ - **Each theme keeps its own accessibility report, in the data directory.**
8
+ The dev server writes a theme's last finished run to
9
+ `<dataDir>/_a11y-reports/<theme>.json`, and the editor shows the open
10
+ theme's report. The single report used to sit in `test-results/live-tokens/`,
11
+ which Playwright and other test tools clear, so a test run could erase it.
12
+ A failed run now leaves the theme's last report in place, and deleting a
13
+ theme deletes its report. `GET /a11y/report` answers `{ running, reports }`.
14
+ Add `_a11y-reports/` to the project's `.gitignore`; projects made with
15
+ `create` now ignore it. Test runs leave it out of their copy of the data
16
+ tree.
17
+ - **A contrast failure fixed since the run turns green.** The editor
18
+ re-measures each contrast failure of the last run as you edit. Once one
19
+ passes, its row in the component's panel and in the Accessibility view
20
+ shows the run's ratio and the ratio now, and it no longer counts as an
21
+ issue.
22
+ - **Component defaults and the shipped themes pass WCAG AA contrast.**
23
+ Every contrast pair the editor checks now passes in the default theme and
24
+ all eight presets. Slider: fill `--border-brand-medium`, thumb and hover
25
+ thumb `--text-primary`. Toggle: resting thumb `--text-primary`, as its
26
+ other states already were. ProgressBar: fill `--border-special-medium`.
27
+ SlidePager: hover icon `--text-primary`. TabBar: selected underline
28
+ `--border-brand-strong`. Input: focus ring `--border-brand-strong`.
29
+ Button secondary, success and warning hovers and the MenuSelect hover sit
30
+ one surface step lighter, and InlineEditActions' cancel hover icon is
31
+ `--text-primary`. Halloween's page gradient ends on canvas 800. Themes you
32
+ saved keep their own values.
33
+ - **The Accessibility overview table lists components by the fixes a theme
34
+ can make.** Rows sort by "Fix with a property", then by "Fix in the
35
+ component's code". The bar column and its colour key are gone.
36
+ - **Results to check by eye carry an eye.** The component list, the
37
+ component panel, and each property row draw a grey eye for them in place of
38
+ the warning triangle or the "?". They no longer count toward a component's
39
+ issues.
40
+
41
+ ### Added
42
+
43
+ - **Looks right marks a result to check by eye as judged.** Each such row in
44
+ the Accessibility view and the component panel has the button at its right
45
+ edge. A marked row dims, stops counting, and drops its marks from the
46
+ editor. The open theme's report keeps the mark through every later run
47
+ that finds the same result. The dev server stores it in the report and
48
+ answers `PUT /a11y/confirmed` with `{ theme, messages, confirmed }`.
49
+ - **The By eye list gathers every result to check by eye.** It closes the
50
+ Accessibility view: the results to check first, under a tally and a
51
+ progress bar, then the marked ones in a closed group to revisit. Marking one moves focus to
52
+ the next. **Mark all as looking right** marks the rest in one step. A
53
+ result marked as looking right raises nothing elsewhere: no sidebar mark,
54
+ no component panel, and no property mark. A component whose results all
55
+ look right leaves the overview table, the component sections, and the
56
+ sidebar.
57
+
58
+ ### Fixed
59
+
60
+ - **Contrast checks pass parts that paint nothing or only decorate.** A non-text part
61
+ painted fully transparent, such as an unselected tab's underline, passes.
62
+ Section divider hairlines are decorative and carry no contrast pair.
63
+ - **The focus checks judge what focus changes.** A state view that forces
64
+ the focused look, such as Input's, lifts it while focus is measured, so
65
+ SIA-R65 sees the change and the focus ring's contrast is measured. A ring
66
+ drawn on a sibling, as Slider draws it, settles SIA-R65's "can't tell". A
67
+ ring that spills past the preview is measured against `--page-bg` and no
68
+ longer against the editor's own surfaces.
69
+
70
+ ## 0.98.2 — Accessibility testing
71
+
72
+ The v0.98.0 and v0.98.1 tags never reached npm. Release checks stopped both:
73
+ a test time limit and a rule count the accessibility suite outgrew.
74
+
75
+ ### Added
76
+
77
+ - **`check-component --tests` scans components for accessibility.** A new
78
+ suite opens every variant and state a component's contract names and runs
79
+ axe-core against WCAG 2 A and AA and Siteimprove Alfa's focus-visibility
80
+ rule SIA-R65 over the preview. Findings report as `a11y-component`,
81
+ `a11y-focus-visible`, and `a11y-review`, the last for checks axe could not
82
+ decide, such as text over a gradient. Editor demo content marked
83
+ `data-preview-demo` stays out of the scan. The library's own test run
84
+ repeats axe's contrast rule under the eight preset themes.
85
+ - **`check-page --tests` scans pages for accessibility.** Each page and
86
+ viewport gains three tests. `page-a11y` runs axe outside the shipped
87
+ components, with contrast left to `page-contrast`. `page-text-spacing`
88
+ reports overflow the WCAG 2.2 SC 1.4.12 text spacing override adds.
89
+ `page-reduced-motion` reports animations still running 200 ms after load
90
+ under `prefers-reduced-motion: reduce`. They report as `a11y-page`,
91
+ `a11y-review`, `a11y-text-spacing`, and `a11y-reduced-motion`.
92
+ - **Accessibility findings alert by default.** All seven `a11y-*` rules
93
+ report at `warn`, and `--strict` makes them errors, as it does every other
94
+ warning. `--off` and `live-tokens.config.json` silence or raise each one.
95
+ A scan that crashes reports `tests-setup`.
96
+ - **Each shipped component's accessibility tree is recorded.** The trees
97
+ live in `src/testing/contracts/__aria__/<id>.yml`, one block per view. A
98
+ run that reads a different tree reports `a11y-component` with engine rule
99
+ `aria-snapshot`, and `check-component --tests --update-aria` records the
100
+ trees anew after a deliberate markup change. A project's own components
101
+ get no recorded tree yet.
102
+ - **Component contracts take keyboard cases.** An `InteractionCase` with
103
+ `rule: 'a11y-keyboard'` reports its failure as an `a11y-keyboard` warning
104
+ and lets the test pass. A `press` action may omit `part`
105
+ to send the key to whatever holds focus. `InteractionOutcome` gains
106
+ `activated`, for a key that fired a click on the part, and `present`, for
107
+ whether the part is in the document. The fifteen interactive shipped
108
+ components carry 27 keyboard cases for activation, arrow movement, and
109
+ Escape.
110
+ - **The accessibility engines ship as optional peers.** `axe-core` 4.11.1 or
111
+ later, `@axe-core/playwright`, and seven `@siteimprove/alfa-*` packages at
112
+ 0.119 join `peerDependencies` as optional, and the `create` template
113
+ installs them. Without one, the tests it drives report as inapplicable and
114
+ the run carries on.
115
+ - **The editor gains an Accessibility view.** Run test checks every
116
+ component in the open theme as saved, in about three and a half minutes,
117
+ and lists each component's findings by repair. An overview opens the
118
+ report with the total, the count in each repair group, and a table of every
119
+ component with a finding, whose names scroll to their sections. Each
120
+ section's **Open component** opens the component's page at its
121
+ accessibility panel. "Fix with a property" holds
122
+ the colour pairs the editor's contrast checker finds below their floor and
123
+ the focus rings and control boundaries below 3:1. "Fix in the component's
124
+ code" holds every other finding: axe failures, unchanged focus styles,
125
+ failed keyboard cases, clipped text, and motion that ignores reduced
126
+ motion. Results to check by eye count in the totals and collect in one
127
+ closed list. Findings that share an engine rule, a help text, and their
128
+ traced properties merge into one row that names its element count and
129
+ views, and each traced property gets a "→". A row's right-hand link opens
130
+ the rule, success criterion, technique, or pattern the finding cites and
131
+ names its kind. A property's label leads with
132
+ the words from its variable name that the editor's label leaves out, such
133
+ as "Focused border color". With edits pending, Run test asks for a save and
134
+ starts nothing. A missing test tool shows its install command. The dev
135
+ server runs one check at a time and keeps the last report in
136
+ `test-results/live-tokens/a11y-report.json`.
137
+ - **The editor marks the components the last run named.** Every finding
138
+ marks its component, results to check by eye included. The component list
139
+ leads each one's name with a warning triangle, in the sidebar, the narrow
140
+ menu, and the standalone component page. A condensed list shows the
141
+ triangle on the component's icon. Each one's page opens with a panel under its
142
+ title that lists its findings in three groups: "Fix with a property", "Fix
143
+ in the component's code", and "Check by eye". Each "→" in the panel opens
144
+ the tab holding the property, an intrinsic's hover tab included, and
145
+ highlights its row. The marks follow the last finished run; the contrast
146
+ readout on each colour row stays live.
147
+ - **Property rows carry the run's findings.** A row whose property a finding
148
+ traces to shows a warning triangle beside its label, or a grey "?" when
149
+ every finding on it is a result to check by eye. The mark opens a popover that lists the
150
+ findings with their elements, views, help pages, and traced properties.
151
+ Token selectors, gradient editors, and toggle rows carry it. The editor's own
152
+ contrast failures add no row mark, because the live readout already shows
153
+ them.
154
+ - **Accessibility findings name the properties that fix them.** The
155
+ component suite reads the CSS rules that match each failing element
156
+ through the Chrome DevTools protocol and follows each `var()` to the
157
+ component's semantic properties and intrinsics. `check-component --tests
158
+ --json` lists them, sorted, in each finding's `details.variables`, and
159
+ leaves the field out when the trace finds nothing. The field is additive.
160
+ - **The component suite runs four more checks.** Focus indicator contrast
161
+ and control boundary contrast measure a focus ring and a control's edge at
162
+ 3:1 against the backdrop, under WCAG 2.2 SC 1.4.11. Text spacing applies
163
+ the WCAG 2.2 SC 1.4.12 override inside each component and reports text
164
+ that clips under it. Reduced motion reports motion longer than 200 ms that
165
+ still runs under `prefers-reduced-motion: reduce`, after WCAG technique
166
+ C39 for SC 2.3.3. The results are warnings under the existing rules:
167
+ `a11y-component` (engine rules `focus-indicator-contrast` and
168
+ `control-boundary-contrast`), `a11y-review` for a ring or edge over a
169
+ gradient or image, `a11y-text-spacing`, and `a11y-reduced-motion`, which
170
+ `check-component` now reports too. `--strict` raises them with the rest.
171
+ - **Component contracts take `boundaries`.** The optional field,
172
+ `{ part: string; label: string }[]`, lists the parts that draw a control's
173
+ visible edge, for the control boundary check. Input, RadioButton, Slider,
174
+ Toggle, and SegmentedControl declare it.
175
+ - **Selected states join the control boundary check.** TabBar's selected
176
+ tab, SegmentedControl's selected option, and SideNavigation's selected
177
+ title, section, item, and footer show their state by surface and edge
178
+ alone, so their contracts list them in `boundaries`. Each measures against
179
+ the bar or panel behind it. A `boundaries` entry takes `when`, a selector
180
+ the part must also match, and the check reads an outline and every border
181
+ side and keeps the best. The trace follows only the winning declaration of
182
+ a private property, so a selected tab's finding names the selected tab's
183
+ properties alone.
184
+ - **Image Lightbox's editor sets the tile fit.** The Tile tab gains a "tile
185
+ fit" row, Contain or Cover, which writes `--imagelightbox-tile-object-fit`
186
+ and carries the accessibility mark. ImageLightbox's `fit` prop no longer
187
+ defaults to `contain` on every tile, so a tile without it follows the
188
+ theme's value.
189
+ - **`check-component --tests` takes `--a11y` and `--open-theme`.** `--a11y`
190
+ runs the accessibility suite and the keyboard cases alone, with no static
191
+ checks or fixes, and reports only `a11y-*` and `tests-*` findings.
192
+ `--open-theme` tests the theme the editor has open, as saved, and still
193
+ leaves every unsaved buffer out of the run.
194
+ - **`--json` output names the tested theme and each finding's component.**
195
+ `check-component --tests --json` adds a top-level `theme`, and each
196
+ Playwright finding that names a component carries it in a `component`
197
+ field. Both fields are additive.
198
+ - **The guide gains an Accessibility chapter.** It covers the suites, the
199
+ rules, the focus indicator, boundary, text spacing, and motion checks,
200
+ property tracing, review findings, the recorded trees, keyboard cases,
201
+ `boundaries`, the Accessibility view, the marks in the editor, the
202
+ engines, and the limits.
203
+ - **The skills cover accessibility.** `live-tokens-check-compliance` lists
204
+ the `a11y-*` findings as alerts for the user to review.
205
+ `live-tokens-create-component` declares keyboard cases for a new component
206
+ and judges `a11y-review` findings by eye.
207
+ - **Component editors show the contrast ratio of every foreground color.** A
208
+ color row whose variable paints text, an icon or another foreground mark
209
+ shows the WCAG ratio between that color and the layers behind it, down to the
210
+ page background. A pair below its floor turns amber and gains an exclamation
211
+ mark. Click the ratio to open the rule it tracks, each pair's measured ratio
212
+ and layers, and a link to the success criterion. A disabled state shows its
213
+ ratio in a dimmer gray, because WCAG exempts disabled controls. The readout
214
+ recomputes after every edit and theme load, and the rows keep their places.
215
+ - **Editor modules and `registerComponent()` take `contrastPairs`.** Each pair
216
+ names a foreground variable, the variables that paint behind it top first, its
217
+ role (`text` or `non-text`), and for text its font size and weight. A pair
218
+ for a disabled state sets `inactive`. A foreground over several backdrops
219
+ takes one pair per backdrop, each with a `context`, and the readout shows the
220
+ pair closest to failing.
221
+ - **`UITokenSelector` takes a `metaAfter` snippet.** It renders after the value
222
+ name in the same column, and the value name truncates first.
223
+
224
+ ### Changed
225
+
226
+ - **Button's catalogue entry covers navigation.** A Button is a labelled
227
+ trigger that runs an action or opens another page, such as View details.
228
+ An anchor stays the choice for a link inside running text.
229
+
230
+ ### Fixed
231
+
232
+ - **Check reports reach a pipe whole.** On macOS, `check-component` and
233
+ `check-page` cut piped output at 64 KiB, because the process exited before
234
+ the pipe drained. Both checkers now write their report synchronously, so
235
+ `--json` output read through a pipe parses.
236
+ - **`page-overflow` passes a component inside a scrolling ancestor.** The
237
+ check reported a component as clipped by the nearest ancestor with any
238
+ overflow other than `visible`, so a Badge at the end of a Table row on a
239
+ phone failed against the Table's own `overflow-x: auto` wrapper. An
240
+ ancestor that scrolls sideways now keeps the component in reach, and only
241
+ one that hides overflow without scrolling clips it.
242
+ - **The theme contract example tests adherence to a saved theme.** The
243
+ example in `live-tokens-create-component`'s `references/contract-tests.md`
244
+ expected a theme to move a new component's radius, which only a theme block
245
+ for that component can do, and told the agent to save one first. The
246
+ example now expects the theme to move a color property through its design
247
+ tokens and lists the component's radius and padding as unchanged.
248
+ - **TabBar follows the WAI-ARIA tabs pattern.** The bar is a `tablist` of
249
+ `tab` elements that carry `aria-selected`. Tab reaches the selected tab
250
+ alone, the arrow keys move focus between enabled tabs and wrap, Home and
251
+ End jump to the first and last, and Enter or Space selects.
252
+ - **TabBar's selected indicator meets 3:1.** `--tabbar-selected-border` maps
253
+ to `--border-brand-medium` in place of `--color-brand-500`, which lifts the
254
+ underline from 2.96:1 to 3.47:1 against the default theme's page.
255
+ - **SegmentedControl follows the WAI-ARIA radio group pattern.** Tab reaches
256
+ the checked option alone, and the arrow keys move focus and selection to
257
+ the next or previous enabled option and wrap. A focused option shows a ring
258
+ outside its box in `--border-neutral-strong`, so focus on the selected
259
+ option reads apart from its brand outline.
260
+ - **SegmentedControl's selected outline meets 3:1.**
261
+ `--segmentedcontrol-selected-border` maps to `--border-brand-medium` in
262
+ place of `--border-brand`, which lifts the outline from 2.67:1 to 4.31:1
263
+ against the default theme's bar.
264
+ - **Control edges meet 3:1.** Input's field, RadioButton's circle in every
265
+ state, SegmentedControl's bar, Slider's track at both variants and Toggle's
266
+ track now draw their edge in `--border-neutral-medium`, in place of
267
+ `--border-neutral` (`--border-neutral-subtle` for Slider). Toggle's on
268
+ track and Slider's thumbs draw `--border-brand-medium` in place of
269
+ `--border-brand`. Against the default theme's page the neutral edges rise
270
+ from 2.42:1 to 3.97:1, Slider's track from 1.45:1, and Toggle's on track
271
+ from 2.14:1 to 3.47:1. Slider's focus ring, drawn in the thumb's border
272
+ colour, rises from 2.14:1 to 3.47:1.
273
+ - **The preset themes carry the 3:1 control edges.** Each preset holds its
274
+ own copy of the component mappings, so the eight presets now carry the new
275
+ edges above and TabBar's and SegmentedControl's selected borders. In
276
+ autumn, ocean, royal-velvet, sketchy and spring-meadow every control edge
277
+ and selected indicator now measures 3:1 or more, where autumn's Slider
278
+ track measured 1.4:1 and its selected tab 2.47:1. Halloween,
279
+ midnight-study and sunset paint a gradient page, so their edges stay a
280
+ check by eye.
281
+ - **VideoFrame, VideoLightbox and SlidePager draw a focus ring at 3:1.** The
282
+ trigger, the close button, the stage and the pager controls drew
283
+ `--ring-focus-md`, a brand colour at 10% opacity that measured 1.06:1 to
284
+ 1.09:1. Each now draws the neutral ring SegmentedControl uses, a
285
+ `--border-width-2` box shadow in `--border-neutral-strong`.
286
+ - **Badge, CornerBadge and Notification text meets 4.5:1.** The brand and
287
+ danger badges, and Notification's danger variant, sit on the next lower
288
+ surface step, `--surface-brand-low` and `--surface-danger-low`, which lifts
289
+ their text from 3.87:1 to 5.38:1 and from 3.30:1 to 4.56:1 on the default
290
+ theme. The canvas badges keep `--surface-canvas` and take
291
+ `--text-secondary`, since no canvas text step reaches 4.5:1 on a canvas
292
+ surface; they rise from 1.55:1 to 7.15:1. The eight presets carry the same
293
+ mappings, and under each of them axe reports no `color-contrast` failure
294
+ on the three components.
295
+ - **Input shows focus in its error state.** The red error outline replaced
296
+ the focus outline, so focusing an invalid field changed nothing visible.
297
+ Focus now adds a `--border-neutral-strong` ring outside the red outline.
298
+ - **Slider's thumbs take the pointer over 24px.** Each native thumb and its
299
+ input now measure `max(thumb size, --space-24)`, the WCAG 2.2 SC 2.5.8
300
+ minimum, while the visible cap keeps its own size. axe reported both range
301
+ thumbs under `target-size`, because their inputs share one box.
302
+ - **RadioButton follows the WAI-ARIA radio group pattern.** Each option is a
303
+ `radio` with `aria-checked`. Tab reaches the checked option alone, and the
304
+ arrow keys move focus and selection to the next or previous option and
305
+ wrap. A group is the nearest `role="radiogroup"` ancestor, else the parent
306
+ the options share; wrap a set in a `radiogroup` with a name.
307
+ - **TabBar's icon-only tabs carry a name.** With `iconOnly` set, each tab
308
+ takes its `label` as `aria-label`, and the icon is hidden from assistive
309
+ technology.
310
+ - **CodeSnippet's code line takes keyboard focus.** A long snippet scrolls
311
+ sideways, so the code element now sits in the tab order and shows the
312
+ neutral focus ring, and the arrow keys scroll it.
313
+ - **MenuSelect follows the WAI-ARIA listbox pattern.** The list items carry
314
+ `role="none"`, so each option sits directly in the listbox or menu. Tab
315
+ reaches the selected option alone, Up and Down move focus between enabled
316
+ options and wrap, and Home and End jump to the ends. A new optional
317
+ `ariaLabel` prop names the list; pass the text of the Button that opens
318
+ it.
319
+ - **Dialog follows the WAI-ARIA dialog pattern.** The panel is a `dialog`
320
+ named by its title, and `aria-modal` outside the inline preview. Escape
321
+ takes the cancel path: it calls `cancel.onClick` when a cancel button is
322
+ set, else `onclose`, as the close icon does.
323
+ - **Button's shimmer stops under reduced motion.** With
324
+ `prefers-reduced-motion: reduce`, the 500 ms sweep across a hovered
325
+ Button no longer draws. The `--button-shimmer` switch is unchanged.
326
+ - **Image's hover zoom jumps under reduced motion.** With
327
+ `prefers-reduced-motion: reduce`, the zoom and grow transforms apply at
328
+ once in place of the 300 ms transition.
329
+ - **Progress Bar's fill moves at once under reduced motion.** With
330
+ `prefers-reduced-motion: reduce`, the fill takes its new width without the
331
+ 400 ms transition.
332
+ - **Image Lightbox's tile holds still under reduced motion.** With
333
+ `prefers-reduced-motion: reduce`, the hovered tile scales without its
334
+ 250 ms transition, as the lightbox's own open and close animations already
335
+ did.
336
+ - **Dialog holds focus while it is open.** Outside the inline preview,
337
+ opening the dialog moves focus to its confirm, cancel or close button,
338
+ else its first focusable element, else the panel. Tab and Shift+Tab cycle
339
+ inside the panel, and closing returns focus to the element that held it
340
+ before. Image Lightbox's Tab handling now shares the same helper.
341
+
3
342
  ## 0.97.0 — Custom colors
4
343
 
5
344
  ### Added
package/README.md CHANGED
@@ -326,8 +326,8 @@ npx @motion-proto/live-tokens <command>
326
326
  | `components [id] [--json]` | List every component the project has, shipped and its own, with the props each takes; with an id, its props, variants, tokens, and defaults. |
327
327
  | `tokens [--scale <name>] [--json]` | List every design token the project's `tokens.css` declares, by scale, with its value. |
328
328
  | `report [--json]` | The project as facts: pending migrations, tokens each component reads, which page renders which component, and both checkers' findings by rule. Always exits 0. |
329
- | `check-component [id]` | Validate a component's runtime, editor, and registration against the authoring contract; with no id, every component authored under `src/system/components`. |
330
- | `check-page [paths...]` | Validate pages against the create-page contract: catalogue components and their props, theme tokens over literals, route wiring. |
329
+ | `check-component [id] [--tests] [--update-aria] [--a11y] [--open-theme]` | Validate a component's runtime, editor, and registration against the authoring contract; with no id, every component authored under `src/system/components`. `--tests` also runs the contract suites, the accessibility scans among them; `--update-aria` records each scanned component's accessibility tree anew after a deliberate markup change; `--a11y` runs the accessibility scans and keyboard cases alone; `--open-theme` tests the theme the editor has open, as saved. |
330
+ | `check-page [paths...] [--tests]` | Validate pages against the create-page contract: catalogue components and their props, theme tokens over literals, route wiring. `--tests` also opens each page in the app and checks its rendered paint, contrast, layout, and accessibility. |
331
331
  | `set-colors <base-colors.json> [--dry-run]` | Build the theme's whole color identity from 10 OKLCH base colors, enforce AA contrast on the derived text tokens, and write the result to the unsaved colors-and-type buffer. |
332
332
  | `set-type <pairing.json> [--dry-run] [--no-verify]` | Bind Google Fonts families to the theme's font stacks, verified against the API. |
333
333
  | `set-geometry <ops.json> [--dry-run]` | Move radius, padding, gap, and border-width aliases along their token scales. |
@@ -33,6 +33,7 @@ import {
33
33
  extractGlobalRootBlocks,
34
34
  loadVocabulary,
35
35
  } from './lib/tokenVocabulary.mjs';
36
+ import * as a11y from './rules/a11y.mjs';
36
37
  import * as componentStructure from './rules/componentStructure.mjs';
37
38
  import * as importsAndRoutes from './rules/importsAndRoutes.mjs';
38
39
  import * as testRuns from './rules/testRuns.mjs';
@@ -83,6 +84,12 @@ export const COMPONENT_RULES = assembleRules(
83
84
  'contract-listed',
84
85
  'contract-sketch',
85
86
  'contract-missing',
87
+ 'a11y-component',
88
+ 'a11y-review',
89
+ 'a11y-focus-visible',
90
+ 'a11y-keyboard',
91
+ 'a11y-text-spacing',
92
+ 'a11y-reduced-motion',
86
93
  'tests-not-installed',
87
94
  'tests-setup',
88
95
  'tests-incomplete',
@@ -91,6 +98,7 @@ export const COMPONENT_RULES = assembleRules(
91
98
  tokenRules.componentRules,
92
99
  importsAndRoutes.componentRules,
93
100
  testRuns.componentRules,
101
+ a11y.componentRules,
94
102
  );
95
103
 
96
104
  function capitalize(id) {
@@ -16,6 +16,7 @@ import { assembleRules, isExcluded, lineOf } from './lib/findings.mjs';
16
16
  import { codeRegion, inlineStyleRegions, styleRegions } from './lib/pageSource.mjs';
17
17
  import { loadVocabulary, walk } from './lib/tokenVocabulary.mjs';
18
18
  import { resolveTokensCssPath } from './migrate.mjs';
19
+ import * as a11y from './rules/a11y.mjs';
19
20
  import * as componentUse from './rules/componentUse.mjs';
20
21
  import * as importsAndRoutes from './rules/importsAndRoutes.mjs';
21
22
  import * as testRuns from './rules/testRuns.mjs';
@@ -49,6 +50,10 @@ export const PAGE_RULES = assembleRules(
49
50
  'page-contrast',
50
51
  'page-grid',
51
52
  'page-overflow',
53
+ 'a11y-page',
54
+ 'a11y-review',
55
+ 'a11y-text-spacing',
56
+ 'a11y-reduced-motion',
52
57
  'tests-not-installed',
53
58
  'tests-setup',
54
59
  'tests-incomplete',
@@ -57,6 +62,7 @@ export const PAGE_RULES = assembleRules(
57
62
  importsAndRoutes.pageRules,
58
63
  tokenRules.pageRules,
59
64
  testRuns.pageRules,
65
+ a11y.pageRules,
60
66
  );
61
67
 
62
68
  // Directories that hold the system, not pages built on it.