Skip to content

@vibe-labs/design-vue-tabs

Vue 3 tab components for the Vibe Design System. Compound component architecture with configurable panel mount strategies, keyboard navigation, and full ARIA tab pattern.

Installation

ts
import { VibeTabs, VibeTabList, VibeTab, VibeTabPanels, VibeTabPanel } from "@vibe-labs/design-vue-tabs";

Requires the CSS layer from @vibe-labs/design-components-tabs.

Components

VibeTabs

Root container that provides shared context to all tab children via inject.

Usage

vue
<!-- Basic (uncontrolled) -->
<VibeTabs default-value="overview">
  <VibeTabList>
    <VibeTab name="overview">Overview</VibeTab>
    <VibeTab name="specs">Specifications</VibeTab>
    <VibeTab name="reviews">Reviews</VibeTab>
  </VibeTabList>
  <VibeTabPanels>
    <VibeTabPanel name="overview">Overview content</VibeTabPanel>
    <VibeTabPanel name="specs">Specs content</VibeTabPanel>
    <VibeTabPanel name="reviews">Reviews content</VibeTabPanel>
  </VibeTabPanels>
</VibeTabs>

<!-- Controlled -->
<VibeTabs v-model="activeTab">
  <VibeTabList>
    <VibeTab name="one">Tab 1</VibeTab>
    <VibeTab name="two">Tab 2</VibeTab>
  </VibeTabList>
  <VibeTabPanels>
    <VibeTabPanel name="one">Content 1</VibeTabPanel>
    <VibeTabPanel name="two">Content 2</VibeTabPanel>
  </VibeTabPanels>
</VibeTabs>

<!-- Mount strategies -->
<VibeTabs mount-strategy="lazy">...</VibeTabs>
<!-- Mount on first activation, keep (default) -->
<VibeTabs mount-strategy="eager">...</VibeTabs>
<!-- Mount all immediately -->
<VibeTabs mount-strategy="unmount">...</VibeTabs>
<!-- Unmount when deactivated -->

Props

PropTypeDefaultDescription
modelValuestringActive tab name (v-model)
defaultValuestringfirst tabDefault tab when uncontrolled
mountStrategyTabMountStrategy"lazy"Panel mount/unmount behaviour

Events

EventPayloadDescription
update:modelValuestringActive tab changed
changestringActive tab changed

VibeTabList

Horizontal tab bar with keyboard navigation and variant styling.

vue
<!-- Underline (default) -->
<VibeTabList>
  <VibeTab name="a">Tab A</VibeTab>
  <VibeTab name="b">Tab B</VibeTab>
</VibeTabList>

<!-- Variants -->
<VibeTabList variant="pills">...</VibeTabList>
<VibeTabList variant="underline">...</VibeTabList>

<!-- Scrollable (overflow) -->
<VibeTabList scrollable>...</VibeTabList>

<!-- Full width (stretch to fill) -->
<VibeTabList full>...</VibeTabList>

Props

PropTypeDefaultDescription
variantTabsVariantVisual style
scrollablebooleanfalseHorizontal scroll on overflow
fullbooleanfalseStretch tabs to fill width
ariaLabelstringAccessible label for the tablist

VibeTab

Individual tab button. Auto-registers with the parent VibeTabs on mount.

vue
<!-- Basic -->
<VibeTab name="settings">Settings</VibeTab>

<!-- With icon -->
<VibeTab name="settings">
  <template #icon><SettingsIcon /></template>
  Settings
</VibeTab>

<!-- With badge -->
<VibeTab name="notifications" badge="3">Notifications</VibeTab>

<!-- Badge slot -->
<VibeTab name="notifications">
  Notifications
  <template #badge><VibeBadgeCount :count="unread" /></template>
</VibeTab>

<!-- Disabled -->
<VibeTab name="admin" disabled>Admin</VibeTab>

Props

PropTypeDefaultDescription
namestringrequiredUnique identifier (must match a panel)
disabledbooleanfalseDisabled state
badgestringBadge text

Slots

SlotDescription
defaultTab label
iconLeading icon
badgeTrailing badge content

VibeTabPanels

Wrapper for tab panels. Thin container — no logic, just layout.

vue
<VibeTabPanels>
  <VibeTabPanel name="one">...</VibeTabPanel>
  <VibeTabPanel name="two">...</VibeTabPanel>
</VibeTabPanels>

VibeTabPanel

Content panel that mounts/shows based on the active tab and mount strategy.

vue
<VibeTabPanel name="settings">
  <h2>Settings</h2>
  <p>Panel content here.</p>
</VibeTabPanel>

Props

PropTypeDefaultDescription
namestringrequiredMust match a VibeTab's name

Mount Strategies

StrategyBehaviourUse When
lazy (default)Mount on first activation, stay mountedMost cases — preserves state, avoids upfront cost
eagerAll panels mounted immediately, toggled with v-showSEO, or panels need to initialise in background
unmountMount on activation, unmount on deactivationFresh state needed each time (e.g. forms that should reset)

Keyboard Navigation

KeyAction
ArrowRight / ArrowDownNext tab (skips disabled, wraps)
ArrowLeft / ArrowUpPrevious tab (skips disabled, wraps)
HomeFirst enabled tab
EndLast enabled tab

Focus follows selection — selecting a tab via keyboard also moves focus to it.

ARIA

The implementation follows the WAI-ARIA Tabs Pattern:

  • VibeTabListrole="tablist"
  • VibeTabrole="tab", aria-selected, aria-controls, roving tabindex
  • VibeTabPanelrole="tabpanel", aria-labelledby
  • IDs auto-generated with unique prefix per VibeTabs instance

Dependencies

PackagePurpose
@vibe-labs/design-components-tabsCSS tokens + generated styles

Build

bash
npm run build

Built with Vite + vite-plugin-dts. Outputs ES module with TypeScript declarations.