Skip to content

Tabs <l-tabs>

Tabs organize content into panels, showing one at a time. Progressive enhancement — plain HTML is enhanced with ARIA roles, keyboard navigation, and an animated indicator.

HTML tag<l-tabs>
Native HTML
Progressive
Custom
Shadow DOM
Progressive Custom Element

Options

Enclosed variant

Add variant="enclosed" for a pill-shaped tablist with a sliding background indicator.

Make changes to your account here.
Change your password here.
Manage your notification preferences.
Code
html
<l-tabs variant="enclosed">
  <div>
    <button name="account">Account</button>
    <button name="password">Password</button>
    <button name="notifications">Notifications</button>
  </div>
  <div class="p-4 text-sm text-secondary">Make changes to your account here.</div>
  <div class="p-4 text-sm text-secondary">Change your password here.</div>
  <div class="p-4 text-sm text-secondary">Manage your notification preferences.</div>
</l-tabs>

Line variant

Add variant="line" for a tablist with a sliding underline indicator.

Make changes to your account here.
Change your password here.
Manage your notification preferences.
Code
html
<l-tabs variant="line">
  <div>
    <button name="account">Account</button>
    <button name="password">Password</button>
    <button name="notifications">Notifications</button>
  </div>
  <div class="p-4 text-sm text-secondary">Make changes to your account here.</div>
  <div class="p-4 text-sm text-secondary">Change your password here.</div>
  <div class="p-4 text-sm text-secondary">Manage your notification preferences.</div>
</l-tabs>

Restyle the active underline and the static bottom border with --indicator-color, --indicator-thickness, --track-color and --track-thickness.

High-level overview.
Traffic and engagement analytics.
Exported reports.
Code
html
<l-tabs
  variant="line"
  style="
    --indicator-color: var(--l-color-bg-fill-info-strong);
    --indicator-thickness: 3px;
    --track-color: var(--l-color-divider);
  "
>
  <div>
    <button name="overview">Overview</button>
    <button name="analytics">Analytics</button>
    <button name="reports">Reports</button>
  </div>
  <div class="p-4 text-sm text-secondary">High-level overview.</div>
  <div class="p-4 text-sm text-secondary">Traffic and engagement analytics.</div>
  <div class="p-4 text-sm text-secondary">Exported reports.</div>
</l-tabs>

Full width

Add full-width to stretch tabs across the container.

Make changes to your account here.
Change your password here.
Manage your notification preferences.
Code
html
<l-tabs
  variant="enclosed"
  full-width
>
  <div>
    <button name="account">Account</button>
    <button name="password">Password</button>
    <button name="notifications">Notifications</button>
  </div>
  <div class="p-4 text-sm text-secondary">Make changes to your account here.</div>
  <div class="p-4 text-sm text-secondary">Change your password here.</div>
  <div class="p-4 text-sm text-secondary">Manage your notification preferences.</div>
</l-tabs>

Default active tab

Set value="1" to activate a specific tab on load (0-based index).

Make changes to your account here.
Change your password here.
Manage your notification preferences.
Code
html
<l-tabs
  variant="enclosed"
  value="1"
>
  <div>
    <button name="account">Account</button>
    <button name="password">Password</button>
    <button name="notifications">Notifications</button>
  </div>
  <div class="p-4 text-sm text-secondary">Make changes to your account here.</div>
  <div class="p-4 text-sm text-secondary">Change your password here.</div>
  <div class="p-4 text-sm text-secondary">Manage your notification preferences.</div>
</l-tabs>

Accessibility

Criteria

Role

First child div gets role="tablist", buttons get role="tab", remaining divs get role="tabpanel"

WCAG4.1.2
RGAA7.1
Linked controls

Each tab has aria-controls pointing to its panel; each panel has aria-labelledby pointing back to its tab

WCAG1.3.1
Selection state

Active tab has aria-selected="true"; inactive tabs have aria-selected="false"

WCAG4.1.2
Focus management

Roving tabindex — active tab has tabindex="0", others tabindex="-1". Panels have tabindex="0" for keyboard access

WCAG2.1.1
RGAA12.13
Hidden content

Inactive panels use the hidden attribute

WCAG1.3.2
Motion

Indicator animation respects prefers-reduced-motion

WCAG2.3.3

Keyboard interactions

ArrowRight
Moves focus to the next tab and activates it (horizontal orientation)
ArrowLeft
Moves focus to the previous tab and activates it (horizontal orientation)
ArrowDown
Moves focus to the next tab and activates it (vertical orientation)
ArrowUp
Moves focus to the previous tab and activates it (vertical orientation)
Home
Moves focus to the first tab and activates it
End
Moves focus to the last tab and activates it
Tab
Moves focus out of the tablist to the active panel

API reference

Importing

js
import 'luxen-ui/tabs';
css
@import 'luxen-ui/css/tabs/enclosed';
css
@import 'luxen-ui/css/tabs/line';

Attributes & Properties

variantTabsVariantdefault:'line'Property
Visual variant.
valuestringdefault:'0'Property
Index of the active tab (0-based).
full-widthbooleandefault:falseProperty
Stretch tabs to fill container width.
orientationTabsOrientationdefault:'horizontal'Property
Tab orientation.

Events

changeEvent
Fired when the active tab changes. Detail: { index: number, name: string | null }.

CSS custom properties

--indicator-colordefault:var(--l-color-text-primary)Custom property
line variant: color of the active underline that slides under the selected tab.
--indicator-thicknessdefault:2pxCustom property
line variant: thickness of the active underline.
--track-colordefault:var(--l-color-border)Custom property
line variant: color of the static bottom border the tabs sit on.
--track-thicknessdefault:1pxCustom property
line variant: thickness of the static bottom border.