Tabs
A set of layered panels where only one is visible at a time.
Usage
<div id="mcr:tabs:demo" data-orientation="horizontal">
<div role="tablist" aria-orientation="horizontal">
<button
role="tab"
id="mct:tabs:t1"
aria-selected="true"
aria-controls="mcc:tabs:t1"
tabindex="0"
>
Overview
</button>
<button
role="tab"
id="mct:tabs:t2"
aria-selected="false"
aria-controls="mcc:tabs:t2"
tabindex="-1"
>
Features
</button>
</div>
<div
role="tabpanel"
id="mcc:tabs:t1"
aria-labelledby="mct:tabs:t1"
aria-hidden="false"
tabindex="0"
>
Overview content...
</div>
<div
role="tabpanel"
id="mcc:tabs:t2"
aria-labelledby="mct:tabs:t2"
aria-hidden="true"
hidden="until-found"
tabindex="-1"
>
Features content...
</div>
</div>Examples
Default Tab
Specify which tab is selected initially.
<div id="mcr:tabs:default" data-orientation="horizontal">
<div role="tablist" aria-orientation="horizontal">
<button
role="tab"
id="mct:tabs:d1"
aria-selected="false"
aria-controls="mcc:tabs:d1"
tabindex="-1"
>
First
</button>
<button
role="tab"
id="mct:tabs:d2"
aria-selected="true"
aria-controls="mcc:tabs:d2"
tabindex="0"
>
Second (Default)
</button>
</div>
<div
role="tabpanel"
id="mcc:tabs:d1"
aria-labelledby="mct:tabs:d1"
aria-hidden="true"
hidden="until-found"
tabindex="-1"
>
First tab content
</div>
<div
role="tabpanel"
id="mcc:tabs:d2"
aria-labelledby="mct:tabs:d2"
aria-hidden="false"
tabindex="0"
>
This tab is selected by default
</div>
</div>Vertical Orientation
Use vertical layout for side navigation.
<div id="mcr:tabs:vertical" data-orientation="vertical">
<div role="tablist" aria-orientation="vertical">
<button
role="tab"
id="mct:tabs:v1"
aria-selected="true"
aria-controls="mcc:tabs:v1"
tabindex="0"
>
Account
</button>
<button
role="tab"
id="mct:tabs:v2"
aria-selected="false"
aria-controls="mcc:tabs:v2"
tabindex="-1"
>
Security
</button>
</div>
<div
role="tabpanel"
id="mcc:tabs:v1"
aria-labelledby="mct:tabs:v1"
aria-hidden="false"
tabindex="0"
>
Account settings...
</div>
<div
role="tabpanel"
id="mcc:tabs:v2"
aria-labelledby="mct:tabs:v2"
aria-hidden="true"
hidden="until-found"
tabindex="-1"
>
Security options...
</div>
</div>Disabled Tabs
Disabled tabs cannot be selected and are skipped during keyboard navigation.
<div id="mcr:tabs:settings" data-orientation="horizontal">
<div role="tablist" aria-orientation="horizontal">
<button
role="tab"
id="mct:tabs:s1"
aria-selected="true"
aria-controls="mcc:tabs:s1"
tabindex="0"
>
Profile
</button>
<button
role="tab"
id="mct:tabs:s2"
aria-selected="false"
aria-controls="mcc:tabs:s2"
tabindex="-1"
aria-disabled="true"
>
Billing (Locked)
</button>
<button
role="tab"
id="mct:tabs:s3"
aria-selected="false"
aria-controls="mcc:tabs:s3"
tabindex="-1"
>
Notifications
</button>
</div>
<div
role="tabpanel"
id="mcc:tabs:s1"
aria-labelledby="mct:tabs:s1"
aria-hidden="false"
tabindex="0"
>
Update your profile...
</div>
<div
role="tabpanel"
id="mcc:tabs:s2"
aria-labelledby="mct:tabs:s2"
aria-hidden="true"
hidden="until-found"
tabindex="-1"
>
Billing settings...
</div>
<div
role="tabpanel"
id="mcc:tabs:s3"
aria-labelledby="mct:tabs:s3"
aria-hidden="true"
hidden="until-found"
tabindex="-1"
>
Notification preferences...
</div>
</div>Non-Focusable Panels
By default, panels are focusable so keyboard users always have somewhere to land after selecting a tab. When panels contain meaningful focusable elements like buttons or links, this can be turned off by setting focusable to false.
<div id="mcr:tabs:focusable" data-orientation="horizontal">
<div role="tablist" aria-orientation="horizontal">
<button
role="tab"
id="mct:tabs:f1"
aria-selected="true"
aria-controls="mcc:tabs:f1"
tabindex="0"
>
Focusable (default)
</button>
<button
role="tab"
id="mct:tabs:f2"
aria-selected="false"
aria-controls="mcc:tabs:f2"
tabindex="-1"
>
Non-Focusable
</button>
</div>
<div
role="tabpanel"
id="mcc:tabs:f1"
aria-labelledby="mct:tabs:f1"
aria-hidden="false"
tabindex="0"
>
<button>Save</button>
<button>Delete</button>
</div>
<div
role="tabpanel"
id="mcc:tabs:f2"
aria-labelledby="mct:tabs:f2"
aria-hidden="true"
hidden="until-found"
>
<button>Save</button>
<button>Delete</button>
</div>
</div>Accessibility
Follows the WAI-ARIA Tabs Pattern.
Keyboard
Horizontal
| Key | Action |
|---|---|
Arrow Right | Move focus to the next tab |
Arrow Left | Move focus to the previous tab |
Home | Move focus to the first tab |
End | Move focus to the last tab |
Vertical
| Key | Action |
|---|---|
Arrow Down | Move focus to the next tab |
Arrow Up | Move focus to the previous tab |
Home | Move focus to the first tab |
End | Move focus to the last tab |
Disabled tabs are skipped during keyboard navigation. Hidden panels use hidden="until-found" so content remains searchable via browser find-in-page (Ctrl+F).
API Reference
JavaScript
| Element | Attributes | Description |
|---|---|---|
| Container | id="mcr:tabs:*" | Root ID (required) |
data-orientation | "horizontal" or "vertical" | |
| Tablist | role="tablist", aria-orientation | Container for tab buttons |
| Tab | role="tab" | Tab button |
id="mct:tabs:*" | Tab ID (required) | |
aria-selected | "true" or "false" | |
aria-controls | Panel element ID | |
tabindex | 0 for selected, -1 for others | |
aria-disabled="true" | For disabled tabs | |
| Panel | role="tabpanel" | Tab content panel |
id="mcc:tabs:*" | Panel ID (required) | |
aria-labelledby | Tab trigger ID | |
aria-hidden | "true" or "false" | |
hidden="until-found" | Set on hidden panels, removed when visible | |
tabindex | 0 for visible, -1 for hidden. Omit if the panel contains focusable content. |
Frameworks
All components accept className (React) or standard attributes (Vue) for styling. The Vue API is identical - use kebab-case in templates for multi-word props (e.g., default-value instead of defaultValue).
Tabs.Root
| Prop | Type | Default | Description |
|---|---|---|---|
defaultValue | string | Required | Initially selected tab value |
orientation | "horizontal" | "vertical" | "horizontal" | Layout direction |
Tabs.List
Container for the tab triggers.
Tabs.Tab
| Prop | Type | Default | Description |
|---|---|---|---|
value | string | Required | Links this trigger to a panel |
disabled | boolean | false | Whether the tab is disabled (cannot be selected) |
Tabs.Panel
| Prop | Type | Default | Description |
|---|---|---|---|
value | string | Required | Links this panel to a trigger |
focusable | boolean | true | Set to false if the panel contains focusable content |