Tabs
An items-driven tab list built on Reka UI's Tabs primitive.
Usage
<script setup lang="ts">
const active = ref('one')
</script>
<template>
<STabs v-model="active" :items="[{ label: 'One', value: 'one' }, { label: 'Two', value: 'two' }]">
<template #one>
Tab one content
</template>
<template #two>
Tab two content
</template>
</STabs>
</template>
Switching tabs slides a shared indicator between them rather than each tab carrying its own static active state - the indicator measures the active trigger itself, so it stays correct even if a tab's own width changes.
Custom label content
The label slot replaces a tab's plain-text label with anything - scoped
with item and index, so a single template can vary per tab:
<STabs v-model="active" :items="items">
<template #label="{ item }">
<span class="flex items-center gap-2">
{{ item.label }}
<SBadge v-if="item.value === 'inbox'" label="3" color="primary" size="sm" />
</span>
</template>
...
</STabs>
Icons
Give an item an icon and it renders before the label automatically - no
label slot needed for the common case:
<STabs
v-model="active"
:items="[
{ label: 'Profile', value: 'profile', icon: 'hugeicons:user' },
{ label: 'Settings', value: 'settings', icon: 'hugeicons:settings-01' },
]"
/>
Variants
variant takes underline (default) or pill, a segmented-control look
with the sliding indicator as a raised background instead of a bottom bar:
<STabs v-model="active" :items="items" variant="pill" />
Scrollable
Set scrollable when a tab list may outgrow its container. Previous and next
chevrons appear only while the labels overflow. The list also supports touch,
trackpad, and keyboard navigation, and brings the selected tab into view:
<STabs v-model="active" :items="items" scrollable />
The controls use neutral ghost SButtons by default. Use ui.scrollButton
and ui.scrollIcon for styling, or the scroll-left-icon and
scroll-right-icon slots to replace only the chevrons. For a different control,
replace scroll-left or scroll-right and bind the supplied buttonProps so
the button keeps its click handler, disabled state, type, and accessible label:
<STabs :items="items" scrollable>
<template #scroll-right="{ buttonProps }">
<MyIconButton v-bind="buttonProps" />
</template>
</STabs>
The full button slots also expose scroll, disabled, and ariaLabel for
custom controls that cannot use v-bind="buttonProps".
Orientation
Set orientation="vertical" to place the tab list beside its content. Both
underline and pill variants work vertically, and Reka changes arrow-key
navigation to Up/Down. scrollable applies only to horizontal tabs.
<STabs v-model="active" :items="items" orientation="vertical" variant="pill" />
Keep inactive panels mounted
Inactive panel content is unmounted by default. Set unmount-on-hide to
false to keep its local state, such as an unfinished form, while the panel
is hidden:
<STabs :items="items" :unmount-on-hide="false">
<!-- panel slots -->
</STabs>
In markdown
Every Selaras component is already globally registered under its S-prefixed
name - a bare ::s-tabs{...} block works with no extra wiring (see
Callout's own "In markdown" section for how
the pattern works generally). items is an array prop, not a string - bind
it with a leading : so @nuxtjs/mdc parses the attribute as an expression
instead of literal text. Each tab's own content is a #slot-name line
matching that item's value:
::s-tabs{:items='[{"label":"One","value":"one"},{"label":"Two","value":"two"}]'}
#one
Content for the first tab.
#two
Content for the second tab.
::
Custom :ui
Global and scoped recipe overrides use ui.tabs.
To see exactly what you'd be overriding - the current default classes for
every slot and variant - here's Tabs's own theme file:
Props
| Prop | Type | Default |
|---|---|---|
items | { label: string; value?: string; disabled?: boolean; icon?: string }[] | - |
variant | 'underline' | 'pill' | 'underline' |
orientation | 'horizontal' | 'vertical' | 'horizontal' |
scrollable | boolean | false |
unmountOnHide | boolean | true |
modelValue | string | - |
ui | Partial<Record<'root' | 'scrollRoot' | 'scrollViewport' | 'scrollButton' | 'scrollIcon' | 'list' | 'trigger' | 'icon' | 'indicator' | 'content', string | object>> | - |
Events
| Event | Payload | Description |
|---|---|---|
update:modelValue | string | Fires when the active tab changes |
Slots
| Slot | Props | Description |
|---|---|---|
[item.value] | - | Content for that tab, one named slot per item |
label | { item, index } | Replaces a tab's label content |
scroll-left-icon, scroll-right-icon | { class } | Replaces a default control's chevron |
scroll-left, scroll-right | { buttonProps, scroll, disabled, ariaLabel } | Replaces an entire overflow control |