Tabs New
A set of label elements with hidden radio inputs inside
a .tabs container. No JavaScript needed -- CSS
:has() handles panel switching.
Default
-
Each
labelwraps a hidden radioinput. Addcheckedto set the default tab.
CSS uses:has()to watch the checked state inside[role="tablist"]and show the panel at the matching position, with no JavaScript. -
Give the
headerrole="tablist"and anaria-label.
The tab bar styling targets[role="tablist"]directly. Screen readers also use this to announce the group and let users navigate between tabs. -
Connect each input to its panel with matching
idandaria-controls.
Lets assistive technology jump directly from a tab to its associated content. -
Mark each panel with
role="tabpanel",aria-labelledby, andtabindex="0".
All[role="tabpanel"]elements are hidden by default. The Nth panel is revealed when the Nth tab is checked.aria-labelledbynames the panel after its tab, andtabindex="0"makes it reachable by keyboard.
Manage your account settings and preferences.
Change your password and security settings.
Configure how and when you receive notifications.
<section class="tabs">
<header role="tablist" aria-label="Account settings">
<label>
<input
type="radio"
name="tabs"
id="tab-1"
checked
aria-controls="panel-1"
/>
Account
</label>
<label>
<input type="radio" name="tabs" id="tab-2" aria-controls="panel-2" />
Password
</label>
<label>
<input type="radio" name="tabs" id="tab-3" aria-controls="panel-3" />
Notifications
</label>
</header>
<div role="tabpanel" id="panel-1" aria-labelledby="tab-1" tabindex="0">
<p>Manage your account settings and preferences.</p>
</div>
<div role="tabpanel" id="panel-2" aria-labelledby="tab-2" tabindex="0">
<p>Change your password and security settings.</p>
</div>
<div role="tabpanel" id="panel-3" aria-labelledby="tab-3" tabindex="0">
<p>Configure how and when you receive notifications.</p>
</div>
</section>
Disabled tab
Add disabled to the radio input to disable a tab.
Overview content goes here.
Analytics content goes here.
Reports content goes here.
<section class="tabs">
<header role="tablist" aria-label="Dashboard">
<label>
<input
type="radio"
name="tabs"
id="tab-1"
checked
aria-controls="panel-1"
/>
Overview
</label>
<label>
<input type="radio" name="tabs" id="tab-2" aria-controls="panel-2" />
Analytics
</label>
<label>
<input
type="radio"
name="tabs"
id="tab-3"
disabled
aria-controls="panel-3"
/>
Reports
</label>
</header>
<div role="tabpanel" id="panel-1" aria-labelledby="tab-1" tabindex="0">
Overview content goes here.
</div>
<div role="tabpanel" id="panel-2" aria-labelledby="tab-2" tabindex="0">
Analytics content goes here.
</div>
<div role="tabpanel" id="panel-3" aria-labelledby="tab-3" tabindex="0">
Reports content goes here.
</div>
</section>
With content
Tab panels can contain any content.
| Name | Role | Status |
|---|---|---|
| Alice | Engineer | Active |
| Bob | Designer | Away |
| Carol | Manager | Active |
No activity yet
Actions taken by your team will appear here.
<section class="tabs" style="display: grid; gap: 1rem">
<header role="tablist" aria-label="Team">
<label>
<input
type="radio"
name="tabs"
id="tab-1"
checked
aria-controls="panel-1"
/>
Members
</label>
<label>
<input type="radio" name="tabs" id="tab-2" aria-controls="panel-2" />
Activity
</label>
<label>
<input type="radio" name="tabs" id="tab-3" aria-controls="panel-3" />
Invite
</label>
</header>
<div role="tabpanel" id="panel-1" aria-labelledby="tab-1" tabindex="0">
<table>
<thead>
<tr>
<th>Name</th>
<th>Role</th>
<th>Status</th>
</tr>
</thead>
<tbody>
<tr>
<td>Alice</td>
<td>Engineer</td>
<td>Active</td>
</tr>
<tr>
<td>Bob</td>
<td>Designer</td>
<td>Away</td>
</tr>
<tr>
<td>Carol</td>
<td>Manager</td>
<td>Active</td>
</tr>
</tbody>
</table>
</div>
<div role="tabpanel" id="panel-2" aria-labelledby="tab-2" tabindex="0">
<section class="empty">
<svg><!-- icon --></svg>
<h3>No activity yet</h3>
<p>Actions taken by your team will appear here.</p>
</section>
</div>
<div role="tabpanel" id="panel-3" aria-labelledby="tab-3" tabindex="0">
<form style="display: grid; gap: 1em">
<label class="field">
<span>Email address</span>
<input type="email" placeholder="colleague@example.com" />
</label>
<div class="field">
<span>Role</span>
<button type="button" class="outlined" popovertarget="role">
Member
</button>
<div id="role" popover>
<menu>
<li>
<label>
<input type="radio" name="role" value="member" checked /> Member
</label>
</li>
<li>
<label>
<input type="radio" name="role" value="admin" /> Admin
</label>
</li>
<li>
<label>
<input type="radio" name="role" value="viewer" /> Viewer
</label>
</li>
</menu>
</div>
</div>
<button type="submit">Send invite</button>
</form>
</div>
</section>
Nested
Tabs can be nested freely. Each level is fully independent — switching tabs in the outer component has no effect on the inner one and vice versa.
Outer panel one — contains a nested tabs:
There's another tabs component in the other tab
Woof
Meow
Outer panel two.
<!-- Level 1 -->
<section class="tabs first">
<header role="tablist" aria-label="Outer tabs">
<label> <input type="radio" name="outer" checked /> Overview </label>
<label><input type="radio" name="outer" /> Settings</label>
</header>
<div role="tabpanel">
<p>Outer panel one — contains a nested tabs:</p>
<!-- Level 2 -->
<section class="tabs second">
<header role="tablist" aria-label="Inner tabs">
<label><input type="radio" name="inner" /> Info</label>
<label> <input type="radio" name="inner" checked /> Animals </label>
</header>
<div role="tabpanel">
<p>There's another tabs component in the other tab</p>
</div>
<div role="tabpanel">
<!-- Level 3 -->
<section class="tabs third">
<header role="tablist" aria-label="Inner tabs">
<label> <input type="radio" name="third" checked /> Dog </label>
<label><input type="radio" name="third" /> Cat</label>
</header>
<div role="tabpanel"><p>Woof</p></div>
<div role="tabpanel"><p>Meow</p></div>
</section>
</div>
</section>
</div>
<div role="tabpanel">
<p>Outer panel two.</p>
</div>
</section>
<style>
.tabs {
padding: 1rem;
.first {
border: 1px solid var(--ui-neutral-100);
}
.second {
background-color: var(--ui-neutral-100);
}
.third {
background-color: var(--ui-neutral-0);
}
}
</style>