# TabBar

Controlled application navigation with press-and-slide selection and optional glass material.

Page: https://sui.draco.dev/docs/components/tab-bar

TabBar presents the primary destinations of an application with labels and optional icons. Its selected indicator follows the current UI style and uses CSS Gaussian blur in `glass` mode, while only the outer bar receives glass material; content and routing remain under application control.

### Example: tab-bar-demo

```tsx
"use client";
import { GlassProvider } from "@workspace/ui/components/glass";
import { TabBar } from "@workspace/ui/components/tab-bar";
import { BellIcon, HomeIcon, SearchIcon, SettingsIcon } from "lucide-react";
import { useRef, useState } from "react";
import type { ExampleProps } from "../types";

export default function Example({ locale }: ExampleProps) {
  const chinese = locale === "zh-CN";
  const scene = useRef<HTMLDivElement>(null);
  const [value, setValue] = useState("home");
  const items = [
    { value: "home", label: chinese ? "首页" : "Home", icon: <HomeIcon /> },
    {
      value: "search",
      label: chinese ? "搜索" : "Search",
      icon: <SearchIcon />,
    },
    {
      value: "activity",
      label: chinese ? "动态" : "Activity",
      icon: <BellIcon />,
    },
    {
      value: "settings",
      label: chinese ? "设置" : "Settings",
      icon: <SettingsIcon />,
    },
  ];
  const current = items.find((item) => item.value === value);
  return (
    <GlassProvider mode="auto" material="clear" captureTarget={scene}>
      <div
        ref={scene}
        className="relative isolate flex min-h-80 w-full flex-col justify-between overflow-hidden rounded-2xl bg-muted p-6"
      >
        <div
          className="absolute inset-0 -z-10 grid grid-cols-3 gap-3 p-3"
          aria-hidden="true"
        >
          <div className="rounded-2xl bg-blue-400/60" />
          <div className="rounded-2xl bg-emerald-400/60" />
          <div className="rounded-2xl bg-rose-400/60" />
        </div>
        <p className="text-center text-sm" aria-live="polite">
          {chinese ? "当前目的地：" : "Current destination: "}
          {current?.label}
        </p>
        <div className="grid gap-6">
          <div className="grid gap-2">
            <p className="text-center text-sm">
              {chinese ? "默认外观" : "Default appearance"}
            </p>
            <TabBar
              aria-label={
                chinese ? "默认应用导航" : "Default application navigation"
              }
              items={items}
              value={value}
              onValueChange={setValue}
              className="mx-auto w-full max-w-md"
            />
          </div>
          <div className="grid gap-2">
            <p className="text-center text-sm">
              {chinese ? "玻璃模式" : "Glass mode"}
            </p>
            <TabBar
              glass
              aria-label={
                chinese ? "玻璃应用导航" : "Glass application navigation"
              }
              items={items}
              value={value}
              onValueChange={setValue}
              className="mx-auto w-full max-w-md"
            />
          </div>
        </div>
      </div>
    </GlassProvider>
  );
}
```

## Installation

```bash
bunx --bun shadcn@latest add @sui/tab-bar
```

Install with the shadcn CLI or use the shared `@workspace/ui` package. Follow the [installation guide](/docs/installation) to configure the registry, load styles, and choose import aliases.

## Usage

```tsx
import { TabBar } from "@workspace/ui/components/tab-bar";
import { useState } from "react";

export function ApplicationNavigation() {
  const [value, setValue] = useState("home");
  return (
    <TabBar
      aria-label="Application navigation"
      value={value}
      onValueChange={setValue}
      items={[
        { value: "home", label: "Home" },
        { value: "search", label: "Search" },
        { value: "activity", label: "Activity" },
      ]}
    />
  );
}
```

## Navigation and selection

`value` identifies the current destination. `onValueChange` asks the application to select another destination, so it can update a router, preserve a query or anchor, or display its own view. Items must have unique values. A disabled item cannot be selected.

TabBar renders a navigation landmark with buttons and exposes the current destination through `aria-current`. Give the landmark an `aria-label` and each item a clear text label; decorative icons are optional. It does not render content panels. Use [Tabs](/docs/components/tabs) when selecting among panels within a page.

## Keyboard and layout

Arrow keys move among enabled destinations in the bar's orientation. Home and End focus the first and last enabled destinations. Moving focus does not change the current destination; Enter or Space activates the focused button. The current destination participates in the Tab sequence; keyboard activation follows the same change callback as pointer activation. Horizontal layout follows the document direction, including RTL.

The bar can scroll when destinations exceed its available space. Set `orientation="vertical"` to arrange destinations vertically. The moving selection background follows the selected button's current bounds after layout, resizing, or scrolling. Reduced-motion preferences disable animated movement while keeping selection visible.

## Material

TabBar defaults to the existing UI appearance with `glass={false}`. Pass `glass` to apply shared glass material to the outer bar, inheriting [GlassProvider](/docs/components/glass) rendering and material settings. Both modes use a theme-colored selection background; glass mode adds approximately `7px` of CSS Gaussian blur, with icons and labels in the theme primary color. A floating glass lens appears only while holding, reusing the shared material and background capture.

Keep the surrounding container ordinary to avoid nested glass. The example compares both modes using the same controlled destinations and background.

## Press and slide

Hold an enabled item for 250ms or drag beyond 6px to expand the selection into a floating lens and gently enlarge its icon and label. In glass mode the lens uses clear glass with very little tint and approximately `0.6px` blur; otherwise it keeps the theme selection background. Slide while holding to continuously move the lens and position the lens over other enabled items. The resting selection background is hidden while holding. The current item’s theme color and `aria-current` remain unchanged until releasing commits the new selection and restores its background. Releasing outside the bar, cancelling the pointer, or losing window focus restores the original selection. Short clicks and keyboard activation keep their usual behavior, and disabled items remain unavailable. Reduced-motion preferences disable scaling and animated movement.

## API reference

| Prop | Type | Default / behavior |
| --- | --- | --- |
| `items` | `readonly TabBarItem[]` | Destination list. |
| Item `value` | `string` | Unique destination identifier. |
| Item `label` | `string` | Visible destination label. |
| Item `icon` | `ReactNode` | Optional decorative icon. |
| Item `disabled` | `boolean` | Prevents selection. |
| `value` | `string` | Controlled current destination. |
| `onValueChange` | `(value: string) => void` | Called when selecting an enabled destination. |
| `glass` | `boolean` | `false`; optional glass treatment. |
| `size` | `"sm" \| "default" \| "lg"` | `"default"`. |
| `onItemActivate` | `(value: string) => void` | Optional activation notification. |
| `orientation` | `"horizontal" \| "vertical"` | `"horizontal"`. |
| Other props | Native nav props | Includes `aria-label`, `className`, `style`, and `ref`. |

The module exports `TabBar` and its public props and item types. Application routing remains outside the component.
