Landmark structure on every page
A screen reader user rarely reads a page top to bottom. They navigate by landmark, heading and form control. That means the structure of a page is the interface, and a page with no landmarks is a page with no navigation.
Every CAMARON page renders the same skeleton:
header— brand, primary navigation, utility cluster.nav— labelled, so multiple navigations are distinguishable.main— exactly one per page, the skip link's target.footer— secondary links, legal, trust signals.
Sections inside main that carry meaning use section with an aria-labelledby pointing at their heading, so they are announced with a name rather than as an anonymous group.
Heading hierarchy and page titles
Each route has exactly one h1, and heading levels descend without skipping. This is checked by the automated sweep, and it is the single highest-leverage structural rule: heading navigation is how most screen reader users move through a long page like this one.
Document titles are set per route so browser tab lists and screen reader window lists are distinguishable. On a client-rendered application this matters more than usual — without it, every route shares the same title and users lose their place.
Live regions for alerts and job status
A lot of CAMARON is asynchronous: alert fan-outs, export jobs, AI analyses, offline queues flushing when a device reconnects. All of that has to be perceivable without watching a spinner.
- Polite regions announce progress and completion — export progress percentages, "Preview regenerated", download row counts, queue flush results.
- Assertive regions are reserved for failures that block the user, and are paired with moving focus to the relevant control.
- Status roles carry synchronisation badges and connection state, so a change from "Saved" to "Save failed — Retry" is announced rather than only recoloured.
We keep announcements short and specific. A live region that reads back an entire table on every poll is noise, and users turn it off.
Forms, errors and required fields
Form accessibility is where most real-world barriers live. Our rules:
- Every input has a programmatically associated label — never a placeholder standing in for one.
- Required fields are marked with
aria-required, and the asterisk is backed by hidden text so it is not conveyed by symbol alone. - Validation errors render inline, are linked to the field with
aria-describedby, and setaria-invalid. - On a failed submit, focus moves to the first invalid field so the user is taken to the problem instead of hunting for it.
- Success states are announced in a live region, not only shown as a colour change.
The contact form and the accessibility report form on the hub both follow this pattern, and it is the pattern new forms are expected to copy.
Charts and the tabular alternative
Real-time visualisations — dissolved oxygen trends, growth curves, cold-chain temperature traces, the disease heatmap — cannot be made fully equivalent through ARIA alone. Rather than bolt on a description that goes stale, every chart ships a "View as Table" control that renders the same underlying series as a semantic table with proper headers, plus a CSV export of the same rows.
That is a genuine alternative, not a workaround: the table is generated from the same data the chart draws, so the two cannot drift.
What is not verified yet
- No manual assistive-technology pass. Everything described here is verified structurally by automated tooling. Structure being correct is necessary but not sufficient — real screen reader testing routinely finds problems that automation misses. That pass is planned and has not been done.
- PDF exports. Generated reports do not yet meet PDF/UA. The same data is available as CSV and on screen.
- Embedded third parties. Payment checkout and video consultation embeds carry their own limitations outside our direct control.
- Image-based AI analysis. Disease detection is inherently visual; text-based symptom entry exists as an alternative pathway to the same protocols.