{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "breadcrumbs",
  "title": "Breadcrumbs",
  "description": "An accessible breadcrumb trail with collapsible overflow — first item + ellipsis + last items once maxItems is exceeded.",
  "dependencies": [
    "lucide-react"
  ],
  "registryDependencies": [
    "utils"
  ],
  "files": [
    {
      "path": "src/registry/ui/breadcrumbs.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\nimport { ChevronRight, MoreHorizontal } from \"lucide-react\"\nimport { cn } from \"@/lib/utils\"\n\nexport interface BreadcrumbItem {\n  label: string\n  /** Omit on the last (current) item — it renders as text with aria-current=\"page\". */\n  href?: string\n}\n\nexport interface BreadcrumbsProps extends React.HTMLAttributes<HTMLElement> {\n  items: BreadcrumbItem[]\n  /** Collapse to first item + ellipsis + last (maxItems - 1) items once items.length exceeds this. 0 disables collapsing. */\n  maxItems?: number\n  /** Rendered between items and after the ellipsis; aria-hidden. Defaults to a chevron. */\n  separator?: React.ReactNode\n}\n\nconst DEFAULT_SEPARATOR = <ChevronRight className=\"size-3.5\" />\n\ntype Cell = { kind: \"item\"; item: BreadcrumbItem } | { kind: \"ellipsis\" }\n\nexport const Breadcrumbs = React.forwardRef<HTMLElement, BreadcrumbsProps>(\n  ({ items, maxItems = 0, separator = DEFAULT_SEPARATOR, className, ...props }, ref) => {\n    // One-way expansion: once the ellipsis is pressed it never collapses again, so this\n    // state stays internal.\n    const [expanded, setExpanded] = React.useState(false)\n    const olRef = React.useRef<HTMLOListElement | null>(null)\n    // maxItems=1 would collapse the aria-current page itself, so clamp to 2 (first + last).\n    const effectiveMax = maxItems > 0 ? Math.max(maxItems, 2) : 0\n    const collapsed = effectiveMax > 0 && items.length > effectiveMax && !expanded\n\n    const cells: Cell[] = collapsed\n      ? [\n          { kind: \"item\", item: items[0] },\n          { kind: \"ellipsis\" },\n          ...items.slice(items.length - (effectiveMax - 1)).map((item): Cell => ({ kind: \"item\", item })),\n        ]\n      : items.map((item): Cell => ({ kind: \"item\", item }))\n\n    const handleExpand = () => {\n      setExpanded(true)\n      // The ellipsis button unmounts with the expansion and focus falls to body — move it\n      // to the first newly revealed link (anchors[0] is the always-present first crumb,\n      // [1] onwards only exist once expanded).\n      requestAnimationFrame(() => {\n        const anchors = olRef.current?.querySelectorAll(\"a\")\n        ;(anchors?.[1] ?? anchors?.[0])?.focus()\n      })\n    }\n\n    return (\n      <nav aria-label=\"Breadcrumb\" className={cn(\"text-sm\", className)} ref={ref} {...props}>\n        <ol className=\"flex flex-wrap items-center gap-1.5\" ref={olRef}>\n          {cells.map((cell, index) => {\n            const isLast = index === cells.length - 1\n            return (\n              // index is a stable identity here: cells is a pure derivation of\n              // items + collapse state (no drag/reorder), and href/label alone\n              // aren't guaranteed unique — e.g. sibling crumbs can share a href.\n              <React.Fragment key={index}>\n                <li className=\"flex items-center\">\n                  {cell.kind === \"ellipsis\" ? (\n                    <button\n                      aria-label=\"Show hidden items\"\n                      className={cn(\n                        \"flex size-6 items-center justify-center rounded-md text-muted-foreground transition-colors\",\n                        \"hover:bg-muted hover:text-foreground\",\n                        \"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring\",\n                      )}\n                      onClick={handleExpand}\n                      type=\"button\"\n                    >\n                      <MoreHorizontal className=\"size-4\" />\n                    </button>\n                  ) : isLast ? (\n                    <span aria-current=\"page\" className=\"font-medium text-foreground\">\n                      {cell.item.label}\n                    </span>\n                  ) : cell.item.href ? (\n                    <a className=\"text-muted-foreground transition-colors hover:text-foreground\" href={cell.item.href}>\n                      {cell.item.label}\n                    </a>\n                  ) : (\n                    <span className=\"text-muted-foreground\">{cell.item.label}</span>\n                  )}\n                </li>\n                {!isLast && (\n                  <li aria-hidden=\"true\" className=\"flex items-center text-muted-foreground/60\" role=\"presentation\">\n                    {separator}\n                  </li>\n                )}\n              </React.Fragment>\n            )\n          })}\n        </ol>\n      </nav>\n    )\n  },\n)\n\nBreadcrumbs.displayName = \"Breadcrumbs\"\n\nexport default Breadcrumbs\n",
      "type": "registry:ui"
    }
  ],
  "type": "registry:ui"
}