Navigation
Tabs
Horizontal tabs that show one panel at a time, with manual activation and arrow-key navigation.
| Class name | Type | Description |
|---|---|---|
tabsplf:tabs | Component | The tab list |
tabplf:tab | Part | A tab |
tab-contentplf:tab-content | Part | A tab's panel |
tabs-boxplf:tabs-box | Style | Tabs in a muted box |
tabs-liftplf:tabs-lift | Style | The selected tab joins a bordered panel |
tabs-borderplf:tabs-border | Style | A line under the list, thicker under the selected tab |
<div data-plf-tabs>
<div class="tabs tabs-box" role="tablist" aria-label="Example tabs">
<button class="tab" role="tab" aria-selected="true">One</button>
<button class="tab" role="tab">Two</button>
<button class="tab" role="tab">Three</button>
</div>
<div class="tab-content" role="tabpanel">Content for tab one.</div>
<div class="tab-content" role="tabpanel">Content for tab two.</div>
<div class="tab-content" role="tabpanel">Content for tab three.</div>
</div><div data-plf-tabs>
<div class="plf:tabs plf:tabs-box" role="tablist" aria-label="Example tabs">
<button class="plf:tab" role="tab" aria-selected="true">One</button>
<button class="plf:tab" role="tab">Two</button>
<button class="plf:tab" role="tab">Three</button>
</div>
<div class="plf:tab-content" role="tabpanel">Content for tab one.</div>
<div class="plf:tab-content" role="tabpanel">Content for tab two.</div>
<div class="plf:tab-content" role="tabpanel">Content for tab three.</div>
</div>import { Tab, TabContent, Tabs, TabsList } from '@pelatform/react';
export function Example() {
return (
<Tabs defaultValue="one">
<TabsList variant="box" aria-label="Example tabs">
<Tab value="one">One</Tab>
<Tab value="two">Two</Tab>
<Tab value="three">Three</Tab>
</TabsList>
<TabContent value="one">Content for tab one.</TabContent>
<TabContent value="two">Content for tab two.</TabContent>
<TabContent value="three">Content for tab three.</TabContent>
</Tabs>
);
}import { Tab, TabContent, Tabs, TabsList } from '@pelatform/react/prefixed';
export function Example() {
return (
<Tabs defaultValue="one">
<TabsList variant="box" aria-label="Example tabs">
<Tab value="one">One</Tab>
<Tab value="two">Two</Tab>
<Tab value="three">Three</Tab>
</TabsList>
<TabContent value="one">Content for tab one.</TabContent>
<TabContent value="two">Content for tab two.</TabContent>
<TabContent value="three">Content for tab three.</TabContent>
</Tabs>
);
}The tabs show the panels in order. Tabs are horizontal only: a tab list with aria-orientation="vertical" does not start.
<div data-plf-tabs>
<div class="tabs tabs-lift" role="tablist" aria-label="Lifted tabs">
<button class="tab" role="tab" aria-selected="true">One</button>
<button class="tab" role="tab">Two</button>
<button class="tab" role="tab">Three</button>
</div>
<div class="tab-content" role="tabpanel">Content for tab one.</div>
<div class="tab-content" role="tabpanel">Content for tab two.</div>
<div class="tab-content" role="tabpanel">Content for tab three.</div>
</div><div data-plf-tabs>
<div class="plf:tabs plf:tabs-lift" role="tablist" aria-label="Lifted tabs">
<button class="plf:tab" role="tab" aria-selected="true">One</button>
<button class="plf:tab" role="tab">Two</button>
<button class="plf:tab" role="tab">Three</button>
</div>
<div class="plf:tab-content" role="tabpanel">Content for tab one.</div>
<div class="plf:tab-content" role="tabpanel">Content for tab two.</div>
<div class="plf:tab-content" role="tabpanel">Content for tab three.</div>
</div>import { Tab, TabContent, Tabs, TabsList } from '@pelatform/react';
export function Example() {
return (
<Tabs defaultValue="one">
<TabsList variant="lift" aria-label="Lifted tabs">
<Tab value="one">One</Tab>
<Tab value="two">Two</Tab>
<Tab value="three">Three</Tab>
</TabsList>
<TabContent value="one">Content for tab one.</TabContent>
<TabContent value="two">Content for tab two.</TabContent>
<TabContent value="three">Content for tab three.</TabContent>
</Tabs>
);
}import { Tab, TabContent, Tabs, TabsList } from '@pelatform/react/prefixed';
export function Example() {
return (
<Tabs defaultValue="one">
<TabsList variant="lift" aria-label="Lifted tabs">
<Tab value="one">One</Tab>
<Tab value="two">Two</Tab>
<Tab value="three">Three</Tab>
</TabsList>
<TabContent value="one">Content for tab one.</TabContent>
<TabContent value="two">Content for tab two.</TabContent>
<TabContent value="three">Content for tab three.</TabContent>
</Tabs>
);
}<div data-plf-tabs>
<div class="tabs tabs-border" role="tablist" aria-label="Bordered tabs">
<button class="tab" role="tab" aria-selected="true">One</button>
<button class="tab" role="tab">Two</button>
<button class="tab" role="tab">Three</button>
</div>
<div class="tab-content" role="tabpanel">Content for tab one.</div>
<div class="tab-content" role="tabpanel">Content for tab two.</div>
<div class="tab-content" role="tabpanel">Content for tab three.</div>
</div><div data-plf-tabs>
<div class="plf:tabs plf:tabs-border" role="tablist" aria-label="Bordered tabs">
<button class="plf:tab" role="tab" aria-selected="true">One</button>
<button class="plf:tab" role="tab">Two</button>
<button class="plf:tab" role="tab">Three</button>
</div>
<div class="plf:tab-content" role="tabpanel">Content for tab one.</div>
<div class="plf:tab-content" role="tabpanel">Content for tab two.</div>
<div class="plf:tab-content" role="tabpanel">Content for tab three.</div>
</div>import { Tab, TabContent, Tabs, TabsList } from '@pelatform/react';
export function Example() {
return (
<Tabs defaultValue="one">
<TabsList variant="border" aria-label="Bordered tabs">
<Tab value="one">One</Tab>
<Tab value="two">Two</Tab>
<Tab value="three">Three</Tab>
</TabsList>
<TabContent value="one">Content for tab one.</TabContent>
<TabContent value="two">Content for tab two.</TabContent>
<TabContent value="three">Content for tab three.</TabContent>
</Tabs>
);
}import { Tab, TabContent, Tabs, TabsList } from '@pelatform/react/prefixed';
export function Example() {
return (
<Tabs defaultValue="one">
<TabsList variant="border" aria-label="Bordered tabs">
<Tab value="one">One</Tab>
<Tab value="two">Two</Tab>
<Tab value="three">Three</Tab>
</TabsList>
<TabContent value="one">Content for tab one.</TabContent>
<TabContent value="two">Content for tab two.</TabContent>
<TabContent value="three">Content for tab three.</TabContent>
</Tabs>
);
}Disabled
At least one tab must stay enabled.
<div data-plf-tabs>
<div class="tabs tabs-box" role="tablist" aria-label="Tabs with a disabled tab">
<button class="tab" role="tab" aria-selected="true">Active</button>
<button class="tab" role="tab">Enabled</button>
<button class="tab" role="tab" disabled>Disabled</button>
</div>
<div class="tab-content" role="tabpanel">Content for the active tab.</div>
<div class="tab-content" role="tabpanel">Content for the enabled tab.</div>
<div class="tab-content" role="tabpanel">Content for the disabled tab.</div>
</div><div data-plf-tabs>
<div class="plf:tabs plf:tabs-box" role="tablist" aria-label="Tabs with a disabled tab">
<button class="plf:tab" role="tab" aria-selected="true">Active</button>
<button class="plf:tab" role="tab">Enabled</button>
<button class="plf:tab" role="tab" disabled>Disabled</button>
</div>
<div class="plf:tab-content" role="tabpanel">Content for the active tab.</div>
<div class="plf:tab-content" role="tabpanel">Content for the enabled tab.</div>
<div class="plf:tab-content" role="tabpanel">Content for the disabled tab.</div>
</div>import { Tab, TabContent, Tabs, TabsList } from '@pelatform/react';
export function Example() {
return (
<Tabs defaultValue="active">
<TabsList variant="box" aria-label="Tabs with a disabled tab">
<Tab value="active">Active</Tab>
<Tab value="enabled">Enabled</Tab>
<Tab value="disabled" disabled>Disabled</Tab>
</TabsList>
<TabContent value="active">Content for the active tab.</TabContent>
<TabContent value="enabled">Content for the enabled tab.</TabContent>
<TabContent value="disabled">Content for the disabled tab.</TabContent>
</Tabs>
);
}import { Tab, TabContent, Tabs, TabsList } from '@pelatform/react/prefixed';
export function Example() {
return (
<Tabs defaultValue="active">
<TabsList variant="box" aria-label="Tabs with a disabled tab">
<Tab value="active">Active</Tab>
<Tab value="enabled">Enabled</Tab>
<Tab value="disabled" disabled>Disabled</Tab>
</TabsList>
<TabContent value="active">Content for the active tab.</TabContent>
<TabContent value="enabled">Content for the enabled tab.</TabContent>
<TabContent value="disabled">Content for the disabled tab.</TabContent>
</Tabs>
);
}Selected from code
The script opens the Billing tab from a button outside the tabs, with select() and the panel’s id.
Your card expires this month
Update it to keep your plan active.
<div class="flex flex-col gap-4">
<div class="alert alert-warning">
<div class="alert-body">
<div class="alert-content">
<h3 class="alert-title">Your card expires this month</h3>
<p class="alert-description">Update it to keep your plan active.</p>
</div>
</div>
<button class="alert-action" id="tabs-update-card">Update card</button>
</div>
<div data-plf-tabs id="tabs-account">
<div class="tabs tabs-box" role="tablist" aria-label="Account settings">
<button class="tab" role="tab" aria-selected="true">Profile</button>
<button class="tab" role="tab">Billing</button>
</div>
<div class="tab-content" role="tabpanel">Name, email and avatar.</div>
<div class="tab-content" role="tabpanel" id="tabs-billing">Payment method and invoices.</div>
</div>
</div>
<script>
document.getElementById('tabs-update-card').addEventListener('click', () => {
PLF.Tabs.getInstance(document.getElementById('tabs-account')).select('tabs-billing');
});
</script><div class="plf:flex plf:flex-col plf:gap-4">
<div class="plf:alert plf:alert-warning">
<div class="plf:alert-body">
<div class="plf:alert-content">
<h3 class="plf:alert-title">Your card expires this month</h3>
<p class="plf:alert-description">Update it to keep your plan active.</p>
</div>
</div>
<button class="plf:alert-action" id="tabs-update-card">Update card</button>
</div>
<div data-plf-tabs id="tabs-account">
<div class="plf:tabs plf:tabs-box" role="tablist" aria-label="Account settings">
<button class="plf:tab" role="tab" aria-selected="true">Profile</button>
<button class="plf:tab" role="tab">Billing</button>
</div>
<div class="plf:tab-content" role="tabpanel">Name, email and avatar.</div>
<div class="plf:tab-content" role="tabpanel" id="tabs-billing">Payment method and invoices.</div>
</div>
</div>
<script>
document.getElementById('tabs-update-card').addEventListener('click', () => {
PLF.Tabs.getInstance(document.getElementById('tabs-account')).select('tabs-billing');
});
</script>'use client';
import { useState } from 'react';
import { Alert, AlertAction, AlertBody, AlertContent, AlertDescription, AlertTitle, Tab, TabContent, Tabs, TabsList } from '@pelatform/react';
export function Example() {
const [tab, setTab] = useState('profile');
return (
<div className="flex flex-col gap-4">
<Alert color="warning">
<AlertBody>
<AlertContent>
<AlertTitle>Your card expires this month</AlertTitle>
<AlertDescription>Update it to keep your plan active.</AlertDescription>
</AlertContent>
</AlertBody>
<AlertAction onClick={() => setTab('billing')}>Update card</AlertAction>
</Alert>
<Tabs value={tab} onValueChange={setTab}>
<TabsList variant="box" aria-label="Account settings">
<Tab value="profile">Profile</Tab>
<Tab value="billing">Billing</Tab>
</TabsList>
<TabContent value="profile">Name, email and avatar.</TabContent>
<TabContent value="billing">Payment method and invoices.</TabContent>
</Tabs>
</div>
);
}'use client';
import { useState } from 'react';
import { Alert, AlertAction, AlertBody, AlertContent, AlertDescription, AlertTitle, Tab, TabContent, Tabs, TabsList } from '@pelatform/react/prefixed';
export function Example() {
const [tab, setTab] = useState('profile');
return (
<div className="plf:flex plf:flex-col plf:gap-4">
<Alert color="warning">
<AlertBody>
<AlertContent>
<AlertTitle>Your card expires this month</AlertTitle>
<AlertDescription>Update it to keep your plan active.</AlertDescription>
</AlertContent>
</AlertBody>
<AlertAction onClick={() => setTab('billing')}>Update card</AlertAction>
</Alert>
<Tabs value={tab} onValueChange={setTab}>
<TabsList variant="box" aria-label="Account settings">
<Tab value="profile">Profile</Tab>
<Tab value="billing">Billing</Tab>
</TabsList>
<TabContent value="profile">Name, email and avatar.</TabContent>
<TabContent value="billing">Payment method and invoices.</TabContent>
</Tabs>
</div>
);
}JavaScript
Tabs needs JavaScript. The Full script (pelatform-ui.min.js) starts every component on page load, with no code of your own; with ESM imports, call init() yourself, as in Installation. For markup you add later, see Dynamic content.
<link rel="stylesheet" href="pelatform-ui.min.css">
<script src="pelatform-ui.min.js" defer></script><link rel="stylesheet" href="core/pelatform-ui-core.min.css">
<link rel="stylesheet" href="components/alert.min.css">
<link rel="stylesheet" href="components/tabs.min.css">
<script src="core/pelatform-ui-core.min.js" defer></script>
<script src="components/tabs.min.js" defer></script>
<script>document.addEventListener('DOMContentLoaded', () => PLF.init());</script><link rel="stylesheet" href="pelatform-ui.prefixed.min.css">
<script src="pelatform-ui.min.js" defer></script><link rel="stylesheet" href="core/pelatform-ui-core.prefixed.min.css">
<link rel="stylesheet" href="components/alert.prefixed.min.css">
<link rel="stylesheet" href="components/tabs.prefixed.min.css">
<script src="core/pelatform-ui-core.min.js" defer></script>
<script src="components/tabs.min.js" defer></script>
<script>document.addEventListener('DOMContentLoaded', () => PLF.init());</script>| Attribute | Purpose |
|---|---|
data-plf-tabs |
Marks the container |
aria-controls |
On a tab, its panel’s id; leave it off every tab to show the panels in order |
aria-selected="true" |
The tab selected at first; otherwise the first enabled tab |
disabled, aria-disabled="true" |
A tab that cannot be selected |
The script sets aria-selected, the tabs’ tabindex, hidden on inactive panels and each panel’s aria-labelledby; it adds the ids and aria-controls the markup leaves out, and tabindex="0" on a panel without a focusable element.
Methods
| Method | Behavior |
|---|---|
PLF. |
Returns the instance, creating it for valid markup |
PLF. |
Returns the instance or null |
instance. |
Selects the tab of the panel with this id, which you write on a panel you select from code. Returns false when canceled or when the tab is disabled, selected or unknown |
instance. |
Removes listeners and shows every panel again |
| Event | When |
|---|---|
plf: |
Before a tab is selected; cancelable |
plf: |
After a tab is selected |
Each event’s detail:
| Property | Value |
|---|---|
instance |
The Tabs instance |
reason |
api, trigger or keyboard |
trigger |
The tab that selected the panel, or null from a method |
previousPanelId |
The id of the panel shown before the change |
panelId |
The id of the panel the change selects |
| Key | Action |
|---|---|
| Left Arrow, Right Arrow | Move focus to the previous or next enabled tab, wrapping; reversed right to left |
| Home, End | Move focus to the first or last enabled tab |
| Enter, Space | Selects the focused tab |
| Tab | Leaves the tab list for the visible panel |
React
Tabs from @pelatform/react renders these tabs and their panels, with the runtime’s keyboard: it keeps its own state, or the application sets it with value, and each part is a component of its own.
| Prop | Values | Default |
|---|---|---|
value |
The selected tab’s value, which the application changes in onValueChange |
Its own state |
defaultValue |
The value of the tab selected at first, when the application does not set value |
Required without value |
onValueChange |
(value), called when a click, Enter or Space selects another tab; keep value to stay on the tab |
None |
| Part | Element |
|---|---|
TabsList |
A div with tabs, the tab list; variant is box, lift or border |
Tab |
A button with tab, type="button", whose value, unique in its Tabs, pairs it with its panel |
TabContent |
A div with tab-content, the panel of the Tab with the same value; tabindex="0" brings Tab to it, and a panel that starts with a control can take tabIndex={-1} |
The other props are the element’s own, className and ref included; Tabs writes the ids that pair each tab with its panel.
| Variable | Default |
|---|---|
--plf-tabs-list-bg |
Muted surface |
--plf-tabs-height |
36px |
--plf-tabs-padding-inline |
12px |
--plf-tabs-radius |
Medium radius (6px) |
--plf-tabs-selected-bg |
Field color; the surface on tabs-lift; none on tabs-border |
--plf-tabs-selected-color |
Text, and the selected tab’s line on tabs-border |
--plf-tabs-lift-border |
Subtle border, around the selected tab and panel of tabs-lift |
--plf-tabs-border-color |
Subtle border, the line under the list of tabs-border |