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
| Variable | Default | Description |
|---|---|---|
--vd-tab-padding-x | 1.3125rem (21px) | Horizontal padding (Fibonacci) |
--vd-tab-padding-y | 0.8125rem (13px) | Vertical padding (Fibonacci, ratio ~phi) |
--vd-tab-padding-x-sm | 0.8125rem | Small variant horizontal |
--vd-tab-padding-y-sm | 0.5rem | Small variant vertical |
--vd-tab-padding-x-lg | 2.125rem (34px) | Large variant horizontal |
--vd-tab-padding-y-lg | 1.3125rem (21px) | Large variant vertical |
--vd-tab-border-width | 2px | Active underline thickness |
--vd-tab-text | var(--vd-text-secondary) | Inactive tab text color |
--vd-tab-text-hover | var(--vd-text-primary) | Hover text color |
--vd-tab-text-active | var(--vd-color-primary) | Active tab text color |
--vd-tab-border | var(--vd-border-color) | Tab list bottom border |
--vd-tab-border-active | var(--vd-color-primary) | Active indicator color |
--vd-tab-bg-hover | var(--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 unmountCSS Classes
| Class | Description |
|---|---|
.vd-tabs | Base container. Flex column wrapping the tab list and content. |
.vd-tab-list | Horizontal list of tab buttons. Wraps to multiple lines on small screens. |
.vd-tab-item | List item wrapper for each tab button. |
.vd-tab-link | The clickable tab button. Use .is-active for the selected tab. |
.vd-tab-link.disabled | Non-interactive tab with muted styling and not-allowed cursor. |
.vd-tab-content | Wrapper around all tab panes with top padding. |
.vd-tab-pane | Individual content panel. Use .is-active to show. |
.vd-tabs-fade | Adds cross-fade animation when switching panes. |
.vd-tabs-boxed | Rounded pill container with elevated active tab. |
.vd-tabs-pills | Solid fill active state on rounded buttons. |
.vd-tabs-bordered | Framed tab list with border wrapping all tabs. |
.vd-tabs-vertical | Stacks tab list on the left, content on the right (desktop). |
.vd-tabs-sm | Compact tab sizing. |
.vd-tabs-lg | Large tab sizing. |
.is-active | State class on both the tab link and its matching pane. |
Data Attributes
| Attribute | Description |
|---|---|
data-tab-target="id" | On a .vd-tab-link, specifies the pane id to activate. |
Composable API
| Symbol | Description |
|---|---|
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, androle="tabpanel"on each pane. - Set
aria-selected="true"on the active tab andaria-selected="false"on inactive tabs. - Connect tabs to panes with
aria-controls(pointing to paneid) andaria-labelledby(on the pane pointing to tabid). - ← / → 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"andaria-disabled="true". - Tab content should be focusable (or contain focusable elements) so keyboard users can reach it.