Skip to content

fix(landing): make map switcher and menu toggle keyboard accessible - #4

Open
olitreadwell wants to merge 1 commit into
masterfrom
fix/landing-a11y-keyboard-controls
Open

fix(landing): make map switcher and menu toggle keyboard accessible#4
olitreadwell wants to merge 1 commit into
masterfrom
fix/landing-a11y-keyboard-controls

Conversation

@olitreadwell

@olitreadwell olitreadwell commented Aug 12, 2026

Copy link
Copy Markdown
Owner

Motivation

Two of the map viewer's own controls can only be used with a mouse.

They are built from non-interactive HTML elements, so keyboard and screen-reader users cannot reach or operate them:

  • The layer switcher (bottom-left mini-map, "Switch map to …") is a plain <div onClick>. It is not in the tab order, does not respond to Enter/Space, and has no button role, so assistive tech does not announce it as a control.
  • The header menu toggle (the hamburger / close icon) is a plain <i onClick>. Same problem: not focusable, not keyboard-operable, no role, no accessible name, and no open/closed state exposed.

For a public government map viewer this is a real barrier.

It fails WCAG 2.1 2.1.1 Keyboard (all functionality available from a keyboard) and 4.1.2 Name, Role, Value (controls must expose a name, a role, and their state).

Self-found gap: there is no existing upstream issue for this.

Modifications

  • map.switcher.tsx: kept the element a <div> (it wraps another <div>, which is not valid inside a <button>) and made it behave like a button: role="button", tabIndex={0}, an aria-label, and an onKeyDown handler that activates it on Enter and Space (with preventDefault so Space does not scroll the page). The existing click behaviour is unchanged.
  • layout.header.tsx: replaced the clickable <i> with a real <button type="button"> wrapping the icon. Added aria-label ("Open menu" / "Close menu") and aria-expanded so the toggle state is announced. The icon is now aria-hidden (decorative). The mouse-only inline cursor: pointer is removed, the button gets it from the existing global rule.
  • static/index.css: reset the native chrome on the new menu button so the icon looks exactly as before, and added a :focus-visible outline on the layer switcher so keyboard focus is visible (WCAG 2.4.7 Focus Visible).

One coherent change: make the two custom map controls keyboard-operable and properly labelled.

No behaviour change for existing mouse users.

How to verify

Ran in packages/landing on Node 24:

git fetch https://github.com/olitreadwell/basemaps.git fix/landing-a11y-keyboard-controls
git checkout FETCH_HEAD
npm install
npm run build      # tsc -b: clean
npm run test       # 27 passed, 0 failed
npx eslint packages/landing/src/components/map.switcher.tsx \
           packages/landing/src/components/layout.header.tsx   # clean

Accessibility, reasoned per control (before → after):

  • Layer switcher: before: not in tab order, no role, Enter/Space do nothing, name only via title (not a reliable accessible name). After: reachable by Tab, announced as "Switch map to …, button", operable with Enter and Space, visible focus ring.
  • Menu toggle: before: <i> not focusable, no role/name/state. After, a real <button>, in the tab order, announced as "Open menu, button" / "Close menu, button" with expanded/collapsed state, Enter/Space work natively.

The repo has no component-DOM test harness (tests run via node --test on compiled JS, no jsdom/testing-library), so this is verified by build + lint + type-check + the existing suite staying green, plus the concrete control-by-control reasoning abov: not by a new UI test.


AI disclosure: Drafted with AI assistance.

I found the gap, verified the fix against the current code, ran the build/lint/tests, and reviewed the diff line by line.

AI code review (open-code-review, delegation mode): clean.

Promote upstream with:

gh pr create --repo linz/basemaps --base master --head olitreadwell:fix/landing-a11y-keyboard-controls-20260805-040920

No CLA, DCO, or commit signing is required by this repo.

Commit message and this PR title follow Conventional Commits, matching the repo's PR-title lint.

AI assistance

Drafted with AI assistance. The change was verified before opening, and the diff was reviewed line by line.


PROMOTION NOTE (remove this section before/when opening against upstream):

Open the compare view to create the upstream PR (scoped to this branch):

linz/basemaps@master...olitreadwell:basemaps:fix/landing-a11y-keyboard-controls

Or via CLI:

gh pr create --repo linz/basemaps --base master --head olitreadwell:fix/landing-a11y-keyboard-controls

Prerequisites: none outstanding (re-verify CLA/DCO/signing before promoting).

- The layer switcher was a div with onClick, so keyboard and screen-reader
  users could not reach or operate it. Give it role=button, tabindex, an
  aria-label, and Enter/Space key handling, plus a visible focus outline.
- The header menu toggle was a clickable <i> icon. Replace it with a real
  button that has an aria-label and aria-expanded, and hide the icon from
  assistive tech.
- Reset the native button chrome in CSS so the icon looks unchanged.

Both controls now meet WCAG 2.1.1 (Keyboard) and 4.1.2 (Name, Role, Value).
@olitreadwell
olitreadwell force-pushed the fix/landing-a11y-keyboard-controls branch from 8a6209d to 5d86496 Compare August 18, 2026 22:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant