This repository was archived by the owner on Nov 4, 2026. It is now read-only.
Repository navigation
Commit 4aa90ba
fix(a11y): name icon-only buttons and give clickable div/span controls a keyboard path (objectui#13461) (#13494)
<!-- claude-projects-attribution: {"github_login":"os-elon-musk"} -->
_Requested by **Elon** · [project
thread](https://claude.ai/code/project/chan_01WedDzV26nniwxv7jC88B9D?thread=cmsg_01WedDzV26nniwxv7jC88B9DMknEB2RRDpu49p9oqa9C6D)_
Fixes #13461
Clause-②: no
Metadata-protocol: none
## What changed
Icon-only buttons in the console get an accessible name, and the
console's mouse-only clickable `div` / `span` controls become real
buttons. A new deletions-only census keeps clickable `div` / `span`
sites from coming back.
### Premise re-measured (the card's counts were a script reading)
The card's 25 / 24 came from a line-window script. Re-measured on the
TypeScript AST at base `c85408f`:
- **Icon-only buttons.** Several of the card's examples already carry
`aria-label` a few lines below the opening tag (the AI chat page, the
invitations page, the chat dock), so they were never unnamed. The
instrument that already re-derives this family is
`iconOnlyButtonCensus-12143.test.ts` (it exists; the card says nobody
filed it). This PR names every entry of its `BACKLOG` inside the claim's
file surface (app-shell, components, fields, plugin-chatbot: 16 sites in
12 files) and deletes those rows. The top bar's avatar button, which
that census cannot see (an `Avatar` is not an icon), is named too.
- **Clickable `div` / `span`.** The new census reads every `div` /
`span` with an `onClick` and no `role`, `tabIndex` or key handler under
`packages/*/src` and `apps/*/src`. Most sites are not controls:
propagation guards around real buttons, scrims behind overlays, empty
design-canvas backgrounds, and drag handles. Each is declared in
`NOT_CONTROLS` with its reason. ⛔ None got `role="button"`.
### Repaired
| Where | Before | After |
|:--|:--|:--|
| Organization picker card, "Manage" | a clickable `span` inside the
card's `button` (unreachable by Tab, read as part of the card's name) |
its own `button` beside the card button; `organization-manage-SLUG` test
id |
| Flow canvas branch label | clickable `span`; the wide hit path is
pointer-only | `role="button"`, `tabIndex`, `aria-pressed`, Enter /
Space when the edge is selectable |
| `tree-view` node row | clickable `div` wrapped round the toggle
`button`; toggle unnamed | the toggle is named (`common.expand` /
`common.collapse`) with `aria-expanded`; the node is a `button` only
when the host passes `onNodeClick` |
| `file-upload` dropzone | clickable `div` | `role="button"`,
`tabIndex={0}`, Enter / Space opens the picker |
| App preview read-only nav row | an `onSelect` click handler that could
never fire (the list renders only outside design mode, where the handler
is undefined) | handler removed |
Icon-only buttons named: AI live canvas close (`common.close`), action
result dialog secret toggle (`actions.resultDialog.showValue` /
`hideValue`), permission set editor remove policy / remove tab
(`perm.rls.remove` / `perm.tabs.remove`, designer table en + zh),
metadata history refresh (`engine.edit.refresh`), sort builder remove
(`sortBuilder.removeSort`), `file-upload` and `FileField` remove
(`fields.file.remove`), `MasterDetailField` open / remove
(`fields.masterDetail.*`), `PasswordField` toggle (`fields.password.*`),
`QRCodeField` copy / toggle (`fields.qrCode.*`), legacy `Chatbot` send
(`chatbot.sendMessage`), chat scroll-to-latest
(`chatbot.scrollToBottom`, before the props spread so a caller's name
wins), top bar avatar (`user.accountMenu`). New pack keys are in all ten
packs.
### The census —
`packages/components/src/__tests__/clickableNonControlCensus-13461.test.ts`
Same shape as the objectui#12143 and objectui#12145 censuses: an
ordinary-suite test, ⛔ not a new CI gate. Sites are keyed by file, tag
and handler (never by line); `NOT_CONTROLS` and `BACKLOG` are
deletions-only and must equal what the scan finds. It carries width
guards, a matcher fixture, and a stated blind-spot list (spread
attributes, other tags, whether a reason is still true).
`BACKLOG` freezes 16 clickable controls that still have no keyboard
path. 15 are outside the claim's file surface (plugin-ai,
plugin-designer, plugin-gantt, plugin-grid, plugin-view). One is in
app-shell: the studio data-form preview selects a field by a click
delegated from its frame, and a keyboard path for that is a design
question. The 12143 census keeps its 14 remaining plugin rows
(plugin-ai, plugin-detail, plugin-list, plugin-view).
## Verification (head after the merge of main, `ecd30ca`)
- Ablation, done once and not kept: restored `OrganizationsPage.tsx`
from `origin/main` and the new census went red with
`OrganizationsPage.tsx · span · stopPropagation,navigate — declared 0,
found 1`. Restored with `git checkout HEAD --`, and `git diff HEAD` was
empty.
- New and extended tests: `keyboardControls-13461.test.tsx` (components:
tree toggle name and state, node button only with `onNodeClick`, zh
pack; dropzone Enter / Space, remove-button name),
`iconButtonNames-13461.test.tsx` (fields: password, QR code,
master-detail names, zh pack), plus an `OrganizationsPage.test.tsx` case
(Manage is a sibling button and does not switch org) and an
`AppHeader.myOrganizations.test.tsx` case (avatar button name).
- Every test file that imports a changed module, plus every test that
reads a changed file by path: 261 files in five runs, all passed before
the merge. After the merge, the three censuses, the new tests and the
tree-view catalog pin (`tree-view-nodes-mirror-6939`, 8 files, 54 tests)
passed.
- `packages/i18n/` whole package: 87 files passed.
- `turbo run type-check` for app-shell, components, fields,
plugin-chatbot and i18n with their dependencies: green after a
test-typing fix in fields.
- Gates: `check:i18n-keys`, `check:i18n-dead-keys`, `check:i18n-drift`,
`check:i18n-designer-parity`, `check:new-line-citations`,
`check-changeset-presence`, `check-changeset-no-major`,
`check-changeset-claims`, `check-pending-changeset-literals` and
`check-changeset-fixed` all exit 0. ESLint on touched files: 0 errors
with inline config. One `react-hooks/static-components` error appears
under `--no-inline-config` in `AppPreview.tsx`, and `origin/main`'s copy
of that file shows the same error.
- NOT MEASURED: `check:eager-locale-catalogues` (needs a built console
bundle; CI builds first). The whole-repo `pnpm test` and `pnpm lint` are
left to CI.
## Acceptance notes
- Literal user-facing strings next to the touched controls are untouched
(the legacy `Chatbot`'s empty-state line, `MasterDetailField`'s record
count, `file-upload`'s launch copy, `ResourceHistoryPage`'s "Back to
item"). They are outside this card.
- The optional per-route axe pass, a required axe check and repo-wide
`eslint-plugin-jsx-a11y` are out of scope, per the claim.
- [x] Adds no product-specific widget, dialog, hook or string (see
AGENTS.md › Product-neutral renderer)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
https://claude.ai/code/session_018qafSnDdNuQgZGfnFwDajn
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent 5698ec7 commit 4aa90ba
35 files changed
Lines changed: 1043 additions & 69 deletions
File tree
- .changeset
- packages
- app-shell/src
- console
- ai
- organizations
- __tests__
- layout
- __tests__
- views
- metadata-admin
- previews
- components/src
- __tests__
- custom
- renderers
- data-display
- form
- fields/src
- __tests__
- widgets
- i18n/src/locales
- plugin-chatbot/src
- elements
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
137 | 137 | | |
138 | 138 | | |
139 | 139 | | |
140 | | - | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
141 | 147 | | |
142 | 148 | | |
143 | 149 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
239 | 239 | | |
240 | 240 | | |
241 | 241 | | |
242 | | - | |
| 242 | + | |
| 243 | + | |
| 244 | + | |
| 245 | + | |
| 246 | + | |
| 247 | + | |
243 | 248 | | |
244 | | - | |
245 | | - | |
246 | | - | |
247 | | - | |
| 249 | + | |
248 | 250 | | |
249 | | - | |
250 | | - | |
251 | | - | |
252 | | - | |
253 | | - | |
254 | | - | |
255 | | - | |
256 | | - | |
257 | | - | |
258 | | - | |
259 | | - | |
260 | | - | |
| 251 | + | |
| 252 | + | |
| 253 | + | |
| 254 | + | |
| 255 | + | |
| 256 | + | |
| 257 | + | |
| 258 | + | |
| 259 | + | |
| 260 | + | |
| 261 | + | |
| 262 | + | |
| 263 | + | |
| 264 | + | |
| 265 | + | |
| 266 | + | |
| 267 | + | |
| 268 | + | |
| 269 | + | |
| 270 | + | |
261 | 271 | | |
262 | | - | |
263 | | - | |
264 | | - | |
265 | | - | |
| 272 | + | |
| 273 | + | |
| 274 | + | |
| 275 | + | |
| 276 | + | |
| 277 | + | |
266 | 278 | | |
267 | 279 | | |
268 | | - | |
| 280 | + | |
269 | 281 | | |
270 | | - | |
| 282 | + | |
271 | 283 | | |
272 | 284 | | |
273 | 285 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
11 | 11 | | |
12 | 12 | | |
13 | 13 | | |
14 | | - | |
| 14 | + | |
15 | 15 | | |
16 | 16 | | |
17 | 17 | | |
| |||
103 | 103 | | |
104 | 104 | | |
105 | 105 | | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1192 | 1192 | | |
1193 | 1193 | | |
1194 | 1194 | | |
1195 | | - | |
| 1195 | + | |
| 1196 | + | |
| 1197 | + | |
| 1198 | + | |
| 1199 | + | |
| 1200 | + | |
1196 | 1201 | | |
1197 | 1202 | | |
1198 | 1203 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
244 | 244 | | |
245 | 245 | | |
246 | 246 | | |
| 247 | + | |
| 248 | + | |
| 249 | + | |
| 250 | + | |
| 251 | + | |
| 252 | + | |
| 253 | + | |
| 254 | + | |
| 255 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
273 | 273 | | |
274 | 274 | | |
275 | 275 | | |
| 276 | + | |
276 | 277 | | |
277 | 278 | | |
278 | 279 | | |
279 | 280 | | |
280 | 281 | | |
281 | 282 | | |
282 | | - | |
| 283 | + | |
| 284 | + | |
| 285 | + | |
| 286 | + | |
| 287 | + | |
| 288 | + | |
| 289 | + | |
283 | 290 | | |
284 | 291 | | |
285 | 292 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
530 | 530 | | |
531 | 531 | | |
532 | 532 | | |
| 533 | + | |
533 | 534 | | |
534 | 535 | | |
535 | 536 | | |
| |||
648 | 649 | | |
649 | 650 | | |
650 | 651 | | |
| 652 | + | |
651 | 653 | | |
652 | 654 | | |
653 | 655 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
98 | 98 | | |
99 | 99 | | |
100 | 100 | | |
| 101 | + | |
101 | 102 | | |
102 | 103 | | |
103 | 104 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
2605 | 2605 | | |
2606 | 2606 | | |
2607 | 2607 | | |
| 2608 | + | |
2608 | 2609 | | |
2609 | 2610 | | |
2610 | 2611 | | |
| |||
2643 | 2644 | | |
2644 | 2645 | | |
2645 | 2646 | | |
| 2647 | + | |
2646 | 2648 | | |
2647 | 2649 | | |
2648 | 2650 | | |
| |||
6208 | 6210 | | |
6209 | 6211 | | |
6210 | 6212 | | |
| 6213 | + | |
6211 | 6214 | | |
6212 | 6215 | | |
6213 | 6216 | | |
| |||
6244 | 6247 | | |
6245 | 6248 | | |
6246 | 6249 | | |
| 6250 | + | |
6247 | 6251 | | |
6248 | 6252 | | |
6249 | 6253 | | |
| |||
0 commit comments