Skip to content
ghostui

Preview

Draft a shorter onboarding flow.

Start with one goal. Ask for workspace details after the first useful result.

Design guidance

Use tabs for peer views of one subject. Use links for navigation to another page. Keep labels short and name the list. Arrow keys move focus; Enter or Space selects. Set activateOnFocus on the list only when switching panels is immediate.

Group width and item width are independent. A full-width group can keep content-sized items, and a content-sized group can give each item the width of the longest label. Vertical items always share a width and align their content to the start.

Installation

Install

Basic usage

Structures

Match each tab's value with its panel's value. Content and selection policy belong to the caller. Tabs is also callable as the root. The default list supplies hover feedback and the selected underline; add Tabs.Indicator only when a custom list render needs Base UI's measured marker.

Examples

Styling

Segmented

Use a contained switcher for a compact group of peer views. SharedBackground moves a card-colored hover surface over the rail; only the selected item has an inset border.

Underline

Keep the selected underline visible while a separate line follows hover or focus. There is no line across the whole list and no gap between items.

Ghost

Use a quiet selected surface without a surrounding rail.

Sizes

All styles use 2px container padding and regular text. Base items are at least 28px high; small items are at least 24px. Their single-row containers are 32px and 28px high, matching base and small Buttons. Touch targets grow to 44px items and 48px containers. Wrapping labels can increase height.

Base

Small

Width

Content items · content group

Both widths follow their content. This is the default.

Equal items · content group

Each item matches the longest label without filling the parent.

Content items · full group

The group fills its parent while items retain their own widths.

Equal items · full group

Items divide the available group width equally. Long labels wrap rather than disappear.

Small screens

Scrollable

Keep labels at their content width and let the list scroll horizontally. The scrollbar stays hidden. Arrow-key focus brings each tab into view; selection still uses Enter or Space.

Move workspace setup after the first useful result.

Select

Below 640px, replace the list with Select. Both controls share one selected value, so the current panel stays open when the viewport changes. The breakpoint belongs to this example, not the Tabs API.

This composition also needs Select.

Move workspace setup after the first useful result.

Orientation

Vertical

Items share the list width, with labels aligned to the start. Arrow Up and Arrow Down move focus. In a full-width vertical layout, the list and panel share available space.

Draft a shorter onboarding flow.

Start with one goal. Ask for workspace details after the first useful result.

Special use case

Timed background / auto-playing tabs

Paint elapsed time directly on the active tab's background. useProgressTimer from Progress supplies elapsed time. The caller advances selection and resets the shared timer with the selected value. This example pauses on hover, focus, a hidden page, or Pause autoplay. Reduced motion turns autoplay off.

This composition also needs Progress and Button.

Move workspace setup after the first useful result.

Autoplay is off because reduced motion is enabled.

Panel content

Panels accept caller-owned content. Give panels a consistent responsive width to prevent the group from moving when their content changes. Keep the visible panel connected to its tab and preserve useful headings, lists, and actions inside the panel.

Move workspace setup after the first useful result.

API reference

Root

List

Tab

Panel and Indicator

Tabs.Panel matches a tab by value and accepts children, native attributes, state styling, render, and a forwarded ref. Base UI controls panel mounting and visibility. keepMounted preserves inactive panel DOM when needed.

Tabs.Indicator exposes Base UI's active-tab geometry for custom list renders. The default underline list already draws its selected line and suppresses a duplicate explicit Indicator. Panels, refs, right-to-left direction, focus looping, and selection callbacks retain Base UI Tabs behavior.

Arrow keys move focus among enabled tabs. Enter or Space selects. Changing width, style, or progress does not change these semantics. Autoplay examples need a pause control and must not move keyboard focus when they advance.

useProgressTimer({ active, duration, paused, resetKey, onComplete }) is exported from @/components/ui/progress. It returns elapsed percent (0–100). active defaults to true, duration to 5000ms, and paused to false. Changing resetKey or duration, or reactivating the timer, starts a new interval. Completion runs once per interval; pause preserves elapsed time.