Requires that structure and relationships shown visually, such as headings, lists, tables, form labels and groups, are also available to assistive technology through code or text.
A sighted user sees at a glance that a large bold line is a heading, that aligned items form a list, that a label belongs to a field and that a grid of numbers is a table. A screen reader user sees none of this unless the page says it in code.
This criterion requires that information, structure and relationships that are conveyed through presentation can also be determined programmatically or are available in text. In practice this means using real headings, lists, tables, labels and groups, instead of styling plain elements so they merely look like them.
Screen reader users navigate by headings, landmarks, lists and tables, and depend on the code to know what each thing is. People with low vision who use custom styles or reader modes, and people who use voice control, rely on real labels and structure too. Clear structure also helps people with cognitive differences and search engines.
The European Accessibility Act (Directive (EU) 2019/882) applies to in-scope products and services placed on the market after 28 June 2025. Recital 47 names four principles for websites and mobile applications: perceivable, operable, understandable and robust. Structure that assistive technology can read is part of "perceivable" and "robust".
The technical counterpart is the standard EN 301 549, which incorporates WCAG 1.3.1 by reference and turns it into separate clauses for each kind of ICT.
| Clause | Type of ICT | What it requires |
|---|---|---|
| 9.1.3.1 | Web pages | Web content must satisfy WCAG success criterion 1.3.1. |
| 10.1.3.1 | Non-web documents (PDF, Office files) | The same criterion applies to the structure of documents (tags in PDF, styles in Office files). |
| 11.1.3.1 | Non-web software (native mobile apps) | Software that supports screen readers must satisfy the criterion. |
Clause numbers follow the "9. + WCAG number" pattern of EN 301 549. In V3.2.1 the software clause is divided into further subclauses for open and closed functionality. Check the exact numbers in the standard when you cite them.
Standard version: EN 301 549 V4.1.1 (September 2026) is based on WCAG 2.2 and is the first version prepared specifically for the EAA. It has not yet been cited in the Official Journal of the European Union, so for now it is best treated as a voluntary technical route to the EAA's requirements. Once cited, it will give a presumption of conformity. The text of criterion 1.3.1 is the same in WCAG 2.1 and 2.2.
A note on EAA scope: Article 2(4) of the Directive excludes certain content from the requirements, for example office file formats published before 28 June 2025 and archives that are not updated after that date. Content you publish or change after that date is not covered by these exclusions.
Directive (EU) 2019/882, EUR-Lex, opens in a new tab
Use heading elements for headings, in a logical order. Do not simulate them with large, bold text.
<div class="big-title">Pricing</div>
<p class="bold-line">Monthly plans</p><h1>Pricing</h1>
<h2>Monthly plans</h2>A set of related items is a list. A screen reader then announces "list, 3 items".
<p>• Fast setup<br>
• No contract<br>
• Free support</p><ul>
<li>Fast setup</li>
<li>No contract</li>
<li>Free support</li>
</ul>Mark header cells with th and a scope, and name the table with a caption.
<table>
<tr><td><b>Plan</b></td><td><b>Price</b></td></tr>
<tr><td>Basic</td><td>€9</td></tr>
</table><table>
<caption>Plans and prices</caption>
<thead>
<tr><th scope="col">Plan</th><th scope="col">Price</th></tr>
</thead>
<tbody>
<tr><th scope="row">Basic</th><td>€9</td></tr>
</tbody>
</table>A label that is only placed next to the field is not connected to it. Connect them with label for.
<span>Email</span>
<input type="email" id="email"><label for="email">Email</label>
<input type="email" id="email">Give a group of related controls, such as radio buttons, a name with fieldset and legend.
<p>Delivery method</p>
<input type="radio" name="d" id="d1"> <label for="d1">Courier</label>
<input type="radio" name="d" id="d2"> <label for="d2">Pickup</label><fieldset>
<legend>Delivery method</legend>
<input type="radio" name="d" id="d1">
<label for="d1">Courier</label>
<input type="radio" name="d" id="d2">
<label for="d2">Pickup</label>
</fieldset>Mark the main regions so screen reader users can jump between them. If there is more than one navigation region, name each one.
<div class="nav">…</div>
<div class="content">…</div>
<div class="footer">…</div><header>…</header>
<nav aria-label="Main">…</nav>
<main>…</main>
<footer>…</footer>The same rule applies in native mobile apps. Mark headings as headings, join a label and its value into one element, and give every field a label.
import SwiftUI
struct AccountView: View {
@State private var email = ""
var body: some View {
VStack(alignment: .leading, spacing: 16) {
// A heading, announced as a heading
Text("Account details")
.font(.title2)
.accessibilityAddTraits(.isHeader)
// A label and its value read as one item
HStack {
Text("Plan")
Spacer()
Text("Business")
}
.accessibilityElement(children: .combine)
// The field has a label ("Email")
TextField("Email", text: $email)
.textFieldStyle(.roundedBorder)
}
.padding()
}
}import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.semantics.heading
import androidx.compose.ui.semantics.semantics
@Composable
fun AccountScreen() {
var email by remember { mutableStateOf("") }
Column(verticalArrangement = Arrangement.spacedBy(16.dp)) {
// A heading, announced as a heading
Text(
text = "Account details",
style = MaterialTheme.typography.titleLarge,
modifier = Modifier.semantics { heading() }
)
// A label and its value read as one item
Row(
modifier = Modifier
.fillMaxWidth()
.semantics(mergeDescendants = true) {},
horizontalArrangement = Arrangement.SpaceBetween
) {
Text("Plan")
Text("Business")
}
// The field has a label
OutlinedTextField(
value = email,
onValueChange = { email = it },
label = { Text("Email") }
)
}
}h1 to h6 elements? Are lists ul, ol or dl? Do tables use th, scope and caption? Is every field connected to a label?div is not a heading. It does not appear in the heading outline.th only for looks: Header cells that do not describe data cells mislead users.