Tabs
Tabs organize related content into panels where only one is visible at a time, such as settings sections, product details, or dashboards.
Try it
Usage
Compose RuiTabList, RuiTab, RuiTabPanels, and RuiTabPanel. Set value to control the active tab.
import { RuiTabs, RuiTabList, RuiTab, RuiTabPanels, RuiTabPanel } from '@ecopages/radiant-ui/tabs';
<RuiTabs value="account" variant="boxed">
<RuiTabList>
<RuiTab id="account">Account</RuiTab>
<RuiTab id="security">Security</RuiTab>
</RuiTabList>
<RuiTabPanels>
<RuiTabPanel id="account">Account settings</RuiTabPanel>
<RuiTabPanel id="security">Security settings</RuiTabPanel>
</RuiTabPanels>
</RuiTabs>Custom markup
<rui-tabs> coordinates any light-DOM tree that matches its query contract. The Rui* helpers stamp these targets; they are not required.
import '@ecopages/radiant-ui/tabs';
<rui-tabs value="account">
<div role="tablist" aria-label="Settings">
<button type="button" role="tab" id="tab-account" data-tab-value="account" aria-controls="panel-account">
Account
</button>
<button type="button" role="tab" id="tab-security" data-tab-value="security" aria-controls="panel-security">
Security
</button>
</div>
<div role="tabpanel" id="panel-account" data-tab-value="account" aria-labelledby="tab-account">
Account settings
</div>
<div role="tabpanel" id="panel-security" data-tab-value="security" aria-labelledby="tab-security" hidden>
Security settings
</div>
</rui-tabs>;Boxed or ghost
boxed tabs have a contained background and work well in settings panels. ghost tabs sit flush with the page header.
Manual activation
Set automatic=\{false} when switching tabs should require an explicit Enter/Space press.
Theming
Tabs map to semantic surface + primary roles, never Tailwind palette steps. Override --rui-tabs-* on rui-tabs. Variants (boxed / ghost) stay; they set layout recipes, not a parallel color API.
| Part / variant | Default | Override |
|---|---|---|
| Tab text | on-background | theme |
| Active indicator | 2px --primary | --rui-tabs-indicator-width, --rui-tabs-indicator-color |
| Tab padding | --space-control-x / --space-control-y | --rui-tabs-tab-padding-x, --rui-tabs-tab-padding-y |
| Boxed card | --background + --border + --rui-tabs-radius | --rui-tabs-radius |
| Boxed tab strip | surface-container-low | theme |
| Panels | --space-inset | --rui-tabs-panel-padding |
| Focus | focus-ring at reduced alpha | theme |
rui-tabs {
--rui-tabs-indicator-color: var(--secondary);
--rui-tabs-panel-padding: var(--space-2);
}Accessibility
- Tab list exposes
role="tablist"witharia-selectedon the active tab. - Arrow keys move between tabs; activation follows the
automaticsetting. - Each panel is labelled by its corresponding tab via
aria-labelledby.
API
RuiTabs is a custom element (<rui-tabs>) coordinating role="tablist" / role="tab" / role="tabpanel" markup authored via the view helpers.
Attributes (<rui-tabs>)
| Attribute | Type | Default | Description |
|---|---|---|---|
variant | ghost ยท boxed | boxed | Visual treatment. |
value | string | '' | Selected tab id; defaults to the first tab. |
label | string | '' | Accessible name for the tab list (prefer aria-label on RuiTabList). |
automatic | boolean | true | Automatic (focus activates) vs manual (Enter/Space) activation. |
Light-DOM contract
| Target | Required | Host writes | Author owns |
|---|---|---|---|
[role="tablist"] | yes | aria-label (from label when absent) | aria-label (preferred) |
[role="tab"] | yes | aria-selected, tabIndex | data-tab-value or id, aria-disabled |
[role="tabpanel"] | yes | hidden, tabIndex when unset | data-tab-value or id |
Do not set aria-selected, tabIndex, or hidden after connect โ the host owns those via resync(). Nested hosts: none.
Events
| Event | Detail | Description |
|---|---|---|
rui-change | { value: string } | Emitted when the selected tab changes. |
Methods
| Method | Description |
|---|---|
resync() | Re-applies tablist labeling and selected/hidden state from value. |
View helpers
| Component | Target stamped | Notes |
|---|---|---|
RuiTabList | [role="tablist"] | Set aria-label here when possible. |
RuiTab | [role="tab"], data-tab-value, id="tab-{id}" | Pairs with RuiTabPanel by id. |
RuiTabPanels | โ | Presentation wrapper only. |
RuiTabPanel | [role="tabpanel"], data-tab-value, id="panel-{id}" | Hidden when not selected. |
CSS classes
Public BEM classes (documented via @cssclass):
| Class | Description |
|---|---|
.rui-tabs__list | role="tablist" strip. |
.rui-tabs__tab | role="tab" button; underline/border per variant. |
.rui-tabs__panels | Panel group. |
.rui-tabs__panel | role="tabpanel"; hidden when not selected. |
Theme roles
| Part | CSS variables consumed |
|---|---|
| Active indicator | --primary |
| Focus ring | --focus-ring |
| Boxed card / panels | --background, --border, --surface-container-low, --shadow-sm, --radius-container, --p-inset |