{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "document-sibling-nav",
  "type": "registry:component",
  "title": "Document Sibling Nav",
  "description": "Newer/older navigator: links to the previous and next item in an ordered series.",
  "dependencies": [
    "@vllnt/ui@^0.2.1",
    "class-variance-authority"
  ],
  "registryDependencies": [],
  "files": [
    {
      "path": "registry/default/document-sibling-nav/document-sibling-nav.tsx",
      "content": "import {\n  type AnchorHTMLAttributes,\n  type ComponentPropsWithoutRef,\n  forwardRef,\n  type ReactNode,\n} from \"react\";\n\nimport { cva, type VariantProps } from \"class-variance-authority\";\n\nimport { cn } from \"@vllnt/ui\";\n\nconst navVariants = cva(\"grid w-full gap-3 text-sm sm:grid-cols-2\", {\n  defaultVariants: {\n    variant: \"with-title\",\n  },\n  variants: {\n    variant: {\n      compact: \"\",\n      \"with-meta\": \"\",\n      \"with-title\": \"\",\n    },\n  },\n});\n\nconst itemVariants = cva(\n  \"group flex flex-col gap-1 rounded-lg border border-border bg-background p-4 text-foreground transition-colors hover:border-foreground/30 hover:bg-accent/50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2\",\n  {\n    defaultVariants: {\n      side: \"previous\",\n    },\n    variants: {\n      side: {\n        next: \"items-end text-right sm:col-start-2\",\n        previous: \"items-start text-left\",\n      },\n    },\n  },\n);\n\n/**\n * Visual variant for {@link DocumentSiblingNav}.\n *\n * - `compact` — single-line caption and arrow, no title.\n * - `with-title` — caption plus the sibling document title (default).\n * - `with-meta` — caption, title, and an optional `meta` line (date, author, etc.).\n *\n * @public\n */\nexport type DocumentSiblingNavVariant = \"compact\" | \"with-meta\" | \"with-title\";\n\ntype AnchorPassthroughProps = Omit<\n  AnchorHTMLAttributes<HTMLAnchorElement>,\n  \"href\" | \"title\"\n>;\n\n/**\n * Per-side description for {@link DocumentSiblingNav}.\n *\n * @public\n */\nexport type DocumentSiblingNavLink = {\n  /** Optional anchor props (rel, target, etc.) forwarded to the rendered `<a>`. */\n  anchorProps?: AnchorPassthroughProps;\n  /** Destination URL. */\n  href: string;\n  /** Optional secondary line shown when `variant=\"with-meta\"`. */\n  meta?: ReactNode;\n  /** Title of the sibling document. */\n  title: ReactNode;\n};\n\ntype LabelDictionary = {\n  /** Aria label for the wrapping `<nav>`. */\n  navigation?: string;\n  /** Caption above the next link. */\n  next?: string;\n  /** Caption above the previous link. */\n  previous?: string;\n};\n\n/**\n * Props for {@link DocumentSiblingNav}.\n *\n * @public\n */\nexport type DocumentSiblingNavProps = {\n  /** Localizable captions. */\n  labels?: LabelDictionary;\n  /** The next sibling, or `undefined` at the end of the series. */\n  next?: DocumentSiblingNavLink;\n  /** The previous sibling, or `undefined` at the start of the series. */\n  previous?: DocumentSiblingNavLink;\n} & Omit<ComponentPropsWithoutRef<\"nav\">, \"title\"> &\n  VariantProps<typeof navVariants>;\n\nconst DEFAULT_LABELS = {\n  navigation: \"Document navigation\",\n  next: \"Newer\",\n  previous: \"Older\",\n} as const satisfies Required<LabelDictionary>;\n\ntype ItemProps = {\n  ariaLabel: string;\n  caption: string;\n  link: DocumentSiblingNavLink;\n  side: \"next\" | \"previous\";\n  variant: DocumentSiblingNavVariant;\n};\n\nfunction SiblingItem({\n  ariaLabel,\n  caption,\n  link,\n  side,\n  variant,\n}: ItemProps): ReactNode {\n  const { anchorProps, href, meta, title } = link;\n  return (\n    <a\n      aria-label={ariaLabel}\n      className={cn(itemVariants({ side }))}\n      href={href}\n      {...anchorProps}\n    >\n      <span className=\"text-xs font-medium uppercase tracking-wide text-muted-foreground\">\n        {caption}\n      </span>\n      {variant === \"compact\" ? null : (\n        <span className=\"line-clamp-2 text-sm font-semibold text-foreground group-hover:underline\">\n          {title}\n        </span>\n      )}\n      {variant === \"with-meta\" && meta ? (\n        <span className=\"text-xs text-muted-foreground\">{meta}</span>\n      ) : null}\n    </a>\n  );\n}\n\nfunction buildAriaLabel(\n  caption: string,\n  link: DocumentSiblingNavLink,\n  variant: DocumentSiblingNavVariant,\n): string {\n  if (variant === \"compact\" || typeof link.title !== \"string\") {\n    return caption;\n  }\n  return `${caption}: ${link.title}`;\n}\n\n/**\n * Sibling-document navigator. Renders previous / next links to the surrounding\n * items in an ordered series (e.g. newer/older blog post, prev/next doc page).\n * Pass `previous`, `next`, or both — the component returns `null` when both\n * are absent.\n *\n * Server-renderable — no client hooks required.\n *\n * @example\n * ```tsx\n * <DocumentSiblingNav\n *   previous={{ href: '/posts/foo', title: 'Foo post' }}\n *   next={{ href: '/posts/bar', title: 'Bar post' }}\n *   labels={{ previous: 'Older', next: 'Newer' }}\n * />\n * ```\n *\n * @public\n */\nexport const DocumentSiblingNav = forwardRef<\n  HTMLElement,\n  DocumentSiblingNavProps\n>(({ className, labels, next, previous, variant, ...rest }, ref) => {\n  if (!previous && !next) return null;\n\n  const resolvedVariant: DocumentSiblingNavVariant = variant ?? \"with-title\";\n  const resolvedLabels = { ...DEFAULT_LABELS, ...labels };\n\n  return (\n    <nav\n      aria-label={resolvedLabels.navigation}\n      className={cn(navVariants({ variant: resolvedVariant }), className)}\n      ref={ref}\n      {...rest}\n    >\n      {previous ? (\n        <SiblingItem\n          ariaLabel={buildAriaLabel(\n            resolvedLabels.previous,\n            previous,\n            resolvedVariant,\n          )}\n          caption={resolvedLabels.previous}\n          link={previous}\n          side=\"previous\"\n          variant={resolvedVariant}\n        />\n      ) : (\n        <span aria-hidden=\"true\" />\n      )}\n      {next ? (\n        <SiblingItem\n          ariaLabel={buildAriaLabel(resolvedLabels.next, next, resolvedVariant)}\n          caption={resolvedLabels.next}\n          link={next}\n          side=\"next\"\n          variant={resolvedVariant}\n        />\n      ) : (\n        <span aria-hidden=\"true\" />\n      )}\n    </nav>\n  );\n});\nDocumentSiblingNav.displayName = \"DocumentSiblingNav\";\n\nexport { navVariants as documentSiblingNavVariants };\n",
      "type": "registry:component"
    }
  ],
  "version": "0.2.1",
  "stability": "stable"
}
