@erclx/canon 5.61.0 → 5.62.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.
- package/CHANGELOG.md +9 -0
- package/README.md +1 -1
- package/claude/.claude-plugin/plugin.json +1 -1
- package/claude/skills/canon-cli/SKILL.md +2 -1
- package/claude/skills/design-identity/references/card-route.md +3 -3
- package/claude/skills/target-update/SKILL.md +9 -1
- package/claude/skills/ux-audit/SKILL.md +1 -1
- package/docs/agents/install-and-sync.md +45 -11
- package/docs/index.md +1 -1
- package/docs/target/migrations.md +6 -8
- package/docs/target/sync.md +20 -1
- package/docs/workflow/ai-workflow.md +1 -1
- package/docs/workflow/index.md +3 -3
- package/docs/workflow/visual-design-workflow.md +58 -32
- package/governance/detect.toml +8 -0
- package/governance/ledger/ui/a11y.md +8 -6
- package/governance/ledger/ui/keyboard.md +20 -21
- package/governance/ledger/ui/pointer.md +24 -0
- package/governance/ledger/ui/surface-capture.md +12 -12
- package/governance/ledger/ui/ux-completeness.md +11 -23
- package/governance/ledger/ui/ux-controls.md +26 -0
- package/governance/rules/ui/a11y.md +2 -0
- package/governance/rules/ui/keyboard.md +4 -7
- package/governance/rules/ui/pointer.md +23 -0
- package/governance/rules/ui/surface-capture.md +5 -5
- package/governance/rules/ui/ux-completeness.md +2 -10
- package/governance/rules/ui/ux-controls.md +25 -0
- package/governance/splits.toml +15 -0
- package/package.json +1 -1
- package/src/commands/sync.ts +3 -1
- package/src/commands/tooling.ts +329 -10
- package/src/gov/adapter.ts +53 -4
- package/src/gov/splits.ts +36 -0
- package/src/sync/check.ts +9 -1
- package/src/sync/stamp.ts +135 -11
- package/src/tooling/disposition.ts +167 -0
- package/src/tooling/inject.ts +17 -2
- package/src/tooling/scan.ts +86 -7
- package/src/tooling/stamp.ts +51 -7
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [5.62.0](https://github.com/erclx/canon/compare/v5.61.0...v5.62.0) (2026-10-24)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* **governance:** cover more WCAG criteria in the ui rules ([#2677](https://github.com/erclx/canon/issues/2677)) ([25b6190](https://github.com/erclx/canon/commit/25b619039aa3656e239385713f796c5e34810485))
|
|
9
|
+
* **governance:** name the partner of a split rule on sync ([#2681](https://github.com/erclx/canon/issues/2681)) ([1d6703e](https://github.com/erclx/canon/commit/1d6703e74deb7968b0121b5e3148e474ed836c89))
|
|
10
|
+
* **tooling:** let a project decline or customize one golden file ([#2675](https://github.com/erclx/canon/issues/2675)) ([41c5ec0](https://github.com/erclx/canon/commit/41c5ec042a8d50b968e853a175f45eac5a59703c))
|
|
11
|
+
|
|
3
12
|
## [5.61.0](https://github.com/erclx/canon/compare/v5.60.0...v5.61.0) (2026-10-24)
|
|
4
13
|
|
|
5
14
|
|
package/README.md
CHANGED
|
@@ -104,7 +104,7 @@ Working with an agent rather than reading this yourself? It loads `CLAUDE.md` at
|
|
|
104
104
|
- [Operating model](docs/workflow/operating-model.md): orchestrator, planner, and worker roles for building across parallel sessions
|
|
105
105
|
- [Skill map](docs/workflow/skill-map.md): plugin skills grouped by the moment a project meets them
|
|
106
106
|
- [Skill map on request](docs/workflow/skill-map-on-request.md): plugin skills that serve a request rather than a moment
|
|
107
|
-
- [Visual design workflow](docs/workflow/visual-design-workflow.md):
|
|
107
|
+
- [Visual design workflow](docs/workflow/visual-design-workflow.md): how canon plans, builds, and reviews a UI
|
|
108
108
|
- [Target projects](docs/target/projects.md): scaffold, add a domain later, sync upstream drift
|
|
109
109
|
- [Agents](docs/agents/index.md): CLI flags, exit codes, and JSON output shapes
|
|
110
110
|
- [Docs index](docs/index.md): every reference doc in this repo
|
|
@@ -83,7 +83,8 @@ Read the doc each line names before running one of these. `canon docs install-an
|
|
|
83
83
|
|
|
84
84
|
- `canon sync` runs every installed domain's sync, so it carries each risk below.
|
|
85
85
|
- `canon gov sync` replaces a rule you edited and deletes a rule the toolkit retired, edited or not.
|
|
86
|
-
- `canon tooling sync --write` replaces
|
|
86
|
+
- `canon tooling sync --write` replaces a golden config canon changed and holds one you edited until `--replace <path>` names it. Nothing writes without the flag. See `canon docs sync`.
|
|
87
|
+
- `canon tooling inject` replaces every golden config a stack ships, skipping only a declined or customized path.
|
|
87
88
|
|
|
88
89
|
### Migrate and indexes
|
|
89
90
|
|
|
@@ -21,11 +21,11 @@ Check for a `card.config.mjs` with a `card-src/pages/og-card.astro` beside it. W
|
|
|
21
21
|
|
|
22
22
|
```plaintext
|
|
23
23
|
No card route installed, so the card shipped as a PNG this run.
|
|
24
|
-
`canon tooling sync astro . --write` installs the route, and it
|
|
25
|
-
|
|
24
|
+
`canon tooling sync astro . --write` installs the route, and it writes the astro
|
|
25
|
+
stack's new and stale configs, so run it when you mean to sync the stack.
|
|
26
26
|
```
|
|
27
27
|
|
|
28
|
-
Never run that sync to obtain the route, since it
|
|
28
|
+
Never run that sync to obtain the route, since it writes the stack's configs beside the project's own framework, lint, typescript, and test configs, holding only the ones the project edited. Never hand-write the scaffold either, since a copy written here drifts from the one the stack maintains.
|
|
29
29
|
|
|
30
30
|
## Writing and capturing the route
|
|
31
31
|
|
|
@@ -68,6 +68,8 @@ Target update: `<path>`, canon `<installed>` to `<running>`.
|
|
|
68
68
|
|
|
69
69
|
**Drift:** <each non-empty cause from sync --check, with its count and paths>
|
|
70
70
|
|
|
71
|
+
**Tooling holds:** <each tooling config the write would hold, being a drifted entry whose `cause` is `edited` or `unattributed`, then each `dispositions` entry, declined ones by path and customized ones marked when `moved` is true, or omitted when none>
|
|
72
|
+
|
|
71
73
|
**Where it lands:** <the linked worktree this run is already in, or the worktree `.claude/worktrees/<name>/` it will enter, or the branch it will create in place when the operator declines a worktree>
|
|
72
74
|
|
|
73
75
|
**Calls for you:** <each judgment call below this run meets, with the default this skill takes>
|
|
@@ -122,7 +124,13 @@ The record tree is untracked, so a `record-tree` rewrite has no undo. Run `canon
|
|
|
122
124
|
|
|
123
125
|
## Step 5: sync
|
|
124
126
|
|
|
125
|
-
Run `canon sync .`, then `canon tooling sync <stack> . --write` when the tooling diff named files to change.
|
|
127
|
+
Run `canon sync .`, then `canon tooling sync <stack> . --write` when the tooling diff named files to change. The write holds every config the preview listed under tooling holds.
|
|
128
|
+
|
|
129
|
+
- Add `--replace <path>` only for a held path the operator named in the reply, never for the whole list the write prints
|
|
130
|
+
- Offer `canon tooling customize <path>` for a held path the operator wants kept, and `canon tooling lift <path>` for a moved customized one they want to take
|
|
131
|
+
- Leave a declined path alone, since no write reaches it
|
|
132
|
+
|
|
133
|
+
Treat each governance drift cause the way the preview stated:
|
|
126
134
|
|
|
127
135
|
| Cause | This skill |
|
|
128
136
|
| --------------------------- | ------------------------------------------------------------------------------------------------ |
|
|
@@ -36,7 +36,7 @@ For each surface, look for:
|
|
|
36
36
|
3. **Inconsistencies**: spacing, tone of voice, interaction patterns, icon use, keyboard affordances
|
|
37
37
|
4. **Roughness in daily use**: friction, redundant steps, unclear affordances, ambiguous labels
|
|
38
38
|
|
|
39
|
-
Use `canon/DESIGN.md` as ground truth for intent where it exists. Observations only, no implementation suggestions or fixes.
|
|
39
|
+
Use `canon/DESIGN.md` as ground truth for intent where it exists. Observations only, no implementation suggestions or fixes. Take what consistent interaction means from the `ui` rules in `.claude/rules/`, and skip that when the project installed none.
|
|
40
40
|
|
|
41
41
|
Where Step 1 found a committed capture for this surface, judge composed output (spacing, overlap, contrast, wrapping) against it rather than inferring layout from markup. Where none exists, judge from source and say so in that surface's findings, since a source read cannot see what only a render shows.
|
|
42
42
|
|
|
@@ -32,7 +32,7 @@ A flat installed-rule layout makes `gov sync` report "No governance surfaces fou
|
|
|
32
32
|
- A retired `.claude/GOV.md` is removed
|
|
33
33
|
- A binary older than the stamp holds every rule it has no source for and asks for an upgrade
|
|
34
34
|
|
|
35
|
-
After a rename the new name shows as `missing` when the stamp records a stack, and `gov install <stack>` adds it.
|
|
35
|
+
After a rename the new name shows as `missing` when the stamp records a stack, and `gov install <stack>` adds it. A partner `governance/splits.toml` names shows as `missing` when the tree holds the split rule and lacks the partner, and `gov install --add <partner>` adds it.
|
|
36
36
|
|
|
37
37
|
`canon gov regen [--root <path>]` runs against the toolkit root alone. It rewrites `.claude/rules/canon/` and `.claude/rules/internal/` from `internal/`, and exits 1 when the record names something that does not resolve.
|
|
38
38
|
|
|
@@ -68,6 +68,40 @@ After a rename the new name shows as `missing` when the stamp records a stack, a
|
|
|
68
68
|
- `present` marks a copy an earlier sync left, which is reported and never deleted
|
|
69
69
|
- `canon tooling sync <stack> [target]` writes only under `--write`. `--check` reports and exits 0, and a headless run with neither flag exits 1.
|
|
70
70
|
|
|
71
|
+
### Drift cause and the write hold
|
|
72
|
+
|
|
73
|
+
A `drifted` entry in `configs` carries a `cause`, read against the golden hash the last write recorded for that path.
|
|
74
|
+
|
|
75
|
+
- `stale`: the target still hashes to the record, so canon moved and the project did not. `--write` replaces it.
|
|
76
|
+
- `edited`: the target no longer hashes to the record. `--write` holds it.
|
|
77
|
+
- `unattributed`: no hash is recorded, as on every target synced before hashes existed. `--write` holds it.
|
|
78
|
+
|
|
79
|
+
A config no stack in the recorded chain ships is a first install and is never held, so a fresh scaffold takes every golden config of a stack new to it, even after `canon init` recorded `base`.
|
|
80
|
+
|
|
81
|
+
A held path stays drifted, so the next headless run still exits 1. Pass `--replace <path>` once per path to take the golden copy, as the command the write prints does. A `--replace` naming no config the chain writes refuses with `unknown-replace`.
|
|
82
|
+
|
|
83
|
+
### Dispositions
|
|
84
|
+
|
|
85
|
+
`decline <path> [target] [--reason <text>]`, `customize <path> [target]`, and `lift <path> [target]` set or clear one path's disposition, each with `--json`. The path is relative to the target and resolves against the recorded chain, never a stack argument.
|
|
86
|
+
|
|
87
|
+
- A success answers `{ ok: true, action, path, entry? }`, where `entry` is the recorded `{ disposition, base?, reason? }` and is absent after a lift
|
|
88
|
+
- A declined or customized path leaves `configs`, `seeds`, and `withheld` for `dispositions`, as `{ rel, stack, kind, disposition, present, moved? }`
|
|
89
|
+
- `moved` is set on a customized config alone, and counts toward the exit once the golden copy no longer matches `base`
|
|
90
|
+
- Sync and inject never write a path in `dispositions`, and nothing deletes one the project still holds
|
|
91
|
+
|
|
92
|
+
A refusal answers `{ ok: false, reason, message }` with exit 1:
|
|
93
|
+
|
|
94
|
+
| `reason` | Meaning |
|
|
95
|
+
| ----------------------- | --------------------------------------------------------- |
|
|
96
|
+
| `no-chain` | No tooling chain is recorded, so run `tooling sync` first |
|
|
97
|
+
| `bad-path` | The path leaves the target |
|
|
98
|
+
| `not-golden` | No stack in the recorded chain ships the path |
|
|
99
|
+
| `already-held` | The path is already declined or customized, so lift it |
|
|
100
|
+
| `seed-not-customizable` | The path is a seed, which sync never overwrites |
|
|
101
|
+
| `missing-file` | `customize` named a config the target does not hold |
|
|
102
|
+
| `not-held` | `lift` named a path with no disposition |
|
|
103
|
+
| `toolkit-root` | The target is the toolkit checkout, the source of truth |
|
|
104
|
+
|
|
71
105
|
`canon tooling inject` and `prune-gitignore` apply one stack with no scan or prompt, and the latter prints the number removed on stdout. Use `sync` unless scripting provisioning, and pass `--nested` inside an open frame.
|
|
72
106
|
|
|
73
107
|
## Whole-project sync
|
|
@@ -78,16 +112,16 @@ After a rename the new name shows as `missing` when the stamp records a stack, a
|
|
|
78
112
|
|
|
79
113
|
`canon sync --check` writes nothing and needs no clean tree. `--json` emits the record, `--exit-code` fails CI.
|
|
80
114
|
|
|
81
|
-
| State | Meaning
|
|
82
|
-
| ------------ |
|
|
83
|
-
| `stale` | Still matches what the toolkit installed
|
|
84
|
-
| `customized` | The project edited it
|
|
85
|
-
| `stranded` | Sits at a path the toolkit no longer installs to
|
|
86
|
-
| `orphaned` | The project authored it
|
|
87
|
-
| `drifted` | No stamp covers it
|
|
88
|
-
| `missing` | Governance only. The recorded stack lists it
|
|
89
|
-
| `retired` | Governance only. The toolkit no longer ships it
|
|
90
|
-
| `renamed` | Governance only. The toolkit ships it under a new name
|
|
115
|
+
| State | Meaning |
|
|
116
|
+
| ------------ | ----------------------------------------------------------------------- |
|
|
117
|
+
| `stale` | Still matches what the toolkit installed |
|
|
118
|
+
| `customized` | The project edited it |
|
|
119
|
+
| `stranded` | Sits at a path the toolkit no longer installs to |
|
|
120
|
+
| `orphaned` | The project authored it |
|
|
121
|
+
| `drifted` | No stamp covers it |
|
|
122
|
+
| `missing` | Governance only. The recorded stack lists it, or a split rule leaves it |
|
|
123
|
+
| `retired` | Governance only. The toolkit no longer ships it |
|
|
124
|
+
| `renamed` | Governance only. The toolkit ships it under a new name |
|
|
91
125
|
|
|
92
126
|
`retired` and `renamed` count toward `--exit-code`. `orphaned` and `missing` do not.
|
|
93
127
|
|
package/docs/index.md
CHANGED
|
@@ -9,4 +9,4 @@ One-line reference for each doc in this folder.
|
|
|
9
9
|
|
|
10
10
|
- [Agents](agents/index.md): CLI catalog and invocation rules for agents, split by command domain. Start with overview.
|
|
11
11
|
- [Target](target/index.md): Scaffold, sync, and migrate a toolkit-managed project, with worked setups for three project shapes.
|
|
12
|
-
- [Workflow](workflow/index.md): How a person and their agents run a feature, from the design
|
|
12
|
+
- [Workflow](workflow/index.md): How a person and their agents run a feature, from the design loop through the parallel-session loop to the shell that launches them.
|
|
@@ -75,7 +75,7 @@ The `memory` record kind left `canon records`, so `canon records validate memory
|
|
|
75
75
|
- `canon repo metadata`, `canon autoship`, and the `120-bash` rule are gone, and `canon gov sync` removes the rule <!-- canon-keep-retired -->
|
|
76
76
|
- `memory-capture`, the `603-memory` rule, and the `memory` standard are gone, so send a lesson to the context entry that owns the fact <!-- canon-keep-retired -->
|
|
77
77
|
|
|
78
|
-
Two held files run, `.husky/post-merge` and `.claude/hooks/pr-create-log.sh`, and a stale copy calls a missing binary or names a removed skill. Resync what the toolkit owns before sweeping what the project wrote, and run the second line once per stack
|
|
78
|
+
Two held files run, `.husky/post-merge` and `.claude/hooks/pr-create-log.sh`, and a stale copy calls a missing binary or names a removed skill. Resync what the toolkit owns before sweeping what the project wrote, and run the second line once per stack, naming a held hook with `--replace`.
|
|
79
79
|
|
|
80
80
|
```bash
|
|
81
81
|
canon gov sync
|
|
@@ -181,20 +181,18 @@ The verb adds `Bash(canon pr merge:*)` and `Bash(gh pr merge:*)` so a session ca
|
|
|
181
181
|
|
|
182
182
|
## Move the capture cases out of the harness, once
|
|
183
183
|
|
|
184
|
-
A web project's `e2e/screenshot.ts` is a config
|
|
184
|
+
A web project's `e2e/screenshot.ts` is a config. It now loads `CASES` from a seeded `e2e/capture/cases.ts` that sync copies once and never overwrites. A harness you edited is held on `--write`, so the new one lands only once you pass `--replace e2e/screenshot.ts`.
|
|
185
185
|
|
|
186
|
-
- Copy your cases from the old `e2e/screenshot.ts` into `e2e/capture/cases.ts
|
|
186
|
+
- Copy your cases from the old `e2e/screenshot.ts` into `e2e/capture/cases.ts`, then sync with `--replace e2e/screenshot.ts`.
|
|
187
187
|
- Evidence now defaults to `e2e/evidence/`. Set `export const EVIDENCE_ROOT = 'evidence'` in `e2e/capture/cases.ts` to keep a committed baseline at the old path.
|
|
188
188
|
- A run from a folder with no `e2e/capture/cases.ts` exits 1 and names the file.
|
|
189
189
|
|
|
190
|
-
## Add the
|
|
190
|
+
## Add the rules a split left behind, once
|
|
191
191
|
|
|
192
|
-
The `
|
|
193
|
-
|
|
194
|
-
A project holding `.claude/rules/canon/ui/a11y.md` adds the new rule alone:
|
|
192
|
+
The splits created `pointer` and `ux-controls`, and sync refreshes only held rules. `canon gov sync` now names the missing partner of `keyboard.md` and `ux-completeness.md` on every run and adds nothing itself. `a11y.md` gained two bullets and lost none, so it needs no partner.
|
|
195
193
|
|
|
196
194
|
```bash
|
|
197
|
-
canon gov install --add
|
|
195
|
+
canon gov install --add pointer,ux-controls --yes .
|
|
198
196
|
```
|
|
199
197
|
|
|
200
198
|
## Group a long paths section in `CLAUDE.md`, once
|
package/docs/target/sync.md
CHANGED
|
@@ -44,13 +44,32 @@ Add `--json` for a machine-readable report and `--exit-code` to fail CI when a t
|
|
|
44
44
|
|
|
45
45
|
- Seed docs such as `CLAUDE.md` and `canon/REQUIREMENTS.md`: merge by hand, keeping the project's customizations
|
|
46
46
|
- Installed governance rules: `canon gov sync <path>`, which adds no new rules and reports coverage afterward
|
|
47
|
-
- Tooling configs and seeds: read differences with `canon tooling diff <stack> <path>`, then `canon tooling sync <stack> <path> --write`, which
|
|
47
|
+
- Tooling configs and seeds: read differences with `canon tooling diff <stack> <path>`, then `canon tooling sync <stack> <path> --write`, which holds an edited config and copies each seed once
|
|
48
48
|
- Index regeneration: `canon indexes regen`
|
|
49
49
|
|
|
50
50
|
After `canon gov sync` applies, it lists each rule it copied with its globs and how many project files match. A `ui/` rule adds a pointer to `/canon:ux-audit`. `--json` writes `{ "coverage": [{ "rule", "paths", "files", "ui" }] }` to stdout, empty when nothing was copied, with `files` null outside git. The report never changes the exit code.
|
|
51
51
|
|
|
52
52
|
Try a scenario from [sandbox](../../canon/context/development/sandbox.md) before syncing a real project.
|
|
53
53
|
|
|
54
|
+
### Held, declined, and customized golden files
|
|
55
|
+
|
|
56
|
+
`--write` replaces a golden config only when canon changed it and the project did not. A config the project edited, or one no earlier write recorded, is held and listed with a `--replace` command that takes the golden copy. A config from a stack the target never synced is a first install and is never held.
|
|
57
|
+
|
|
58
|
+
A project can also opt out of one golden file without leaving the stack. Each verb below takes the path relative to the target, as `canon tooling diff` lists it.
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
canon tooling decline .github/workflows/readme-screenshot.yml --reason "no screenshots here"
|
|
62
|
+
canon tooling customize e2e/home.spec.ts
|
|
63
|
+
canon tooling lift .github/workflows/readme-screenshot.yml
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
- `decline` keeps sync and inject from writing the file again, and leaves a copy the project still holds in place
|
|
67
|
+
- `customize` keeps the project's copy of a config through every write, and `diff` exits 1 once the stack ships a newer golden copy
|
|
68
|
+
- A seed cannot be customized, since sync never overwrites one
|
|
69
|
+
- `lift` hands the file back, so the next sync writes it
|
|
70
|
+
|
|
71
|
+
These verbs are the only writers of the record in `canon/config/config.json`, so never edit it there by hand.
|
|
72
|
+
|
|
54
73
|
## Running sync from an agent session
|
|
55
74
|
|
|
56
75
|
`canon sync .` applies every domain sync, then offers a commit and pull request that needs a terminal. With no terminal on stdin the syncs apply, the git step is refused, and the command exits 0. Commit yourself. Sync refuses a dirty working tree, and `canon sync --check .` does not.
|
|
@@ -66,7 +66,7 @@ An independent session invokes `canon:review-pr` once the pull request is open,
|
|
|
66
66
|
|
|
67
67
|
### UI-heavy project
|
|
68
68
|
|
|
69
|
-
|
|
69
|
+
A UI feature runs the same plan, build, and review loop at any size. See [visual design workflow](visual-design-workflow.md).
|
|
70
70
|
|
|
71
71
|
## Skills
|
|
72
72
|
|
package/docs/workflow/index.md
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: Workflow
|
|
3
|
-
subtitle: How a person and their agents run a feature, from the design
|
|
3
|
+
subtitle: How a person and their agents run a feature, from the design loop through the parallel-session loop to the shell that launches them.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Workflow
|
|
7
7
|
|
|
8
|
-
How a person and their agents run a feature, from the design
|
|
8
|
+
How a person and their agents run a feature, from the design loop through the parallel-session loop to the shell that launches them.
|
|
9
9
|
|
|
10
10
|
## Agent surface
|
|
11
11
|
|
|
@@ -17,5 +17,5 @@ How a person and their agents run a feature, from the design tier through the pa
|
|
|
17
17
|
- [Parallel features](parallel-features.md): Run independent features in parallel worktrees, with the records and board every session shares
|
|
18
18
|
- [Skill map on request](skill-map-on-request.md): When to reach for the plugin skills that serve a request rather than a moment in a project's life
|
|
19
19
|
- [Skill map](skill-map.md): When to reach for each plugin skill, grouped by the moment a project meets it
|
|
20
|
-
- [Visual design workflow](visual-design-workflow.md):
|
|
20
|
+
- [Visual design workflow](visual-design-workflow.md): How canon plans, builds, and reviews a UI, from the design reference a plan cites to the browser pass that holds a pull request open
|
|
21
21
|
- [Zshrc aliases for Claude Code](zshrc-aliases.md): Shell aliases that shorten common Claude Code invocations
|
|
@@ -1,57 +1,83 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: Visual design workflow
|
|
3
|
-
description:
|
|
3
|
+
description: How canon plans, builds, and reviews a UI, from the design reference a plan cites to the browser pass that holds a pull request open
|
|
4
4
|
category: Workflow
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# Visual design workflow
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
Canon runs a UI feature through one loop. A plan cites the design, a worker builds against it and captures what it painted, and two independent reviews check the result. A project with no graphical design tool runs all of it, since the design lives in prose and in tracked frames.
|
|
10
10
|
|
|
11
|
-
##
|
|
11
|
+
## Plan
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
`canon:plan-feature` reads `canon/DESIGN.md` and `canon/design/index.md` before it places a UI change. It loads `canon:design-taste`, which decides the layer order a surface is designed in, the coherence locks that keep it consistent, and the defaults a model reaches for that the surface should reach past.
|
|
14
14
|
|
|
15
|
-
- The
|
|
15
|
+
- The plan carries a `## Design references` section naming the accepted design for each surface it changes, or `None identified.` when there is none.
|
|
16
|
+
- A design becomes a reference through `canon:design-record`, which lands an accepted pick in `canon/design/` and carries its values into `canon/DESIGN.md`.
|
|
16
17
|
|
|
17
|
-
|
|
18
|
+
## Build
|
|
18
19
|
|
|
19
|
-
|
|
20
|
-
- `canon:pick-by-eye` drafts candidates as frames on a [Frameboard](https://www.npmjs.com/package/frameboard) board and takes your pick
|
|
21
|
-
- `canon:design-record` lands a pick in `canon/design/` and carries its values into `canon/DESIGN.md`
|
|
22
|
-
- `canon:design-identity` drafts a logo mark and social card
|
|
20
|
+
The worker opens each cited reference, light and dark, before its first edit to that surface. It takes sizes, spacing, and colors from the frame HTML rather than the image, and reports a reference that does not resolve instead of building without it.
|
|
23
21
|
|
|
24
|
-
|
|
22
|
+
- The ui rules fire as a family on the files the worker edits, and `governance/rules/ui/` holds the set.
|
|
23
|
+
- The `surface-capture` rule has the worker capture the full page in every theme the surface ships, against a preview server and never a dev server.
|
|
24
|
+
- It sets the widths too, being 320 plus each breakpoint in `canon/DESIGN.md` for a layout case, and one width for a state case.
|
|
25
25
|
|
|
26
|
-
##
|
|
26
|
+
## Self-check
|
|
27
27
|
|
|
28
|
-
|
|
28
|
+
Before the pull request opens, the worker runs two checks.
|
|
29
29
|
|
|
30
|
-
-
|
|
31
|
-
-
|
|
32
|
-
- Chrome DevTools MCP ([`ChromeDevTools/chrome-devtools-mcp`](https://github.com/ChromeDevTools/chrome-devtools-mcp)) for traces, network, DOM, and CSS
|
|
33
|
-
- Claude Design for codebase extraction or a polished handoff bundle
|
|
34
|
-
- A frontend design skill: [pbakaus/impeccable](https://github.com/pbakaus/impeccable), [nextlevelbuilder/ui-ux-pro-max-skill](https://github.com/nextlevelbuilder/ui-ux-pro-max-skill), or Anthropic's `frontend-design`
|
|
30
|
+
- `canon:ui-checklist` writes the boxes a reviewer reads beside the captures. Each box is one observable behavior, and a box that needs eyes carries a `(taste)` mark.
|
|
31
|
+
- `canon:pr-evidence` posts the captures and the checklist on the pull request as one comment, which the reviews read.
|
|
35
32
|
|
|
36
|
-
|
|
33
|
+
`canon drive <url> <run>` covers states a first render cannot reach. A run file walks a page through named interactions and measures each state, with three probes:
|
|
37
34
|
|
|
38
|
-
|
|
35
|
+
- `focus` reads where keyboard focus lands and whether it shows
|
|
36
|
+
- `details` reads disclosure state
|
|
37
|
+
- `axe` runs the accessibility engine
|
|
39
38
|
|
|
40
|
-
|
|
39
|
+
Run `canon drive --help` for the step kinds and the run file shape.
|
|
41
40
|
|
|
42
|
-
|
|
41
|
+
## Review
|
|
43
42
|
|
|
44
|
-
|
|
45
|
-
- Claude Design with its Claude Code handoff bundle for teams without Figma. The handoff is one-way.
|
|
46
|
-
- The Playwright and Chrome DevTools MCPs from tier 1
|
|
43
|
+
Two reviews run beside each other and finish on their own clocks.
|
|
47
44
|
|
|
48
|
-
|
|
45
|
+
- `canon:review-pr` reads the code and the stills in the evidence comment, and posts under `## Review`.
|
|
46
|
+
- `canon:review-ui` never reads the diff. It drives each checklist box in the running app through the pinned `@playwright/cli` and posts under `## UI review`.
|
|
47
|
+
- Every box gets one verdict, being pass, fail, needs eyes, or not driven.
|
|
49
48
|
|
|
50
|
-
|
|
49
|
+
After the boxes, `review-ui` sweeps every route they reached:
|
|
51
50
|
|
|
52
|
-
|
|
51
|
+
- console errors on each route
|
|
52
|
+
- sideways overflow at each width a box named
|
|
53
|
+
- the `focus`, `details`, and `axe` probes through `canon drive`
|
|
53
54
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
55
|
+
## What holds a review open
|
|
56
|
+
|
|
57
|
+
A UI review stays open while the branch owes a fix. Three things owe one:
|
|
58
|
+
|
|
59
|
+
- a failed box
|
|
60
|
+
- a sweep finding, being a console error, an overflow, or an axe violation of a gated class
|
|
61
|
+
- a box the page kept the pass from driving
|
|
62
|
+
|
|
63
|
+
Two kinds of result report without holding the review open.
|
|
64
|
+
|
|
65
|
+
- A taste box needs eyes, so the pass captures both themes and leaves the call to you.
|
|
66
|
+
- Every other probe result, and every axe `incomplete`, is a reading to confirm, since each probe carries a known class of false finding.
|
|
67
|
+
|
|
68
|
+
At the time of writing, the gated axe classes are contrast, target size, accessible names, labels, alt text, document title and language, valid ARIA, and nested interactive controls. A rule leaves the list on its first false finding, so `canon drive --help` and the `review-ui` skill carry the current one.
|
|
69
|
+
|
|
70
|
+
A close posts as `## UI review closed`. The reviewers lift the draft mark on their close, and you merge.
|
|
71
|
+
|
|
72
|
+
## Design authoring
|
|
73
|
+
|
|
74
|
+
Most projects author design in the loop above and need nothing more. Start with `canon:pick-by-eye`, which drafts candidates as frames on a [Frameboard](https://www.npmjs.com/package/frameboard) board and takes your pick, then record the pick with `canon:design-record`. `canon:design-identity` drafts a logo mark and social card the same way.
|
|
75
|
+
|
|
76
|
+
A graphical tool earns its place when design is the product, a designer owns it, and it changes more often than code. Then the tool is the source of truth, and `canon/DESIGN.md` carries a top note naming the upstream project.
|
|
77
|
+
|
|
78
|
+
- Figma with the official [Figma Dev Mode MCP](https://www.figma.com/blog/introducing-claude-code-to-figma/), for a team already on Figma. It syncs both ways and captures a running UI into editable frames.
|
|
79
|
+
- [Claude Design](../../wiki/claude/design.md) with its Claude Code handoff bundle, for a team without Figma. The handoff is one-way.
|
|
80
|
+
|
|
81
|
+
The loop above runs unchanged on either, with the tool's export standing in for the pick.
|
|
82
|
+
|
|
83
|
+
For the skills by moment, see the [skill map](skill-map.md).
|
package/governance/detect.toml
CHANGED
|
@@ -172,6 +172,10 @@ files = ["index.html", "*/index.html"]
|
|
|
172
172
|
deps = ["react", "vue", "svelte", "astro", "next", "solid-js", "preact"]
|
|
173
173
|
files = ["index.html", "*/index.html"]
|
|
174
174
|
|
|
175
|
+
["pointer"]
|
|
176
|
+
deps = ["react", "vue", "svelte", "astro", "next", "solid-js", "preact"]
|
|
177
|
+
files = ["index.html", "*/index.html"]
|
|
178
|
+
|
|
175
179
|
["forms"]
|
|
176
180
|
deps = ["react", "vue", "svelte", "astro", "next", "solid-js", "preact"]
|
|
177
181
|
files = ["index.html", "*/index.html"]
|
|
@@ -180,6 +184,10 @@ files = ["index.html", "*/index.html"]
|
|
|
180
184
|
deps = ["react", "vue", "svelte", "astro", "next", "solid-js", "preact"]
|
|
181
185
|
files = ["index.html", "*/index.html"]
|
|
182
186
|
|
|
187
|
+
["ux-controls"]
|
|
188
|
+
deps = ["react", "vue", "svelte", "astro", "next", "solid-js", "preact"]
|
|
189
|
+
files = ["index.html", "*/index.html"]
|
|
190
|
+
|
|
183
191
|
["surface-capture"]
|
|
184
192
|
deps = ["react", "vue", "svelte", "astro", "next", "solid-js", "preact"]
|
|
185
193
|
files = ["index.html", "*/index.html"]
|
|
@@ -9,12 +9,13 @@ The field is web accessibility, and the authority is the W3C, through WCAG 2.2 a
|
|
|
9
9
|
|
|
10
10
|
## Semantics
|
|
11
11
|
|
|
12
|
-
| Bullet | Source
|
|
13
|
-
| ----------------------------------------- |
|
|
14
|
-
| Native `<button>` or `<a>` over a `<div>` | [Using ARIA, first rule](https://www.w3.org/TR/using-aria/): "If you can use a native HTML element or attribute with the semantics and behavior you require already built in", use it "instead of re-purposing an element and adding an ARIA role"
|
|
15
|
-
| Attribute spelling per syntax | [React common components](https://react.dev/reference/react-dom/components/common): the DOM prop is `tabIndex`, "A number. Overrides the default Tab button behavior", where HTML spells it `tabindex`
|
|
16
|
-
| Role and synchronized state | [Understanding Name, Role, Value](https://www.w3.org/WAI/WCAG22/Understanding/name-role-value.html): "the name and role can be programmatically determined" and "notification of changes to these items is available to user agents"
|
|
17
|
-
| Name for a control with no visible text | [Understanding Name, Role, Value](https://www.w3.org/WAI/WCAG22/Understanding/name-role-value.html): every user interface component's "name and role can be programmatically determined"
|
|
12
|
+
| Bullet | Source | Read |
|
|
13
|
+
| ----------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------- |
|
|
14
|
+
| Native `<button>` or `<a>` over a `<div>` | [Using ARIA, first rule](https://www.w3.org/TR/using-aria/): "If you can use a native HTML element or attribute with the semantics and behavior you require already built in", use it "instead of re-purposing an element and adding an ARIA role" | 2026-10-18 |
|
|
15
|
+
| Attribute spelling per syntax | [React common components](https://react.dev/reference/react-dom/components/common): the DOM prop is `tabIndex`, "A number. Overrides the default Tab button behavior", where HTML spells it `tabindex` | 2026-10-18, React 19 |
|
|
16
|
+
| Role and synchronized state | [Understanding Name, Role, Value](https://www.w3.org/WAI/WCAG22/Understanding/name-role-value.html): "the name and role can be programmatically determined" and "notification of changes to these items is available to user agents" | 2026-10-18, WCAG 2.2 |
|
|
17
|
+
| Name for a control with no visible text | [Understanding Name, Role, Value](https://www.w3.org/WAI/WCAG22/Understanding/name-role-value.html): every user interface component's "name and role can be programmatically determined" | 2026-10-18, WCAG 2.2 |
|
|
18
|
+
| Name contains the visible label | [Understanding Label in Name](https://www.w3.org/WAI/WCAG22/Understanding/label-in-name.html): "the name contains the text that is presented visually", and the start position is "A best practice". "People who operate with voice interaction use the visible labels in their commands" | 2026-10-23, WCAG 2.2 |
|
|
18
19
|
|
|
19
20
|
## Text alternatives
|
|
20
21
|
|
|
@@ -28,6 +29,7 @@ The field is web accessibility, and the authority is the W3C, through WCAG 2.2 a
|
|
|
28
29
|
| Bullet | Source | Read |
|
|
29
30
|
| ------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------- |
|
|
30
31
|
| 3:1 for boundaries and graphics | [Understanding Non-text Contrast](https://www.w3.org/WAI/WCAG22/Understanding/non-text-contrast.html): "Ensure meaningful visual cues achieve 3:1 against the background" | 2026-10-18, WCAG 2.2 |
|
|
32
|
+
| 4.5:1 for text | [Understanding Contrast (Minimum)](https://www.w3.org/WAI/WCAG22/Understanding/contrast-minimum.html): "The visual presentation of text and images of text has a contrast ratio of at least 4.5:1", with "Large-scale text" at 3:1 | 2026-10-23, WCAG 2.2 |
|
|
31
33
|
| Color never the only carrier | [Understanding Use of Color](https://www.w3.org/WAI/WCAG22/Understanding/use-of-color.html): "Color is not used as the only visual means of conveying information, indicating an action, prompting a response, or distinguishing a visual element" | 2026-10-18, WCAG 2.2 |
|
|
32
34
|
|
|
33
35
|
## Reflow
|
|
@@ -9,26 +9,28 @@ The field is web accessibility, and the authorities are the W3C, through WCAG 2.
|
|
|
9
9
|
|
|
10
10
|
## Focus styles
|
|
11
11
|
|
|
12
|
-
| Bullet | Source
|
|
13
|
-
| ---------------------------------------- |
|
|
14
|
-
| One project focus ring | House choice: per-component focus styles drift apart, and `canon inventory` reports the ring by computed style, so a one-off treatment shows as its own row
|
|
15
|
-
| No outline removal without a replacement | [outline](https://developer.mozilla.org/en-US/docs/Web/CSS/outline): "Assigning outline a value of 0 or none will remove the browser's default focus style. If an element can be interacted with it must have a visible focus indicator."
|
|
16
|
-
| 3:1 ring in every theme | [Understanding Non-text Contrast](https://www.w3.org/WAI/WCAG22/Understanding/non-text-contrast.html): visual information needed to identify a state needs "a contrast ratio of at least 3:1 against adjacent color(s)"
|
|
12
|
+
| Bullet | Source | Read |
|
|
13
|
+
| ---------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------- |
|
|
14
|
+
| One project focus ring | House choice: per-component focus styles drift apart, and `canon inventory` reports the ring by computed style, so a one-off treatment shows as its own row | 2026-10-18 |
|
|
15
|
+
| No outline removal without a replacement | [outline](https://developer.mozilla.org/en-US/docs/Web/CSS/outline): "Assigning outline a value of 0 or none will remove the browser's default focus style. If an element can be interacted with it must have a visible focus indicator." | 2026-10-18 |
|
|
16
|
+
| 3:1 ring in every theme | [Understanding Non-text Contrast](https://www.w3.org/WAI/WCAG22/Understanding/non-text-contrast.html): visual information needed to identify a state needs "a contrast ratio of at least 3:1 against adjacent color(s)" | 2026-10-18, WCAG 2.2 |
|
|
17
|
+
| Ring kept whole | [CSS UI 4](https://www.w3.org/TR/css-ui-4/): "By default, the outline is drawn starting just outside the border edge", and negative `outline-offset` values "must cause the outline to shrink into the border box". [MDN overflow](https://developer.mozilla.org/en-US/docs/Web/CSS/overflow): with `hidden`, "Overflow content is clipped at the element's padding box". [Understanding Focus Appearance](https://www.w3.org/WAI/WCAG22/Understanding/focus-appearance.html), Level AAA, asks for an indicator "at least as large as the area of a 2 CSS pixel thick perimeter". A partial clip passes AA, so the bullet rests on the project's own ring rule | 2026-10-23 |
|
|
17
18
|
|
|
18
19
|
## Keyboard operation
|
|
19
20
|
|
|
20
|
-
| Bullet
|
|
21
|
-
|
|
|
22
|
-
| Escape closes
|
|
23
|
-
| No bare character shortcut
|
|
24
|
-
|
|
|
21
|
+
| Bullet | Source | Read |
|
|
22
|
+
| -------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------- |
|
|
23
|
+
| Escape closes | [Dialog (modal) pattern](https://www.w3.org/WAI/ARIA/apg/patterns/dialog-modal/): "Escape : Closes the dialog", and the [combobox pattern](https://www.w3.org/WAI/ARIA/apg/patterns/combobox/) gives Escape the same job for a popup | 2026-10-18, APG |
|
|
24
|
+
| No bare character shortcut | [Understanding Character Key Shortcuts](https://www.w3.org/WAI/WCAG22/Understanding/character-key-shortcuts.html): a character-only shortcut needs a way to "Turn off", to "Remap", or to be "Active only on focus" | 2026-10-18, WCAG 2.2 |
|
|
25
|
+
| No keyboard trap | [Understanding No Keyboard Trap](https://www.w3.org/WAI/WCAG22/Understanding/no-keyboard-trap.html): "If keyboard focus can be moved to a component of the page using a keyboard interface, then focus can be moved away from that component using only a keyboard interface" | 2026-10-23, WCAG 2.2 |
|
|
25
26
|
|
|
26
27
|
## Composite widgets
|
|
27
28
|
|
|
28
|
-
| Bullet
|
|
29
|
-
|
|
|
30
|
-
| One item in the tab sequence
|
|
31
|
-
| Combobox keys
|
|
29
|
+
| Bullet | Source | Read |
|
|
30
|
+
| --------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------- |
|
|
31
|
+
| One item in the tab sequence | [Developing a keyboard interface](https://www.w3.org/WAI/ARIA/apg/practices/keyboard-interface/): "the tab sequence should include only one focusable element of a composite UI component", managed by "a roving tabindex" or "the aria-activedescendant property" | 2026-10-18, APG |
|
|
32
|
+
| Combobox keys | [Combobox pattern](https://www.w3.org/WAI/ARIA/apg/patterns/combobox/): "Down Arrow : If the popup is available, moves focus into the popup", Enter accepts the focused option, and "Escape : Dismisses the popup if it is visible" | 2026-10-18, APG |
|
|
33
|
+
| Spinbutton keys and the pattern pointer | [Spinbutton pattern](https://www.w3.org/WAI/ARIA/apg/patterns/spinbutton/): Up Arrow "Increases the value", Down Arrow decreases it, Home and End set the limits where they exist, and Page Up is optional, "by a larger step than Up Arrow". The pointer to other patterns rests on the [APG patterns index](https://www.w3.org/WAI/ARIA/apg/patterns/), "How to build accessibility semantics into web patterns and widgets", and a bullet per pattern is declined below | 2026-10-23, APG |
|
|
32
34
|
|
|
33
35
|
## Focus management
|
|
34
36
|
|
|
@@ -37,17 +39,14 @@ The field is web accessibility, and the authorities are the W3C, through WCAG 2.
|
|
|
37
39
|
| Focus into a dialog and back | [Dialog (modal) pattern](https://www.w3.org/WAI/ARIA/apg/patterns/dialog-modal/): "When a dialog opens, focus moves to an element inside the dialog", and "When a dialog closes, focus returns to the element that invoked the dialog" | 2026-10-18, APG |
|
|
38
40
|
| Focus never covered | [Understanding Focus Not Obscured (Minimum)](https://www.w3.org/WAI/WCAG22/Understanding/focus-not-obscured-minimum.html): "the component is not entirely hidden due to author-created content" | 2026-10-18, WCAG 2.2 |
|
|
39
41
|
|
|
40
|
-
## Pointer targets
|
|
41
|
-
|
|
42
|
-
| Bullet | Source | Read |
|
|
43
|
-
| ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------- |
|
|
44
|
-
| 24 by 24 on the web | [Understanding Target Size (Minimum)](https://www.w3.org/WAI/WCAG22/Understanding/target-size-minimum.html): "at least 24 by 24 CSS pixels", except where "a 24 CSS pixel diameter circle is centered on the bounding box of each" and "the circles do not intersect" | 2026-10-18, WCAG 2.2 |
|
|
45
|
-
| Native figures | [Apple accessibility guidelines](https://developer.apple.com/design/human-interface-guidelines/accessibility): a default "44x44 pt" control size on iOS and iPadOS, and [Android accessibility](https://developer.android.com/guide/topics/ui/accessibility/apps): "a focusable area, or touch target size , of at least 48dpx48dp" | 2026-10-18 |
|
|
46
|
-
|
|
47
42
|
## Declined
|
|
48
43
|
|
|
49
44
|
Every bullet the split removed or reshaped, grouped by why.
|
|
50
45
|
|
|
46
|
+
### Left to axe or to a person
|
|
47
|
+
|
|
48
|
+
- Declined a bullet per APG pattern. One pointer to the pattern page costs a line, and a bullet per pattern costs context on every component edit.
|
|
49
|
+
|
|
51
50
|
### Browser defaults
|
|
52
51
|
|
|
53
52
|
- Cut the Tab, Enter, and Space bullets of the old keyboard model. A native element does each by default, and `a11y.md` already sends a session to the native element.
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Pointer rule ledger
|
|
3
|
+
description: Sources for governance/rules/ui/pointer.md, one row per bullet
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Pointer rule ledger
|
|
7
|
+
|
|
8
|
+
The field is web accessibility, and the authorities are the W3C, through WCAG 2.2 and the ARIA Authoring Practices Guide, MDN for popover behavior, and the platform guidelines from Apple and Android for native targets. These bullets were split from `keyboard.md`.
|
|
9
|
+
|
|
10
|
+
## Pointer actions
|
|
11
|
+
|
|
12
|
+
| Bullet | Source | Read |
|
|
13
|
+
| ------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------- |
|
|
14
|
+
| Drag has a single-pointer path | [Understanding Dragging Movements](https://www.w3.org/WAI/WCAG22/Understanding/dragging-movements.html): "For any action that involves dragging, provide a simple pointer alternative" | 2026-10-18, WCAG 2.2 |
|
|
15
|
+
| Outside press and focus leave dismiss | [Using the Popover API](https://developer.mozilla.org/en-US/docs/Web/API/Popover_API/Using): you can hide the popover "by clicking outside it", and [the APG disclosure navigation example](https://www.w3.org/WAI/ARIA/apg/patterns/disclosure/examples/disclosure-navigation/): "Moving focus out of the navigation region also closes an open dropdown" | 2026-10-23 |
|
|
16
|
+
| Pointer action on release | [Understanding Pointer Cancellation](https://www.w3.org/WAI/WCAG22/Understanding/pointer-cancellation.html): one of "No Down-Event", "Abort or Undo", or "Up Reversal" holds for single-pointer functionality | 2026-10-23, WCAG 2.2 |
|
|
17
|
+
| Gestures have a single-pointer path | [Understanding Pointer Gestures](https://www.w3.org/WAI/WCAG22/Understanding/pointer-gestures.html): "All functionality that uses multipoint or path-based gestures for operation can be operated with a single pointer without a path-based gesture" | 2026-10-23, WCAG 2.2 |
|
|
18
|
+
|
|
19
|
+
## Pointer targets
|
|
20
|
+
|
|
21
|
+
| Bullet | Source | Read |
|
|
22
|
+
| ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------- |
|
|
23
|
+
| 24 by 24 on the web | [Understanding Target Size (Minimum)](https://www.w3.org/WAI/WCAG22/Understanding/target-size-minimum.html): "at least 24 by 24 CSS pixels", except where "a 24 CSS pixel diameter circle is centered on the bounding box of each" and "the circles do not intersect" | 2026-10-18, WCAG 2.2 |
|
|
24
|
+
| Native figures | [Apple accessibility guidelines](https://developer.apple.com/design/human-interface-guidelines/accessibility): a default "44x44 pt" control size on iOS and iPadOS, and [Android accessibility](https://developer.android.com/guide/topics/ui/accessibility/apps): "a focusable area, or touch target size , of at least 48dpx48dp" | 2026-10-18 |
|