Overview content.
Activity content.
Settings content.
Tabs shows one of several related panels in the same space. It is authored ARIA markup plus a small init() module for keyboard behavior, with two variants, two sizes, two orientations, and a CSS-only links form.
Write the full ARIA markup on the server, with the first panel visible. init() adds only behavior.
<div data-bitsy-tabs>
<div data-bitsy-tabs-list role="tablist" aria-label="Project">
<button data-bitsy-tabs-tab role="tab" type="button" id="tab-overview" aria-controls="panel-overview" aria-selected="true">Overview</button>
<button data-bitsy-tabs-tab role="tab" type="button" id="tab-activity" aria-controls="panel-activity" aria-selected="false" tabindex="-1">Activity</button>
</div>
<div data-bitsy-tabs-panel role="tabpanel" id="panel-overview" aria-labelledby="tab-overview" tabindex="0">…</div>
<div data-bitsy-tabs-panel role="tabpanel" id="panel-activity" aria-labelledby="tab-activity" tabindex="0" hidden="until-found">…</div>
</div>
<script type="module">
import { init } from "@bitsyui/bits/tabs/init";
init();
</script> data-bitsy-tabs; parts data-bitsy-tabs-list, -tab, and -panel.data-bitsy-variant="segmented", data-bitsy-size="sm", and data-bitsy-tabs-activation="manual" go on the root; aria-orientation="vertical" goes on the list.hidden="until-found", so nothing flashes and find-in-page can still reveal them.aria-label, and keep interactive controls out of tabs.init() fires bitsy:tabs-change on the root with the tab and panel in detail.The selected state is the ARIA state, so what you see matches what assistive tech hears.
Overview content.
Activity content.
Settings content.
Overview content.
Activity content.
Settings content.
Overview content.
Activity content.
Settings content.
Overview content.
Activity content.
Settings content.
A vertical list sits beside the panels. A horizontal list that is too wide scrolls on its own and fades at the clipped edge.
Overview content.
Activity content.
Settings content.
Overview content.
Activity content.
Deployments content.
Analytics content.
Integrations content.
Members content.
Billing content.
Audit log content.
Settings content.
For navigation that changes the URL, use links styled as tabs. No roles, no panels, no script, and the normal Tab order.
<nav data-bitsy-tabs-list aria-label="Settings">
<a data-bitsy-tabs-tab href="/settings/profile" aria-current="page">Profile</a>
<a data-bitsy-tabs-tab href="/settings/billing">Billing</a>
<a data-bitsy-tabs-tab href="/settings/team">Team</a>
</nav> aria-disabled="true") can be focused but not selected.init() twice is safe, and it returns a cleanup function.The foundation styles button and a but has no tab list. data-bitsy-tabs resets that chrome on its own tabs and adds the variants, sizes, and states, all read from ARIA. The keyboard model needs a script because the platform has no tabs element yet.