@surstromming/tabs

Switch between panels of content.
281
Install
npm install @surstromming/tabs
Latest Version:0.1.0
Vue:3
License:MIT
Last Updated:Oct 17, 2026
Links: GitHub  ·  npm
Maintainer: evgeniy_mnatsakanov

@surstromming/tabs

Switch between panels of content. Data-driven tab list; each panel is a slot named after its value.

Dependency graph

graph LR
  tabs["@surstromming/tabs"]
  design["@surstromming/design"]
  tabs --> design

Usage

<template>
  <Tabs v-model="tab" :tabs="tabs">
    <template #account>Account settings go here.</template>
    <template #password>Change your password here.</template>
  </Tabs>
</template>

<script setup lang="ts">
import { ref } from 'vue'
import { Tabs, type TabItem } from '@surstromming/tabs'

const tab = ref('account')
const tabs: TabItem[] = [
  { label: 'Account', value: 'account' },
  { label: 'Password', value: 'password' },
  { label: 'Billing', value: 'billing', disabled: true },
]
</script>

Props

Prop Type Default Notes
v-model string — (required) The active tab's value
tabs TabItem[] — (required) { label, value, disabled? }
variant solid | line solid solid = pill in a trough; line = underline
orientation horizontal | vertical horizontal Vertical stacks the list beside the panel

orientation also swaps the arrow keys: ←/→ for a row, ↑/↓ for a column.

Slots

One named slot per tab, matching its value (#account, #password…). Only the active panel is rendered — a hidden panel costs nothing, and its onMounted doesn't fire until you actually open it.

Anatomy

div
  ├─ div.list[role=tablist]
  │    └─ button.tab[role=tab]     // .isActive
  └─ div.panel[role=tabpanel]      // <slot :name="model" />

Keyboard

Key Action
Tab Enter/leave the tab list — one stop, not one per tab
← / → (or ↑ / ↓ when vertical) Move to the previous/next tab (wraps, skips disabled)

That's a roving tabindex: the active tab has tabindex="0", the rest -1. Without it, a 6-tab bar would cost a keyboard user 6 presses to walk past.

Tokens

muted list background with a background-colored active pill, muted-foreground → foreground text, radius(md) / radius(sm), 3px ring focus ring.

Tokens

solid — muted trough, background active pill with shadow(sm) (dark: input border + fill). line — no trough, a 2px primary underline (a side-line when vertical) under the active tab. Both: muted-foreground → foreground text, 3px ring focus ring.

Notes

Selecting a tab moves focus to it (standard for automatic-activation tabs). The tab state is a plain v-model — put it in the URL yourself if the tab should survive a reload.

Related Packages

@surstromming/popover

Panel anchored under a trigger; closes on outside click and Escape.

1,481 0
@surstromming/dialog

Modal dialog, with an alert-dialog mode and sheets on the screen's edges.

1,382 0
@surstromming/design

Everything related to design system, including colors, typography, and spacing.

1,363 0
@surstromming/scroll-area

A scroll container with an Edge-style overlay scrollbar.

1,157 0

Version History

Version Released Vue License
0.1.0 ^3.4.0 MIT