This is a small guide to marking up fully accessible tabs. Consider using PostHTML ARIA Tabs as a practical solution.
tablist
role to the <ul>
to indicate it contains tabs.presentation
role to each <li>
to bypass its list item state.tab
role to each <a>
to incidate it is an actual tab.href
and aria-controls
to each <a>
to reference its tab panel.id
to each <a>
as a reference for its tab panel.aria-selected="true"
to the active <a>
tab.tabpanel
role to each <section>
to indicate it is a tab panel.id
to each <section>
as a reference for its tab.aria-labelledby
to each <section>
to reference its label.hidden
to each inactive <section>
to indicate it is hidden.<ul role="tablist"> <li role="presentation"> <a id="foo-tab" href="#foo" role="tab" aria-controls="foo" aria-selected="true">Foo</a> </li> <li role="presentation"> <a id="bar-tab" href="#bar" role="tab" aria-controls="bar">Bar</a> </li> <li role="presentation"> <a id="qux-tab" href="#qux" role="tab" aria-controls="qux">Qux</a> </li> </ul> <section id="foo" role="tabpanel" aria-labelledby="foo-tab"> ... </section> <section id="bar" role="tabpanel" aria-labelledby="bar-tab" hidden> ... </section> <section id="qux" role="tabpanel" aria-labelledby="qux-tab" hidden> ... </section>
[hidden]
, e.g. [hidden] { display: none; }
.<ul>
and <li>
elements are used for source readability and (to a much lesser extent) progressive enhancement. While both of their implied semantics are overwritten, these elements best represent a collection of items.