<u-tabs>
<u-tabs> is not a native HTML element, but follows ARIA authoring practices for tabs. It lets you navigate between groups of information that appear in the same context.
Quick intro:
- Use
<u-tabs>to group all tabbing-elements - Use
<u-tablist>around multiple<u-tab>direct children - these are the labels - Use
<u-tab aria-selected="true">to set the open tab (defaults to first tab) - Use
<u-tab aria-controls="id-of-panel">if you need panels outside<u-tabs> - Use
<u-tabpanel>s after<u-tablist>- these hide/show content of related<u-tab> - ARIA Authoring Practices Guide Docs: Tabs
Example
<u-tabs>
<u-tablist>
<u-tab>Tab 1</u-tab>
<u-tab>Tab 2</u-tab>
<u-tab>Tab 3</u-tab>
</u-tablist>
<u-tabpanel>Panel 1 with <a href="#">link</a></u-tabpanel>
<u-tabpanel>Panel 2 with <a href="#">link</a></u-tabpanel>
<u-tabpanel>Panel 3 with <a href="#">link</a></u-tabpanel>
</u-tabs>
<style>
/* Styling just for example: */
u-tab { padding: .5em }
u-tab[aria-selected="true"] { border-bottom: 4px solid }
u-tabpanel { padding: 1em; border: 1px solid }
</style>
Install
bash
npm add -S @u-elements/u-tabsbash
pnpm add -S @u-elements/u-tabsbash
yarn add @u-elements/u-tabsbash
bun add -S @u-elements/u-tabshtml
<script type="module" src="https://unpkg.com/@u-elements/u-tabs@latest/dist/u-tabs.js"></script>Attributes and props
<u-tabs>
- Attributes: all global HTML attributes such as
id,class,data- - DOM interface:
UHTMLTabsElementextendsHTMLElementUHTMLTabsElement.tabListreturns the containedUHTMLTabListElementUHTMLTabsElement.selectedIndexsets or gets anumberreflecting the index of the first selected<u-tab>element. Will ignore invalid indexes.UHTMLTabsElement.tabsreturns aNodeListOf<UHTMLTabElement>UHTMLTabsElement.panelsreturns aNodeListOf<UHTMLTabPanelElement>
<u-tablist>
- Attributes: all global HTML attributes such as
id,class,data- - DOM interface:
UHTMLTabListElementextendsHTMLElementUHTMLTabListElement.tabsElementreturns the parentUHTMLTabsElementUHTMLTabsElement.selectedIndexsets or gets anumberreflecting the index of the first selected<u-tab>element. Will ignore invalid indexes.UHTMLTabsElement.tabsreturns aNodeListOf<UHTMLTabElement>
<u-tab>
- Attributes: all global HTML attributes such as
id,class,data-aria-selectedcan contain"true"or"false"to set the currently selected tabaria-controlscan contain ID the<u-tabpanel>to control
- DOM interface:
UHTMLTabElementextendsHTMLElementUHTMLTabElement.tabsElementreturns the parentUHTMLTabsElementUHTMLTabElement.selectedsets or getstrueorfalse, indicating whether this tab is currently selectedUHTMLTabElement.indexreturns anumberrepresenting the position/index within the list of tabsUHTMLTabElement.panelreturns the associatedUHTMLTabPanelElement
<u-tabpanel>
- Attributes: all global HTML attributes such as
id,class,data- - DOM interface:
UHTMLTabPanelElementextendsHTMLElementUHTMLTabPanelElement.tabsElementreturns the parentUHTMLTabsElementUHTMLTabPanelElement.tabsreturns a associatedNodeListOf<UHTMLTabElement>
Events
No custom events beyond the usual events supported by HTML elements. Tabbing triggers a click (both with mouse and keyboard) so listen for this to check for tab change:
js
document.addEventListener('click', ({ target }) => {
const tab = target instanceof Element && target.closest('u-tab');
if (tab) {
console.log('changed to tab:', tab);
}
})Styling
<u-tabs>, <u-tablist> and <u-tabpanel> renders as display: block, while <u-tab> renders as display: inline-block.
Styling the tab state
<u-tab> automatically gets a aria-selected="true" or aria-selected="false" attribute, which can be utilized for styling:
css
.my-tab[aria-selected="true"] {
/* Your active tab styling here */
}
.my-tab[aria-selected="false"] {
/* Your inactive tab styling here */
}Styling example: Scrolling tablist
<style>
.my-tablist-scrolls {
display: flex;
overflow: auto;
white-space: nowrap;
}
</style>
<u-tabs>
<u-tablist class="my-tablist-scrolls">
<u-tab>Tab 1</u-tab><u-tab>Tab 2</u-tab><u-tab>Tab 3</u-tab><u-tab>Tab 4</u-tab><u-tab>Tab 5</u-tab><u-tab>Tab 6</u-tab><u-tab>Tab 7</u-tab>
</u-tablist>
<u-tabpanel>Panel 1</u-tabpanel>
</u-tabs>
Styling example: Wrapping tablist
<style>
.my-tablist-wrapps {
display: flex;
flex-wrap: wrap;
}
</style>
<u-tabs>
<u-tablist class="my-tablist-wrapps">
<u-tab>Tab 1</u-tab><u-tab>Tab 2</u-tab><u-tab>Tab 3</u-tab><u-tab>Tab 4</u-tab><u-tab>Tab 5</u-tab><u-tab>Tab 6</u-tab><u-tab>Tab 7</u-tab>
</u-tablist>
<u-tabpanel>Panel 1</u-tabpanel>
</u-tabs>
Server side rendering
You can server side render <u-tabs> by using Declarative Shadow DOM. Example:
TS
import { UHTMLTabsShadowRoot, UHTMLTabListShadowRoot, UHTMLTabShadowRoot, UHTMLTabPanelShadowRoot } from '@u-elements/u-tabs';
const renderToStaticMarkup = (tabs: string[], panels[]) =>
`<u-tabs>
${UHTMLTabsShadowRoot}
<u-tablist>
${UHTMLTabListShadowRoot}
${tabs.map((tab: string) =>
`<u-tab>${UHTMLTabShadowRoot}${tab}</u-tab>`
).join('')}
</u-tablist>
${panels.map((panel: string) =>
`<u-tabpanel>${UHTMLTabPanelShadowRoot}${panel}</u-tabpanel>`
).join('')}
</u-tabs>`Accessibility
| Screen reader | <u-tabs> |
|---|---|
| VoiceOver (Mac) + Chrome | ✅ |
| VoiceOver (Mac) + Edge | ✅ |
| VoiceOver (Mac) + Firefox | ✅ |
| VoiceOver (Mac) + Safari | ✅ |
| VoiceOver (iOS) + Safari | ✅ |
| Jaws (PC) + Chrome | ✅ |
| Jaws (PC) + Edge | ✅ |
| Jaws (PC) + Firefox | ✅ |
| NVDA (PC) + Chrome | ✅ |
| NVDA (PC) + Edge | ✅ |
| NVDA (PC) + Firefox | ✅ |
| Narrator (PC) + Chrome | ✅ |
| Narrator (PC) + Edge | ✅ |
| Narrator (PC) + Firefox | ✅ |
| TalkBack (Android) + Chrome | ✅ |
| TalkBack (Android) + Firefox | ✅ |
| TalkBack (Android) + Samsung Internet | ✅ |
Specifications
- DOM interface: HTMLElement
- ARIA Authoring Practices: Tabs
Changelog
- 0.0.12: Respect
aria-disabled="true" - 0.0.11: Enable declarative shadow root support and export
UHTMLTabsShadowRoot,UHTMLTabListShadowRoot,UHTMLTabShadowRootandUHTMLTabPanelShadowRootfor easier server side rendering - 0.0.10: Automatically adds
aria-hidden="true"whenhiddento prevent Safari 18.6 from announcing hidden panels