Skip to content
EMBOSS
Docs menu

Tabs

Layered panels switched by a flush rail of triggers — the active tab seats into the rail like a latched key.

@emboss/tabs
Input gain, phase, and high-pass filtering.

Installation

pnpm dlx shadcn@latest add @emboss/tabs

Usage

example.tsx
import {
  Tabs,
  TabsContent,
  TabsList,
  TabsTrigger,
} from "@/components/ui/tabs";

export function Example() {
  return (
    <Tabs defaultValue="one">
      <TabsList>
        <TabsTrigger value="one">One</TabsTrigger>
        <TabsTrigger value="two">Two</TabsTrigger>
      </TabsList>
      <TabsContent value="one">First panel</TabsContent>
      <TabsContent value="two">Second panel</TabsContent>
    </Tabs>
  );
}

API reference

Tabs

The root. Provides value state to the rail and panels.

PropTypeDefaultDescription
value / defaultValue / onValueChangestring / string / (value) => voidControlled and uncontrolled selection.

TabsList

The recessed rail that houses the triggers.

PropTypeDefaultDescription
activateOnFocusbooleantrueWhether arrow-key focus also selects the tab. Emboss defaults this on.

TabsTrigger

PropTypeDefaultDescription
valuestringThe tab this trigger activates.
Data attributeDescription
data-activePresent on the active trigger.

TabsContent

PropTypeDefaultDescription
valuestringThe tab this panel belongs to.

Keyboard

KeysAction
ArrowLeftArrowRightMoves focus and selection along the rail.
HomeSelects the first tab.
EndSelects the last tab.

Source

View source — tabs.tsx
tabs.tsx
"use client";

import { Tabs as BaseTabs } from "@base-ui/react/tabs";
import { cn } from "@/lib/utils";

/**
 * Layered panels switched by a recessed rail of triggers. The active tab
 * rises out of the rail like a key sitting proud in a milled slot.
 *
 * @example
 * <Tabs defaultValue="cli">
 *   <TabsList>
 *     <TabsTrigger value="cli">CLI</TabsTrigger>
 *     <TabsTrigger value="manual">Manual</TabsTrigger>
 *   </TabsList>
 *   <TabsContent value="cli">…</TabsContent>
 *   <TabsContent value="manual">…</TabsContent>
 * </Tabs>
 */
function Tabs({
  className,
  ...props
}: React.ComponentProps<typeof BaseTabs.Root>) {
  return (
    <BaseTabs.Root
      data-slot="tabs"
      className={cn("flex flex-col gap-3", className)}
      {...props}
    />
  );
}

function TabsList({
  className,
  ...props
}: React.ComponentProps<typeof BaseTabs.List>) {
  return (
    <BaseTabs.List
      data-slot="tabs-list"
      activateOnFocus
      className={cn(
        "inline-flex w-fit items-center gap-1 rounded-md bg-well p-1 shadow-deboss-1",
        className,
      )}
      {...props}
    />
  );
}

function TabsTrigger({
  className,
  ...props
}: React.ComponentProps<typeof BaseTabs.Tab>) {
  return (
    <BaseTabs.Tab
      data-slot="tabs-trigger"
      className={cn(
        "inline-flex h-7 cursor-pointer items-center justify-center rounded-sm px-3 text-sm font-medium whitespace-nowrap text-ink-muted focus-ring transition-[box-shadow,background-color,color] duration-(--duration-settle) ease-(--ease-settle) hover:text-ink data-active:bg-surface-2 data-active:text-ink data-active:shadow-emboss-1 data-disabled:pointer-events-none data-disabled:opacity-50",
        className,
      )}
      {...props}
    />
  );
}

function TabsContent({
  className,
  ...props
}: React.ComponentProps<typeof BaseTabs.Panel>) {
  return (
    <BaseTabs.Panel
      data-slot="tabs-content"
      className={cn("flex-1 outline-none", className)}
      {...props}
    />
  );
}

export { Tabs, TabsList, TabsTrigger, TabsContent };