Tabset
An in-page tab set with no JavaScript and no IDs. Inside a div class="tabset",
a label wrapping a radio is a tab, and the element right after it is that
tab's content. The radios share a name, so only one is ever selected, and the stylesheet
shows that tab's content and hides the rest.
A tab set
The element straight after a tab holds its content. It drops onto its own row below the strip and takes the panel surface, so the tab and its content read as one unit. Pick another tab and the stylesheet swaps which content shows. The radios carry the state, so it holds without a line of script. Every tab follows the same shape: a label with a radio, then the block of content it reveals.
Overview
The content of the first tab.
Rules
The content of the second tab.
Log
Mark one radio checked and that tab opens on load.
<div class="tabset">
<label><input type="radio" name="tabs-basic" checked> Overview</label>
<div>
<h3>Overview</h3>
<p>The content of the first tab.</p>
</div>
<label><input type="radio" name="tabs-basic"> Rules</label>
<div>
<h3>Rules</h3>
<p>The content of the second tab.</p>
</div>
<label><input type="radio" name="tabs-basic"> Log</label>
<div>
<h3>Log</h3>
<p>Mark one radio <code>checked</code> and that tab opens on load.</p>
</div>
</div>
The content is any element
The content carries no class, so it is whatever element the content needs: a
div of mixed content, a bare p, a table, a
form. The stylesheet only cares that it sits right after the tab.
A single paragraph can be the whole content, with no wrapper around it.
| Login | Group |
|---|---|
| ada | Editors |
| grace | Viewers |
<div class="tabset">
<label><input type="radio" name="tabs-el" checked> Summary</label>
<p>A single paragraph can be the whole content, with no wrapper around it.</p>
<label><input type="radio" name="tabs-el"> People</label>
<div>
<table>
<thead><tr><th>Login</th><th>Group</th></tr></thead>
<tbody>
<tr><td>ada</td><td>Editors</td></tr>
<tr><td>grace</td><td>Viewers</td></tr>
</tbody>
</table>
</div>
<label><input type="radio" name="tabs-el"> Actions</label>
<div class="buttons">
<button>Save</button>
<button class="secondary">Duplicate</button>
<button class="ghost">Delete</button>
</div>
</div>
Reading the selection
The tabs are a radio group, so a script reads the chosen tab from the checked
radio. Give each radio a value and listen for change.
This radio's value is overview.
This radio's value is rules.
<div class="tabset">
<label><input type="radio" name="view" value="overview" checked> Overview</label>
<p>This radio's value is <code>overview</code>.</p>
<label><input type="radio" name="view" value="rules"> Rules</label>
<p>This radio's value is <code>rules</code>.</p>
</div>
const tabs = document.querySelector('.tabset');
tabs.addEventListener('change', e => console.log(e.target.value));
tabs.querySelector('input:checked').value; // the current tab
Nested tabs
Tabs nest: put a second set inside a tab's content and it runs on its own, because
the child combinator keeps each set's rules to its own direct children. The one thing
to settle is grouping. Radios group by a shared name within a form, so wrap a set in a
form and every set on the page, outer and inner, can reuse the one name.
A form cannot nest, so the outer set stays a div and each
inner set is the form. The inner sets below are vertical.
Overview
Rules
<div class="tabset">
<label><input type="radio" name="tab" checked> Overview</label>
<div>
<h3>Overview</h3>
<form class="tabset vertical">
<label><input type="radio" name="tab" checked> Details</label>
<div><p>The inner tabs stack down the left, their content beside them.</p></div>
<label><input type="radio" name="tab"> History</label>
<div><p>Selecting an inner tab leaves the outer tab untouched.</p></div>
</form>
</div>
<label><input type="radio" name="tab"> Rules</label>
<div>
<h3>Rules</h3>
<form class="tabset vertical">
<label><input type="radio" name="tab" checked> Access</label>
<div><p>A separate form, so its selection is independent of the set above.</p></div>
<label><input type="radio" name="tab"> Limits</label>
<div><p>One constant name still serves every set on the page.</p></div>
</form>
</div>
</div>