Skip to content

Make Commune work by keyboard and screen reader (app review batch 5, accessibility) - #30

Merged
EmotiveImpact merged 24 commits into
mainfrom
claude/app-review-accessibility
Oct 12, 2026
Merged

EmotiveImpact merged 24 commits into
mainfrom
claude/app-review-accessibility

Conversation

@EmotiveImpact

Copy link
Copy Markdown
Owner

Before: a keyboard user pressed Tab 14 times before reaching any content, and moving to another page announced nothing and left focus where it was. The green focus ring measured 2.6:1 on the navy sidebar and between 2.2 and 2.7:1 in dark mode. Several states showed only by colour, a row of Nudge or Remove buttons all had the same name, form errors sat away from the field they were about, and Escape threw away whatever had been typed. An automated check (axe in Chromium) found 184 WCAG problems over 15 screens, in light and dark, at desktop and phone width.

After: "Skip to content" is the first Tab stop and a new page puts focus on its heading. Focus rings inside the app measure at least 4.13:1. Dates, the calendar's today and selected day, the bell's unread count and each repeated button are named for screen readers. A form error marks its field and takes focus there, and Escape over typed text asks "Discard your changes?" first. The same check now finds 8 problems, all one grey "£0" on Home and Places cards, listed below for you.

This is the first of three pull requests for batch 5 of the app review fixes: this one, then getting around, then Home and notices. It is built on #29 (the lock order fix), so review that first; the diff here shows only this batch.

What changed, by finding

Finding Change
27 The content area is a <main> landmark with "Skip to content" as the first Tab stop. Moving to another page or commune puts focus on the new page's heading; a window, the note reader or a search result keeps the focus it takes, and focus goes to the heading when one closes and focus would otherwise be lost. The commune switcher is a show-and-hide list named from the visible commune, and Escape closes it and returns focus to its button. A control focused in a window scrolls clear of the sticky Save row.
28 New --focus-ring and --text-green tokens: today's green in light mode, and the Hub's approved lighter green (#649578) in dark mode and on navy surfaces. Dark mode gets a placeholder colour (6.18:1) and switches whose on and off states differ, the sidebar's secondary text rises to 0.6 white, and --danger and --danger-soft give error text one colour in both themes.
29 Calendar dates carry their full wording as hidden text. The Operations month marks today and the selected day for screen readers, outlines the selected day more strongly, and gives tasks, events and late items a shape as well as a colour. The phone tab bar marks the current page with a small bar as well as brighter text. The bell reads "Notifications, 3 unread". Repeated buttons (Nudge, Confirm received, Make admin, Remove, Edit, Archive, View fund and others) name the person or item they act on, and every control's name starts with its visible text.
30 A useFormError hook marks the field an error is about, links the message to it and moves focus there. Add expense, Create a commune, Invite, notes, announcements, tasks, sign-in, sign-up and email verification use it. Escape, the backdrop and the close button ask "Discard your changes?" when a window holds typed text, with Keep editing as the default; Cancel inside a form still closes at once. Sign-up shows the 12-character password rule before anything is submitted.
60 Desktop links that were 16 to 18px tall reach 24px without moving. On phones, filter chips, settings switches, Hub links, View buttons and task boxes get a 44px touch area without changing how they look. Signed-out pages gain header, main and footer landmarks, keep an h1 on phones and use darker grey text (4.99:1, was 4.41:1). The last admin sees why their role, remove and leave buttons are unavailable. Operations' Week, People and Month are pressed buttons, and its Undo message takes focus and stays while it is in use. The note reader's details are a valid description list, Settings and Places no longer skip a heading level, and Contact us says it opens a new tab.
Errors shown twice (found while fixing 30) A failure that a form or screen catches is shown there only; it also raised the alert strip, so a refused commune settings save showed its message twice. Failures nothing catches still reach the strip. The strip and the form error line are styled with the design tokens, and Operations shows a refused task action on the screen until the next action.

Not in this batch: the phone menu (27), the Notes toolbar at 320px (60) and a 44px touch area for the phone header icons wait for #22, which rebuilds that bar. Text over commune photos (58) is in the Home and notices pull request. Faint input borders and chips (59) wait for your design sign-off.

Releasing

Nothing hosted was touched and there is no migration: this batch is the web client only, and it can go out whenever the pull requests below it have.

Needs the owner

  • On Home and Places cards the grey "£0" (.a-place-foot .amt.even) measures 4.2:1, just under the 4.5:1 rule. It is the only WCAG failure the check still finds on the 15 screens; say if you want it darker.
  • In the light Operations month, the day numbers outside the month are #aaa on #fffdf9 (2.28:1). They are working buttons, so their numbers need 4.5:1; dark mode is already fixed. Darkening them changes the approved month look, so it is your call.
  • On the sign-in page the field's focus outline is 1.67:1 against the page. The 1px border turning green also marks focus, at a strong contrast; a heavier ring would change the approved sign-in look.

How it was checked

On the final branch:

  • 487 web tests in 44 files, including the new accessibility.test.tsx (28 tests: the skip link and landmark, focus after navigating, the switcher, the bell, calendar states, errors tied to fields, the discard question, the password rule, Undo, the view switch, the error display, the last admin's explanation, the description list and heading order).
  • 24 database tests through run-local.sh (2 more need a Neon branch), 18 local API suites on a disposable database and port, and the reminder worker's unit tests and 4 database suites.
  • Typechecks for the web app, local API and worker, a production build, and the secret scan.

In Chromium, axe over 15 screens in light and dark at 1280 and 390 pixels wide went from 184 WCAG problems to 8, over 22 window and state views from 80 to 6 (the light out-of-month days), and over the signed-out pages from 39 to none. By hand: an empty save focuses the field to fix, Keep editing returns to the field and Discard returns focus to the button that opened the window, Undo keeps focus past 10 seconds, and tapping a Hub task's label completes it.

How

The colours come from tokens, so light mode keeps today's look and dark mode and navy surfaces switch in one place. Touch areas are an invisible ::before on phones and padding with a matching negative margin on desktop, so no line moves. Chrome closes a native dialog after repeated Escapes whatever the page asks, which left the discard question open inside a closed dialog and blocked the page; the dialog now follows the browser and closes as if dismissed. docs/HANDOFF.md and docs/DESIGN-LANGUAGE.md record the landmark, focus rules, tokens and touch area rule.

🤖 Generated with Claude Code

https://claude.ai/code/session_01A6P7aTcMBccooYYr4xYnVS


Generated by Claude Code

claude added 24 commits October 9, 2026 17:46
…ark mode

The green focus outline measured 2.6:1 on the navy sidebar and 2.2 to
2.7:1 everywhere in dark mode. Add --focus-ring and --text-green tokens:
light mode keeps today's green, dark mode and navy surfaces (sidebar,
commune list, phone tab bar and top bar, Operations toast) take the Hub's
approved lighter green. Dark mode also gets a placeholder colour, switches
whose on and off states differ, readable Operations week details and
out-of-month days, and the sidebar's secondary text rises to 0.6 white.
--danger and --danger-soft give error text one token in both themes.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A6P7aTcMBccooYYr4xYnVS
…r navigating

Keyboard users pressed Tab 14 times before reaching any content and a page
change announced nothing. The content area is now <main> with a skip link
as the first stop, and moving to another page or commune puts focus on the
new page's heading. A window, the note reader or a search result keeps the
focus it takes; when it closes and focus would be lost, focus goes to the
heading. The phone tab bar marks the current page with aria-current and a
small bar, so it no longer relies on brighter text alone.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A6P7aTcMBccooYYr4xYnVS
It announced itself as a menu but had no menu keyboard behaviour, its
name ("Switch commune") did not match what it shows, and Escape dropped
focus. It is now a disclosure button named from its visible commune name
with hidden "Switch commune" after it, controlling a labelled group of
buttons with the current commune marked. Escape closes the list and
returns focus to the button; a click or focus outside closes it.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A6P7aTcMBccooYYr4xYnVS
Tabbing to a share's buttons in an expense window could leave them hidden
behind the sticky Save or Close row. The window body now has bottom scroll
padding, so the browser scrolls a focused control above the row.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A6P7aTcMBccooYYr4xYnVS
- Calendar dates carry their full wording as visually hidden text inside
  the time element, so the visible short form is read in context.
- The Operations month marks today with aria-current and the selected day
  with aria-pressed, outlines the selected day more strongly, and gives
  tasks, events and late tasks a shape as well as a colour.
- The bell says "Notifications, 3 unread"; the search, people and
  reminder controls have names that start with their visible text.
- Repeated buttons (Nudge, Confirm received, Make admin, Remove, Edit,
  Archive, View fund and others) carry hidden context naming the person,
  expense or item they act on.
- Note tiles are named by their content and the quick finder's group
  headings are hidden from its list.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A6P7aTcMBccooYYr4xYnVS
…ltip

When the last admin cannot change role, be removed or leave, the reason
is shown under the buttons and linked to them with aria-describedby.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A6P7aTcMBccooYYr4xYnVS
…ocus

- Week, People and Month are toggle buttons with aria-pressed in a named
  group; they had tab roles without tab behaviour.
- A late card's buttons go once its task moves on, so the message with an
  Undo now takes focus and stays while focus or the pointer is on it,
  instead of closing after nine seconds out of reach. Undo puts focus back
  on the button that was used on the restored card.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A6P7aTcMBccooYYr4xYnVS
- mutate no longer raises the alert strip for every failed command. A
  failure that a form or screen catches is shown there only; one that
  nothing caught still reaches the strip, through the browser's
  unhandledrejection event. A refused commune settings save showed its
  message twice.
- .a-api-error and .a-form-error had no general style; both now use the
  design tokens, as .a-form-success does.
- Operations shows a refused task action on the screen, as a form does,
  until the next action, rather than in a message that closes itself.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A6P7aTcMBccooYYr4xYnVS
- A new useFormError hook marks the field an error is about with
  aria-invalid, points it at the message with aria-describedby, shows the
  message under the field and moves focus there. A message about no field
  takes focus only when focus would otherwise be lost, as after a refused
  sign-in. Add expense, Create a commune, Invite, notes, announcements,
  tasks, sign-in, sign-up and email verification use it.
- Escape, the backdrop and the close button now ask "Discard your
  changes?" when a window or dialog holds typed text. Keep editing (the
  default) goes back to the field; Cancel inside a form still closes at
  once, and search fields, locked fields and emptied fields do not count.
- Sign-up shows the 12-character password rule under the field before
  anything is submitted, and names the first field to fix.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A6P7aTcMBccooYYr4xYnVS
The sign-in pages now have header, main and footer landmarks, and phones keep
the page's h1 for screen readers after the brand panel is hidden. The grey
helper and footer text moves from #667085 (4.41:1 on the page) to #5f6878
(4.99:1). "Forgot your password?" and "Back to sign in" get a 24px target
without moving the form.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A6P7aTcMBccooYYr4xYnVS
The account name in the sidebar was 16px tall and the reminders summary under
it 18px. Each now reaches 24px into the empty space beside it, with a matching
negative margin, so neither line moves.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A6P7aTcMBccooYYr4xYnVS
On phones the filter chips (32px), settings switches (26px), Hub links (26px),
the Hub's View buttons (30px) and its task boxes (17px) were too small to tap
reliably. Each now has an invisible touch area that reaches 44px, so nothing
moves or changes colour. The task box sits in a label, which phones widen to
44px. A scrolling chip row gets room for the chips' touch area.

The phone header icons are left for after PR #22, which rebuilds that bar.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A6P7aTcMBccooYYr4xYnVS
Each group in the note reader's list held a wrapper div, the buttons and the
notes beside its term and value, which a list group may not contain. The value,
the buttons and the notes are now descriptions of the term, laid out with a
grid so the reader looks the same.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A6P7aTcMBccooYYr4xYnVS
Settings went from the page's h1 straight to h3 group headings, and Places
listed each commune as an h3 under the h1. Both now use h2, styled as before.
Home keeps its featured commune as an h2 with the others under it.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A6P7aTcMBccooYYr4xYnVS
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A6P7aTcMBccooYYr4xYnVS
The earlier rule lost to the general footer margin, so "Forgot your password?"
grew and pushed the form 8px taller. The link now reaches 24px into the space
above it and the margin shrinks to match, so every line stays where it was and
the target no longer touches the Sign in button.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A6P7aTcMBccooYYr4xYnVS
The phone top bar takes the lighter ring because it is navy, and the search
window opened from it inherited that ring on its white surface. The window
now keeps the ring of the theme: today's green in light mode, the lighter
green in dark mode.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A6P7aTcMBccooYYr4xYnVS
At phone width the Expenses chips already run under the filter field and the
Add expense button. Giving the chips a touch area put them on top of both;
the field and the button now stay on top, as before. Once the toolbar wraps
on narrow screens, nothing overlaps and every chip keeps its 44px area.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A6P7aTcMBccooYYr4xYnVS
With the content area now a main landmark, the settings pages' side column,
an aside, became a complementary landmark nested inside it, which axe flags.
It holds the account and commune cards rather than related content, so it is
now a plain container, styled as before.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A6P7aTcMBccooYYr4xYnVS
The test asked for no h3 on Settings at all, which is stricter than the rule
and would fail a section that sits correctly one level down. It now checks
that no heading is more than one level below the one before it.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A6P7aTcMBccooYYr4xYnVS
After repeated Escapes with nothing in between, Chrome sends a dialog a
cancel that cannot be stopped and closes it anyway. With typed text the
dialog then opened its discard question inside the closed dialog, where it
could not be seen but still blocked the page, and the screen kept thinking
the dialog was open, so its button no longer opened it. The dialog now
follows the browser and closes as if dismissed.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A6P7aTcMBccooYYr4xYnVS
Password and sign-in arrived in the security batch as an h3, while
Preferences and Support & legal around it are now h2 groups. It is the same
kind of group, so it takes the same level and the same styling, and the
heading test checks it.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A6P7aTcMBccooYYr4xYnVS
The link in Settings opens ourcommune.io in a new tab, and only its icon said
so. Screen readers now hear "(opens in a new tab)" after the address.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A6P7aTcMBccooYYr4xYnVS
- HANDOFF.md: a Start here entry after the lock order fix: the main landmark
  and skip link, focus after navigating, errors tied to their fields, the
  discard question, the alert strip, Operations' view switch and Undo, and
  landmarks on signed-out pages.
- DESIGN-LANGUAGE.md: the focus ring, green text and error colour tokens,
  and the touch area rule for phones and desktop.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A6P7aTcMBccooYYr4xYnVS
@vercel

vercel Bot commented Oct 9, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
commune-web Error Error Oct 9, 2026 6:29pm UTC

Copy link
Copy Markdown
Owner Author

The Vercel preview for commune-web shows Error here, as it has on every branch since 6 October. It is the project or account issue explained on #24 (app review finding 45), not this diff: the Web app check, which builds the app, passes on this head (759029e). It needs someone with the Vercel login to read a failed deployment's log.


Generated by Claude Code

@EmotiveImpact
EmotiveImpact changed the base branch from claude/app-review-task-locks to main October 12, 2026 02:00
@EmotiveImpact
EmotiveImpact marked this pull request as ready for review October 12, 2026 02:00
@EmotiveImpact
EmotiveImpact merged commit 8019c2a into main Oct 12, 2026
6 of 7 checks passed
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 12, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-12T02:02:14.426803Z 759029e Draft marked ready
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

This branch had an error being deployed

1 failed deployment
Preview — 759029e6 Deployed Oct 9, 2026 by vercel[bot]
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.

2 participants