icon

Morainev0.5.0

Tabs
navigationtabs

Tabs

Tabbed navigation component with configurable orientation and variant styles.

Import#

import { Tabs } from 'moraine'

Slot Structure#

Tab list with triggers and associated content panels.

root
├── list
│ ├── indicator (optional)
│ └── trigger (×n)
│ ├── leading (Icon, optional)
│ └── label (optional)
└── content (×n)

Examples#

Variants#

Pill and link visual variants for the tab list.

function Variants() {
return (
<div class="flex flex-col gap-8 w-xl">
<Tabs
defaultValue="overview"
variant="pill"
items={[
{
label: 'Overview',
value: 'overview',
icon: 'i-lucide:layout-dashboard',
content: <p class="text-sm text-foreground">Overview panel content.</p>,
},
{
label: 'Settings',
value: 'settings',
icon: 'i-lucide:settings',
content: <p class="text-sm text-foreground">Settings panel content.</p>,
},
{
label: 'Billing',
value: 'billing',
icon: 'i-lucide:credit-card',
content: <p class="text-sm text-foreground">Billing panel content.</p>,
},
]}
/>
<Tabs
defaultValue="settings"
variant="link"
items={[
{
label: 'Overview',
value: 'overview',
icon: 'i-lucide:layout-dashboard',
content: <p class="text-sm text-foreground">Overview panel content.</p>,
},
{
label: 'Settings',
value: 'settings',
icon: 'i-lucide:settings',
content: <p class="text-sm text-foreground">Settings panel content.</p>,
},
{
label: 'Billing',
value: 'billing',
icon: 'i-lucide:credit-card',
content: <p class="text-sm text-foreground">Billing panel content.</p>,
},
]}
/>
</div>
)
}

Sizes#

Trigger size scale from xs to xl.

function Sizes() {
const SIZE_OPTIONS = ['xs', 'sm', 'md', 'lg', 'xl'] as const
return (
<div class="w-xl space-y-4">
<For each={SIZE_OPTIONS}>
{(size) => (
<div class="space-y-1">
<p class="text-xs text-muted-foreground tracking-wide uppercase">{size}</p>
<Tabs
size={size}
defaultValue="overview"
items={[
{
label: 'Overview',
value: 'overview',
content: <p class="text-sm text-foreground">Overview</p>,
},
{
label: 'Metrics',
value: 'metrics',
content: <p class="text-sm text-foreground">Metrics</p>,
},
{
label: 'Activity',
value: 'activity',
content: <p class="text-sm text-foreground">Activity</p>,
},
]}
/>
</div>
)}
</For>
</div>
)
}

Orientations#

Horizontal and vertical orientation with the same items contract.

function Orientations() {
return (
<div class="flex gap-8 w-2xl">
<Tabs
defaultValue="overview"
orientation="horizontal"
items={[
{
label: 'Overview',
value: 'overview',
icon: 'i-lucide:layout-dashboard',
content: <p class="text-sm text-foreground">Overview panel content.</p>,
},
{
label: 'Settings',
value: 'settings',
icon: 'i-lucide:settings',
content: <p class="text-sm text-foreground">Settings panel content.</p>,
},
{
label: 'Billing',
value: 'billing',
icon: 'i-lucide:credit-card',
content: <p class="text-sm text-foreground">Billing panel content.</p>,
},
]}
/>
<Tabs
defaultValue="settings"
orientation="vertical"
classes={{
root: 'max-w-md',
list: 'w-40',
}}
items={[
{
label: 'Overview',
value: 'overview',
icon: 'i-lucide:layout-dashboard',
content: <p class="text-sm text-foreground w-sm">Overview panel content.</p>,
},
{
label: 'Settings',
value: 'settings',
icon: 'i-lucide:settings',
content: <p class="text-sm text-foreground w-sm">Settings panel content.</p>,
},
{
label: 'Billing',
value: 'billing',
icon: 'i-lucide:credit-card',
content: <p class="text-sm text-foreground w-sm">Billing panel content.</p>,
},
]}
/>
</div>
)
}

Controlled + Disabled Items#

Controlled value with disabled tab options and external navigation controls.

function ControlledDisabledItems() {
const [value, setValue] = createSignal('overview')
return (
<div class="space-y-3">
<div class="flex flex-wrap gap-2">
<Button size="sm" variant="outline" onClick={() => setValue('overview')}>
Go overview
</Button>
<Button size="sm" variant="outline" onClick={() => setValue('settings')}>
Go settings
</Button>
<Button size="sm" variant="secondary" onClick={() => setValue('billing')}>
Try disabled billing
</Button>
</div>
<Tabs
value={value()}
onChange={setValue}
variant="link"
items={[
{
label: 'Overview',
value: 'overview',
content: <p class="text-sm text-foreground">Overview section with release status.</p>,
},
{
label: 'Settings',
value: 'settings',
content: (
<p class="text-sm text-foreground">Settings section with environment options.</p>
),
},
{
label: 'Billing (Disabled)',
value: 'billing',
disabled: true,
content: <p class="text-sm text-foreground">This panel is intentionally disabled.</p>,
},
]}
/>
<p class="text-xs text-muted-foreground">Current tab value: {value()}</p>
</div>
)
}

API Reference#

Attributes#

Slotroot1 attribute
Tabs container that owns tab selection and panel rendering.

Data Attributes

1
Data AttributeDescription
data-orientation
Stores the rendered orientation.

Props#

PropTypeDefaultDescription
activationMode"manual" | "automatic" | undefinedautomatic
Whether keyboard navigation activates the tab immediately or waits for confirmation.
classClassValue
Class applied to the component root or trigger element.
classesTabsT.Classes | undefined
defaultValuestring | undefined
Default active tab value for uncontrolled usage.
disabledboolean | undefinedfalse
Whether the tab list is disabled.
idstring | undefined
Unique identifier for the tabs root element.
itemsTabsT.Item[] | undefined
Array of tabs to display.
keyboardLoopboolean | undefinedtrue
Whether arrow-key navigation wraps from the ends.
onChange((value: string) => void) | undefined
Callback when the active tab changes.
orientation"horizontal" | "vertical" | undefinedhorizontal
The orientation of the tab list.
refJSX.HTMLElementTags["div"] extends { ref?: infer Ref; } ? Ref : never | undefined
size"xs" | "sm" | "md" | "lg" | "xl" | undefined
styleJSX.CSSProperties | undefined
stylesTabsT.Styles | undefined
valuestring | undefined
Controlled active tab value.
variant"link" | "pill" | undefined

Items#

An individual tab in the tabs component.
PropTypeDefaultDescription
contentJSX.Element
Content to display when the tab is active.
disabledboolean | undefinedfalse
Whether the tab is disabled.
iconIconT.Name
Icon to display next to the label.
labelJSX.Element
Label to display on the tab trigger.
valuestring | undefinedindex of the item
Unique value for the tab.