Structure that sighted users infer from layout, such as headings, lists, tables and field labels, must also exist in the code for assistive technology.
On a clinic booking page, a sighted visitor sees a large bold line and knows it starts a new section. They see three indented lines and know it is a list. They see a grid of times and know the column on the left is the day. A screen reader user gets none of that unless the markup says so.
WCAG 1.3.1 (Level A) requires that information, structure and relationships conveyed through presentation can be programmatically determined or are available in text. The goal is that the meaning survives when the visual layout is replaced by speech, braille or a user style sheet. Where the technology supports real markup, use it rather than describing the structure in a sentence.
Color alone is handled by a different criterion, 1.4.1; this one concerns structure and relationships.
div.divs, or a real table without header cells or a caption.Public sites are made of forms and tables: a clinic appointment request, a fee schedule for permits, a bus timetable, an agenda with numbered items. DOJ's list of common barriers names inaccessible forms, including unlabeled fields, which is a failure of this criterion in practice. A fee table built from divs reads as a stream of dollar amounts without any hint of which service each belongs to; an agenda built from paragraphs hides how many items there are.
Organizations that rely on page-builder components often discover that the "look" and the "structure" were set in different places. If the template emits the wrong element, every page that uses it inherits the problem. See the overview of common barriers for how this pairs with other recurring issues.
Give each field a label that is programmatically tied to it, and put related radio buttons in a group with a name.
<div class="field">Preferred clinic</div>
<input type="text" name="clinic">
<div class="field">Visit type</div>
<input type="radio" name="vt" value="new"> New patient
<input type="radio" name="vt" value="follow"> Follow-up<label for="clinic">Preferred clinic</label>
<input id="clinic" name="clinic" type="text">
<fieldset>
<legend>Visit type</legend>
<label><input type="radio" name="vt" value="new"> New patient</label>
<label><input type="radio" name="vt" value="follow"> Follow-up</label>
</fieldset>A timetable is a table. Header cells say whether a value is a stop or a departure time.
<div class="row">
<span>Stop</span><span>Route 12</span><span>Route 40</span>
</div>
<div class="row">
<span>Library</span><span>8:10</span><span>8:25</span>
</div><table>
<caption>Weekday departures from Main Street</caption>
<thead>
<tr><th scope="col">Stop</th>
<th scope="col">Route 12</th>
<th scope="col">Route 40</th></tr>
</thead>
<tbody>
<tr><th scope="row">Library</th><td>8:10</td><td>8:25</td></tr>
</tbody>
</table>Headings and lists carry the outline that screen reader users navigate by.
<p class="big bold">Agenda</p>
<p>1. Call to order<br>
2. Public comment<br>
3. Parking ordinance</p><h2>Agenda</h2>
<ol>
<li>Call to order</li>
<li>Public comment</li>
<li>Parking ordinance</li>
</ol>Native toolkits have the same idea under different names. A large title text is not a heading unless you mark it as one.
Text("Water bill")
.font(.title)Text("Water bill")
.font(.title)
.accessibilityAddTraits(.isHeader)Text(
"Water bill",
style = MaterialTheme.typography.headlineMedium
)Text(
"Water bill",
style = MaterialTheme.typography.headlineMedium,
modifier = Modifier.semantics { heading() }
)div, it fails.Automated tools find unlabeled inputs and empty headers; they cannot tell that a bold paragraph is a heading in disguise.
div grids used for tables.