Info and Relationships
Structure and relationships conveyed visually are also available programmatically. Headings are headings, lists are lists, tables have header cells, and form fields have real labels.
Who this affects
Screen reader users navigate by structure — jumping heading to heading, listing landmarks, reading a table cell with its row and column headers. Bold text styled to look like a heading provides none of that.
The failure and the fix
Each example below is a self-contained page, loaded in a frame so its markup cannot affect this page. Open either one on its own to test it with a keyboard or screen reader.
Fails 1.3.1
Open the failing example in a new tab
fail.html — the problem
<!-- FAILURE 1: styled to look like a heading, but it is a plain div.
A screen reader user pressing H to jump between headings finds nothing. -->
<div class="looks-like-heading">Opening hours</div>
<!-- FAILURE 2: a list to the eye, unrelated divs to assistive technology.
Screen readers announce "list with 3 items" - here they announce nothing. -->
<div class="looks-like-list">
<div>Monday to Friday, 9am to 6pm</div>
<div>Saturday, 10am to 4pm</div>
<div>Sunday, closed</div>
</div>
<div class="looks-like-heading">Stock by branch</div>
<!-- FAILURE 3: a data table with no header cells. Every cell is a td, so a
screen reader cannot say which column or row a value belongs to.
Reading cell "12" tells the user nothing at all. -->
<table>
<tr>
<td><strong>Branch</strong></td>
<td><strong>Hardback</strong></td>
<td><strong>Paperback</strong></td>
</tr>
<tr><td>Riverside</td><td>12</td><td>34</td></tr>
<tr><td>Old Town</td><td>4</td><td>19</td></tr>
</table>
<div class="looks-like-heading">Join the mailing list</div>
<form>
<!-- FAILURE 4: the label is just text sitting next to the input. There is no
programmatic association, so the field announces as "edit, blank". -->
<div class="field">
Email address
<input type="email" name="email">
</div>
<!-- FAILURE 5: a radio group with no fieldset and no legend. Each option
announces its own label, but the question they answer is invisible to
assistive technology. -->
<div class="field">
<div class="looks-like-heading">How often?</div>
<input type="radio" id="f-weekly" name="freq" value="weekly">
<label for="f-weekly">Weekly</label>
<input type="radio" id="f-monthly" name="freq" value="monthly">
<label for="f-monthly">Monthly</label>
</div>
<button type="button" class="button">Subscribe</button>
</form>
Meets 1.3.1
Open the passing example in a new tab
pass.html — the fix
<!-- FIX 1: a real heading. It looks the same, and now it is in the heading
outline, reachable with the H key and listed in the rotor. -->
<h2>Opening hours</h2>
<!-- FIX 2: a real list. Assistive technology announces "list with 3 items",
which tells the user how much there is before they commit to reading it. -->
<ul>
<li>Monday to Friday, 9am to 6pm</li>
<li>Saturday, 10am to 4pm</li>
<li>Sunday, closed</li>
</ul>
<h2>Stock by branch</h2>
<!-- FIX 3: real header cells with an explicit scope. Now a screen reader reads
"Old Town, Paperback, 19" instead of a bare "19". The caption names the
table so it can be found in a list of tables. -->
<table>
<caption>Copies in stock by branch and format</caption>
<thead>
<tr>
<th scope="col">Branch</th>
<th scope="col">Hardback</th>
<th scope="col">Paperback</th>
</tr>
</thead>
<tbody>
<tr><th scope="row">Riverside</th><td>12</td><td>34</td></tr>
<tr><th scope="row">Old Town</th><td>4</td><td>19</td></tr>
</tbody>
</table>
<h2>Join the mailing list</h2>
<form>
<!-- FIX 4: label's "for" matches the input's "id", so the field announces as
"Email address, edit". Clicking the label also focuses the field. -->
<div class="field">
<label for="email">Email address</label>
<input type="email" id="email" name="email" autocomplete="email">
</div>
<!-- FIX 5: fieldset and legend bind the question to its options, so each
radio announces as "How often? Weekly, radio button, 1 of 2". -->
<fieldset class="field">
<legend>How often?</legend>
<div>
<input type="radio" id="p-weekly" name="freq" value="weekly">
<label for="p-weekly">Weekly</label>
</div>
<div>
<input type="radio" id="p-monthly" name="freq" value="monthly">
<label for="p-monthly">Monthly</label>
</div>
</fieldset>
<button type="submit" class="button">Subscribe</button>
</form>
How to test it
Turn off CSS, or view the accessibility tree. Every visual grouping, heading level, list, and table relationship should still be expressed in the markup. Check label/for pairings, th with scope, fieldset/legend for radio groups.
-
Automated
Scanners catch missing form labels, empty headings, and layout tables with headers. They cannot detect a div styled to look like an h2, or a heading hierarchy that is technically valid but semantically wrong.
Relevant axe rules:
labelform-field-multiple-labelsth-has-data-cellstd-headers-attrempty-headingdefinition-listlistlistitemaria-required-childrenaria-required-parent
-
Keyboard
Unplug the mouse. Move through the page with Tab, Shift+Tab, Enter, Space and the arrow keys. See the keyboard testing script.
-
Screen reader
Listen to both examples with NVDA, JAWS, VoiceOver or TalkBack and compare what is announced. See the screen reader cheat sheets.
-
Visual
Zoom to 200% and 400%, narrow the viewport to 320 px, and apply the text-spacing overrides. See the visual testing procedures.
Related criteria
In the specification
- Understanding 1.3.1 Info and Relationships — the W3C explanation, intent and exceptions
- 1.3.1 in the WCAG 2.2 Recommendation — the normative wording
WCAG 2.2 Demo Suite