A9 reflow (#3571): 320 px sweep and 400% zoom checklist
Step 3 of roadmap item A9 (WCAG 2.1 SC 1.4.10 Reflow). Steps 1 (TableScroller, #3649) and 2 (StackedTable, #3663) are recorded in accessibility-roadmap.md.
Automated 320 px sweep (2026-09-27)
A width of 320 CSS px is what a 1280 px window shows at 400% zoom. tools/a11y-reflow-sweep/sweep.cjs loaded every URL in tools/a11y-reflow-sweep/urls.txt at 320 × 640, once in light mode and once in dark mode. It hits the live sites, so it isn’t in CI; re-run it after any big layout change. Accepted findings live in allowlist.json, so the script exits 0 unless something new appears:
node tools/a11y-reflow-sweep/sweep.cjs tools/a11y-reflow-sweep/urls.txt out.jsonOn each page it checked:
document.documentElement.scrollWidthagainstclientWidth, to catch horizontal page scroll;- every visible element whose box leaves the viewport and has no overflow container around it, to catch clipped content;
- every
<table>wider than the viewport.
Routes: 57 live URLs, 114 page loads in all:
- 29 gevell.com pages: its sitemap plus the routes the apps/home gate lists that the sitemap doesn’t (
/about,/sso,/third-party-licenses,/legal/terms, …); - 8 orgchart.gevell.com pages;
- 6 pdf.gevell.com pages (its sitemap uses relative
<loc>s, #3803, so they were listed by hand); - the landing page of each of: md, remediate, audit, acr, worksheet, map, pptx, forms, links, music, flow, polish, status and help.gevell.com.
Gevell Photos was withdrawn from the registry (#3404) and wasn’t swept.
Dark mode really applied: every app except help and status set html.dark with a dark body background. Help and status use prefers-color-scheme directly, and their backgrounds were dark (rgb(23,24,28), rgb(14,17,22)).
| Finding | Where | Fix |
|---|---|---|
Page scrolled 7 px sideways. The H1 word “TheAccessible.org” at text-4xl didn’t fit in 272 px. | gevell.com/why-gevell | text-3xl below 360 px, plus overflow-wrap: break-word on every heading in apps/home, since headings are CMS-editable (#3801) |
The criteria table scrolled sideways inside an overflow-x-auto region that couldn’t take keyboard focus (SC 2.1.1) | orgchart.gevell.com/wcag | Open PR #3802 stacks it into labelled cards below 640 px with StackedTable. Not merged yet. |
| The API reference’s “Download OpenAPI Document” button is pushed 79 px past the left edge, so its label is clipped | pdf.gevell.com/docs/api | Not fixed. It’s third-party API-reference markup; see help-site-redoc-a11y-debt.md. Needs a CSS override in apps/web or an upstream fix. |
| Skip links positioned off-screen | flow, map, worksheet, status | None needed. They’re hidden on purpose until focused. |
The script reported nothing else on any route, in either color scheme.
Limits of the script. It’s a fast heuristic, not a proof of 1.4.10:
- Content clipped inside a region that scrolls vertically, or inside a fixed header or bar, isn’t reported.
- The
allowlist.jsonentries excuse a whole finding type on their URL. - Text truncated with an ellipsis is reported as clipped.
The manual checklist below is still the conformance check. The CI-grade detector is reflowProblems in @accessible-org/a11y-harness, which the follow-up should reuse.
Not covered by the sweep. These need a manual pass or a seeded session:
- Signed-in app screens (dashboard, conversion results, billing, team and tenant admin, remediation console, Polish batch, the Report Card editor). This script doesn’t sign in. The nightly
tools/contrast-sweepjob already sweeps signed-in screens with the smoke account, so the follow-up is to add this reflow check there, along with a 320 px entry in the a11y-harnessVIEWPORTS, which would make it a CI gate. TheTableScroller/StackedTablework in steps 1–2 covers their tables inapps/web. - States that only exist after an action: toasts, dialogs, error banners, expanded menus, and the file list during an upload.
400% zoom manual checklist (for Larry)
Automation can’t judge these. Use a desktop browser with the window at 1280 px wide, set zoom to 400% (or use a 320 px responsive-mode window), and repeat each item in light and dark mode. Record pass or fail per app on #3571.
Every app: home, pdf, audit, acr, orgchart, remediate, polish, forms, md, worksheet, map, pptx, links, music, flow, status, help
- No sideways scrolling. Scroll to the bottom of each main route. The page should only scroll vertically. Wide tables and code blocks may scroll inside their own box.
- Nothing clipped or overlapping. Headings, buttons and badges wrap instead of being cut off. No text sits on top of other text. Sticky headers and footers don’t cover more than about a quarter of the screen.
- The navigation still works. The hamburger menu opens, every link can be reached with Tab, Escape closes it, and focus returns to the button.
- Focus is always visible. Tab through the whole page. The app’s focus ring is visible on every stop and never hidden under a sticky bar (SC 2.4.11). The color and width vary by app, so check that it’s visible, not what it looks like.
- Forms are usable. Labels stay next to their fields, error messages appear next to the field and wrap, and dropdowns and date pickers fit the screen.
- Dialogs and toasts fit. Open at least one dialog and trigger one toast. Both fit the screen, can be scrolled if tall, and have a reachable close button.
Signed-in screens (apps/web, apps/pro, apps/vpat)
- Stacked tables (step 2). On transactions, API keys and team members, the rows become labelled cards: each value shows its column name, and nothing is cut off.
- Scrolling tables (step 1). On pricing, benchmark and the remediation console, Tab reaches the table’s scroll area. It shows an inset focus ring, and the arrow keys scroll it.
- Polish batch. Add three files. Each row’s status, message and action buttons wrap cleanly, including the “still working” and “refunded” states.
- Report Card editor. The criteria list and the remarks fields fit, and long criterion names wrap.
Screen reader spot check (VoiceOver, Safari)
- On one stacked table, each cell reads as “column name, value”.
- On one scrolling table, the scroll region is announced as a labelled group.
When 1–12 pass, set roadmap row A9 to Done and close #3571.