Glide tabs
Tabs with a pill that has mass. The edge moving toward the new tab leads on a spring and the far edge trails it, so the pill stretches across the gap and snaps back. A second set of labels rides on the pill and is clipped to it, so each label changes colour exactly where the pill's edge crosses it. Panels slide the way the pill went, and the height eases between them.
Three deploys today. The last one went out at 14:02 and is serving all traffic.
Installation
npx @neelshha/ui@latest add glide-tabsUsage
import { GlideTabs } from "@/components/ui/glide-tabs";
<GlideTabs
aria-label="Project"
tabs={[
{ value: "overview", label: "Overview", content: <Overview /> },
{ value: "activity", label: "Activity", content: <Activity /> },
{ value: "settings", label: "Settings", content: <Settings /> },
]}
/>Tones
Leave out content for a segmented control. The ink pill makes the colour change at its edge easiest to see.
<GlideTabs aria-label="Range" tabs={range} defaultValue="week" />
<GlideTabs
aria-label="Range"
tabs={range}
defaultValue="week"
tone="ink"
/>
<GlideTabs
aria-label="Range"
tabs={range}
defaultValue="week"
tone="key"
/>With icons
Labels are any content. An icon goes on both copies of the label, so it changes colour with the text.
<GlideTabs
aria-label="View"
tone="ink"
tabs={[
{ value: "list", label: <><List />List</> },
{ value: "grid", label: <><LayoutGrid />Grid</> },
{ value: "board", label: <><Columns3 />Board</> },
]}
/>Keyboard
The list follows the WAI-ARIA tabs pattern. Tab moves into the list and on to the panel. The arrow keys move and select, and wrap at the ends. Home and End jump to the first and last tab. Disabled tabs are skipped.
API
Other props go to the outer div. With reduced motion, the pill and the panels change at once.
| Prop | Type | Default | Description |
|---|---|---|---|
tabs | GlideTab[] | Each tab has a value and a label, and can have content and disabled. With no content on any tab, it is a segmented control. | |
defaultValue / value | string | Uncontrolled default, or controlled value. Defaults to the first enabled tab. | |
onValueChange | (value: string) => void | Fires with the next value on click and on the arrow keys. | |
tone | "chrome" | "ink" | "key" | "chrome" | The pill's material: silver chrome, solid ink, or the blue key. |
aria-label | string | Names the tab list. Use aria-labelledby to point at a visible heading instead. |