{
  "schemaVersion": 1,
  "name": "tabs",
  "useFor": "Use for related views such as plans, FAQ topics or item details. Give each TabsTrigger a matching TabsContent. Use glide={false} when supplying a custom selected state.",
  "constraints": [
    "Tabs for actions",
    "Underlined tabs (except the install box)",
    "Pill-shaped tabs",
    "A blue key as the selected tab"
  ],
  "documentation": "",
  "source": "opencommerce-agentcard-web src/components/nav-pills.tsx; src/app/globals.css (.nav-tray, .nav-link, .nav-lit, .nav-lit-shadow)",
  "exports": [
    "Tabs",
    "TabsList",
    "TabsTrigger",
    "TabsContent",
    "tabsTriggerVariants"
  ],
  "client": true,
  "docsUrl": "https://opencommerce-ds.vercel.app/components/tabs",
  "markdownUrl": "https://opencommerce-ds.vercel.app/r/tabs.md",
  "examples": [
    {
      "path": "examples/tabs.tsx",
      "content": "import { Tabs, TabsContent, TabsList, TabsTrigger } from \"@/components/ui/tabs\";\n\nconst PLANS = [\n  { value: \"search\", label: \"Search\", body: \"Your agent searches any store's catalog conversationally and gets back products it can buy.\" },\n  { value: \"checkout\", label: \"Checkout\", body: \"One call places the order, with the customer's stored card, at any online store.\" },\n  { value: \"tracking\", label: \"Tracking\", body: \"Follow every order from confirmation to delivery and report back to the user.\" },\n];\n\nexport function Site() {\n  return (\n    <Tabs defaultValue=\"search\" className=\"max-w-170\">\n      <TabsList>\n        {PLANS.map((plan) => (\n          <TabsTrigger key={plan.value} value={plan.value}>\n            {plan.label}\n          </TabsTrigger>\n        ))}\n      </TabsList>\n      {PLANS.map((plan) => (\n        <TabsContent key={plan.value} value={plan.value} className=\"text-body text-fg-muted\">\n          {plan.body}\n        </TabsContent>\n      ))}\n    </Tabs>\n  );\n}\n\nexport function Topics() {\n  return (\n    <div className=\"grid gap-6\">\n      <Tabs defaultValue=\"general\">\n        <TabsList>\n          <TabsTrigger value=\"general\">General</TabsTrigger>\n          <TabsTrigger value=\"payments\">Payments</TabsTrigger>\n          <TabsTrigger value=\"merchants\">Merchants</TabsTrigger>\n        </TabsList>\n        <TabsContent value=\"general\">One endpoint for every online store.</TabsContent>\n        <TabsContent value=\"payments\">Cards are tokenized through your payment provider.</TabsContent>\n        <TabsContent value=\"merchants\">Any online store, monitored around the clock.</TabsContent>\n      </Tabs>\n      <Tabs defaultValue=\"month\">\n        <TabsList size=\"sm\">\n          <TabsTrigger value=\"month\">Monthly</TabsTrigger>\n          <TabsTrigger value=\"year\">Yearly</TabsTrigger>\n        </TabsList>\n        <TabsContent value=\"month\">Billed each month.</TabsContent>\n        <TabsContent value=\"year\">Billed once a year.</TabsContent>\n      </Tabs>\n    </div>\n  );\n}\n\nexport function Plain() {\n  return (\n    <Tabs defaultValue=\"all\">\n      <TabsList variant=\"plain\">\n        <TabsTrigger value=\"all\">All orders</TabsTrigger>\n        <TabsTrigger value=\"approved\">Approved</TabsTrigger>\n        <TabsTrigger value=\"declined\">Declined</TabsTrigger>\n      </TabsList>\n      <TabsContent value=\"all\">Target · Approved. DoorDash · Declined.</TabsContent>\n      <TabsContent value=\"approved\">Target · Approved.</TabsContent>\n      <TabsContent value=\"declined\">DoorDash · Declined.</TabsContent>\n    </Tabs>\n  );\n}\n",
      "exports": [
        "Site",
        "Topics",
        "Plain"
      ],
      "requires": [
        "tabs"
      ],
      "packages": [],
      "previews": [
        {
          "name": "Site",
          "url": "https://opencommerce-ds.vercel.app/preview/tabs/Site"
        },
        {
          "name": "Topics",
          "url": "https://opencommerce-ds.vercel.app/preview/tabs/Topics"
        },
        {
          "name": "Plain",
          "url": "https://opencommerce-ds.vercel.app/preview/tabs/Plain"
        }
      ]
    }
  ],
  "type": "ui",
  "title": "Tabs",
  "description": "Tabbed views in the header's glass tray: a white pill glides to the selected tab.",
  "surface": "site",
  "kinds": [
    "tabs"
  ],
  "requires": [
    "theme",
    "use-tab-glide",
    "utils"
  ],
  "packages": [
    "@radix-ui/react-tabs",
    "class-variance-authority"
  ],
  "files": [
    {
      "path": "components/ui/tabs.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\nimport * as TabsPrimitive from \"@radix-ui/react-tabs\";\nimport { cva } from \"class-variance-authority\";\n\nimport { useTabGlide } from \"@/hooks/use-tab-glide\";\nimport { cn } from \"@/lib/utils\";\n\n// The brand's tabs are the header's tray (nav-pills.tsx, globals.css .nav-tray, .nav-link, .nav-lit; computed): a pane\n// of frosted glass (white at 72 percent over a 16px blur, a 10 percent ink edge, a lit top line, 8px corners, 3px in)\n// holding 14px Inter labels at 64 percent ink with 5px corners. Under the pointer a label turns ink on a 5 percent wash;\n// the selected one is blue on a white pill with a hairline and a small lift that glides between tabs (it travels and\n// stays on screen, so it moves on a spring that keeps its speed if you switch mid-way; reduced motion jumps). `plain`\n// drops the tray for dense rows: quiet labels with a pale wash gliding between them.\ntype Variant = \"default\" | \"plain\";\ntype Size = \"sm\" | \"md\";\n\n// The key, and the same surface on the selected trigger for the first paint before the key lands (or with no glide).\nconst KEY: Record<Variant, string> = {\n  default: \"rounded-menu bg-surface shadow-lit\",\n  plain: \"rounded-sm bg-wash-pill\",\n};\nconst SELF_KEY: Record<Variant, string> = {\n  default: \"data-[state=active]:bg-surface data-[state=active]:shadow-lit\",\n  plain: \"data-[state=active]:bg-wash-pill\",\n};\n\nconst tabsTriggerVariants = cva(\n  \"relative inline-flex items-center justify-center gap-1.5 font-sans leading-none whitespace-nowrap outline-none focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-solid focus-visible:outline-focus-ring disabled:pointer-events-none disabled:opacity-50 motion-reduce:transition-none [&_svg]:size-4 [&_svg]:shrink-0\",\n  {\n    variants: {\n      variant: {\n        // Only the label and a faint wash answer the pointer, so nothing fades over the key gliding in beneath.\n        default:\n          \"rounded-menu text-fg-muted transition-[background-color,color] duration-160 ease-out data-[state=active]:text-fg-brand data-[state=inactive]:hover:bg-wash-hover data-[state=inactive]:hover:text-fg\",\n        plain: \"rounded-sm text-fg-muted transition-[color] duration-150 ease-out hover:text-fg data-[state=active]:text-fg\",\n      },\n      size: { sm: \"\", md: \"\" },\n    },\n    compoundVariants: [\n      // The header's links: 32px, 14px in, 14px Inter.\n      { variant: \"default\", size: \"md\", className: \"h-8 px-3.5 text-body-sm\" },\n      { variant: \"default\", size: \"sm\", className: \"h-7 px-2.5 text-caption\" },\n      { variant: \"plain\", size: \"md\", className: \"h-8 px-3 text-body-sm\" },\n      { variant: \"plain\", size: \"sm\", className: \"h-7 px-2.5 text-caption\" },\n    ],\n    defaultVariants: { variant: \"default\", size: \"md\" },\n  },\n);\n\nconst ActiveValue = React.createContext<string | undefined>(undefined);\nconst ListState = React.createContext<{ variant: Variant; size: Size; isKeyPlaced: boolean }>({ variant: \"default\", size: \"md\", isKeyPlaced: false });\n\n// The active value is mirrored here so the list can move its key in the same render Radix switches tabs, whether the\n// value is controlled or not.\nfunction Tabs({ className, value, defaultValue, onValueChange, ...props }: React.ComponentProps<typeof TabsPrimitive.Root>) {\n  const [uncontrolled, setUncontrolled] = React.useState(defaultValue);\n  const active = value ?? uncontrolled;\n  return (\n    <ActiveValue.Provider value={active}>\n      <TabsPrimitive.Root\n        data-slot=\"tabs\"\n        value={active}\n        onValueChange={(next) => {\n          setUncontrolled(next);\n          onValueChange?.(next);\n        }}\n        className={cn(\"flex flex-col gap-6\", className)}\n        {...props}\n      />\n    </ActiveValue.Provider>\n  );\n}\n\nfunction TabsList({\n  className,\n  variant = \"default\",\n  size = \"md\",\n  glide = true,\n  pillClassName,\n  children,\n  ...props\n}: React.ComponentProps<typeof TabsPrimitive.List> & {\n  variant?: Variant;\n  size?: Size;\n  /** Off for lists whose triggers draw their own selected surface. */\n  glide?: boolean;\n  /** The gliding key's surface, for a list styled its own way. */\n  pillClassName?: string;\n}) {\n  const active = React.useContext(ActiveValue);\n  const keyRef = React.useRef<HTMLSpanElement>(null);\n  const isPlaced = useTabGlide(keyRef, active ?? \"\");\n\n  return (\n    <TabsPrimitive.List\n      data-slot=\"tabs-list\"\n      className={cn(\n        \"relative isolate w-fit\",\n                variant === \"default\" ? \"inline-flex items-center gap-0.5 rounded-control border border-line-glass bg-glass p-0.75 shadow-glass backdrop-blur-lg backdrop-saturate-140\" : \"inline-flex items-center gap-0.5\",\n        className,\n      )}\n      {...props}\n    >\n      {glide && (\n        // The hook measures from the list's outer edge; -1px puts the key back inside the track's border.\n        <span ref={keyRef} aria-hidden className={cn(\"pointer-events-none absolute -z-10\", variant === \"default\" ? \"-top-px -left-px\" : \"top-0 left-0\", KEY[variant], pillClassName, !isPlaced && \"invisible\")} />\n      )}\n      <ListState.Provider value={{ variant, size, isKeyPlaced: glide && isPlaced }}>{children}</ListState.Provider>\n    </TabsPrimitive.List>\n  );\n}\n\nfunction TabsTrigger({ className, value, ...props }: React.ComponentProps<typeof TabsPrimitive.Trigger>) {\n  const { variant, size, isKeyPlaced } = React.useContext(ListState);\n  // Until the key lands (or with no key), the selected tab draws the key itself, so the first paint matches.\n  return <TabsPrimitive.Trigger data-slot=\"tabs-trigger\" data-tab-id={value} value={value} className={cn(tabsTriggerVariants({ variant, size }), !isKeyPlaced && SELF_KEY[variant], className)} {...props} />;\n}\n\nfunction TabsContent({ className, ...props }: React.ComponentProps<typeof TabsPrimitive.Content>) {\n  return <TabsPrimitive.Content data-slot=\"tabs-content\" className={cn(\"flex-1 outline-none\", className)} {...props} />;\n}\n\nexport { Tabs, TabsList, TabsTrigger, TabsContent, tabsTriggerVariants };\n"
    }
  ]
}
