Asked for complete tags, correct nesting, no duplicate attributes and unique IDs. Removed from WCAG 2.2, but still in EN 301 549 V3.2.1.
This page is for teams that still work to EN 301 549 V3.2.1. Criterion 4.1.1 asked for clean markup: complete tags, correct nesting, no duplicate attributes and unique IDs. W3C marked it obsolete and removed it from WCAG 2.2, because browsers now parse markup in a consistent way that assistive technology relies on.
The underlying problems have not gone away. Broken markup can still break a label, an aria-labelledby reference or a control, and those cases are covered by 1.3.1 and 4.1.2 instead. Treat clean markup as good practice for every project.
label association.Users of assistive technology, whose software depends on a consistent structure. Users of browsers that recover from errors differently. Anyone who depends on labels, references and controls that clean markup keeps working.
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. Robust markup is part of "robust".
The technical counterpart is the standard EN 301 549, which incorporates WCAG 4.1.1 by reference and turns it into separate clauses for each kind of ICT.
| Clause | Type of ICT | What it requires |
|---|---|---|
| 9.4.1.1 | Web pages (V3.2.1) | Web content must satisfy WCAG 2.1 success criterion 4.1.1 in EN 301 549 V3.2.1. |
The standard's document and software clauses for this criterion should be checked in V3.2.1. Whether and how the criterion applies to non-web documents is stated in clause 10 of the standard. 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: Criterion 4.1.1 appears in EN 301 549 V3.2.1, which is based on WCAG 2.1. It was removed from WCAG 2.2, on which EN 301 549 V4.1.1 (September 2026) is based, so check V4.1.1 to see whether the clause was kept. V4.1.1 has not yet been cited in the Official Journal of the European Union. If you work to V3.2.1, treat this criterion as part of it.
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
A duplicate ID can make a label or an ARIA reference point at the wrong element.
<label for="name">Name</label>
<input id="name">
<input id="name"> <!-- duplicate --><label for="billing-name">Billing name</label>
<input id="billing-name">
<label for="shipping-name">Shipping name</label>
<input id="shipping-name">A missing closing quote can swallow the rest of the tag, and the next attributes with it.
<a href="/contact class="button">Contact</a><a href="/contact" class="button">Contact</a>Elements must be nested as their specification allows, for example no link inside a link.
<a href="/a">One <a href="/b">Two</a></a><a href="/a">One</a> <a href="/b">Two</a>When an attribute appears twice, browsers keep one and ignore the other.
<button class="primary" class="large" type="button">Save</button><button class="primary large" type="button">Save</button>An element that is never closed can change the structure of everything after it.
<a href="/a">Read more
<p>The next paragraph ends up inside the link.</p><a href="/a">Read more</a>
<p>The next paragraph is outside the link.</p>A validator catches most of these problems in seconds. Run it in the build process.
<!-- Markup is never validated --># in CI
npx html-validate "dist/**/*.html"This criterion is about markup languages. Native iOS and Android screens are not written in markup that a browser parses, so there is nothing to validate in the same way. Using standard components keeps the accessibility tree consistent.
import SwiftUI
struct Contact: View {
var body: some View {
// Standard components give the platform a reliable structure
VStack {
Text("Contact us").font(.headline)
Button("Email us") { }
}
}
}import androidx.compose.foundation.layout.Column
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
@Composable
fun Contact() {
// Standard components give the platform a reliable structure
Column {
Text("Contact us")
Button(onClick = { }) { Text("Email us") }
}
}id values, especially in components rendered several times on one page.for, aria-labelledby and aria-describedby point to the right element?class or two aria-label attributes on the same element.