HTML Guides for disclosure
Learn how to identify and fix common HTML validation errors flagged by the W3C Validator — so your pages are standards-compliant and render correctly across every browser. Also check our Accessibility Guides.
The first child of a <details> element must be a <summary>, and this error means the validator found something else there, or nothing at all.
The <summary> is the visible label of the disclosure widget, the part people click or tab to when they want to open or close the rest of the content. If it's missing, browsers make up a label of their own, usually "Details" in the browser's language. That tells sighted users and screen reader users nothing about what's inside.
A <summary> only counts when it comes first. Comments and whitespace before it are fine, but any element or text pushes it out of place. A <summary> after a paragraph, or wrapped in a <div>, leaves the <details> without one, and the validator adds a second error for the misplaced element: Element “summary” not allowed as child of element “details” in this context. (or … child of element “div” …). A second <summary> in the same <details> gets that error too.
Sometimes <details> ends up as a plain wrapper, for example around anchor targets or layout blocks, when nobody meant to build a collapsible section. In that case, replace it with a <div> or <section>. Otherwise, add a short <summary> as the first child, or move the existing one to the top.
Invalid example
<details>
<p>Orders ship within two business days.</p>
</details>
<details>
<p>Unused items can be returned within 30 days.</p>
<summary>Returns</summary>
</details>
Valid example
<details>
<summary>Shipping</summary>
<p>Orders ship within two business days.</p>
</details>
<details>
<summary>Returns</summary>
<p>Unused items can be returned within 30 days.</p>
</details>
A <label> whose for attribute points at a form control can't carry aria-expanded, because the label only names that control and has no open or closed state to report.
aria-expanded tells assistive technologies whether the content a control shows and hides, such as a dropdown menu or an accordion panel, is open right now. Screen readers read it out when the control gets focus, as in "Menu, button, collapsed". ARIA permits it on interactive roles like button, checkbox, combobox, link and tab.
Assistive technologies don't expose a <label> linked by for as an element in its own right. They read its text as the control's accessible name, and focus goes to the control, so a state set on the label is never announced. A <label> that wraps its control instead of using for gets a separate message, about a label that is an ancestor of a labelable element. The fix is the same.
The message usually comes from a CSS-only toggle: a hidden checkbox opens a mobile menu, or hidden radio buttons switch slides or tabs, and the <label> is styled as the button. CSS can't change attributes, so a hard-coded aria-expanded="false" stays false after the menu opens. Moving it onto the checkbox clears the error but leaves the value just as stale. Use a <button type="button"> with aria-expanded and aria-controls instead, with a short script that flips the value and shows or hides the content. If you'd rather avoid script, <details> and <summary> report their open state to assistive technologies with no ARIA at all.
For a slider or tabs built from radio buttons, remove aria-expanded. The checked radio already tells screen readers which slide is selected, and the validator rejects aria-expanded on <input type="radio"> unless the input also has a role.
Invalid example
<nav>
<input type="checkbox" id="menu-toggle" class="menu-toggle">
<label for="menu-toggle" aria-expanded="false">Menu</label>
<ul class="menu">
<li><a href="/">Home</a></li>
<li><a href="/about">About</a></li>
</ul>
</nav>
Valid example
<nav>
<button type="button" id="menu-button" aria-expanded="false" aria-controls="main-menu">
Menu
</button>
<ul id="main-menu" hidden>
<li><a href="/">Home</a></li>
<li><a href="/about">About</a></li>
</ul>
</nav>
<script>
const button = document.getElementById("menu-button");
const menu = document.getElementById("main-menu");
button.addEventListener("click", () => {
const open = button.getAttribute("aria-expanded") === "true";
button.setAttribute("aria-expanded", String(!open));
menu.hidden = open;
});
</script>
Validate at scale.
Ship accessible websites, faster.
Automated HTML & accessibility validation for large sites. Check thousands of pages against WCAG guidelines and W3C standards in minutes, not days.
1K Weekly
1,000 monthly checks. Cancel anytime.
Get 1K Weekly →Join teams across 40+ countries