Tabs Component — Astro

Accessible tabs component with keyboard navigation and ARIA tab pattern

Tabs Component

An accessible tabs component for organizing content into multiple panels. Supports full keyboard navigation and follows ARIA tab pattern best practices.

Props

  • tabs (array, required) - Array of tab objects with id and label properties
  • id (string, optional) - Unique identifier for the tabs component
  • defaultTab (string, optional) - ID of the tab to show by default (defaults to first tab)
  • variant (string, optional) - Visual variant: 'default', 'pills', or 'underline' (default: 'default')
  • class (string, optional) - Additional CSS classes

Tab Object Structure

  • id (string, required) - Unique identifier for the tab
  • label (string, required) - Display text for the tab button
  • content (string, optional) - HTML content for the tab panel (alternative to using slots)

Basic Usage

Provide panel content as children in the same order as the tabs array:

Live Example
Overview Features Pricing
astro astro
---
import Tabs from '../../components/Tabs.astro';
---

<Tabs
  tabs={[
    { id: 'overview', label: 'Overview' },
    { id: 'features', label: 'Features' },
    { id: 'pricing', label: 'Pricing' },
  ]}
>
  <div>
    <h4>Overview</h4>
    <p>Overview content here</p>
  </div>
  <div>
    <h4>Features</h4>
    <p>Features content here</p>
  </div>
  <div>
    <h4>Pricing</h4>
    <p>Pricing content here</p>
  </div>
</Tabs>

With Content Property

You can also provide content directly in the tab object:

Live Example
Tab 1 Tab 2 Tab 3

Content for tab 1

astro astro
<Tabs
  tabs={[
    { id: 'tab1', label: 'Tab 1', content: '<p>Content for tab 1</p>' },
    { id: 'tab2', label: 'Tab 2', content: '<p>Content for tab 2</p>' },
    { id: 'tab3', label: 'Tab 3', content: '<p>Content for tab 3</p>' },
  ]}
/>

Variants

Default Variant

Live Example
Tab 1 Tab 2 Tab 3

Pills Variant

Live Example
Tab 1 Tab 2 Tab 3

Underline Variant

Live Example
Tab 1 Tab 2 Tab 3

Default Tab

You can specify which tab should be active by default:

Live Example
First Second Third
astro astro
<Tabs
  defaultTab="second"
  tabs={[
    { id: 'first', label: 'First' },
    { id: 'second', label: 'Second' },
    { id: 'third', label: 'Third' },
  ]}
>
  <div>First tab content</div>
  <div>Second tab content</div>
  <div>Third tab content</div>
</Tabs>

Features

  • ARIA Tab Pattern - Full ARIA support with role="tablist", role="tab", role="tabpanel", aria-selected, aria-controls, and aria-labelledby
  • Keyboard Navigation - Arrow keys (Left/Right or Up/Down), Home, End, Enter, and Space for activation
  • Three Variants - Default (border indicator), Pills (filled background), and Underline (thicker border)
  • Theme-Aware - Automatically adapts to all 14 available themes
  • Responsive - Horizontal scrolling on mobile for many tabs
  • Accessible - WCAG AA compliant with proper focus indicators

Keyboard Navigation

  • Arrow Right / Arrow Down - Move to next tab
  • Arrow Left / Arrow Up - Move to previous tab
  • Home - Move to first tab
  • End - Move to last tab
  • Enter / Space - Activate focused tab
  • Tab - Move focus to tab panel content

Accessibility

  • ARIA Attributes - Proper role, aria-selected, aria-controls, aria-labelledby, and aria-hidden attributes
  • Focus Management - Only active tab is in tab order (tabindex="0"), others are tabindex="-1"
  • Screen Reader Support - Proper announcements when tabs change
  • Focus Indicators - Visible focus outline using --accent color

Svelte & Vanilla: Svelte ¡ Vanilla: same HTML and BEM as in Usage above; add minimal JS for tab switch and keyboard.