When order changes meaning, the code must follow it: steps, instructions and notices should read in the same sequence screen readers announce.
A court's e-filing page shows five steps in a tidy row. Behind the scenes, a stylesheet moved "Step 3: Pay the fee" in front of "Step 2: Upload documents". Sighted users see 1, 2, 3. A screen reader, reader mode or a user style sheet that ignores the layout reads the code order: payment before upload.
WCAG 1.3.2 (Level A) says that when the sequence in which content is presented affects its meaning, a correct reading sequence can be programmatically determined. Software, including assistive technology, must be able to present content in an order that keeps its meaning even when the layout changes.
Only order that affects meaning is covered. Navigation before main content, or after it, are both acceptable. A form instruction must come before the field it explains; a table caption before the table; a step before the next step. More than one correct order can exist, and only one has to be available programmatically.
Where focus moves is a different question, handled by 2.4.3 Focus Order. This criterion concerns what is read and in which sequence.
Court e-filing, benefit applications and permit wizards are sequences: the order is the content. They are also the pages most likely to be restyled with flexbox, grid and absolute positioning, because designers want the summary on the right or the call-to-action above the text. A legal notice that places the deadline after the "Accept" button in the code, or an error message appended at the very end of the page after a dynamic update, leaves a person to find meaning out of sequence. This is a Level A item; the overall standard is described in WCAG 2.1 AA and the ADA.
CSS can move a box without moving its place in the reading order. Fix the HTML, not the style sheet.
<ol class="steps">
<li>Step 1: Enter case number</li>
<li class="s3">Step 3: Pay the fee</li>
<li>Step 2: Upload documents</li>
</ol>
.steps { display: flex; }
.s3 { order: 3; }<ol class="steps">
<li>Step 1: Enter case number</li>
<li>Step 2: Upload documents</li>
<li>Step 3: Pay the fee</li>
</ol>
.steps { display: flex; }Read the markup from top to bottom and ask whether it would make sense as a spoken page.
<button type="submit">Accept and file</button>
<p class="terms">Filing deadline: 5 p.m. on the last day.</p>
.terms { position: absolute; top: 0; }<p class="terms">Filing deadline: 5 p.m. on the last day.</p>
<button type="submit">Accept and file</button>Messages written by a script should land next to what they belong to, not at the end of the document. Keep an empty live region in place from the start and fill it, rather than injecting a region together with its text.
const msg = document.createElement('p');
msg.textContent = 'Case number not found.';
document.body.append(msg);// <p id="case-error" role="alert"></p> already sits
// right after the field in the HTML, empty.
document.getElementById('case-error').textContent =
'Case number not found.';When the visual layout and the natural order disagree, native toolkits let you set the reading sequence. Better still, arrange the views in the order they should be read.
ZStack(alignment: .topLeading) {
Text("Total due: $84.20")
Text("Water bill, due Nov 3")
.offset(y: -24)
}ZStack(alignment: .topLeading) {
Text("Total due: $84.20")
.accessibilitySortPriority(1)
Text("Water bill, due Nov 3")
.offset(y: -24)
.accessibilitySortPriority(2)
}Box {
Text("Total due: $84.20")
Text("Water bill, due Nov 3",
Modifier.offset(y = (-24).dp))
}Box(Modifier.semantics { isTraversalGroup = true }) {
Text("Total due: $84.20",
Modifier.semantics { traversalIndex = 1f })
Text("Water bill, due Nov 3",
Modifier.semantics { traversalIndex = 0f }
.offset(y = (-24).dp))
}Automated tools cannot judge whether a sequence is meaningful; this check is manual.
order, row-reverse or grid placement to rearrange content that has a sequence.body.