Tab bar
A tab bar switches between sections of content in place, one section at a time.
<tk-tab-bar label="Project" value="overview"> <tk-tab value="overview">Overview</tk-tab> <tk-tab value="activity">Activity</tk-tab> <tk-tab value="members">Members</tk-tab> <tk-tab value="billing" disabled>Billing</tk-tab> </tk-tab-bar>
Anatomy
| Part | What it is |
|---|---|
tk-tab-bar |
the row of tabs on a full-width hairline; one Tab stop |
tk-tab |
one tab, 40px tall |
| underline | a 2px line under the selected tab |
| panel | your own element, shown for the selected tab |
Panels
The tab bar draws the tabs; the panels are your own elements. For each panel:
- give the tab an
idand apanelthat names the panel'sid; - give the panel
role="tabpanel",aria-labelledbywith the tab'sid, andtabindex="0"; - show the selected panel and hide the others when
changefires.
<tk-tab-bar id="settings-tabs" label="Settings" value="profile">
<tk-tab id="tab-profile" value="profile" panel="panel-profile">Profile</tk-tab>
<tk-tab id="tab-alerts" value="alerts" panel="panel-alerts">Notifications</tk-tab>
</tk-tab-bar>
<div id="panel-profile" role="tabpanel" aria-labelledby="tab-profile" tabindex="0">Your name and photo</div>
<div id="panel-alerts" role="tabpanel" aria-labelledby="tab-alerts" tabindex="0" hidden>Email and push alerts</div>
<script>
const tabs = document.getElementById('settings-tabs');
tabs.addEventListener('change', () => {
for (const tab of tabs.querySelectorAll('tk-tab'))
document.getElementById(tab.panel).hidden = tab.value !== tabs.value;
});
</script>A router can swap the content instead. Either way, show the new panel at once: moving focus between tabs selects them.
Shape
shape="round" makes the hover and press tint behind each tab a pill. The underline
stays the same.
<tk-tab-bar label="Reports" value="week" shape="round"> <tk-tab value="week">This week</tk-tab> <tk-tab value="month">This month</tk-tab> <tk-tab value="year">This year</tk-tab> </tk-tab-bar>
States
Hover darkens a tab's label and tints it, press tints it the same, and keyboard focus shows a ring. The selected tab gets a dark label and the underline.
disabledon a tab: it's dimmed, can't be clicked, and the keyboard skips it. Say elsewhere why the section is unavailable.
When to use
- Use a tab bar to switch between sections with different content on one page, such as Profile, Notifications, and Billing.
- Use a
Segmented control to change how the same content is shown, such as List or Grid. - Use a
Navigation menu orSidebar to go to another page. - Use a
Radio group for a value a form submits, and aMenu for actions.
Content
- Use one or two words per tab, in sentence case: "Overview", "Activity".
- Keep labels parallel: all nouns, not "Overview" next to "See all activity".
- Don't put counts in labels.
- Give the bar a
labelthat names the set of sections, such as "Settings".
Do / Don't
| ✅ Do | ❌ Don't |
|---|---|
| Switch between sections of content | Switch views of the same content; use a segmented control |
| Write one or two parallel words | Write a sentence on a tab |
Mark up every panel with role="tabpanel" |
Leave panels unconnected to their tabs |
Listen for change on the bar |
Add a click handler to each tab |
| Show the new panel at once | Load a panel slowly while focus moves through the tabs |
Related
Segmented control : changes how the same content is shown.Navigation menu : moves to another page.Page header : a tab bar often sits at its bottom; it draws its own hairline, so add no divider above it.
API
tk-tab-bar
| Attribute | Type | Default | What it does |
|---|---|---|---|
value |
string | '' |
the selected tab's value; updates when someone picks a tab |
label |
string | — | the tab list's accessible name; required |
shape |
rect | round |
rect |
the tint behind each tab: rounded corners or a pill |
| Slot | Content |
|---|---|
| default | tk-tab elements |
tk-tab
| Attribute | Type | Default | What it does |
|---|---|---|---|
value |
string | '' |
the tab's identity; required |
panel |
string | — | the id of the panel this tab shows; sets aria-controls |
disabled |
boolean | false |
dimmed, not clickable, skipped by the keyboard |
| Slot | Content |
|---|---|
| default | the label |
A tab without an id gets a generated one.
Events
change fires on tk-tab-bar when someone picks a different tab. Read the new value.
It doesn't fire when you set value in code.
Tokens
| Part | Tokens |
|---|---|
| tab | text/secondary, Label Medium (14/20) |
| tab, hover | text/default + state/hover |
| tab, press | state/press |
| selected tab | text/default + a 2px border/selected underline |
| disabled tab | text/disabled |
| tint | radius-8 (radius-full with shape="round"), space-4 / space-8 padding |
| hairline | border/subtle |
| focus ring | border/accent/focus |
Values per theme:
Accessibility
Built in:
- The bar is a tab list named by
label; each tab is a tab with its selected state. - One Tab stop: Tab lands on the selected tab, then moves on to the panel.
panelconnects each tab to its panel.- The selected tab is marked by its underline, not by color alone.
Your part:
- Set
labelon the bar andvalueon every tab. - Mark up the panels as in
Panels . - Show the new panel at once, without a load step.
| Key | Action |
|---|---|
Tab |
moves focus to the selected tab, then to the panel |
Right / Left |
selects the next or previous tab; wraps around |
Home / End |
selects the first or last tab |
Contrast limits
- Press: in light and dark, a pressed tab looks the same as a hovered one.