{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "docs-page-template",
  "type": "registry:block",
  "title": "Docs Page Template",
  "description": "Documentation page surface. Topbar at top, optional sidebar nav on the left (md+), main content in the centre with a `<Prose>` wrapper, and an optional TOC rail on the right (xl+).",
  "author": "ofri-peretz <https://github.com/ofri-peretz>",
  "categories": [
    "blog",
    "template"
  ],
  "dependencies": [],
  "registryDependencies": [
    "https://ds.interlace.tools/r/theme.json",
    "https://ds.interlace.tools/r/cn.json",
    "https://ds.interlace.tools/r/container.json",
    "https://ds.interlace.tools/r/footer.json",
    "https://ds.interlace.tools/r/prose.json",
    "https://ds.interlace.tools/r/section-boundary.json",
    "https://ds.interlace.tools/r/skeleton.json",
    "https://ds.interlace.tools/r/stack.json",
    "https://ds.interlace.tools/r/topbar.json"
  ],
  "files": [
    {
      "path": "registry/interlace-ui/templates/docs-page-template.tsx",
      "target": "components/ui/templates/docs-page-template.tsx",
      "type": "registry:block",
      "content": "import * as React from 'react';\n\n// @interlace/docs-page-template v1.1.0 — Interlace design system.\n// Docs, props and live preview: https://ds.interlace.tools/c/docs-page-template\n// What changed since: https://ds.interlace.tools/c/docs-page-template#history\n// Generated banner — keep it, the upgrade diff reads this version.\n\n/**\n * @interlace/ui — DocsPageTemplate\n *\n * Documentation page surface. Topbar at top, optional sidebar nav on the\n * left (md+), main content in the centre with a `<Prose>` wrapper, and an\n * optional TOC rail on the right (xl+).\n *\n * Used by `ds.interlace.tools` (the registry app's docs pages) and by\n * `apps/docs` (the ESLint plugins docs). One template, two consumers.\n *\n * ## MIN_VIEWPORT — 320\n */\n\nimport { cn } from '@/lib/utils';\nimport { Container } from '@/components/ui/container';\nimport { Prose } from '@/components/ui/prose';\nimport { SectionBoundary } from '@/components/ui/section-boundary';\nimport { Topbar } from '@/components/ui/patterns/topbar';\nimport { Footer } from '@/components/ui/patterns/footer';\nimport { Skeleton } from '@/components/ui/skeleton';\nimport { Stack } from '@/components/ui/stack';\n\nexport const MIN_VIEWPORT = 320 as const;\n\ninterface DocsPageTemplateProps extends React.ComponentProps<'div'> {\n  topbar: React.ComponentProps<typeof Topbar>;\n  /** Left sidebar (typically a nav tree). Hidden below md. */\n  sidebar?: React.ReactNode;\n  /** Right rail (typically a TOC). Hidden below xl. */\n  toc?: React.ReactNode;\n  /** Page body — wrapped in <Prose>. */\n  body: React.ReactNode;\n  /** Optional footer at the bottom. */\n  footer?: React.ComponentProps<typeof Footer>;\n}\n\nfunction DocsPageTemplate({\n  topbar,\n  sidebar,\n  toc,\n  body,\n  footer,\n  className,\n  ...props\n}: DocsPageTemplateProps) {\n  return (\n    <div\n      data-slot=\"docs-page-template\"\n      data-min-viewport={String(MIN_VIEWPORT)}\n      className={cn(\n        'bg-background text-foreground flex min-h-screen flex-col',\n        className,\n      )}\n      {...props}\n    >\n      <Topbar {...topbar} />\n\n      <div className=\"flex flex-1\">\n        {sidebar ? (\n          <aside\n            className=\"border-border bg-card hidden w-64 shrink-0 border-r md:block\"\n            aria-label=\"Section navigation\"\n          >\n            <SectionBoundary name=\"docs-sidebar\" skeletonVariant=\"card\">\n              {sidebar}\n            </SectionBoundary>\n          </aside>\n        ) : null}\n\n        <main className=\"flex-1 px-md py-xl\">\n          <SectionBoundary name=\"docs-body\" skeletonVariant=\"prose\">\n            <Container size=\"prose\">\n              <Prose>{body}</Prose>\n            </Container>\n          </SectionBoundary>\n        </main>\n\n        {toc ? (\n          <aside\n            className=\"hidden w-64 shrink-0 px-md py-xl xl:block\"\n            aria-label=\"Table of contents\"\n          >\n            <SectionBoundary name=\"docs-toc\" skeletonVariant=\"text\">\n              {toc}\n            </SectionBoundary>\n          </aside>\n        ) : null}\n      </div>\n\n      {footer ? <Footer {...footer} /> : null}\n    </div>\n  );\n}\nDocsPageTemplate.displayName = 'DocsPageTemplate';\n\n/**\n * Page-level loading state — the full-page silhouette of\n * `<DocsPageTemplate>`, not a single rect. Rendered by the consumer while\n * the whole route's data is in flight (`loading.tsx` in Next.js), or as\n * a `<SectionBoundary skeleton={…}>` override.\n *\n * Shapes mirror the real layout so the swap is CLS-neutral (R23). One\n * `role=\"status\"` region for the page; inner skeletons pass\n * `label={null}` so screen readers hear one announcement, not ten.\n *\n * The flanking columns are opt-in (`sidebar` / `toc`), mirroring the\n * template's own optional props — a body-only docs page that painted a\n * 3-column skeleton would shift its content on hydration, which is the\n * exact bug the skeleton exists to prevent. A full docs route renders\n * `<DocsPageTemplate.Skeleton sidebar toc />`.\n */\nfunction DocsPageTemplateSkeleton({\n  sidebar = false,\n  toc = false,\n  className,\n  ...props\n}: React.ComponentProps<'div'> & {\n  /** Reserve the left nav column (md+). Match the page's own `sidebar` prop. */\n  sidebar?: boolean;\n  /** Reserve the right TOC rail (xl+). Match the page's own `toc` prop. */\n  toc?: boolean;\n}) {\n  return (\n    <div\n      data-slot=\"docs-page-template-skeleton\"\n      data-min-viewport={String(MIN_VIEWPORT)}\n      role=\"status\"\n      aria-busy=\"true\"\n      aria-label=\"Loading page…\"\n      className={cn(\n        'bg-background text-foreground flex min-h-screen flex-col',\n        className,\n      )}\n      {...props}\n    >\n      <Skeleton\n        variant=\"rect\"\n        className=\"h-14 w-full rounded-none\"\n        label={null}\n      />\n      <div className=\"flex flex-1\">\n        {sidebar ? (\n          <div className=\"border-border hidden w-64 shrink-0 border-r p-md md:block\">\n            <Skeleton variant=\"text\" count={8} label={null} />\n          </div>\n        ) : null}\n        <div className=\"flex-1 px-md py-xl\">\n          <Container size=\"prose\">\n            <Stack gap=\"lg\">\n              <Skeleton variant=\"page-header\" label={null} />\n              <Skeleton variant=\"prose\" count={10} label={null} />\n            </Stack>\n          </Container>\n        </div>\n        {toc ? (\n          <div className=\"hidden w-64 shrink-0 px-md py-xl xl:block\">\n            <Skeleton variant=\"text\" count={5} label={null} />\n          </div>\n        ) : null}\n      </div>\n    </div>\n  );\n}\nDocsPageTemplateSkeleton.displayName = 'DocsPageTemplateSkeleton';\nDocsPageTemplate.Skeleton = DocsPageTemplateSkeleton;\n\nexport { DocsPageTemplate };\nexport type { DocsPageTemplateProps };\n"
    }
  ],
  "meta": {
    "tier": "template",
    "client": false,
    "minViewport": 320,
    "loading": false,
    "version": "1.1.0",
    "since": "1.0.0"
  },
  "docs": "## @interlace/docs-page-template\n\nInstalled to `components/ui/templates/docs-page-template.tsx`.\n\n```tsx\nimport { /* … */ } from '@/components/ui/templates/docs-page-template';\n```\n\nProps, a11y contract, live preview and source: https://ds.interlace.tools/c/docs-page-template\n\nRequires the `@interlace/theme` CSS baseline (installed automatically as a registry dependency)."
}
