Privatium Accessibility

Target: WCAG 2.2 AA. This applies to every tier. Load it alongside the tier skill, not instead of it.

The framework serves people managing medications, chronic conditions, and mental health. Assume some of them are tired, in pain, using a screen reader, or dyslexic — because some of them are.

MUST

Forms

Icons

Colour and contrast

Keyboard

JavaScript off

Structure

Motion

The framework’s own pages

Pairing — both paths are required

The pairing flow MUST be completable without reading text (the 16-glyph emoji pad, with labels) and without seeing images (the two-word code, read by a screen reader). Both. Not either. See spec/protocol.md §7.

Additional requirements there:

The framework’s own pairing screen is the worked example: the node’s code page shows the four emoji with their labels, the two words, and the QR code as a labelled image with the URL as text beside it; the phone’s screen is a pad of sixteen labelled buttons beside a word field, both visible at once, and every outcome — paired, wrong code, closed — is said in a role="status" region. The emoji inside each key is hidden from the screen reader so its label is read once. Match it when an app of yours shows or takes a code.

Anti-patterns

<!-- WRONG: no accessible name -->
<button hx-delete="/fill/1"><?= icon('trash') ?></button>
<!-- RIGHT -->
<button hx-delete="/fill/1"><?= icon('trash', 'Delete this fill') ?></button>

<!-- WRONG: placeholder as label, colour-only error -->
<input name="drug" placeholder="Drug name" style="border-color:red">
<!-- RIGHT -->
<label for="drug">Drug name</label>
<input id="drug" name="drug" aria-describedby="drug-err">
<p id="drug-err" role="alert">Enter the drug name.</p>

<!-- WRONG: status by colour -->
<span class="dot red"></span>
<!-- RIGHT -->
<span class="dot red"><?= icon('exclamation-triangle') ?> Overdue</span>

<!-- WRONG: fake table -->
<div class="row"><div class="cell">Drug</div></div>
<!-- RIGHT -->
<table><thead><tr><th scope="col">Drug</th></tr></thead>…</table>

Verify

privatium lint apps/<slug>          # catches labels, icon names, heading order, contrast

Then, by hand — the linter cannot do these:

  1. Unplug the mouse. Complete the main task.
  2. Turn on the screen reader. Complete the main task.
  3. Zoom to 200%. Nothing overlaps or is cut off.
  4. Disable images. The pairing flow still works.
  5. Disable JavaScript. Every write still lands.

Do not present an app as finished before doing all five.