Accessibility, and one part of it is a self-defeating feature rather than a missing one.
The canvas has no keyboard path in
web/src/editor/GardenCanvas.tsx:234-240 — the <svg> has no role, no aria-label, no <title>, no tabIndex
web/src/editor/ObjectShape.tsx:64 and PlopMarker.tsx:50 — objects and plops are <g onPointerDown={...}: not focusable, no role, no accessible name
A screen reader gets an empty graphic where the garden is.
The part that makes it worse
Arrow-key nudging is implemented (GardenEditorPage.tsx:184-256), with grid snapping and Escape to peel back layers (:163-175). But it only acts on the current selection, and there is no keyboard way to make a selection — selection happens exclusively through onPointerDown.
So the keyboard feature is unusable by exactly the people who need a keyboard. Someone who can use a pointer to select doesn't need arrow-key nudging; someone who can't, can't reach it.
Scoped first slice
Full canvas accessibility is large. A worthwhile first cut is small and makes the existing nudge feature reachable:
role="application" + aria-label on the <svg>, and a <title> naming the garden.
Make each object focusable (tabIndex, role="button" or role="img") with an aria-label of name + kind + size.
Enter/Space selects the focused object.
That turns a dead feature into a working one. Roving-tabindex between plops inside a focused bed, and announcing positions usefully, are natural follow-ups.
Two smaller a11y items worth folding in
Modal has no focus trap and no focus restoration (web/src/components/ui/Modal.tsx:28-56). It sets role="dialog" aria-modal="true" and focuses the card, but Tab walks straight out into the page behind (nothing is inert/aria-hidden), and on close focus lands on <body> rather than the trigger. Every destructive confirmation goes through this component — DeleteGardenModal, ClearBedModal, DeletePlantModal, DeleteSeedLotModal, ShareGardenModal. Well-bounded standard fix.
EditorRail tabs aren't tabs (web/src/editor/EditorRail.tsx:61). They use aria-current="page" on plain buttons instead of role="tablist"/role="tab"/aria-selected with arrow-key roving focus, so a screen reader announces four unrelated buttons rather than a tab set.
Noted as clean
Form controls are actually good — TextField/Select wire htmlFor/id/aria-describedby correctly through useFieldId. No changes needed there.
Accessibility, and one part of it is a self-defeating feature rather than a missing one.
## The canvas has no keyboard path in
- `web/src/editor/GardenCanvas.tsx:234-240` — the `<svg>` has no `role`, no `aria-label`, no `<title>`, no `tabIndex`
- `web/src/editor/ObjectShape.tsx:64` and `PlopMarker.tsx:50` — objects and plops are `<g onPointerDown={...}`: not focusable, no role, no accessible name
A screen reader gets an empty graphic where the garden is.
## The part that makes it worse
Arrow-key nudging is implemented (`GardenEditorPage.tsx:184-256`), with grid snapping and Escape to peel back layers (`:163-175`). But it only acts on the **current selection**, and there is **no keyboard way to make a selection** — selection happens exclusively through `onPointerDown`.
So the keyboard feature is unusable by exactly the people who need a keyboard. Someone who can use a pointer to select doesn't need arrow-key nudging; someone who can't, can't reach it.
## Scoped first slice
Full canvas accessibility is large. A worthwhile first cut is small and makes the existing nudge feature reachable:
1. `role="application"` + `aria-label` on the `<svg>`, and a `<title>` naming the garden.
2. Make each object focusable (`tabIndex`, `role="button"` or `role="img"`) with an `aria-label` of name + kind + size.
3. Enter/Space selects the focused object.
That turns a dead feature into a working one. Roving-tabindex between plops inside a focused bed, and announcing positions usefully, are natural follow-ups.
## Two smaller a11y items worth folding in
- **`Modal` has no focus trap and no focus restoration** (`web/src/components/ui/Modal.tsx:28-56`). It sets `role="dialog" aria-modal="true"` and focuses the card, but Tab walks straight out into the page behind (nothing is `inert`/`aria-hidden`), and on close focus lands on `<body>` rather than the trigger. **Every destructive confirmation goes through this component** — `DeleteGardenModal`, `ClearBedModal`, `DeletePlantModal`, `DeleteSeedLotModal`, `ShareGardenModal`. Well-bounded standard fix.
- **`EditorRail` tabs aren't tabs** (`web/src/editor/EditorRail.tsx:61`). They use `aria-current="page"` on plain buttons instead of `role="tablist"`/`role="tab"`/`aria-selected` with arrow-key roving focus, so a screen reader announces four unrelated buttons rather than a tab set.
## Noted as clean
Form controls are actually good — `TextField`/`Select` wire `htmlFor`/`id`/`aria-describedby` correctly through `useFieldId`. No changes needed there.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Accessibility, and one part of it is a self-defeating feature rather than a missing one.
The canvas has no keyboard path in
web/src/editor/GardenCanvas.tsx:234-240— the<svg>has norole, noaria-label, no<title>, notabIndexweb/src/editor/ObjectShape.tsx:64andPlopMarker.tsx:50— objects and plops are<g onPointerDown={...}: not focusable, no role, no accessible nameA screen reader gets an empty graphic where the garden is.
The part that makes it worse
Arrow-key nudging is implemented (
GardenEditorPage.tsx:184-256), with grid snapping and Escape to peel back layers (:163-175). But it only acts on the current selection, and there is no keyboard way to make a selection — selection happens exclusively throughonPointerDown.So the keyboard feature is unusable by exactly the people who need a keyboard. Someone who can use a pointer to select doesn't need arrow-key nudging; someone who can't, can't reach it.
Scoped first slice
Full canvas accessibility is large. A worthwhile first cut is small and makes the existing nudge feature reachable:
role="application"+aria-labelon the<svg>, and a<title>naming the garden.tabIndex,role="button"orrole="img") with anaria-labelof name + kind + size.That turns a dead feature into a working one. Roving-tabindex between plops inside a focused bed, and announcing positions usefully, are natural follow-ups.
Two smaller a11y items worth folding in
Modalhas no focus trap and no focus restoration (web/src/components/ui/Modal.tsx:28-56). It setsrole="dialog" aria-modal="true"and focuses the card, but Tab walks straight out into the page behind (nothing isinert/aria-hidden), and on close focus lands on<body>rather than the trigger. Every destructive confirmation goes through this component —DeleteGardenModal,ClearBedModal,DeletePlantModal,DeleteSeedLotModal,ShareGardenModal. Well-bounded standard fix.EditorRailtabs aren't tabs (web/src/editor/EditorRail.tsx:61). They usearia-current="page"on plain buttons instead ofrole="tablist"/role="tab"/aria-selectedwith arrow-key roving focus, so a screen reader announces four unrelated buttons rather than a tab set.Noted as clean
Form controls are actually good —
TextField/SelectwirehtmlFor/id/aria-describedbycorrectly throughuseFieldId. No changes needed there.