{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "table",
  "title": "Table",
  "author": "Tori Bryan",
  "description": "Rows and columns of data, at one compact density.",
  "dependencies": [],
  "registryDependencies": [
    "utils"
  ],
  "files": [
    {
      "path": "registry/ui/table.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\n\nimport { cn } from \"@/lib/utils\"\n\n/*\n * The wrapper scrolls sideways so a wide table never widens the page. While\n * it overflows it's a named region in the tab order, so keyboard users can\n * scroll it too. The name goes on one of the two, never both, so it isn't\n * announced twice: `aria-label` moves to the region, and a caption names it\n * when there's no label.\n */\nfunction Table({\n  className,\n  \"aria-label\": label,\n  ...props\n}: React.ComponentProps<\"table\">) {\n  const containerRef = React.useRef<HTMLDivElement>(null)\n  const [overflowing, setOverflowing] = React.useState(false)\n  const [captionId, setCaptionId] = React.useState<string>()\n\n  React.useEffect(() => {\n    const container = containerRef.current\n    if (!container) return\n    const update = () => {\n      setOverflowing(container.scrollWidth > container.clientWidth + 1)\n      setCaptionId(\n        container.querySelector(\":scope > table > caption\")?.id || undefined\n      )\n    }\n    update()\n    const observer = new ResizeObserver(update)\n    observer.observe(container)\n    if (container.firstElementChild) {\n      observer.observe(container.firstElementChild)\n    }\n    return () => observer.disconnect()\n  }, [])\n\n  return (\n    <div\n      ref={containerRef}\n      data-slot=\"table-container\"\n      tabIndex={overflowing ? 0 : undefined}\n      role={overflowing ? \"region\" : undefined}\n      aria-label={\n        overflowing && (label || !captionId)\n          ? (label ?? \"Scrollable table\")\n          : undefined\n      }\n      aria-labelledby={overflowing && !label ? captionId : undefined}\n      className=\"relative w-full overflow-x-auto outline-none focus-visible:ring-[3px] focus-visible:ring-ring-subtle focus-visible:ring-inset\"\n    >\n      <table\n        data-slot=\"table\"\n        className={cn(\n          \"w-full caption-bottom border-separate border-spacing-0 text-sm\",\n          className\n        )}\n        aria-label={overflowing ? undefined : label}\n        {...props}\n      />\n    </div>\n  )\n}\n\nfunction TableHeader({ className, ...props }: React.ComponentProps<\"thead\">) {\n  return (\n    <thead\n      data-slot=\"table-header\"\n      className={cn(\"bg-muted\", className)}\n      {...props}\n    />\n  )\n}\n\nfunction TableBody({ className, ...props }: React.ComponentProps<\"tbody\">) {\n  return (\n    <tbody\n      data-slot=\"table-body\"\n      className={cn(\"[&_tr:last-child>*]:border-b-0\", className)}\n      {...props}\n    />\n  )\n}\n\nfunction TableFooter({ className, ...props }: React.ComponentProps<\"tfoot\">) {\n  return (\n    <tfoot\n      data-slot=\"table-footer\"\n      className={cn(\n        \"bg-muted font-medium [&>tr>*]:border-t [&>tr>*]:border-b-0 [&>tr>*]:border-border\",\n        className\n      )}\n      {...props}\n    />\n  )\n}\n\n/*\n * Borders sit on the cells, not the row: a separated table can't draw a row\n * border, and a pinned cell has to carry its own to stay lined up.\n */\nfunction TableRow({ className, ...props }: React.ComponentProps<\"tr\">) {\n  return (\n    <tr\n      data-slot=\"table-row\"\n      className={cn(\n        \"bg-background transition-colors data-selected:bg-muted [&>*]:border-b [&>*]:border-border [tbody>&]:hover:bg-muted\",\n        className\n      )}\n      {...props}\n    />\n  )\n}\n\n/*\n * Labels are foreground, not muted: muted-foreground on the muted header\n * band is 4.34:1, under AA for 12px text.\n */\nfunction TableHead({ className, ...props }: React.ComponentProps<\"th\">) {\n  return (\n    <th\n      data-slot=\"table-head\"\n      className={cn(\n        \"h-9 bg-muted px-2 text-left align-middle text-xs font-medium whitespace-nowrap text-foreground [&:has([role=checkbox])]:w-9 [&:has([role=checkbox])]:pl-3\",\n        className\n      )}\n      {...props}\n    />\n  )\n}\n\nfunction TableCell({ className, ...props }: React.ComponentProps<\"td\">) {\n  return (\n    <td\n      data-slot=\"table-cell\"\n      className={cn(\n        \"h-10 bg-inherit p-2 align-middle whitespace-nowrap [&:has([role=checkbox])]:w-9 [&:has([role=checkbox])]:pl-3\",\n        className\n      )}\n      {...props}\n    />\n  )\n}\n\nfunction TableCaption({\n  className,\n  id,\n  ...props\n}: React.ComponentProps<\"caption\">) {\n  // Table reads this id to name its scroll region after the caption.\n  const fallbackId = React.useId()\n  return (\n    <caption\n      id={id ?? fallbackId}\n      data-slot=\"table-caption\"\n      className={cn(\"mt-4 text-sm text-muted-foreground\", className)}\n      {...props}\n    />\n  )\n}\n\nexport {\n  Table,\n  TableBody,\n  TableCaption,\n  TableCell,\n  TableFooter,\n  TableHead,\n  TableHeader,\n  TableRow,\n}\n",
      "type": "registry:ui"
    }
  ],
  "cssVars": {
    "theme": {
      "color-ring-subtle": "var(--ring-subtle)"
    },
    "light": {
      "ring-subtle": "color-mix(in oklch, var(--color-neutral-400) 50%, transparent)"
    },
    "dark": {
      "ring-subtle": "color-mix(in oklch, var(--color-neutral-500) 50%, transparent)"
    }
  },
  "meta": {
    "tier": "base-components",
    "group": "Display",
    "docs": "https://fibo.toribryan.com/?path=/docs/base-components-display-table--docs"
  },
  "docs": "Docs and live examples: https://fibo.toribryan.com/?path=/docs/base-components-display-table--docs",
  "categories": [
    "base-components",
    "display"
  ],
  "type": "registry:ui"
}