{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "settings-template",
  "type": "registry:block",
  "title": "Settings Template",
  "description": "Account / app settings surface. Topbar + left tab-rail (sections) + main content area (rendered section). Server-friendly; the active section is driven by the consumer's routing (each section is a route).",
  "author": "ofri-peretz <https://github.com/ofri-peretz>",
  "categories": [
    "form",
    "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/section-boundary.json",
    "https://ds.interlace.tools/r/skeleton.json",
    "https://ds.interlace.tools/r/stack.json",
    "https://ds.interlace.tools/r/topbar.json",
    "https://ds.interlace.tools/r/typography.json"
  ],
  "files": [
    {
      "path": "registry/interlace-ui/templates/settings-template.tsx",
      "target": "components/ui/templates/settings-template.tsx",
      "type": "registry:block",
      "content": "import * as React from 'react';\n\n// @interlace/settings-template v1.1.0 — Interlace design system.\n// Docs, props and live preview: https://ds.interlace.tools/c/settings-template\n// What changed since: https://ds.interlace.tools/c/settings-template#history\n// Generated banner — keep it, the upgrade diff reads this version.\n\n/**\n * @interlace/ui — SettingsTemplate\n *\n * Account / app settings surface. Topbar + left tab-rail (sections) +\n * main content area (rendered section). Server-friendly; the active\n * section is driven by the consumer's routing (each section is a route).\n *\n * ## MIN_VIEWPORT — 480\n *\n * Below 480, the tab-rail becomes a horizontal scrolling row above the\n * content.\n */\n\nimport { cn } from '@/lib/utils';\nimport { Container } from '@/components/ui/container';\nimport { SectionBoundary } from '@/components/ui/section-boundary';\nimport { Stack } from '@/components/ui/stack';\nimport { Typography } from '@/components/ui/typography';\nimport { Topbar } from '@/components/ui/patterns/topbar';\nimport { Skeleton } from '@/components/ui/skeleton';\n\nexport const MIN_VIEWPORT = 480 as const;\n\ninterface SettingsSection {\n  id: string;\n  label: React.ReactNode;\n  href: string;\n}\n\ninterface SettingsTemplateProps extends Omit<React.ComponentProps<'div'>, 'title'> {\n  topbar: React.ComponentProps<typeof Topbar>;\n  /** Page title (rendered above the tab rail). */\n  title?: React.ReactNode;\n  /** Section list shown in the rail. */\n  sections: SettingsSection[];\n  /** ID of the currently-active section (matches `SettingsSection.id`). */\n  activeSection: string;\n  /** Main content (the active section's form / settings). */\n  children: React.ReactNode;\n}\n\nfunction SettingsTemplate({\n  topbar,\n  title = 'Settings',\n  sections,\n  activeSection,\n  children,\n  className,\n  ...props\n}: SettingsTemplateProps) {\n  return (\n    <div\n      data-slot=\"settings-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      <main className=\"flex-1 py-xl\">\n        <Container size=\"content\">\n          <Stack gap=\"lg\">\n            <Typography variant=\"h2\" as=\"h1\" className=\"text-balance\">\n              {title}\n            </Typography>\n\n            <div className=\"flex flex-col gap-lg md:flex-row\">\n              <nav\n                aria-label=\"Settings sections\"\n                className=\"md:w-56 md:shrink-0\"\n              >\n                <ul className=\"flex gap-xs overflow-x-auto md:flex-col md:gap-xs\">\n                  {sections.map((section) => (\n                    <li key={section.id}>\n                      <a\n                        href={section.href}\n                        aria-current={\n                          activeSection === section.id ? 'page' : undefined\n                        }\n                        className={cn(\n                          'text-muted-foreground hover:text-foreground hover:bg-muted block rounded-md px-sm py-xs text-sm transition-colors',\n                          activeSection === section.id &&\n                            'bg-muted text-foreground font-semibold',\n                        )}\n                      >\n                        {section.label}\n                      </a>\n                    </li>\n                  ))}\n                </ul>\n              </nav>\n\n              <div className=\"flex-1\">\n                <SectionBoundary\n                  name={`settings-${activeSection}`}\n                  skeletonVariant=\"card\"\n                >\n                  {children}\n                </SectionBoundary>\n              </div>\n            </div>\n          </Stack>\n        </Container>\n      </main>\n    </div>\n  );\n}\nSettingsTemplate.displayName = 'SettingsTemplate';\n\n/**\n * Page-level loading state — the full-page silhouette of\n * `<SettingsTemplate>`, 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 */\nfunction SettingsTemplateSkeleton({\n  className,\n  ...props\n}: React.ComponentProps<'div'>) {\n  return (\n    <div\n      data-slot=\"settings-template-skeleton\"\n      data-min-viewport={String(MIN_VIEWPORT)}\n      role=\"status\"\n      aria-busy=\"true\"\n      aria-label=\"Loading settings…\"\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      <Container size=\"content\">\n        <Stack gap=\"lg\" className=\"py-xl\">\n          <Skeleton variant=\"page-header\" label={null} />\n          <div className=\"flex flex-col gap-lg md:flex-row\">\n            <div className=\"md:w-56 md:shrink-0\">\n              <Skeleton variant=\"text\" count={5} label={null} />\n            </div>\n            <div className=\"flex-1\">\n              <Skeleton variant=\"card\" count={2} label={null} />\n            </div>\n          </div>\n        </Stack>\n      </Container>\n    </div>\n  );\n}\nSettingsTemplateSkeleton.displayName = 'SettingsTemplateSkeleton';\nSettingsTemplate.Skeleton = SettingsTemplateSkeleton;\n\nexport { SettingsTemplate };\nexport type { SettingsTemplateProps, SettingsSection };\n"
    }
  ],
  "meta": {
    "tier": "template",
    "client": false,
    "minViewport": 480,
    "loading": false,
    "version": "1.1.0",
    "since": "1.0.0"
  },
  "docs": "## @interlace/settings-template\n\nInstalled to `components/ui/templates/settings-template.tsx`.\n\n```tsx\nimport { /* … */ } from '@/components/ui/templates/settings-template';\n```\n\nProps, a11y contract, live preview and source: https://ds.interlace.tools/c/settings-template\n\nRequires the `@interlace/theme` CSS baseline (installed automatically as a registry dependency)."
}
