M5-10: Accessibility on mobile¶
Status: ready | Depends on: M5-06, M5-07, M5-08, M5-09 | Milestone: M5
Goal¶
After this ticket the phone apps meet the accessibility rules of 01 8 (WCAG 2.2 level AA) on iOS with VoiceOver and on Android with TalkBack, held by tests instead of intentions: a checklist docs/quality/accessibility-mobile.md lists every rule of 01 8 for every phone screen of M5 (M1 to M7, E1 to E7, the share sheet of M5-06, the recording of M5-07, the lock of M5-08, the emergency actions of M5-09) with how it is checked; every item that a machine can check is a test (Flutter semantics and guideline tests, XCUIApplication.performAccessibilityAudit() on the iOS simulator, the Accessibility Test Framework checks on Android), and the session works through the checklist on the simulator and emulator with VoiceOver and TalkBack where the tools allow it; every gap found is fixed in this ticket and pinned by a test; the items that need a person with a screen reader on a physical device stay on the checklist with the state "manual, before every app release" (PLAN 5 row "Accessibility") and are named in the report. Terminal screens M8 and M9 are checked by M5-13, the live views M10 by M6-16. Why: exit criterion 3 of M5 requires the checklist with VoiceOver and TalkBack, and 01 8 makes accessibility a test, not a hope.
Read first¶
docs/spec/01-product.md: 8 "Accessibility" (the table: contrast, screen reader, live content, font size, focus, targets, motion, color, language), 5.3 and 5.4 (screens), 5.6 (onboarding rules: codes read character by character, remaining time announcements), 9 (languages: the semantics tree carries the app language).docs/spec/15-client-architecture.md: 20 (rows "Widget", "Accessibility by hand").docs/PLAN.md: section 5 (row "Accessibility": checklist by hand before every app release), section 11 (Exit criteria item 3), rows M4-32 (the desktop checklist this one mirrors), M5-10, M5-13.docs/design/components.md(semantics per component),docs/design/contrast.md.- Apple:
XCUIApplication.performAccessibilityAudit(for:_:)(Xcode 15 and later). Android: the Accessibility Test Framework (AccessibilityChecks.enable()in Espresso, orAccessibilityValidator). docs/DECISIONS.md: D-W-100 and its extension to M5 and M6 (D-W-120); section 10 (format ofD-M5-10-<n>rows).- Briefs (merged names win, D-W-100): the brief of M4-32 (desktop checklist format; use it where its checklist exists on the base branch),
M5-02-iphone-layout.mdtoM5-09-mobile-emergency.md(the screens and their semantics),M4-07-core-components.md(component semantics). - Code on the base branch:
apps/client,packages/admin_ui,packages/design_system.
This ticket touches no API operation, no table, no action, no event and no configuration key.
Fixed requirements that apply¶
Owner's fixed build rules (apply verbatim):
- No MVP. New from scratch and directly in the final state; stages are only the order.
- Strictly modular: clear module and service boundaries, dependencies only through defined interfaces, no cross access.
- Documentation in code: every public module, class, function and interface with a docstring/doc comment (purpose, parameters, return value, errors, example where useful); comments explain the why, not the what. API documentation is generated from code (OpenAPI from the routes, dartdoc, Python API docs in MkDocs).
- Documentation in the repo: README per module/service, ADR for every architecture decision, CHANGELOG per release, keep the documentation site current. A change without matching documentation is not done.
- Quality gates (SonarQube, linters, type checking, security scanners, test coverage) block the merge.
Rules for every brief:
- Test first: write the failing test, run it and keep the red output for the report, then make it green, then clean up.
- No en dash and no em dash in any file you write; the
text-rulespre-commit hook rejects them, and the ARB test of M4-06 rejects them in interface texts. Use a hyphen or rephrase. - White label: examples and test data use
example.comand the people Alex, Sam and Robin; no company, host or personal names. Interface texts come frompackages/l10n. - No configuration key is added; announcements follow
a11y.live_announce_min_interval. - Specification first: a change to behaviour updates
docs/spec/in the same change. The core contract is not edited by this ticket.
Product requirements implemented here:
- Every rule of 01 8 holds on every phone screen named in the Goal, in de and en, light and dark, text scaling 1 and 2 (up to 200 percent).
- Every machine-checkable item is an automated test in the gate or in the platform tests; every gap found is fixed here with a test that was red before.
- The checklist is complete: every screen times every rule has a row with the check and its state; manual items are explicit.
Scope¶
Create:
docs/quality/accessibility-mobile.md: the checklist (Design note 1), linked fromdocs/quality/README.mdand the site navigation inmkdocs.yml.apps/client/test/a11y/: semantics and guideline tests per phone screen (Design note 2).apps/client/ios/RunnerUITests/AccessibilityAuditTests.swift: the iOS audit per screen through a test entry route (Design note 3).apps/client/android/app/src/androidTest/.../AccessibilityChecksTest.kt: the Android checks per screen (Design note 3).- Fixes in
apps/client,packages/admin_uiorpackages/design_systemfor every gap, each with its test.
Change:
apps/client/platform-tests.json: the iOS audit inmacos, the Android checks inandroid.packages/l10nARB files for any label added.- Root
CHANGELOG.mdunder[Unreleased],docs/DECISIONS.mdsection 10 rowsD-M5-10-1toD-M5-10-3.
Out of scope¶
- Terminal screens M8 and M9 (M5-13) and live views M10 (M6-16); the desktop (M4-32); the console (M6).
- Manual checks on physical devices with a person using a screen reader: they stay on the checklist for the release (M7).
- Changes to the core contract.
Boundaries¶
- Work in a dedicated git worktree on the branch
feat/M5-10-mobile-a11y; one writer per worktree. - Conventional Commits with the component as scope (for example
fix(client): ...,test(client): ...,docs(quality): ...) and a DCO sign-off on every commit (git commit -s); squash into meaningful commits before the merge (docs/quality/push-discipline.md). - Never push to
main, never tag and never create remote repositories; the only remote steps are the branch push and the pull request ofAGENTS.md. - Do not modify files outside the scope above, except the CHANGELOG, README and
docs/DECISIONS.mdentries named in this brief. - Follow the host rules supplied by the launcher (ports, machine rules).
- Tests bind only
127.0.0.1with port 0 and use the fake server of M4-17. Temporary directories only viamktemp -d, removed at the end. Stop only processes you started, by PID; simulators by UDID, emulators by serial. - Never read or print secrets.
- The platform audits need macOS with Xcode and an iOS simulator, and an Android emulator; where missing,
make apps-platformprints its SKIP line; the report names where they ran. - Never lower a threshold, disable a gate, delete a test, raise
repo.max_file_kbor extendquality/licenses/allowed.txt; never exclude hand-written code from a lint, format, coverage or doc gate; never suppress an audit finding without a row in the checklist that names the reason and the follow-up. - If the core contract or
docs/api/lacks something this ticket needs, stop withVerdict: BLOCKED <reason>instead of building a deviation (P-11). - This brief was written before milestone M1 closed (D-W-100, extended to M5 and M6 by D-W-120). Where the merged code of an earlier ticket names a package, type, function, file, provider or make target differently from this brief, follow the code and list each difference in the report; where it behaves differently from the specification, stop with
Verdict: BLOCKED <reason>.
Design notes¶
-
Checklist format (01 8; PLAN 5; Ruling for docs/DECISIONS.md). A table per screen with one row per rule of 01 8 (contrast, screen reader role and label, live content, font size, focus order, targets, motion, color, language) and the columns "Check" (test name and file, or "manual on device:
"), "State" ( automated,manual before release,not applicablewith reason) and "Platform" (iOS, Android, both). If the M4-32 checklist exists, use the same columns. The checklist names the assistive technology settings used (VoiceOver, TalkBack, larger text, reduce motion, bold text, high contrast). -
Flutter tests (01 8; 15 20). Per screen:
meetsGuidelinefortextContrastGuideline,androidTapTargetGuideline,iOSTapTargetGuideline,labeledTapTargetGuideline; a semantics snapshot (the list of labels and actions in reading order) compared with a checked-in expectation, so a lost label fails; text scaling 2 without overflow; reduced motion without running animations; the locale of the semantics tree; live regions announce at most once pera11y.live_announce_min_interval(fake clock); codes read character by character; the gesture of M5-07 has its button mode. -
Platform audits (Ruling for docs/DECISIONS.md). iOS: a UI test target that launches the app with a test route per screen against the fake server and calls
performAccessibilityAudit()with all audit types; known Flutter engine limitations that the audit reports (if any) are listed with a reason, never silently ignored. Android: an instrumented test with the Accessibility Test Framework on each screen (touch target size, contrast, labels, duplicate descriptions). Both run inplatform-tests.json. -
Rulings for
docs/DECISIONS.md(D-M5-10-<n>): the checklist format and states (note 1); the semantics snapshot as a regression test (note 2); the platform audits and how a reported engine limitation is handled (note 3).
Tests (write them first)¶
- Red first: run the new Flutter tests and the platform audits against the current app, keep the failures in the report, then fix each gap.
apps/client/test/a11y/<screen>_a11y_test.dartper screen of the Goal (Design note 2).AccessibilityAuditTests.swift(iOS simulator) andAccessibilityChecksTest.kt(Android emulator) per screen (Design note 3).- A test that every phone route of the router has a row in the checklist (parse the checklist table and the route list), so a new screen without a row fails.
Commands that must pass, from the repository root:
cd apps/client && flutter analyze --fatal-infos && flutter test --coverage- on macOS with Xcode and with an attached Android emulator:
make apps-platform; otherwise the SKIP lines make arch,make lint,make fmt-check,make coverage,make docs,make linkcheckmake check
Acceptance criteria¶
- The checklist with VoiceOver and TalkBack is complete for every phone screen named in the Goal; manual items are explicit.
- Every machine-checkable item is a test; every gap found is fixed with a test that was red before.
- The iOS audit and the Android checks run in the platform tests and pass.
- Checklist, site navigation, CHANGELOG and the rulings are updated.
-
make checkis green.
Definition of done¶
- Code and tests as specified; doc comments on every public symbol; comments explain why.
docs/quality/accessibility-mobile.mdwritten in English and linked.- Rulings of Design note 4 recorded in
docs/DECISIONS.mdsection 10. - Root
CHANGELOG.mdentry under 'Unreleased'. - An ADR only if you take an architecture decision beyond this brief (MADR 4 as in
docs/adr/0000-template-madr-4.md, headings and text in English, next free number,make adr-index). make checkgreen.- An independent code review over the diff, then the red team gate (
docs/quality/red-team-gate.md, verdictJAorNEIN); onNEINfix with a test per finding and have the delta checked again. Record both verdicts. - Land the change as
AGENTS.md("How work lands onmain") describes: a pull request from this branch, squash-merged only when review, red team andmake check,ci-okand the review threads are green; thengit worktree remove.
Report¶
Write to {{REPORT_PATH}}:
- First line exactly
Verdict: DONEorVerdict: BLOCKED <reason>. - Commits (SHA and subject), changed files with line counts, test results with counts (the red run before the change and the green run after), the summary of
make check. - Review and red team verdicts and the findings fixed.
- The gaps found and fixed, the manual items left for the release, where the audits ran.
- What is not proven.
- Follow-ups that could not be done in this scope, each with its reason, every difference to the names of this brief (D-W-100), and spec gaps found.
Then write {{DONE_PATH}} with exit=0 sha=<sha> or exit=1 <reason>.