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 label wraps a radio input. Add
checked to the first input to set the default tab. Panels
are plain div elements placed after the labels. Give the
header a role="tablist" and an
aria-label, connect each input to its panel via
id / aria-controls, and mark each panel with
role="tabpanel", aria-labelledby, and
tabindex="0" so keyboard users can Tab into the content.
Manage your account settings and preferences.
Change your password and security settings.
Configure how and when you receive notifications.
<section class="tabs" style="display: grid; gap: 1rem">
<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"
style="grid-column: 1; grid-row: 2"
>
<p>Manage your account settings and preferences.</p>
</div>
<div
role="tabpanel"
id="panel-2"
aria-labelledby="tab-2"
tabindex="0"
style="grid-column: 1; grid-row: 2"
>
<p>Change your password and security settings.</p>
</div>
<div
role="tabpanel"
id="panel-3"
aria-labelledby="tab-3"
tabindex="0"
style="grid-column: 1; grid-row: 2"
>
<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:
Weekly view
Monthly view
Yearly view
Outer panel two.
<section class="tabs parent">
<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">
<section class="tabs child">
<header role="tablist" aria-label="Inner tabs">
<label><input type="radio" name="inner" checked /> Week</label>
<label><input type="radio" name="inner" /> Month</label>
<label><input type="radio" name="inner" /> Year</label>
</header>
<div role="tabpanel"><p>Weekly view</p></div>
<div role="tabpanel"><p>Monthly view</p></div>
<div role="tabpanel"><p>Yearly view</p></div>
</section>
</div>
<div role="tabpanel">
<p>Outer panel two.</p>
</div>
</section>
<style>
.tabs {
padding: 1rem;
.parent {
border: 1px solid var(--ui-neutral-100);
}
.child {
background-color: var(--ui-neutral-100);
}
}
</style>