Tabs
wu-tabs shows one panel at a time under a row of tabs. The kit script adds the keyboard handling and switches the panels. Without the script, the panel without hidden shows.
| Class or attribute | Effect |
|---|---|
wu-tabs |
Root |
wu-tabs-list |
Part, role="tablist". Row of tabs with a line below, scrolls sideways when narrow; the edge with hidden tabs fades out |
wu-tabs-tab |
Part, a <button role="tab">. Muted text, a brand bar when selected |
wu-tabs-panel |
Part, role="tabpanel". Content of one tab, lined up with the tab labels |
aria-selected="true" on a tab |
Selected tab |
hidden on a panel |
Panel not shown |
disabled or aria-disabled="true" on a tab |
Dimmed, skipped by the arrow keys |
card |
Tabs as joined boxes; the selected one opens into the content below |
boxed |
Tabs and panels inside one bordered box, the selected tab joined to the panel |
vertical |
Tab list at the inline start, panels beside it. Up and Down arrows move between tabs |
An <svg> in a tab |
App icon, sized to the text |
animated |
The bar grows from the center and the panel fades in, opt-in |
Default
Section titled “Default”Click a tab, or focus the selected tab and press the arrow keys, Home or End.
<div class="wu-tabs"> <div class="wu-tabs-list" role="tablist" aria-label="Account"> <button class="wu-tabs-tab" type="button" role="tab" id="tabs-profile" aria-controls="tabs-profile-panel" aria-selected="true">Profile</button> <button class="wu-tabs-tab" type="button" role="tab" id="tabs-billing" aria-controls="tabs-billing-panel" aria-selected="false" tabindex="-1">Billing</button> <button class="wu-tabs-tab" type="button" role="tab" id="tabs-team" aria-controls="tabs-team-panel" aria-selected="false" tabindex="-1">Team</button> </div> <div class="wu-tabs-panel" role="tabpanel" id="tabs-profile-panel" aria-labelledby="tabs-profile" tabindex="0">Name, photo and contact details.</div> <div class="wu-tabs-panel" role="tabpanel" id="tabs-billing-panel" aria-labelledby="tabs-billing" tabindex="0" hidden>Plan, invoices and payment method.</div> <div class="wu-tabs-panel" role="tabpanel" id="tabs-team-panel" aria-labelledby="tabs-team" tabindex="0" hidden>Members and their roles.</div></div>Shorter markup
Section titled “Shorter markup”The script fills in the roles, the ids, aria-controls, aria-labelledby and tabindex when they are missing, pairing tabs and panels in order. The full markup above also reads correctly before the script runs.
<div class="wu-tabs"> <div class="wu-tabs-list" aria-label="Report"> <button class="wu-tabs-tab" type="button" aria-selected="true">Summary</button> <button class="wu-tabs-tab" type="button">Details</button> <button class="wu-tabs-tab" type="button" disabled>Export</button> </div> <div class="wu-tabs-panel">Totals for the selected period.</div> <div class="wu-tabs-panel" hidden>One row per order.</div> <div class="wu-tabs-panel" hidden>Download as CSV.</div></div>Many tabs
Section titled “Many tabs”When the tabs do not fit, the list scrolls sideways and fades out at the edge that has more tabs behind it.
<div class="wu-tabs" style="max-inline-size: 20rem"> <div class="wu-tabs-list" aria-label="Project"> <button class="wu-tabs-tab" type="button" aria-selected="true">Overview</button> <button class="wu-tabs-tab" type="button">Tasks</button> <button class="wu-tabs-tab" type="button">Files</button> <button class="wu-tabs-tab" type="button">Members</button> <button class="wu-tabs-tab" type="button">Activity</button> <button class="wu-tabs-tab" type="button">Settings</button> </div> <div class="wu-tabs-panel">Status, owner and due date.</div> <div class="wu-tabs-panel" hidden>Open and done tasks.</div> <div class="wu-tabs-panel" hidden>Shared documents.</div> <div class="wu-tabs-panel" hidden>People on the project.</div> <div class="wu-tabs-panel" hidden>Recent changes.</div> <div class="wu-tabs-panel" hidden>Name and visibility.</div></div>Card and boxed
Section titled “Card and boxed”<div class="flex col gap-6"> <div class="wu-tabs card"> <div class="wu-tabs-list" aria-label="Order, card"> <button class="wu-tabs-tab" type="button" aria-selected="true">Items</button> <button class="wu-tabs-tab" type="button">Shipping</button> <button class="wu-tabs-tab" type="button">History</button> </div> <div class="wu-tabs-panel">Three items, $1,240.00.</div> <div class="wu-tabs-panel" hidden>Ships from Dallas.</div> <div class="wu-tabs-panel" hidden>Placed May 4, paid May 4.</div> </div> <div class="wu-tabs boxed"> <div class="wu-tabs-list" aria-label="Order, boxed"> <button class="wu-tabs-tab" type="button" aria-selected="true">Items</button> <button class="wu-tabs-tab" type="button">Shipping</button> <button class="wu-tabs-tab" type="button">History</button> </div> <div class="wu-tabs-panel">Three items, $1,240.00.</div> <div class="wu-tabs-panel" hidden>Ships from Dallas.</div> <div class="wu-tabs-panel" hidden>Placed May 4, paid May 4.</div> </div></div>Vertical
Section titled “Vertical”vertical puts the tab list at the inline start with the selected bar on its inline end edge. The script sets aria-orientation="vertical" on the list, and Up and Down arrows move between tabs. Without the script, add aria-orientation="vertical" yourself.
<div class="wu-tabs vertical"> <div class="wu-tabs-list" aria-label="Settings" aria-orientation="vertical"> <button class="wu-tabs-tab" type="button" aria-selected="true">General</button> <button class="wu-tabs-tab" type="button">Notifications</button> <button class="wu-tabs-tab" type="button">Security</button> </div> <div class="wu-tabs-panel">Name, language and time zone.</div> <div class="wu-tabs-panel" hidden>Email and push alerts.</div> <div class="wu-tabs-panel" hidden>Password and two-step sign-in.</div></div>An <svg> placed in a tab is sized to the text and takes the text color. Bring your own icons.
<div class="wu-tabs"> <div class="wu-tabs-list" aria-label="Inbox"> <button class="wu-tabs-tab" type="button" aria-selected="true"><svg viewBox="0 0 16 16" aria-hidden="true"><circle cx="8" cy="8" r="6" fill="currentColor"/></svg>Open</button> <button class="wu-tabs-tab" type="button"><svg viewBox="0 0 16 16" aria-hidden="true"><circle cx="8" cy="8" r="6" fill="none" stroke="currentColor" stroke-width="2"/></svg>Closed</button> </div> <div class="wu-tabs-panel">Twelve open threads.</div> <div class="wu-tabs-panel" hidden>Forty closed threads.</div></div>Right to left
Section titled “Right to left”The list starts at the right, the fade and the arrow keys follow the reading direction, and vertical tabs sit on the right.
<div class="wu-tabs vertical"> <div class="wu-tabs-list" aria-label="הפרופיל" aria-orientation="vertical"> <button class="wu-tabs-tab" type="button" aria-selected="true">חשבון</button> <button class="wu-tabs-tab" type="button">פרטיות</button> </div> <div class="wu-tabs-panel">שם וכתובת אימייל.</div> <div class="wu-tabs-panel" hidden>מי יכול לראות את הפרופיל שלך.</div></div>Animation
Section titled “Animation”The animated word grows the selected bar from the center with a spring and fades each panel in. wu-animated on an ancestor turns it on for every tab set inside. Motion runs only under prefers-reduced-motion: no-preference.
<div class="wu-tabs animated"> <div class="wu-tabs-list" aria-label="Period"> <button class="wu-tabs-tab" type="button" aria-selected="true">Week</button> <button class="wu-tabs-tab" type="button">Month</button> <button class="wu-tabs-tab" type="button">Year</button> </div> <div class="wu-tabs-panel">42 orders this week.</div> <div class="wu-tabs-panel" hidden>180 orders this month.</div> <div class="wu-tabs-panel" hidden>2,140 orders this year.</div></div>Script
Section titled “Script”The drop-in script and the ES module both start every wu-tabs on the page. For tabs added later, call init() on their container:
import { init } from '@wovenui/components/js';
init(document.getElementById('settings'));Each tab set starts once, so calling init() again is safe. See JavaScript.
Accessibility
Section titled “Accessibility”- The script follows the WAI-ARIA tabs pattern with automatic activation: Left and Right arrows select the previous or next tab, Home and End the first and last, mirrored in right-to-left text. In
verticaltabs, Up and Down arrows do it instead. Only the selected tab is in the Tab order. - Panels get
tabindex="0", so Tab moves from the tab list into the panel. Focus shows a ring on tabs and panels. - Give the tab list an
aria-labeloraria-labelledby. - In forced colors mode the selected bar uses the highlight color.
Load only the tabs
Section titled “Load only the tabs”@use "@wovenui/components/tabs";The tabs need no other component.