Tabs

Organize content into switchable panes. Tabs support horizontal and vertical layouts, multiple visual styles (default underline, boxed, pills, bordered), size variants, and fade animations — all CSS-driven with optional JavaScript for pane switching.

Default (Underline)

The default tab style uses an underline indicator on the active tab. Clean and minimal.

Each tab pane can contain any HTML content — text, forms, tables, or cards.

Tab content is lazy-loaded by default: only the active pane is visible until switched.

<!-- Default underline tabs -->
<div class="vd-tabs">
  <ul class="vd-tab-list" role="tablist">
    <li class="vd-tab-item" role="presentation">
      <button class="vd-tab-link is-active" role="tab"
              aria-selected="true" data-tab-target="pane-1">Tab 1</button>
    </li>
    <li class="vd-tab-item" role="presentation">
      <button class="vd-tab-link" role="tab"
              aria-selected="false" data-tab-target="pane-2">Tab 2</button>
    </li>
  </ul>
  <div class="vd-tab-content">
    <div class="vd-tab-pane is-active" id="pane-1" role="tabpanel">...</div>
    <div class="vd-tab-pane" id="pane-2" role="tabpanel">...</div>
  </div>
</div>
Boxed Tabs

Boxed tabs sit inside a rounded container with a subtle background. The active tab gets an elevated card appearance.

Security settings include two-factor authentication, session management, and API keys.

Billing history, upcoming invoices, and payment method management.

<!-- Boxed tabs -->
<div class="vd-tabs vd-tabs-boxed">...</div>

<!-- Pill tabs -->
<div class="vd-tabs vd-tabs-pills">...</div>

<!-- Bordered tabs -->
<div class="vd-tabs vd-tabs-bordered">...</div>
Pills & Bordered

Pills

Pill tabs use a solid background fill for the active state — great for dashboards and toolbars.

Inactive pills blend into the background until hovered or selected.

Bordered

Bordered tabs wrap the entire list in a frame for a more defined look.

Useful when tabs sit inside a card or panel with other content nearby.

Vertical Tabs

Vertical tabs stack the tab list on the left and content on the right — ideal for settings pages and multi-step forms.

Settings include theme, language, password, and connected apps.

Notification preferences for email, push, and in-app alerts.

Payment methods, invoices, and subscription details.

<!-- Vertical tabs -->
<div class="vd-tabs vd-tabs-vertical">...</div>

<!-- Fade animation -->
<div class="vd-tabs vd-tabs-fade">...</div>

<!-- Small / Large tabs -->
<div class="vd-tabs vd-tabs-sm">...</div>
<div class="vd-tabs vd-tabs-lg">...</div>
CSS Variables
VariableDefaultDescription
--vd-tab-padding-x1.3125rem (21px)Horizontal padding (Fibonacci)
--vd-tab-padding-y0.8125rem (13px)Vertical padding (Fibonacci, ratio ~phi)
--vd-tab-padding-x-sm0.8125remSmall variant horizontal
--vd-tab-padding-y-sm0.5remSmall variant vertical
--vd-tab-padding-x-lg2.125rem (34px)Large variant horizontal
--vd-tab-padding-y-lg1.3125rem (21px)Large variant vertical
--vd-tab-border-width2pxActive underline thickness
--vd-tab-textvar(--vd-text-secondary)Inactive tab text color
--vd-tab-text-hovervar(--vd-text-primary)Hover text color
--vd-tab-text-activevar(--vd-color-primary)Active tab text color
--vd-tab-bordervar(--vd-border-color)Tab list bottom border
--vd-tab-border-activevar(--vd-color-primary)Active indicator color
--vd-tab-bg-hovervar(--vd-bg-secondary)Hover background
API Reference

Wiring

import { ref } from 'vue';
import { useTabs } from "@vanduo-oss/vd3";

const root = ref<HTMLElement | null>(null);
useTabs(root);   // wires [data-tab-target] inside root; cleanup on unmount

CSS Classes

ClassDescription
.vd-tabsBase container. Flex column wrapping the tab list and content.
.vd-tab-listHorizontal list of tab buttons. Wraps to multiple lines on small screens.
.vd-tab-itemList item wrapper for each tab button.
.vd-tab-linkThe clickable tab button. Use .is-active for the selected tab.
.vd-tab-link.disabledNon-interactive tab with muted styling and not-allowed cursor.
.vd-tab-contentWrapper around all tab panes with top padding.
.vd-tab-paneIndividual content panel. Use .is-active to show.
.vd-tabs-fadeAdds cross-fade animation when switching panes.
.vd-tabs-boxedRounded pill container with elevated active tab.
.vd-tabs-pillsSolid fill active state on rounded buttons.
.vd-tabs-borderedFramed tab list with border wrapping all tabs.
.vd-tabs-verticalStacks tab list on the left, content on the right (desktop).
.vd-tabs-smCompact tab sizing.
.vd-tabs-lgLarge tab sizing.
.is-activeState class on both the tab link and its matching pane.

Data Attributes

AttributeDescription
data-tab-target="id" On a .vd-tab-link, specifies the pane id to activate.

Composable API

SymbolDescription
useTabs(root)Composable — wires every [data-tab-target] trigger inside the root ref; clicking a tab shows its pane. Call once in setup().
(automatic cleanup)Click listeners are removed on component unmount — no manual teardown.
Accessibility
  • Use role="tablist" on .vd-tab-list, role="tab" on each button, and role="tabpanel" on each pane.
  • Set aria-selected="true" on the active tab and aria-selected="false" on inactive tabs.
  • Connect tabs to panes with aria-controls (pointing to pane id) and aria-labelledby (on the pane pointing to tab id).
  • / arrow keys move focus between tabs when focus is inside the tab list.
  • Home focuses the first tab; End focuses the last.
  • Disabled tabs should have tabindex="-1" and aria-disabled="true".
  • Tab content should be focusable (or contain focusable elements) so keyboard users can reach it.