Navigation · astro-ui · Tabs

TabsJS

WAI-ARIA tab switcher — line, pill, and card variants.

Tabs implements the WAI-ARIA tabs pattern: SSR renders the list with the initial tab pre-selected, panels hide via the hidden attribute, and a small inline script wires click + arrow-key navigation with automatic activation. Without JS everything stays visible and meaningful.

Features

  • WAI-ARIA tabs pattern: roving tabindex, automatic activation
  • Arrow keys, Home/End navigation; RTL-aware
  • 3 variants: line (underline), pill, card (segmented)
  • 5 sizes, horizontal or vertical orientation
  • SSR pre-selection; panels visible without JS
  • Automatic tab/panel id + aria wiring

Usage

---
import { Tabs } from "astro-ui";
---

<Tabs id="settings" ariaLabel="Settings" defaultValue="billing"
  items={[
    { value: "account", label: "Account" },
    { value: "billing", label: "Billing" },
  ]}>
  <div data-tab="account">Account panel.</div>
  <div data-tab="billing">Billing panel.</div>
</Tabs>

Demos

Line variant

Account panel.
Billing panel.
<Tabs ariaLabel="Settings" defaultValue="billing"
  items={[{ value: "account", label: "Account" }, { value: "billing", label: "Billing" }]}>
  <div data-tab="account">Account panel.</div>
  <div data-tab="billing">Billing panel.</div>
</Tabs>

Card variant

Panel one.
Panel two.
<Tabs variant="card" ariaLabel="View"
  items={[{ value: "preview", label: "Preview" }, { value: "code", label: "Code" }]}>
  <div data-tab="preview">…</div>
  <div data-tab="code">…</div>
</Tabs>

Vertical

General settings.
Security settings.
<Tabs orientation="vertical" ariaLabel="Settings"
  items={[{ value: "general", label: "General" }, { value: "security", label: "Security" }]}>
  <div data-tab="general">…</div>
  <div data-tab="security">…</div>
</Tabs>

API

Props

PropTypeDefaultDescription
itemsTabItem[][]Tabs in display order: { value, label, disabled? }.
defaultValuestringfirst enabledInitially selected tab.
variant"line" | "pill" | "card""line"Visual style.
size"xs" | "sm" | "md" | "lg" | "xl""md"Scale.
orientation"horizontal" | "vertical""horizontal"Layout direction.
ariaLabelstring—Accessible name for the tab list.
idstringrecommendedPrefixes generated tab/panel ids.
classstring—Merged via cn(); Tailwind classes welcome.
idstring—Element id.

Slots

SlotDescription
defaultPanel elements, each carrying data-tab="<value>".

Accessibility

Implemented accessibility features — semantics, ARIA wiring, and keyboard behavior that ship with the component.

  • Full ARIA tabs pattern: role="tablist" / tab / tabpanel
  • aria-selected, aria-controls, and aria-labelledby wire the relationships
  • Arrow keys move between tabs with roving tabindex (script)

FAQ

Does this this work without JS?

The tab list and all panels render server-side; without JS every panel stays visible so content is never lost.

Multiple groups on one page?

Give each a unique id — generated tab/panel ids derive from it.

Changelog

Version history mentioning Tabs.

v0.1.0 — 2026-11-11
added
  • Navigation (7): Tabs, Breadcrumb, Pagination, Stepper, Tree, Navbar, Sidebar.
  • Native-first interactive behavior: Dialog, AlertDialog, and Sheet on <dialog>; Accordion and Collapsible on <details>; Tabs, Menu, Tree, and Combobox with WAI-ARIA roles and scoped keyboard scripts.