HTML Guides for tablist
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 WAI-ARIA specification defines a strict ownership model for tab-related roles. An element with role="tab" controls the visibility of an associated role="tabpanel" element, and tabs are expected to be grouped within a tablist. This relationship is how assistive technologies like screen readers understand and communicate the tab interface pattern to users — for example, announcing "tab 2 of 4" when focus moves between tabs.
When a tab is not contained in or owned by a tablist, screen readers cannot determine how many tabs exist in the group, which tab is currently selected, or how to navigate between them. This fundamentally breaks the accessibility of the tab interface, making it confusing or unusable for people who rely on assistive technologies.
There are two ways to establish the required relationship:
- Direct containment: Place the
role="tab"elements as direct children of therole="tablist"element. This is the most common and straightforward approach. - Using
aria-owns: If the DOM structure prevents direct nesting, addaria-ownsto thetablistelement with a space-separated list ofidvalues referencing each tab. This tells assistive technologies that thetablistowns those tabs even though they aren't direct children in the DOM.
Examples
Incorrect: tab outside of a tablist
In this example, the role="tab" buttons are siblings of the tablist rather than children of it, which triggers the validation error.
<divclass="tabs">
<divrole="tablist"aria-label="Sample Tabs"></div>
<buttonrole="tab"aria-selected="true"aria-controls="panel-1"id="tab-1">
First Tab
</button>
<buttonrole="tab"aria-selected="false"aria-controls="panel-2"id="tab-2">
Second Tab
</button>
</div>
Correct: tabs as direct children of tablist
The simplest fix is to place the role="tab" elements directly inside the role="tablist" element.
<divclass="tabs">
<divrole="tablist"aria-label="Sample Tabs">
<buttonrole="tab"aria-selected="true"aria-controls="panel-1"id="tab-1"tabindex="0">
First Tab
</button>
<buttonrole="tab"aria-selected="false"aria-controls="panel-2"id="tab-2"tabindex="-1">
Second Tab
</button>
</div>
<divid="panel-1"role="tabpanel"tabindex="0"aria-labelledby="tab-1">
<p>Content for the first panel</p>
</div>
<divid="panel-2"role="tabpanel"tabindex="0"aria-labelledby="tab-2"hidden>
<p>Content for the second panel</p>
</div>
</div>
Correct: using aria-owns when DOM nesting isn't possible
If your layout or framework makes it difficult to nest the tabs directly inside the tablist, you can use aria-owns to establish the relationship programmatically.
<divclass="tabs">
<divrole="tablist"aria-label="Sample Tabs"aria-owns="tab-1 tab-2"></div>
<divclass="tab-wrapper">
<buttonrole="tab"aria-selected="true"aria-controls="panel-1"id="tab-1"tabindex="0">
First Tab
</button>
<buttonrole="tab"aria-selected="false"aria-controls="panel-2"id="tab-2"tabindex="-1">
Second Tab
</button>
</div>
<divid="panel-1"role="tabpanel"tabindex="0"aria-labelledby="tab-1">
<p>Content for the first panel</p>
</div>
<divid="panel-2"role="tabpanel"tabindex="0"aria-labelledby="tab-2"hidden>
<p>Content for the second panel</p>
</div>
</div>
Additional notes
- Each
role="tab"element should usearia-selectedto indicate which tab is active ("true") and which are not ("false"). - Use
aria-controlson each tab to reference theidof its associatedtabpanel. - Use
aria-labelledbyon eachtabpanelto point back to its controlling tab. - Set
tabindex="0"on the active tab andtabindex="-1"on inactive tabs to support keyboard navigation with arrow keys within thetablist. - Always include an
aria-labeloraria-labelledbyon thetablistto give it an accessible name.
An element with role="tab" must sit inside an element with role="tablist", or be claimed by one through the aria-owns attribute. When a tab lives outside a tablist, screen readers can't tell how many tabs are in the group or which one is selected, so the whole tab interface breaks for people who rely on assistive technology. To fix it, place your role="tab" elements inside a role="tablist" container.
The tab role only carries meaning inside a tablist. A tab controls the visibility of a matching role="tabpanel", and the tablist groups the tabs together so assistive technologies can announce position (for example, "tab 2 of 4") and move focus between tabs with the arrow keys. Take the tab out of its tablist and that grouping disappears, leaving the screen reader with a button it can't place in any structure.
The W3C validator emits this message in two slightly different wordings depending on its version: an element with the “role” value “tablist” and an element with “role=tablist”. Both report the same problem, and the fix is identical.
There are two ways to establish the required relationship. Nesting the tabs directly inside the tablist is the common one. When your layout or framework makes that nesting impossible, point aria-owns from the tablist at the tab ids instead.
Invalid example
Here the role="tab" buttons are siblings of the tablist rather than children of it, so no tablist contains or owns them.
<divclass="tabs">
<divrole="tablist"aria-label="Account settings"></div>
<buttonrole="tab"aria-selected="true"aria-controls="panel-1"id="tab-1">
Profile
</button>
<buttonrole="tab"aria-selected="false"aria-controls="panel-2"id="tab-2">
Billing
</button>
</div>
Valid example
Moving the tabs inside the role="tablist" element gives them the parent they need.
<divclass="tabs">
<divrole="tablist"aria-label="Account settings">
<buttonrole="tab"aria-selected="true"aria-controls="panel-1"id="tab-1"tabindex="0">
Profile
</button>
<buttonrole="tab"aria-selected="false"aria-controls="panel-2"id="tab-2"tabindex="-1">
Billing
</button>
</div>
<divid="panel-1"role="tabpanel"aria-labelledby="tab-1">
<p>Profile settings</p>
</div>
<divid="panel-2"role="tabpanel"aria-labelledby="tab-2"hidden>
<p>Billing settings</p>
</div>
</div>
If you genuinely can't nest the tabs in the DOM, keep them where they are and add aria-owns="tab-1 tab-2" to the role="tablist" element so assistive technologies still treat the list as the owner of those tabs.
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.
Pro Trial
Full Pro access. Cancel anytime.
Start Pro Trial →Join teams across 40+ countries