@motion-proto/live-tokens 0.96.0 → 0.98.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/skills/live-tokens-check-compliance/SKILL.md +5 -0
- package/.claude/skills/live-tokens-create-component/SKILL.md +4 -3
- package/.claude/skills/live-tokens-create-component/references/contract-tests.md +71 -11
- package/CHANGELOG.md +309 -0
- package/README.md +2 -2
- package/bin/check-component.mjs +8 -0
- package/bin/check-page.mjs +6 -0
- package/bin/cli.mjs +36 -6
- package/bin/contractRunner.mjs +142 -17
- package/bin/lib/findings.mjs +20 -4
- package/bin/rules/a11y.mjs +53 -0
- package/bin/set-colors.mjs +1 -0
- package/bin/setup-claude.mjs +2 -2
- package/dist-plugin/{chunk-A4TBP4MM.js → chunk-SLICH4IH.js} +2 -2
- package/dist-plugin/index.cjs +148 -3
- package/dist-plugin/index.js +250 -105
- package/dist-plugin/setColors/index.cjs +7 -1
- package/dist-plugin/setColors/index.d.cts +9 -4
- package/dist-plugin/setColors/index.d.ts +9 -4
- package/dist-plugin/setColors/index.js +8 -2
- package/dist-plugin/setGeometry/index.d.cts +1 -1
- package/dist-plugin/setGeometry/index.d.ts +1 -1
- package/dist-plugin/setType/index.d.cts +1 -1
- package/dist-plugin/setType/index.d.ts +1 -1
- package/dist-plugin/{themeTypes-B8_Idrp4.d.cts → themeTypes-BaTQUyac.d.cts} +10 -1
- package/dist-plugin/{themeTypes-B8_Idrp4.d.ts → themeTypes-BaTQUyac.d.ts} +10 -1
- package/package.json +49 -2
- package/src/demo/LabeledSelect.svelte +1 -1
- package/src/editor/bootstrap.ts +3 -1
- package/src/editor/component-editor/BadgeEditor.svelte +13 -0
- package/src/editor/component-editor/ButtonEditor.svelte +33 -2
- package/src/editor/component-editor/CalloutEditor.svelte +13 -0
- package/src/editor/component-editor/CardEditor.svelte +12 -2
- package/src/editor/component-editor/CodeSnippetEditor.svelte +11 -1
- package/src/editor/component-editor/CollapsibleSectionEditor.svelte +18 -1
- package/src/editor/component-editor/CornerBadgeEditor.svelte +13 -0
- package/src/editor/component-editor/DialogEditor.svelte +15 -0
- package/src/editor/component-editor/IconButtonEditor.svelte +29 -1
- package/src/editor/component-editor/ImageEditor.svelte +20 -9
- package/src/editor/component-editor/ImageLightboxEditor.svelte +44 -0
- package/src/editor/component-editor/InlineEditActionsEditor.svelte +13 -1
- package/src/editor/component-editor/InputEditor.svelte +23 -0
- package/src/editor/component-editor/MenuSelectEditor.svelte +27 -1
- package/src/editor/component-editor/NotificationEditor.svelte +15 -0
- package/src/editor/component-editor/PanelEditor.svelte +1 -1
- package/src/editor/component-editor/ProgressBarEditor.svelte +13 -0
- package/src/editor/component-editor/RadioButtonEditor.svelte +20 -1
- package/src/editor/component-editor/SectionDividerEditor.svelte +41 -13
- package/src/editor/component-editor/SegmentedControlEditor.svelte +29 -1
- package/src/editor/component-editor/SideNavigationEditor.svelte +40 -1
- package/src/editor/component-editor/SlidePagerEditor.svelte +20 -0
- package/src/editor/component-editor/SliderEditor.svelte +33 -0
- package/src/editor/component-editor/TabBarEditor.svelte +23 -2
- package/src/editor/component-editor/TableEditor.svelte +14 -0
- package/src/editor/component-editor/ToggleEditor.svelte +17 -0
- package/src/editor/component-editor/TooltipEditor.svelte +11 -2
- package/src/editor/component-editor/VideoFrameEditor.svelte +8 -0
- package/src/editor/component-editor/VideoLightboxEditor.svelte +10 -0
- package/src/editor/component-editor/registration.ts +2 -1
- package/src/editor/component-editor/registry.ts +69 -27
- package/src/editor/component-editor/scaffolding/DemoHeader.svelte +2 -0
- package/src/editor/component-editor/scaffolding/StateBlock.svelte +19 -8
- package/src/editor/component-editor/scaffolding/TokenLayout.svelte +3 -0
- package/src/editor/component-editor/scaffolding/VariantGroup.svelte +70 -10
- package/src/editor/component-editor/scaffolding/editorContext.ts +7 -0
- package/src/editor/core/a11y/a11yReport.ts +101 -0
- package/src/editor/core/a11y/a11yReportTypes.ts +69 -0
- package/src/editor/core/a11y/contrastFailures.ts +22 -0
- package/src/editor/core/components/contrastPairRegistry.ts +18 -0
- package/src/editor/core/palettes/contrast.ts +44 -0
- package/src/editor/core/palettes/contrastPairs.ts +79 -0
- package/src/editor/core/palettes/resolveColor.ts +77 -0
- package/src/editor/core/store/editorPersistence.ts +9 -1
- package/src/editor/core/store/editorRenderer.ts +2 -0
- package/src/editor/core/store/editorStore.ts +29 -0
- package/src/editor/core/store/editorTypes.ts +9 -0
- package/src/editor/core/store/editorViewStore.ts +2 -2
- package/src/editor/core/store/unsavedEdits.ts +10 -0
- package/src/editor/core/themes/buildColors.ts +16 -5
- package/src/editor/core/themes/liveStateStream.ts +7 -1
- package/src/editor/core/themes/parsers/gradient.ts +5 -2
- package/src/editor/core/themes/slices/customColors.ts +226 -0
- package/src/editor/core/themes/themeTypes.ts +10 -0
- package/src/editor/docs/chapters.ts +1 -0
- package/src/editor/docs/content/accessibility.md +362 -0
- package/src/editor/docs/content/editing-tokens.md +56 -1
- package/src/editor/docs/content.generated.ts +2 -1
- package/src/editor/pages/ComponentEditorPage.svelte +10 -1
- package/src/editor/pages/EditorShell.svelte +97 -10
- package/src/editor/skill-atlas/skillSources.generated.ts +3 -3
- package/src/editor/skill-atlas/trees/check-compliance.ts +3 -3
- package/src/editor/skill-atlas/trees/create-component.ts +12 -12
- package/src/editor/ui/CustomColorReplaceStep.svelte +118 -0
- package/src/editor/ui/EditorViewSwitcher.svelte +15 -2
- package/src/editor/ui/GradientEditor.svelte +9 -1
- package/src/editor/ui/ThemePanel.svelte +4 -4
- package/src/editor/ui/UIContrastReadout.svelte +186 -0
- package/src/editor/ui/UIInfoPopover.svelte +16 -2
- package/src/editor/ui/UIPaletteSelector.svelte +347 -15
- package/src/editor/ui/UITokenSelector.svelte +33 -14
- package/src/editor/ui/VariablesTab.svelte +3 -0
- package/src/editor/ui/a11y/A11yComponentIssues.svelte +103 -0
- package/src/editor/ui/a11y/A11yFindingRow.svelte +57 -0
- package/src/editor/ui/a11y/A11yIssueRow.svelte +121 -0
- package/src/editor/ui/a11y/A11yOverview.svelte +242 -0
- package/src/editor/ui/a11y/A11yPanel.svelte +128 -0
- package/src/editor/ui/a11y/A11yPropertyMark.svelte +84 -0
- package/src/editor/ui/a11y/A11yRailMark.svelte +34 -0
- package/src/editor/ui/a11y/AccessibilityTab.svelte +334 -0
- package/src/editor/ui/a11y/a11yView.ts +218 -0
- package/src/editor/ui/anchorToTrigger.ts +63 -0
- package/src/editor/ui/contrastReadout.ts +45 -0
- package/src/editor/ui/sections/CustomColorsSection.svelte +254 -0
- package/src/live-tokens/data/themes/autumn.json +20 -20
- package/src/live-tokens/data/themes/halloween.json +20 -20
- package/src/live-tokens/data/themes/midnight-study.json +20 -20
- package/src/live-tokens/data/themes/ocean.json +20 -20
- package/src/live-tokens/data/themes/royal-velvet.json +20 -20
- package/src/live-tokens/data/themes/sketchy.json +20 -20
- package/src/live-tokens/data/themes/spring-meadow.json +20 -20
- package/src/live-tokens/data/themes/sunset.json +20 -20
- package/src/system/components/Badge.svelte +3 -3
- package/src/system/components/Button.svelte +9 -3
- package/src/system/components/CodeSnippet.svelte +8 -1
- package/src/system/components/CornerBadge.svelte +3 -3
- package/src/system/components/Dialog.svelte +43 -14
- package/src/system/components/IconButton.svelte +1 -1
- package/src/system/components/Image.svelte +7 -0
- package/src/system/components/ImageLightbox.svelte +26 -26
- package/src/system/components/Input.svelte +8 -1
- package/src/system/components/MenuSelect.svelte +30 -2
- package/src/system/components/Notification.svelte +1 -1
- package/src/system/components/ProgressBar.svelte +6 -0
- package/src/system/components/RadioButton.svelte +36 -3
- package/src/system/components/SegmentedControl.svelte +36 -2
- package/src/system/components/SlidePager.svelte +2 -2
- package/src/system/components/Slider.svelte +13 -9
- package/src/system/components/TabBar.svelte +33 -3
- package/src/system/components/Toggle.svelte +2 -2
- package/src/system/components/VideoFrame.svelte +1 -1
- package/src/system/components/VideoLightbox.svelte +2 -2
- package/src/system/internal/focusTrap.ts +35 -0
- package/src/testing-js/__aria__/badge.yml +40 -0
- package/src/testing-js/__aria__/button.yml +88 -0
- package/src/testing-js/__aria__/callout.yml +12 -0
- package/src/testing-js/__aria__/card.yml +8 -0
- package/src/testing-js/__aria__/codesnippet.yml +21 -0
- package/src/testing-js/__aria__/collapsiblesection.yml +23 -0
- package/src/testing-js/__aria__/cornerbadge.yml +24 -0
- package/src/testing-js/__aria__/dialog.yml +42 -0
- package/src/testing-js/__aria__/iconbutton.yml +57 -0
- package/src/testing-js/__aria__/image.yml +10 -0
- package/src/testing-js/__aria__/imagelightbox.yml +50 -0
- package/src/testing-js/__aria__/inlineeditactions.yml +16 -0
- package/src/testing-js/__aria__/input.yml +61 -0
- package/src/testing-js/__aria__/menuselect.yml +30 -0
- package/src/testing-js/__aria__/notification.yml +30 -0
- package/src/testing-js/__aria__/panel.yml +2 -0
- package/src/testing-js/__aria__/progressbar.yml +2 -0
- package/src/testing-js/__aria__/radiobutton.yml +20 -0
- package/src/testing-js/__aria__/sectiondivider.yml +9 -0
- package/src/testing-js/__aria__/segmentedcontrol.yml +35 -0
- package/src/testing-js/__aria__/sidenavigation.yml +304 -0
- package/src/testing-js/__aria__/slidepager.yml +72 -0
- package/src/testing-js/__aria__/slider.yml +28 -0
- package/src/testing-js/__aria__/tabbar.yml +35 -0
- package/src/testing-js/__aria__/table.yml +144 -0
- package/src/testing-js/__aria__/toggle.yml +12 -0
- package/src/testing-js/__aria__/tooltip.yml +2 -0
- package/src/testing-js/__aria__/videoframe.yml +6 -0
- package/src/testing-js/__aria__/videolightbox.yml +12 -0
- package/src/testing-js/{chunk-WIZ6W7UT.js → chunk-3WCLMOM7.js} +2 -2
- package/src/testing-js/{chunk-SHBYZUPD.js → chunk-5HNZHIHG.js} +211 -14
- package/src/testing-js/chunk-5HNZHIHG.js.map +1 -0
- package/src/testing-js/{chunk-NB3NZRBM.js → chunk-7CM5D7PF.js} +5 -1
- package/src/testing-js/chunk-7CM5D7PF.js.map +1 -0
- package/src/testing-js/chunk-EWSGE27V.js +168 -0
- package/src/testing-js/chunk-EWSGE27V.js.map +1 -0
- package/src/testing-js/{chunk-GKO27NGN.js → chunk-R54I7YL6.js} +2 -2
- package/src/testing-js/{chunk-X6KHEVIG.js → chunk-RWH46IU5.js} +97 -29
- package/src/testing-js/chunk-RWH46IU5.js.map +1 -0
- package/src/testing-js/chunk-SHLPDNAQ.js +658 -0
- package/src/testing-js/chunk-SHLPDNAQ.js.map +1 -0
- package/src/testing-js/{chunk-3UKGXCDL.js → chunk-YVVR3GJZ.js} +1 -1
- package/src/testing-js/chunk-YVVR3GJZ.js.map +1 -0
- package/src/testing-js/component-a11y.contract.js +283 -0
- package/src/testing-js/component-a11y.contract.js.map +1 -0
- package/src/testing-js/component-alias.contract.js +2 -2
- package/src/testing-js/component-behavior.contract.js +2 -2
- package/src/testing-js/component-editor.contract.js +6 -5
- package/src/testing-js/component-editor.contract.js.map +1 -1
- package/src/testing-js/component-render.contract.js +10 -6
- package/src/testing-js/component-render.contract.js.map +1 -1
- package/src/testing-js/index.d.ts +44 -16
- package/src/testing-js/index.js +9 -9
- package/src/testing-js/page-compliance.contract.js +163 -18
- package/src/testing-js/page-compliance.contract.js.map +1 -1
- package/src/testing-js/vitest.js +3 -3
- package/template/package.json +9 -0
- package/src/editor/ui/keepInViewport.ts +0 -84
- package/src/testing-js/chunk-3UKGXCDL.js.map +0 -1
- package/src/testing-js/chunk-NB3NZRBM.js.map +0 -1
- package/src/testing-js/chunk-SHBYZUPD.js.map +0 -1
- package/src/testing-js/chunk-X6KHEVIG.js.map +0 -1
- /package/src/testing-js/{chunk-WIZ6W7UT.js.map → chunk-3WCLMOM7.js.map} +0 -0
- /package/src/testing-js/{chunk-GKO27NGN.js.map → chunk-R54I7YL6.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
|
|
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-
|
|
123
|
-
unchanged: ['--statcard-padding'],
|
|
124
|
-
aliasedTo: { '--statcard-
|
|
125
|
-
observe: { part: 'root', css: '
|
|
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
|
|
149
|
-
|
|
150
|
-
`changed` names the properties the theme moves
|
|
151
|
-
leaves, and `aliasedTo` the design token each
|
|
152
|
-
to under it.
|
|
153
|
-
|
|
154
|
-
|
|
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,314 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.98.2 — Accessibility testing
|
|
4
|
+
|
|
5
|
+
The v0.98.0 and v0.98.1 tags never reached npm. Release checks stopped both:
|
|
6
|
+
a test time limit and a rule count the accessibility suite outgrew.
|
|
7
|
+
|
|
8
|
+
### Added
|
|
9
|
+
|
|
10
|
+
- **`check-component --tests` scans components for accessibility.** A new
|
|
11
|
+
suite opens every variant and state a component's contract names and runs
|
|
12
|
+
axe-core against WCAG 2 A and AA and Siteimprove Alfa's focus-visibility
|
|
13
|
+
rule SIA-R65 over the preview. Findings report as `a11y-component`,
|
|
14
|
+
`a11y-focus-visible`, and `a11y-review`, the last for checks axe could not
|
|
15
|
+
decide, such as text over a gradient. Editor demo content marked
|
|
16
|
+
`data-preview-demo` stays out of the scan. The library's own test run
|
|
17
|
+
repeats axe's contrast rule under the eight preset themes.
|
|
18
|
+
- **`check-page --tests` scans pages for accessibility.** Each page and
|
|
19
|
+
viewport gains three tests. `page-a11y` runs axe outside the shipped
|
|
20
|
+
components, with contrast left to `page-contrast`. `page-text-spacing`
|
|
21
|
+
reports overflow the WCAG 2.2 SC 1.4.12 text spacing override adds.
|
|
22
|
+
`page-reduced-motion` reports animations still running 200 ms after load
|
|
23
|
+
under `prefers-reduced-motion: reduce`. They report as `a11y-page`,
|
|
24
|
+
`a11y-review`, `a11y-text-spacing`, and `a11y-reduced-motion`.
|
|
25
|
+
- **Accessibility findings alert by default.** All seven `a11y-*` rules
|
|
26
|
+
report at `warn`, and `--strict` makes them errors, as it does every other
|
|
27
|
+
warning. `--off` and `live-tokens.config.json` silence or raise each one.
|
|
28
|
+
A scan that crashes reports `tests-setup`.
|
|
29
|
+
- **Each shipped component's accessibility tree is recorded.** The trees
|
|
30
|
+
live in `src/testing/contracts/__aria__/<id>.yml`, one block per view. A
|
|
31
|
+
run that reads a different tree reports `a11y-component` with engine rule
|
|
32
|
+
`aria-snapshot`, and `check-component --tests --update-aria` records the
|
|
33
|
+
trees anew after a deliberate markup change. A project's own components
|
|
34
|
+
get no recorded tree yet.
|
|
35
|
+
- **Component contracts take keyboard cases.** An `InteractionCase` with
|
|
36
|
+
`rule: 'a11y-keyboard'` reports its failure as an `a11y-keyboard` warning
|
|
37
|
+
and lets the test pass. A `press` action may omit `part`
|
|
38
|
+
to send the key to whatever holds focus. `InteractionOutcome` gains
|
|
39
|
+
`activated`, for a key that fired a click on the part, and `present`, for
|
|
40
|
+
whether the part is in the document. The fifteen interactive shipped
|
|
41
|
+
components carry 27 keyboard cases for activation, arrow movement, and
|
|
42
|
+
Escape.
|
|
43
|
+
- **The accessibility engines ship as optional peers.** `axe-core` 4.11.1 or
|
|
44
|
+
later, `@axe-core/playwright`, and seven `@siteimprove/alfa-*` packages at
|
|
45
|
+
0.119 join `peerDependencies` as optional, and the `create` template
|
|
46
|
+
installs them. Without one, the tests it drives report as inapplicable and
|
|
47
|
+
the run carries on.
|
|
48
|
+
- **The editor gains an Accessibility view.** Run test checks every
|
|
49
|
+
component in the open theme as saved, in about three and a half minutes,
|
|
50
|
+
and lists each component's findings by repair. An overview opens the
|
|
51
|
+
report with the total, the count in each repair group, and a table of every
|
|
52
|
+
component with a finding, whose names scroll to their sections. Each
|
|
53
|
+
section's **Open component** opens the component's page at its
|
|
54
|
+
accessibility panel. "Fix with a property" holds
|
|
55
|
+
the colour pairs the editor's contrast checker finds below their floor and
|
|
56
|
+
the focus rings and control boundaries below 3:1. "Fix in the component's
|
|
57
|
+
code" holds every other finding: axe failures, unchanged focus styles,
|
|
58
|
+
failed keyboard cases, clipped text, and motion that ignores reduced
|
|
59
|
+
motion. Results to check by eye count in the totals and collect in one
|
|
60
|
+
closed list. Findings that share an engine rule, a help text, and their
|
|
61
|
+
traced properties merge into one row that names its element count and
|
|
62
|
+
views, and each traced property gets a "→". A row's right-hand link opens
|
|
63
|
+
the rule, success criterion, technique, or pattern the finding cites and
|
|
64
|
+
names its kind. A property's label leads with
|
|
65
|
+
the words from its variable name that the editor's label leaves out, such
|
|
66
|
+
as "Focused border color". With edits pending, Run test asks for a save and
|
|
67
|
+
starts nothing. A missing test tool shows its install command. The dev
|
|
68
|
+
server runs one check at a time and keeps the last report in
|
|
69
|
+
`test-results/live-tokens/a11y-report.json`.
|
|
70
|
+
- **The editor marks the components the last run named.** Every finding
|
|
71
|
+
marks its component, results to check by eye included. The component list
|
|
72
|
+
leads each one's name with a warning triangle, in the sidebar, the narrow
|
|
73
|
+
menu, and the standalone component page. A condensed list shows the
|
|
74
|
+
triangle on the component's icon. Each one's page opens with a panel under its
|
|
75
|
+
title that lists its findings in three groups: "Fix with a property", "Fix
|
|
76
|
+
in the component's code", and "Check by eye". Each "→" in the panel opens
|
|
77
|
+
the tab holding the property, an intrinsic's hover tab included, and
|
|
78
|
+
highlights its row. The marks follow the last finished run; the contrast
|
|
79
|
+
readout on each colour row stays live.
|
|
80
|
+
- **Property rows carry the run's findings.** A row whose property a finding
|
|
81
|
+
traces to shows a warning triangle beside its label, or a grey "?" when
|
|
82
|
+
every finding on it is a result to check by eye. The mark opens a popover that lists the
|
|
83
|
+
findings with their elements, views, help pages, and traced properties.
|
|
84
|
+
Token selectors, gradient editors, and toggle rows carry it. The editor's own
|
|
85
|
+
contrast failures add no row mark, because the live readout already shows
|
|
86
|
+
them.
|
|
87
|
+
- **Accessibility findings name the properties that fix them.** The
|
|
88
|
+
component suite reads the CSS rules that match each failing element
|
|
89
|
+
through the Chrome DevTools protocol and follows each `var()` to the
|
|
90
|
+
component's semantic properties and intrinsics. `check-component --tests
|
|
91
|
+
--json` lists them, sorted, in each finding's `details.variables`, and
|
|
92
|
+
leaves the field out when the trace finds nothing. The field is additive.
|
|
93
|
+
- **The component suite runs four more checks.** Focus indicator contrast
|
|
94
|
+
and control boundary contrast measure a focus ring and a control's edge at
|
|
95
|
+
3:1 against the backdrop, under WCAG 2.2 SC 1.4.11. Text spacing applies
|
|
96
|
+
the WCAG 2.2 SC 1.4.12 override inside each component and reports text
|
|
97
|
+
that clips under it. Reduced motion reports motion longer than 200 ms that
|
|
98
|
+
still runs under `prefers-reduced-motion: reduce`, after WCAG technique
|
|
99
|
+
C39 for SC 2.3.3. The results are warnings under the existing rules:
|
|
100
|
+
`a11y-component` (engine rules `focus-indicator-contrast` and
|
|
101
|
+
`control-boundary-contrast`), `a11y-review` for a ring or edge over a
|
|
102
|
+
gradient or image, `a11y-text-spacing`, and `a11y-reduced-motion`, which
|
|
103
|
+
`check-component` now reports too. `--strict` raises them with the rest.
|
|
104
|
+
- **Component contracts take `boundaries`.** The optional field,
|
|
105
|
+
`{ part: string; label: string }[]`, lists the parts that draw a control's
|
|
106
|
+
visible edge, for the control boundary check. Input, RadioButton, Slider,
|
|
107
|
+
Toggle, and SegmentedControl declare it.
|
|
108
|
+
- **Selected states join the control boundary check.** TabBar's selected
|
|
109
|
+
tab, SegmentedControl's selected option, and SideNavigation's selected
|
|
110
|
+
title, section, item, and footer show their state by surface and edge
|
|
111
|
+
alone, so their contracts list them in `boundaries`. Each measures against
|
|
112
|
+
the bar or panel behind it. A `boundaries` entry takes `when`, a selector
|
|
113
|
+
the part must also match, and the check reads an outline and every border
|
|
114
|
+
side and keeps the best. The trace follows only the winning declaration of
|
|
115
|
+
a private property, so a selected tab's finding names the selected tab's
|
|
116
|
+
properties alone.
|
|
117
|
+
- **Image Lightbox's editor sets the tile fit.** The Tile tab gains a "tile
|
|
118
|
+
fit" row, Contain or Cover, which writes `--imagelightbox-tile-object-fit`
|
|
119
|
+
and carries the accessibility mark. ImageLightbox's `fit` prop no longer
|
|
120
|
+
defaults to `contain` on every tile, so a tile without it follows the
|
|
121
|
+
theme's value.
|
|
122
|
+
- **`check-component --tests` takes `--a11y` and `--open-theme`.** `--a11y`
|
|
123
|
+
runs the accessibility suite and the keyboard cases alone, with no static
|
|
124
|
+
checks or fixes, and reports only `a11y-*` and `tests-*` findings.
|
|
125
|
+
`--open-theme` tests the theme the editor has open, as saved, and still
|
|
126
|
+
leaves every unsaved buffer out of the run.
|
|
127
|
+
- **`--json` output names the tested theme and each finding's component.**
|
|
128
|
+
`check-component --tests --json` adds a top-level `theme`, and each
|
|
129
|
+
Playwright finding that names a component carries it in a `component`
|
|
130
|
+
field. Both fields are additive.
|
|
131
|
+
- **The guide gains an Accessibility chapter.** It covers the suites, the
|
|
132
|
+
rules, the focus indicator, boundary, text spacing, and motion checks,
|
|
133
|
+
property tracing, review findings, the recorded trees, keyboard cases,
|
|
134
|
+
`boundaries`, the Accessibility view, the marks in the editor, the
|
|
135
|
+
engines, and the limits.
|
|
136
|
+
- **The skills cover accessibility.** `live-tokens-check-compliance` lists
|
|
137
|
+
the `a11y-*` findings as alerts for the user to review.
|
|
138
|
+
`live-tokens-create-component` declares keyboard cases for a new component
|
|
139
|
+
and judges `a11y-review` findings by eye.
|
|
140
|
+
- **Component editors show the contrast ratio of every foreground color.** A
|
|
141
|
+
color row whose variable paints text, an icon or another foreground mark
|
|
142
|
+
shows the WCAG ratio between that color and the layers behind it, down to the
|
|
143
|
+
page background. A pair below its floor turns amber and gains an exclamation
|
|
144
|
+
mark. Click the ratio to open the rule it tracks, each pair's measured ratio
|
|
145
|
+
and layers, and a link to the success criterion. A disabled state shows its
|
|
146
|
+
ratio in a dimmer gray, because WCAG exempts disabled controls. The readout
|
|
147
|
+
recomputes after every edit and theme load, and the rows keep their places.
|
|
148
|
+
- **Editor modules and `registerComponent()` take `contrastPairs`.** Each pair
|
|
149
|
+
names a foreground variable, the variables that paint behind it top first, its
|
|
150
|
+
role (`text` or `non-text`), and for text its font size and weight. A pair
|
|
151
|
+
for a disabled state sets `inactive`. A foreground over several backdrops
|
|
152
|
+
takes one pair per backdrop, each with a `context`, and the readout shows the
|
|
153
|
+
pair closest to failing.
|
|
154
|
+
- **`UITokenSelector` takes a `metaAfter` snippet.** It renders after the value
|
|
155
|
+
name in the same column, and the value name truncates first.
|
|
156
|
+
|
|
157
|
+
### Changed
|
|
158
|
+
|
|
159
|
+
- **Button's catalogue entry covers navigation.** A Button is a labelled
|
|
160
|
+
trigger that runs an action or opens another page, such as View details.
|
|
161
|
+
An anchor stays the choice for a link inside running text.
|
|
162
|
+
|
|
163
|
+
### Fixed
|
|
164
|
+
|
|
165
|
+
- **Check reports reach a pipe whole.** On macOS, `check-component` and
|
|
166
|
+
`check-page` cut piped output at 64 KiB, because the process exited before
|
|
167
|
+
the pipe drained. Both checkers now write their report synchronously, so
|
|
168
|
+
`--json` output read through a pipe parses.
|
|
169
|
+
- **`page-overflow` passes a component inside a scrolling ancestor.** The
|
|
170
|
+
check reported a component as clipped by the nearest ancestor with any
|
|
171
|
+
overflow other than `visible`, so a Badge at the end of a Table row on a
|
|
172
|
+
phone failed against the Table's own `overflow-x: auto` wrapper. An
|
|
173
|
+
ancestor that scrolls sideways now keeps the component in reach, and only
|
|
174
|
+
one that hides overflow without scrolling clips it.
|
|
175
|
+
- **The theme contract example tests adherence to a saved theme.** The
|
|
176
|
+
example in `live-tokens-create-component`'s `references/contract-tests.md`
|
|
177
|
+
expected a theme to move a new component's radius, which only a theme block
|
|
178
|
+
for that component can do, and told the agent to save one first. The
|
|
179
|
+
example now expects the theme to move a color property through its design
|
|
180
|
+
tokens and lists the component's radius and padding as unchanged.
|
|
181
|
+
- **TabBar follows the WAI-ARIA tabs pattern.** The bar is a `tablist` of
|
|
182
|
+
`tab` elements that carry `aria-selected`. Tab reaches the selected tab
|
|
183
|
+
alone, the arrow keys move focus between enabled tabs and wrap, Home and
|
|
184
|
+
End jump to the first and last, and Enter or Space selects.
|
|
185
|
+
- **TabBar's selected indicator meets 3:1.** `--tabbar-selected-border` maps
|
|
186
|
+
to `--border-brand-medium` in place of `--color-brand-500`, which lifts the
|
|
187
|
+
underline from 2.96:1 to 3.47:1 against the default theme's page.
|
|
188
|
+
- **SegmentedControl follows the WAI-ARIA radio group pattern.** Tab reaches
|
|
189
|
+
the checked option alone, and the arrow keys move focus and selection to
|
|
190
|
+
the next or previous enabled option and wrap. A focused option shows a ring
|
|
191
|
+
outside its box in `--border-neutral-strong`, so focus on the selected
|
|
192
|
+
option reads apart from its brand outline.
|
|
193
|
+
- **SegmentedControl's selected outline meets 3:1.**
|
|
194
|
+
`--segmentedcontrol-selected-border` maps to `--border-brand-medium` in
|
|
195
|
+
place of `--border-brand`, which lifts the outline from 2.67:1 to 4.31:1
|
|
196
|
+
against the default theme's bar.
|
|
197
|
+
- **Control edges meet 3:1.** Input's field, RadioButton's circle in every
|
|
198
|
+
state, SegmentedControl's bar, Slider's track at both variants and Toggle's
|
|
199
|
+
track now draw their edge in `--border-neutral-medium`, in place of
|
|
200
|
+
`--border-neutral` (`--border-neutral-subtle` for Slider). Toggle's on
|
|
201
|
+
track and Slider's thumbs draw `--border-brand-medium` in place of
|
|
202
|
+
`--border-brand`. Against the default theme's page the neutral edges rise
|
|
203
|
+
from 2.42:1 to 3.97:1, Slider's track from 1.45:1, and Toggle's on track
|
|
204
|
+
from 2.14:1 to 3.47:1. Slider's focus ring, drawn in the thumb's border
|
|
205
|
+
colour, rises from 2.14:1 to 3.47:1.
|
|
206
|
+
- **The preset themes carry the 3:1 control edges.** Each preset holds its
|
|
207
|
+
own copy of the component mappings, so the eight presets now carry the new
|
|
208
|
+
edges above and TabBar's and SegmentedControl's selected borders. In
|
|
209
|
+
autumn, ocean, royal-velvet, sketchy and spring-meadow every control edge
|
|
210
|
+
and selected indicator now measures 3:1 or more, where autumn's Slider
|
|
211
|
+
track measured 1.4:1 and its selected tab 2.47:1. Halloween,
|
|
212
|
+
midnight-study and sunset paint a gradient page, so their edges stay a
|
|
213
|
+
check by eye.
|
|
214
|
+
- **VideoFrame, VideoLightbox and SlidePager draw a focus ring at 3:1.** The
|
|
215
|
+
trigger, the close button, the stage and the pager controls drew
|
|
216
|
+
`--ring-focus-md`, a brand colour at 10% opacity that measured 1.06:1 to
|
|
217
|
+
1.09:1. Each now draws the neutral ring SegmentedControl uses, a
|
|
218
|
+
`--border-width-2` box shadow in `--border-neutral-strong`.
|
|
219
|
+
- **Badge, CornerBadge and Notification text meets 4.5:1.** The brand and
|
|
220
|
+
danger badges, and Notification's danger variant, sit on the next lower
|
|
221
|
+
surface step, `--surface-brand-low` and `--surface-danger-low`, which lifts
|
|
222
|
+
their text from 3.87:1 to 5.38:1 and from 3.30:1 to 4.56:1 on the default
|
|
223
|
+
theme. The canvas badges keep `--surface-canvas` and take
|
|
224
|
+
`--text-secondary`, since no canvas text step reaches 4.5:1 on a canvas
|
|
225
|
+
surface; they rise from 1.55:1 to 7.15:1. The eight presets carry the same
|
|
226
|
+
mappings, and under each of them axe reports no `color-contrast` failure
|
|
227
|
+
on the three components.
|
|
228
|
+
- **Input shows focus in its error state.** The red error outline replaced
|
|
229
|
+
the focus outline, so focusing an invalid field changed nothing visible.
|
|
230
|
+
Focus now adds a `--border-neutral-strong` ring outside the red outline.
|
|
231
|
+
- **Slider's thumbs take the pointer over 24px.** Each native thumb and its
|
|
232
|
+
input now measure `max(thumb size, --space-24)`, the WCAG 2.2 SC 2.5.8
|
|
233
|
+
minimum, while the visible cap keeps its own size. axe reported both range
|
|
234
|
+
thumbs under `target-size`, because their inputs share one box.
|
|
235
|
+
- **RadioButton follows the WAI-ARIA radio group pattern.** Each option is a
|
|
236
|
+
`radio` with `aria-checked`. Tab reaches the checked option alone, and the
|
|
237
|
+
arrow keys move focus and selection to the next or previous option and
|
|
238
|
+
wrap. A group is the nearest `role="radiogroup"` ancestor, else the parent
|
|
239
|
+
the options share; wrap a set in a `radiogroup` with a name.
|
|
240
|
+
- **TabBar's icon-only tabs carry a name.** With `iconOnly` set, each tab
|
|
241
|
+
takes its `label` as `aria-label`, and the icon is hidden from assistive
|
|
242
|
+
technology.
|
|
243
|
+
- **CodeSnippet's code line takes keyboard focus.** A long snippet scrolls
|
|
244
|
+
sideways, so the code element now sits in the tab order and shows the
|
|
245
|
+
neutral focus ring, and the arrow keys scroll it.
|
|
246
|
+
- **MenuSelect follows the WAI-ARIA listbox pattern.** The list items carry
|
|
247
|
+
`role="none"`, so each option sits directly in the listbox or menu. Tab
|
|
248
|
+
reaches the selected option alone, Up and Down move focus between enabled
|
|
249
|
+
options and wrap, and Home and End jump to the ends. A new optional
|
|
250
|
+
`ariaLabel` prop names the list; pass the text of the Button that opens
|
|
251
|
+
it.
|
|
252
|
+
- **Dialog follows the WAI-ARIA dialog pattern.** The panel is a `dialog`
|
|
253
|
+
named by its title, and `aria-modal` outside the inline preview. Escape
|
|
254
|
+
takes the cancel path: it calls `cancel.onClick` when a cancel button is
|
|
255
|
+
set, else `onclose`, as the close icon does.
|
|
256
|
+
- **Button's shimmer stops under reduced motion.** With
|
|
257
|
+
`prefers-reduced-motion: reduce`, the 500 ms sweep across a hovered
|
|
258
|
+
Button no longer draws. The `--button-shimmer` switch is unchanged.
|
|
259
|
+
- **Image's hover zoom jumps under reduced motion.** With
|
|
260
|
+
`prefers-reduced-motion: reduce`, the zoom and grow transforms apply at
|
|
261
|
+
once in place of the 300 ms transition.
|
|
262
|
+
- **Progress Bar's fill moves at once under reduced motion.** With
|
|
263
|
+
`prefers-reduced-motion: reduce`, the fill takes its new width without the
|
|
264
|
+
400 ms transition.
|
|
265
|
+
- **Image Lightbox's tile holds still under reduced motion.** With
|
|
266
|
+
`prefers-reduced-motion: reduce`, the hovered tile scales without its
|
|
267
|
+
250 ms transition, as the lightbox's own open and close animations already
|
|
268
|
+
did.
|
|
269
|
+
- **Dialog holds focus while it is open.** Outside the inline preview,
|
|
270
|
+
opening the dialog moves focus to its confirm, cancel or close button,
|
|
271
|
+
else its first focusable element, else the panel. Tab and Shift+Tab cycle
|
|
272
|
+
inside the panel, and closing returns focus to the element that held it
|
|
273
|
+
before. Image Lightbox's Tab handling now shares the same helper.
|
|
274
|
+
|
|
275
|
+
## 0.97.0 — Custom colors
|
|
276
|
+
|
|
277
|
+
### Added
|
|
278
|
+
|
|
279
|
+
- **Every color picker offers custom colors.** A **Custom** row after Danger
|
|
280
|
+
lists the open theme's own colors, `--color-custom-1`, `--color-custom-2`
|
|
281
|
+
and so on, each holding one OKLCH value for light and dark. The row's
|
|
282
|
+
plus swatch starts a new color from the slot's current color and opens
|
|
283
|
+
the OKLCH editor; confirm assigns it to the slot in one undo step, and
|
|
284
|
+
cancel removes it. A slot picks an existing custom color as it picks a
|
|
285
|
+
palette step, opacity included. The pencil on a custom swatch edits it in
|
|
286
|
+
place, and the edit moves every slot that names it. The Tokens view gains a
|
|
287
|
+
**Custom Colours** section that lists, edits and removes them. Removing a
|
|
288
|
+
color that a component property, gradient stop or wash names first asks
|
|
289
|
+
for a replacement, preselects the nearest token, and rewrites each
|
|
290
|
+
reference with its opacity intact, all in one undo step.
|
|
291
|
+
- **Themes carry an optional `customColors` field.**
|
|
292
|
+
`ColorsAndType.customColors` sits beside `gradients` and holds
|
|
293
|
+
`{ variable, color }` entries. The field is additive: a theme without it
|
|
294
|
+
loads with no custom colors, `THEME_SCHEMA_VERSION` stays 5, and no
|
|
295
|
+
migration runs. Save, export and import keep the list, and Adopt writes
|
|
296
|
+
each custom color into `tokens.generated.css` with the theme's other
|
|
297
|
+
variables. `set-colors` and `save-theme` carry the field through
|
|
298
|
+
unchanged. `tokens.css` and the shipped themes declare no custom color, so
|
|
299
|
+
`check-page` reports `unknown-token` for page CSS that names
|
|
300
|
+
`var(--color-custom-1)`; reach a custom color through a component property.
|
|
301
|
+
|
|
302
|
+
## 0.96.1 — Vitest 5 peer range
|
|
303
|
+
|
|
304
|
+
### Fixed
|
|
305
|
+
|
|
306
|
+
- **Installs alongside Vitest 5.** The optional `vitest` peer range now reads
|
|
307
|
+
`^4.0 || ^5.0`. With `^4.0` alone, npm stopped any install that re-resolved
|
|
308
|
+
the tree with `ERESOLVE` once Vitest 5 sat anywhere in it, including as an
|
|
309
|
+
uninstalled optional peer of Vite's devtools. The `testing` contract helpers
|
|
310
|
+
pass the full suite on Vitest 5.
|
|
311
|
+
|
|
3
312
|
## 0.96.0 — Lightbox zoom readout in natural pixels
|
|
4
313
|
|
|
5
314
|
### Changed
|
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. |
|
package/bin/check-component.mjs
CHANGED
|
@@ -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) {
|
package/bin/check-page.mjs
CHANGED
|
@@ -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.
|
package/bin/cli.mjs
CHANGED
|
@@ -80,7 +80,7 @@ Commands:
|
|
|
80
80
|
renders which component, and both checkers'
|
|
81
81
|
findings by rule under the project's severities
|
|
82
82
|
and under --strict. Always exits 0
|
|
83
|
-
check-component [id] [--tests]
|
|
83
|
+
check-component [id] [--tests] [--update-aria] [--a11y] [--open-theme]
|
|
84
84
|
Validate <id>'s runtime, editor, and registration
|
|
85
85
|
against the live-tokens-create-component contract.
|
|
86
86
|
--tests also runs the registry contract under
|
|
@@ -88,7 +88,16 @@ Commands:
|
|
|
88
88
|
Playwright, reporting coverage by rule. Needs
|
|
89
89
|
@playwright/test, vitest, and happy-dom; a
|
|
90
90
|
missing one is a tests-not-installed finding
|
|
91
|
-
naming the install command
|
|
91
|
+
naming the install command. --update-aria
|
|
92
|
+
records each component's accessibility tree
|
|
93
|
+
anew, after a deliberate markup change.
|
|
94
|
+
--a11y runs the accessibility checks alone: the
|
|
95
|
+
accessibility suite and the keyboard cases,
|
|
96
|
+
with no static checks or fixes, reporting only
|
|
97
|
+
a11y-* and tests-* findings. --open-theme tests
|
|
98
|
+
the theme the editor has loaded, as saved; the
|
|
99
|
+
buffer stays out of the run. Both need --tests.
|
|
100
|
+
--json names the tested theme under "theme"
|
|
92
101
|
check-page [paths...] [--tests]
|
|
93
102
|
Validate pages against the live-tokens-create-page
|
|
94
103
|
contract: catalogue components only, and every CSS
|
|
@@ -222,13 +231,13 @@ if (command === 'create') {
|
|
|
222
231
|
}
|
|
223
232
|
}
|
|
224
233
|
|
|
225
|
-
function reportChecks(label, findings, checked, rules, opts, { coverage, hardFailure, exclude, fix } = {}) {
|
|
234
|
+
function reportChecks(label, findings, checked, rules, opts, { coverage, hardFailure, exclude, fix, theme } = {}) {
|
|
226
235
|
const checksConfig = readChecksConfig(process.cwd());
|
|
227
236
|
const resolved = applySeverity(findings, rules, opts, checksConfig, { exclude });
|
|
228
237
|
const resolvedCoverage = coverage ? applyCoverageSeverity(coverage, rules, opts, checksConfig) : coverage;
|
|
229
|
-
|
|
238
|
+
writeOut(
|
|
230
239
|
opts.json
|
|
231
|
-
? toJson(resolved, { label, checked, coverage: resolvedCoverage, fix })
|
|
240
|
+
? toJson(resolved, { label, checked, coverage: resolvedCoverage, fix, theme })
|
|
232
241
|
: [fix ? formatFixes(fix) : '', formatFindings(resolved, { label, checked })].filter(Boolean).join('\n'),
|
|
233
242
|
);
|
|
234
243
|
// Decision 2: a missing tool or a setup failure under --tests is an error
|
|
@@ -319,7 +328,23 @@ if (command === 'report') {
|
|
|
319
328
|
|
|
320
329
|
if (command === 'check-component') {
|
|
321
330
|
const opts = parseCheckFlags(rest);
|
|
331
|
+
if ((opts.a11y || opts.openTheme) && !opts.tests) fail('--a11y and --open-theme run with --tests.');
|
|
322
332
|
const ids = opts.rest.length > 0 ? [opts.rest[0]] : discoverComponents();
|
|
333
|
+
if (opts.a11y) {
|
|
334
|
+
const { hasHardFailure, runContractTests } = await import('./contractRunner.mjs');
|
|
335
|
+
const outcome = await runContractTests(opts.rest[0], {
|
|
336
|
+
root: process.cwd(),
|
|
337
|
+
updateAria: opts.updateAria,
|
|
338
|
+
a11y: true,
|
|
339
|
+
openTheme: opts.openTheme,
|
|
340
|
+
});
|
|
341
|
+
const label = `check-component${ids.length === 1 ? ` ${ids[0]}` : ''} --tests --a11y`;
|
|
342
|
+
reportChecks(label, outcome.findings, Math.max(ids.length, 1), COMPONENT_RULES, opts, {
|
|
343
|
+
coverage: outcome.coverage,
|
|
344
|
+
hardFailure: hasHardFailure(outcome.findings),
|
|
345
|
+
theme: outcome.theme,
|
|
346
|
+
});
|
|
347
|
+
}
|
|
323
348
|
if (ids.length === 0 && !opts.tests) {
|
|
324
349
|
console.log('✓ check-component: no component authored under src/system/components yet.');
|
|
325
350
|
process.exit(0);
|
|
@@ -342,12 +367,17 @@ if (command === 'check-component') {
|
|
|
342
367
|
reportChecks(label, [...migration.findings, ...results.flatMap(([, r]) => r.findings)], ids.length, COMPONENT_RULES, opts, { fix });
|
|
343
368
|
}
|
|
344
369
|
const { hasHardFailure, runContractTests } = await import('./contractRunner.mjs');
|
|
345
|
-
const testOutcome = await runContractTests(opts.rest[0], {
|
|
370
|
+
const testOutcome = await runContractTests(opts.rest[0], {
|
|
371
|
+
root: process.cwd(),
|
|
372
|
+
updateAria: opts.updateAria,
|
|
373
|
+
openTheme: opts.openTheme,
|
|
374
|
+
});
|
|
346
375
|
const findings = dedupeAliasFindings([...migration.findings, ...results.flatMap(([, r]) => r.findings), ...testOutcome.findings]);
|
|
347
376
|
reportChecks(label, findings, Math.max(ids.length, 1), COMPONENT_RULES, opts, {
|
|
348
377
|
fix,
|
|
349
378
|
coverage: testOutcome.coverage,
|
|
350
379
|
hardFailure: hasHardFailure(testOutcome.findings),
|
|
380
|
+
theme: testOutcome.theme,
|
|
351
381
|
});
|
|
352
382
|
}
|
|
353
383
|
|