Noctis

Tabs

Switch between views in place. A strip of tabs sits over swappable panels, and a single highlight slides to the active tab as you move between them.

Variants

Three looks for three contexts. line underlines the active tab against a bottom rule — the quiet default for page-level sections. pill floats a filled neutral pill on bare ground. segmented seats the tabs in a sunken track where the active one rides up as a solid chip — the boxed control for compact toggles. The active chip fills with the bold primary key by default.

Project overview and current status.
Project overview and current status.
Project overview and current status.

Accent color

The active chip defaults to the neutral-on-dark primary key. When a tab strip is itself the page's headline control and you want it to carry the brand, set color="accent" on a segmented strip to fill the active chip with the accent instead. Reach for it sparingly — primary is the right default almost everywhere.

Project overview and current status.

Sizes

Two heights — small and medium — sharing the control type and spacing rhythm.

With icons

Pair a leading glyph with the label to speed recognition. Icons are decorative; the label names the tab.

Controlled

Drive the active tab yourself with value and onValueChange on Tabs.Root — for syncing the selection to the URL, persisting it, or reacting to a change. Omit them (use defaultValue) to let the component own its state.

Active value: activity

Anatomy

Compose tabs from their parts. Tabs.Root owns the active value (controlled via value / onValueChange, or uncontrolled via defaultValue) and the shared variant and size.

  • Tabs.Root — the container. Props: variant (default line), size (default md), color (the segmented active chip's fill — primary default, or accent), plus the Base UI Tabs.Root props (value, defaultValue, onValueChange).
  • Tabs.List — the row of tabs. Give it an aria-label so the strip is named for assistive tech.
  • Tabs.Tab — one tab. Pass a value to match a Panel; takes an optional leading icon and can be disabled.
  • Tabs.Indicator — the single sliding highlight. Render it once inside Tabs.List; it follows the active tab and respects prefers-reduced-motion.
  • Tabs.Panel — the content for one value. Only the active panel is shown.

Every part carries a data-slot (tabs, tabs-list, tabs-tab, tabs-indicator, tabs-panel) for host-side styling — pair it with the Base UI state attributes (data-active, data-highlighted, data-disabled, data-orientation). The strip is fully keyboard-operable — arrow keys move between tabs, Home/End jump to the ends — and RTL-aware.

On surfaces

The same control re-tuned across the elevation scopes — the root canvas, an elevated panel, a menu, and a sunken well. It stays legible on every layer.

root
elevated
menu
sunken

Design tokens

Generated from the component's declaration — the same graph that mints the CSS, so a variable name or its resolution default can't drift. The minted tokens are the public override seam: set one on any ancestor and every tab strip in that region retunes — e.g. .brand { --noctis-tabs-indicator-background-color: var(--noctis-color-accent); } recolors the active indicator. Knobs that aren't minted are reached through the part's data-slot. See Customization for the full override ladder and Tokens for the whole graph.

Token

API reference

Generated from the component's types — every prop, type, default, and description comes straight from the source. Each part gets its own table; parts that only forward to Base UI (List, Indicator, Panel) list just the props they pass through. Expand a row for the full type and description.

Tabs.Root

Prop

Tabs.List

Prop

Tabs.Tab

Prop

Tabs.Indicator

Prop

Tabs.Panel

Prop

AttributeDescription
data-slotThe root tabs element.
data-activePresent on the active tab and its panel.
data-highlightedPresent on the tab the keyboard is focused on.
data-disabledPresent on a disabled tab.
data-orientation`horizontal` | `vertical` — the strip's orientation.