{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "sidebar",
  "type": "registry:ui",
  "title": "Sidebar",
  "description": "A responsive app-navigation sidebar: a fixed rail on wide screens that collapses to an off-canvas drawer behind a labelled hamburger on narrow screens. Nav links are real <a href> anchors and the open/close works without JavaScript — layout is CSS grid (grid-template-columns: minmax() 1fr) and the drawer toggles on the CSS :target pseudo-class.",
  "registryDependencies": [
    "utils"
  ],
  "files": [
    {
      "path": "registry/ui/sidebar.tsx",
      "type": "registry:ui",
      "target": "components/ui/sidebar.tsx",
      "content": "/** @jsxImportSource hono/jsx */\nimport type { Child, PropsWithChildren } from \"hono/jsx\"\nimport { cn, type ClassValue } from \"@/registry/lib/cn\"\n\n// Sidebar — shadcn-htmx, htmx v4 + Tailwind v4.\n//\n// A responsive app-navigation sidebar: a fixed rail on wide screens that\n// collapses to an off-canvas drawer (opened by a labelled hamburger) on narrow\n// screens. The nav links are real <a href> anchors and the open/close works\n// WITHOUT JavaScript.\n//\n// How it works (web-standards, progressive enhancement):\n//   - LAYOUT is CSS grid. The shell uses grid-template-columns:\n//     minmax(<rail>, …) 1fr — the rail track + a 1fr content track — exactly\n//     the one-line \"sidebar says\" pattern. On narrow screens both children\n//     collapse to one stacked column so the rail can float over the content.\n//       repos/web.dev/src/site/content/en/patterns/layout/sidebar-says/index.md\n//       repos/web.dev/src/site/content/en/patterns/layout/sidebar-says/assets/style.css\n//       repos/mdn/files/en-us/web/css/reference/properties/grid-template-columns/index.md\n//       repos/mdn/files/en-us/web/css/reference/values/minmax/index.md\n//   - OPEN / CLOSE on narrow screens is the CSS :target pseudo-class, the same\n//     no-JS technique as the web.dev Sidenav component. The hamburger is an\n//     <a href=\"#nav\"> and the scrim/close is an <a href=\"#\">; navigating the\n//     URL fragment flips the drawer's `:target` state, which CSS animates in.\n//       repos/web.dev/src/site/content/en/patterns/components/sidenav/index.md\n//       repos/web.dev/src/site/content/en/patterns/components/sidenav/assets/body.html\n//       repos/web.dev/src/site/content/en/patterns/components/sidenav/assets/style.css\n//       repos/mdn/files/en-us/web/css/reference/selectors/_colon_target/index.md\n//   - SEMANTICS: the rail is a <nav> (navigation landmark — name it when a page\n//     has more than one nav). Links are native <a>, so role=link + Enter-to-\n//     activate come from the platform; no JS, no ARIA needed.\n//       repos/mdn/files/en-us/web/accessibility/aria/reference/roles/navigation_role/index.md\n//       repos/mdn/files/en-us/web/html/reference/elements/a/index.md\n//\n// The responsive drawer mechanics (the @media + :target transform/visibility\n// transition and the reduced-motion guard) live in app/styles/input.css keyed\n// off data-slot=\"sidebar\", because a media-scoped :target transition can't be\n// expressed in utility classes alone — see the CSS block returned with this\n// component. A TINY shared script in public/site.js adds the web.dev Sidenav\n// keyboard nicety: Escape closes the open drawer (history.back so the fragment\n// clears) and focus moves to the toggle/close after the slide. It is keyed on\n// data-slot=\"sidebar\" and is purely an enhancement — the component opens and\n// closes with the script absent.\n\n// --- Layout shell ------------------------------------------------------\n\n// The shell is a grid. On narrow screens it is a single stacked column\n// (grid-cols-1) so the rail can become an absolutely-positioned drawer that\n// floats over the content. From `sm` up it becomes the two-track rail+content\n// layout: a minmax() rail track + a 1fr content track (\"sidebar says\").\n//\n// Height comes from the --sidebar-h custom property (default 100svh — the full\n// viewport for a real app shell). A docs/demo host can shrink it by setting\n// --sidebar-h on the layout (e.g. style=\"--sidebar-h: 22rem\") without touching\n// the class strings, so the rail + drawer fit a bounded preview box.\nconst sidebarLayoutBase =\n  \"relative grid w-full grid-cols-1 [--sidebar-h:100svh] [min-height:var(--sidebar-h)] \" +\n  \"sm:grid-cols-[minmax(var(--sidebar-w,16rem),20rem)_minmax(0,1fr)]\"\n\nexport function SidebarLayout(\n  props: PropsWithChildren<{ class?: ClassValue }> & Record<string, any>,\n) {\n  const { class: className, children, ...rest } = props\n  return (\n    <div\n      data-slot=\"sidebar-layout\"\n      class={cn(sidebarLayoutBase, className)}\n      {...rest}\n    >\n      {children}\n    </div>\n  )\n}\n\n// --- Trigger (labelled hamburger, narrow screens only) -----------------\n\n// A real anchor to the drawer's id, so it works with zero JS: navigating to\n// #<id> makes the <aside> match :target and the CSS slides it in. Hidden from\n// `sm` up where the rail is always visible.\nconst sidebarTriggerBase =\n  \"inline-flex h-9 items-center gap-2 rounded-md border bg-background px-3 text-sm font-medium shadow-xs \" +\n  \"hover:bg-accent hover:text-accent-foreground focus-visible:ring-[3px] focus-visible:ring-ring/50 focus-visible:outline-none \" +\n  \"sm:hidden\"\n\nexport function SidebarTrigger(\n  props: {\n    // Id of the <Sidebar> to open. Becomes the href fragment (#<sidebarFor>).\n    sidebarFor: string\n    label?: string\n    class?: ClassValue\n  } & Record<string, any>,\n) {\n  const { sidebarFor, label = \"Menu\", class: className, ...rest } = props\n  return (\n    <a\n      href={`#${sidebarFor}`}\n      data-slot=\"sidebar-trigger\"\n      data-sidebar-open={sidebarFor}\n      aria-label={`Open ${label}`}\n      aria-controls={sidebarFor}\n      class={cn(sidebarTriggerBase, className)}\n      {...rest}\n    >\n      {/* Hamburger glyph — three lines. role/aria handled by the anchor. */}\n      <svg\n        xmlns=\"http://www.w3.org/2000/svg\"\n        viewBox=\"0 0 24 24\"\n        fill=\"none\"\n        stroke=\"currentColor\"\n        stroke-width=\"2\"\n        stroke-linecap=\"round\"\n        stroke-linejoin=\"round\"\n        class=\"size-4\"\n        aria-hidden=\"true\"\n      >\n        <line x1=\"4\" x2=\"20\" y1=\"6\" y2=\"6\" />\n        <line x1=\"4\" x2=\"20\" y1=\"12\" y2=\"12\" />\n        <line x1=\"4\" x2=\"20\" y1=\"18\" y2=\"18\" />\n      </svg>\n      {label}\n    </a>\n  )\n}\n\n// --- Sidebar (the rail / off-canvas drawer) ----------------------------\n\n// Wide screens: a bordered, full-height rail in the first grid track.\n// Narrow screens: positioned off-canvas (the @media :target rule in input.css\n// slides it in). z-50 so it floats over the content; the scrim sits behind it.\nconst sidebarBase =\n  \"flex h-[var(--sidebar-h,100svh)] flex-col gap-2 border-r bg-card text-card-foreground \" +\n  // Narrow-screen drawer footprint. The slide-in transform + visibility\n  // transition is the :target rule in input.css (data-slot=\"sidebar\"). On\n  // narrow screens the drawer is absolute (relative to the layout shell) so a\n  // bounded docs preview confines it instead of covering the whole viewport.\n  \"max-sm:absolute max-sm:inset-y-0 max-sm:left-0 max-sm:z-50 max-sm:h-full max-sm:w-72 max-sm:max-w-[85vw] max-sm:shadow-lg \" +\n  // Wide screens: the rail is sticky to the top of the content track so it\n  // stays put while the main column scrolls.\n  \"sm:sticky sm:top-0\"\n\nexport function Sidebar(\n  props: PropsWithChildren<{\n    // Id targeted by the trigger's href fragment. Required for the no-JS\n    // :target open/close.\n    id: string\n    // Accessible name for the <nav> landmark. Give a unique one when a page\n    // has more than one navigation landmark.\n    ariaLabel?: string\n    ariaLabelledby?: string\n    class?: ClassValue\n  }> & Record<string, any>,\n) {\n  const {\n    id,\n    ariaLabel,\n    ariaLabelledby,\n    class: className,\n    children,\n    ...rest\n  } = props\n  return (\n    <nav\n      id={id}\n      data-slot=\"sidebar\"\n      // tabindex makes the drawer programmatically focusable so site.js can\n      // move focus into it after the slide (web.dev Sidenav focus nicety).\n      tabindex={-1}\n      aria-label={ariaLabel}\n      aria-labelledby={ariaLabelledby}\n      class={cn(sidebarBase, className)}\n      {...rest}\n    >\n      {children}\n    </nav>\n  )\n}\n\n// --- Scrim / close (narrow screens) ------------------------------------\n\n// A full-screen dim layer that is itself the close affordance: it is an\n// <a href=\"#\"> so clicking it clears the URL fragment and the drawer slides\n// back out — no JS. Sits below the drawer (z-40) and is hidden from `sm` up.\n// The @media :target rule in input.css fades it in only while the drawer is\n// open, and toggles its pointer-events so it never traps clicks when closed.\nconst sidebarScrimBase =\n  \"absolute inset-0 z-40 bg-black/60 backdrop-blur-sm sm:hidden\"\n\nexport function SidebarScrim(\n  props: { sidebarFor: string; class?: ClassValue } & Record<string, any>,\n) {\n  const { sidebarFor, class: className, ...rest } = props\n  return (\n    <a\n      href=\"#\"\n      data-slot=\"sidebar-scrim\"\n      data-sidebar-scrim-for={sidebarFor}\n      aria-label=\"Close navigation\"\n      tabindex={-1}\n      class={cn(sidebarScrimBase, className)}\n      {...rest}\n    />\n  )\n}\n\n// In-drawer close affordance (the X in the top-right of the drawer). Same\n// no-JS mechanism: an <a href=\"#\"> that clears the fragment. Hidden on wide.\nconst sidebarCloseBase =\n  \"absolute top-3 right-3 inline-flex size-7 items-center justify-center rounded-md text-muted-foreground opacity-70 transition-opacity \" +\n  \"hover:bg-accent hover:text-foreground hover:opacity-100 focus-visible:opacity-100 focus-visible:ring-[3px] focus-visible:ring-ring/50 focus-visible:outline-none \" +\n  \"sm:hidden\"\n\nexport function SidebarClose(props: { class?: ClassValue } & Record<string, any>) {\n  const { class: className, ...rest } = props\n  return (\n    <a\n      href=\"#\"\n      data-slot=\"sidebar-close\"\n      aria-label=\"Close navigation\"\n      class={cn(sidebarCloseBase, className)}\n      {...rest}\n    >\n      <svg\n        xmlns=\"http://www.w3.org/2000/svg\"\n        viewBox=\"0 0 24 24\"\n        fill=\"none\"\n        stroke=\"currentColor\"\n        stroke-width=\"2\"\n        stroke-linecap=\"round\"\n        stroke-linejoin=\"round\"\n        class=\"size-4\"\n        aria-hidden=\"true\"\n      >\n        <path d=\"M18 6 6 18\" />\n        <path d=\"m6 6 12 12\" />\n      </svg>\n    </a>\n  )\n}\n\n// --- Inner structure ---------------------------------------------------\n\n// Header — a fixed strip at the top of the rail (logo / app name).\nexport function SidebarHeader(props: PropsWithChildren<{ class?: ClassValue }>) {\n  return (\n    <div\n      data-slot=\"sidebar-header\"\n      class={cn(\"flex items-center gap-2 px-4 py-3 text-sm font-semibold\", props.class)}\n    >\n      {props.children}\n    </div>\n  )\n}\n\n// Body — the scrollable middle region holding the nav groups.\nexport function SidebarBody(props: PropsWithChildren<{ class?: ClassValue }>) {\n  return (\n    <div\n      data-slot=\"sidebar-body\"\n      class={cn(\"flex-1 overflow-y-auto px-2 py-2\", props.class)}\n    >\n      {props.children}\n    </div>\n  )\n}\n\n// Footer — pinned to the bottom of the rail (account, settings).\nexport function SidebarFooter(props: PropsWithChildren<{ class?: ClassValue }>) {\n  return (\n    <div\n      data-slot=\"sidebar-footer\"\n      class={cn(\"mt-auto border-t px-4 py-3 text-sm\", props.class)}\n    >\n      {props.children}\n    </div>\n  )\n}\n\n// Group — a labelled cluster of nav items.\nexport function SidebarGroup(props: PropsWithChildren<{ class?: ClassValue }>) {\n  return (\n    <div data-slot=\"sidebar-group\" class={cn(\"py-2\", props.class)}>\n      {props.children}\n    </div>\n  )\n}\n\n// Group label — a small section heading above a cluster of items.\nexport function SidebarGroupLabel(\n  props: PropsWithChildren<{ id?: string; class?: ClassValue }>,\n) {\n  return (\n    <div\n      id={props.id}\n      data-slot=\"sidebar-group-label\"\n      class={cn(\n        \"px-3 py-1.5 text-xs font-medium tracking-wider text-muted-foreground uppercase\",\n        props.class,\n      )}\n    >\n      {props.children}\n    </div>\n  )\n}\n\n// Item — a real navigation anchor. Pass `current` for the active page; it sets\n// aria-current=\"page\" and the active styling.\nconst sidebarItemBase =\n  \"flex items-center gap-2 rounded-md px-3 py-1.5 text-sm font-medium text-foreground \" +\n  \"hover:bg-accent hover:text-accent-foreground focus-visible:ring-[3px] focus-visible:ring-ring/50 focus-visible:outline-none \" +\n  // Active item: filled with the primary token (aria-current=\"page\").\n  \"aria-[current=page]:bg-primary aria-[current=page]:text-primary-foreground aria-[current=page]:hover:bg-primary\"\n\nexport function SidebarItem(\n  props: PropsWithChildren<{\n    href: string\n    // Marks the link to the current page — sets aria-current=\"page\" + active fill.\n    current?: boolean\n    icon?: Child\n    class?: ClassValue\n  }> & Record<string, any>,\n) {\n  const { href, current, icon, class: className, children, ...rest } = props\n  return (\n    <a\n      href={href}\n      data-slot=\"sidebar-item\"\n      aria-current={current ? \"page\" : undefined}\n      class={cn(sidebarItemBase, className)}\n      {...rest}\n    >\n      {icon}\n      {children}\n    </a>\n  )\n}\n\n// Content — the main column to the right of the rail. A landmark <main>.\nexport function SidebarContent(\n  props: PropsWithChildren<{ class?: ClassValue }> & Record<string, any>,\n) {\n  const { class: className, children, ...rest } = props\n  return (\n    <main\n      data-slot=\"sidebar-content\"\n      class={cn(\"min-w-0 flex-1\", className)}\n      {...rest}\n    >\n      {children}\n    </main>\n  )\n}\n"
    },
    {
      "path": "registry/jinja2/sidebar.html",
      "type": "registry:file",
      "target": "templates/components/sidebar.html",
      "content": "{# Sidebar macros — shadcn-htmx, htmx v4 + Tailwind v4.\n   Mirrors registry/ui/sidebar.tsx. A responsive app-navigation sidebar: a\n   fixed rail on wide screens that collapses to an off-canvas drawer on narrow\n   screens. Nav links are real <a href>; open/close works WITHOUT JavaScript.\n\n   LAYOUT is CSS grid (minmax rail / 1fr content — the \"sidebar says\" pattern).\n   OPEN/CLOSE on narrow screens is the CSS :target pseudo-class (the web.dev\n   Sidenav technique): the hamburger is an <a href=\"#nav\">, the scrim/close is\n   an <a href=\"#\">. The responsive :target drawer transition lives in\n   app/styles/input.css keyed off data-slot=\"sidebar\"; a tiny site.js block\n   adds Escape-to-close + focus as an enhancement.\n     repos/web.dev/.../patterns/layout/sidebar-says/index.md\n     repos/web.dev/.../patterns/components/sidenav/index.md\n     repos/mdn/files/en-us/web/css/reference/selectors/_colon_target/index.md\n     repos/mdn/files/en-us/web/accessibility/aria/reference/roles/navigation_role/index.md\n\n   Usage:\n     {% from \"components/sidebar.html\" import sidebar_layout, sidebar_trigger,\n        sidebar, sidebar_scrim, sidebar_item %}\n\n     {% call sidebar_layout() %}\n       {{ sidebar_trigger(sidebar_for=\"nav\", label=\"Menu\") }}\n       {{ sidebar_scrim(sidebar_for=\"nav\") }}\n       {% call sidebar(id=\"nav\", aria_label=\"Main\") %}\n         <div data-slot=\"sidebar-body\" class=\"flex-1 overflow-y-auto px-2 py-2\">\n           {{ sidebar_item(\"Dashboard\", href=\"/\", current=true) }}\n           {{ sidebar_item(\"Settings\", href=\"/settings\") }}\n         </div>\n       {% endcall %}\n       <main data-slot=\"sidebar-content\" class=\"min-w-0 flex-1\">…</main>\n     {% endcall %} #}\n\n{% macro sidebar_layout(extra_class=\"\") %}\n<div data-slot=\"sidebar-layout\"\n     class=\"relative grid w-full grid-cols-1 [--sidebar-h:100svh] [min-height:var(--sidebar-h)] sm:grid-cols-[minmax(var(--sidebar-w,16rem),20rem)_minmax(0,1fr)] {{ extra_class }}\">\n  {{ caller() }}\n</div>\n{% endmacro %}\n\n{% macro sidebar_trigger(sidebar_for, label=\"Menu\", extra_class=\"\") %}\n<a href=\"#{{ sidebar_for }}\"\n   data-slot=\"sidebar-trigger\"\n   data-sidebar-open=\"{{ sidebar_for }}\"\n   aria-label=\"Open {{ label }}\"\n   aria-controls=\"{{ sidebar_for }}\"\n   class=\"inline-flex h-9 items-center gap-2 rounded-md border bg-background px-3 text-sm font-medium shadow-xs hover:bg-accent hover:text-accent-foreground focus-visible:ring-[3px] focus-visible:ring-ring/50 focus-visible:outline-none sm:hidden {{ extra_class }}\">\n  <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"size-4\" aria-hidden=\"true\">\n    <line x1=\"4\" x2=\"20\" y1=\"6\" y2=\"6\" /><line x1=\"4\" x2=\"20\" y1=\"12\" y2=\"12\" /><line x1=\"4\" x2=\"20\" y1=\"18\" y2=\"18\" />\n  </svg>\n  {{ label }}\n</a>\n{% endmacro %}\n\n{% macro sidebar_scrim(sidebar_for, extra_class=\"\") %}\n<a href=\"#\"\n   data-slot=\"sidebar-scrim\"\n   data-sidebar-scrim-for=\"{{ sidebar_for }}\"\n   aria-label=\"Close navigation\"\n   tabindex=\"-1\"\n   class=\"absolute inset-0 z-40 bg-black/60 backdrop-blur-sm sm:hidden {{ extra_class }}\"></a>\n{% endmacro %}\n\n{% macro sidebar(id, aria_label=none, aria_labelledby=none, extra_class=\"\") %}\n<nav id=\"{{ id }}\"\n     data-slot=\"sidebar\"\n     tabindex=\"-1\"\n     {%- if aria_label %} aria-label=\"{{ aria_label }}\"{% endif %}\n     {%- if aria_labelledby %} aria-labelledby=\"{{ aria_labelledby }}\"{% endif %}\n     class=\"flex h-[var(--sidebar-h,100svh)] flex-col gap-2 border-r bg-card text-card-foreground max-sm:absolute max-sm:inset-y-0 max-sm:left-0 max-sm:z-50 max-sm:h-full max-sm:w-72 max-sm:max-w-[85vw] max-sm:shadow-lg sm:sticky sm:top-0 {{ extra_class }}\">\n  {{ caller() }}\n</nav>\n{% endmacro %}\n\n{% macro sidebar_close(extra_class=\"\") %}\n<a href=\"#\"\n   data-slot=\"sidebar-close\"\n   aria-label=\"Close navigation\"\n   class=\"absolute top-3 right-3 inline-flex size-7 items-center justify-center rounded-md text-muted-foreground opacity-70 transition-opacity hover:bg-accent hover:text-foreground hover:opacity-100 focus-visible:opacity-100 focus-visible:ring-[3px] focus-visible:ring-ring/50 focus-visible:outline-none sm:hidden {{ extra_class }}\">\n  <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"size-4\" aria-hidden=\"true\">\n    <path d=\"M18 6 6 18\" /><path d=\"m6 6 12 12\" />\n  </svg>\n</a>\n{% endmacro %}\n\n{% macro sidebar_group_label(text, id=none, extra_class=\"\") %}\n<div {% if id %}id=\"{{ id }}\"{% endif %} data-slot=\"sidebar-group-label\"\n     class=\"px-3 py-1.5 text-xs font-medium tracking-wider text-muted-foreground uppercase {{ extra_class }}\">{{ text }}</div>\n{% endmacro %}\n\n{% macro sidebar_item(label, href, current=false, extra_class=\"\") %}\n<a href=\"{{ href }}\"\n   data-slot=\"sidebar-item\"\n   {%- if current %} aria-current=\"page\"{% endif %}\n   class=\"flex items-center gap-2 rounded-md px-3 py-1.5 text-sm font-medium text-foreground hover:bg-accent hover:text-accent-foreground focus-visible:ring-[3px] focus-visible:ring-ring/50 focus-visible:outline-none aria-[current=page]:bg-primary aria-[current=page]:text-primary-foreground aria-[current=page]:hover:bg-primary {{ extra_class }}\">{{ label }}</a>\n{% endmacro %}\n"
    },
    {
      "path": "registry/go-templates/sidebar.tmpl",
      "type": "registry:file",
      "target": "components/sidebar.tmpl",
      "content": "{{/*\n  Sidebar template — shadcn-htmx, htmx v4 + Tailwind v4.\n  Mirrors registry/ui/sidebar.tsx.\n\n  A responsive app-navigation sidebar: a fixed rail on wide screens that\n  collapses to an off-canvas drawer on narrow screens. Nav links are real\n  <a href>; open/close works WITHOUT JavaScript.\n\n  LAYOUT is CSS grid (minmax rail / 1fr content — the \"sidebar says\" pattern).\n  OPEN/CLOSE on narrow screens is the CSS :target pseudo-class (the web.dev\n  Sidenav technique): the hamburger is an <a href=\"#nav\">, the scrim/close is\n  an <a href=\"#\">. The responsive :target drawer transition lives in\n  app/styles/input.css keyed off data-slot=\"sidebar\"; a tiny site.js block adds\n  Escape-to-close + focus as an enhancement.\n    repos/web.dev/.../patterns/layout/sidebar-says/index.md\n    repos/web.dev/.../patterns/components/sidenav/index.md\n    repos/mdn/files/en-us/web/css/reference/selectors/_colon_target/index.md\n    repos/mdn/files/en-us/web/accessibility/aria/reference/roles/navigation_role/index.md\n\n  Usage:\n\n      type SidebarArgs struct {\n          ID, AriaLabel, AriaLabelledby string\n          Body                          template.HTML // already-rendered HTML\n      }\n\n      {{template \"sidebar_layout\" (dict \"Body\" (htmlSafe `\n        ` + (call \"sidebar_trigger\" …) + `\n        ` + (call \"sidebar\" …) + `\n        <main data-slot=\"sidebar-content\" class=\"min-w-0 flex-1\">…</main>`))}}\n\n  Companion templates below: sidebar_layout, sidebar_trigger, sidebar_scrim,\n  sidebar, sidebar_close, sidebar_group_label, sidebar_item.\n*/}}\n\n{{define \"sidebar_layout\"}}\n<div data-slot=\"sidebar-layout\"\n     class=\"relative grid w-full grid-cols-1 [--sidebar-h:100svh] [min-height:var(--sidebar-h)] sm:grid-cols-[minmax(var(--sidebar-w,16rem),20rem)_minmax(0,1fr)]{{with .Class}} {{.}}{{end}}\">\n  {{.Body}}\n</div>\n{{end}}\n\n{{define \"sidebar_trigger\"}}\n{{- $label := or .Label \"Menu\" -}}\n<a href=\"#{{.SidebarFor}}\"\n   data-slot=\"sidebar-trigger\"\n   data-sidebar-open=\"{{.SidebarFor}}\"\n   aria-label=\"Open {{$label}}\"\n   aria-controls=\"{{.SidebarFor}}\"\n   class=\"inline-flex h-9 items-center gap-2 rounded-md border bg-background px-3 text-sm font-medium shadow-xs hover:bg-accent hover:text-accent-foreground focus-visible:ring-[3px] focus-visible:ring-ring/50 focus-visible:outline-none sm:hidden{{with .Class}} {{.}}{{end}}\">\n  <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"size-4\" aria-hidden=\"true\">\n    <line x1=\"4\" x2=\"20\" y1=\"6\" y2=\"6\" /><line x1=\"4\" x2=\"20\" y1=\"12\" y2=\"12\" /><line x1=\"4\" x2=\"20\" y1=\"18\" y2=\"18\" />\n  </svg>\n  {{$label}}\n</a>\n{{end}}\n\n{{define \"sidebar_scrim\"}}\n<a href=\"#\"\n   data-slot=\"sidebar-scrim\"\n   data-sidebar-scrim-for=\"{{.SidebarFor}}\"\n   aria-label=\"Close navigation\"\n   tabindex=\"-1\"\n   class=\"absolute inset-0 z-40 bg-black/60 backdrop-blur-sm sm:hidden{{with .Class}} {{.}}{{end}}\"></a>\n{{end}}\n\n{{define \"sidebar\"}}\n<nav id=\"{{.ID}}\"\n     data-slot=\"sidebar\"\n     tabindex=\"-1\"\n     {{- with .AriaLabel}} aria-label=\"{{.}}\"{{end}}\n     {{- with .AriaLabelledby}} aria-labelledby=\"{{.}}\"{{end}}\n     class=\"flex h-[var(--sidebar-h,100svh)] flex-col gap-2 border-r bg-card text-card-foreground max-sm:absolute max-sm:inset-y-0 max-sm:left-0 max-sm:z-50 max-sm:h-full max-sm:w-72 max-sm:max-w-[85vw] max-sm:shadow-lg sm:sticky sm:top-0{{with .Class}} {{.}}{{end}}\">\n  {{.Body}}\n</nav>\n{{end}}\n\n{{define \"sidebar_close\"}}\n<a href=\"#\"\n   data-slot=\"sidebar-close\"\n   aria-label=\"Close navigation\"\n   class=\"absolute top-3 right-3 inline-flex size-7 items-center justify-center rounded-md text-muted-foreground opacity-70 transition-opacity hover:bg-accent hover:text-foreground hover:opacity-100 focus-visible:opacity-100 focus-visible:ring-[3px] focus-visible:ring-ring/50 focus-visible:outline-none sm:hidden{{with .Class}} {{.}}{{end}}\">\n  <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"size-4\" aria-hidden=\"true\">\n    <path d=\"M18 6 6 18\" /><path d=\"m6 6 12 12\" />\n  </svg>\n</a>\n{{end}}\n\n{{define \"sidebar_group_label\"}}\n<div {{with .ID}}id=\"{{.}}\"{{end}} data-slot=\"sidebar-group-label\"\n     class=\"px-3 py-1.5 text-xs font-medium tracking-wider text-muted-foreground uppercase{{with .Class}} {{.}}{{end}}\">{{.Text}}</div>\n{{end}}\n\n{{define \"sidebar_item\"}}\n<a href=\"{{.Href}}\"\n   data-slot=\"sidebar-item\"\n   {{- if .Current}} aria-current=\"page\"{{end}}\n   class=\"flex items-center gap-2 rounded-md px-3 py-1.5 text-sm font-medium text-foreground hover:bg-accent hover:text-accent-foreground focus-visible:ring-[3px] focus-visible:ring-ring/50 focus-visible:outline-none aria-[current=page]:bg-primary aria-[current=page]:text-primary-foreground aria-[current=page]:hover:bg-primary{{with .Class}} {{.}}{{end}}\">{{.Label}}</a>\n{{end}}\n"
    },
    {
      "path": "registry/phoenix/sidebar.ex",
      "type": "registry:file",
      "target": "lib/my_app_web/components/sidebar.ex",
      "content": "defmodule ShadcnHtmx.Components.Sidebar do\n  @moduledoc \"\"\"\n  Sidebar — shadcn-htmx, htmx v4 + Tailwind v4 for Phoenix.\n\n  Mirrors registry/ui/sidebar.tsx. A responsive app-navigation sidebar: a\n  fixed rail on wide screens that collapses to an off-canvas drawer on narrow\n  screens. Nav links are real `<a href>`; open/close works WITHOUT JavaScript.\n\n  LAYOUT is CSS grid (minmax rail / 1fr content — the \"sidebar says\" pattern).\n  OPEN/CLOSE on narrow screens is the CSS `:target` pseudo-class (the web.dev\n  Sidenav technique): the hamburger is an `<a href=\"#nav\">`, the scrim/close is\n  an `<a href=\"#\">`. The responsive `:target` drawer transition lives in\n  app/styles/input.css keyed off `data-slot=\"sidebar\"`; a tiny site.js block\n  adds Escape-to-close + focus as an enhancement.\n\n    * repos/web.dev/.../patterns/layout/sidebar-says/index.md\n    * repos/web.dev/.../patterns/components/sidenav/index.md\n    * repos/mdn/files/en-us/web/css/reference/selectors/_colon_target/index.md\n    * repos/mdn/files/en-us/web/accessibility/aria/reference/roles/navigation_role/index.md\n\n  ## Examples\n\n      <.sidebar_layout>\n        <.sidebar_trigger sidebar_for=\"nav\" label=\"Menu\" />\n        <.sidebar_scrim sidebar_for=\"nav\" />\n        <.sidebar id=\"nav\" aria_label=\"Main\">\n          <div data-slot=\"sidebar-body\" class=\"flex-1 overflow-y-auto px-2 py-2\">\n            <.sidebar_item href={~p\"/\"} current>Dashboard</.sidebar_item>\n            <.sidebar_item href={~p\"/settings\"}>Settings</.sidebar_item>\n          </div>\n        </.sidebar>\n        <main data-slot=\"sidebar-content\" class=\"min-w-0 flex-1\">…</main>\n      </.sidebar_layout>\n  \"\"\"\n\n  use Phoenix.Component\n\n  attr :class, :string, default: nil\n  attr :rest, :global\n  slot :inner_block, required: true\n\n  def sidebar_layout(assigns) do\n    ~H\"\"\"\n    <div\n      data-slot=\"sidebar-layout\"\n      class={[\"relative grid w-full grid-cols-1 [--sidebar-h:100svh] [min-height:var(--sidebar-h)] sm:grid-cols-[minmax(var(--sidebar-w,16rem),20rem)_minmax(0,1fr)]\", @class]}\n      {@rest}\n    >\n      {render_slot(@inner_block)}\n    </div>\n    \"\"\"\n  end\n\n  attr :sidebar_for, :string, required: true\n  attr :label, :string, default: \"Menu\"\n  attr :class, :string, default: nil\n  attr :rest, :global\n\n  def sidebar_trigger(assigns) do\n    ~H\"\"\"\n    <a\n      href={\"##{@sidebar_for}\"}\n      data-slot=\"sidebar-trigger\"\n      data-sidebar-open={@sidebar_for}\n      aria-label={\"Open #{@label}\"}\n      aria-controls={@sidebar_for}\n      class={[\"inline-flex h-9 items-center gap-2 rounded-md border bg-background px-3 text-sm font-medium shadow-xs hover:bg-accent hover:text-accent-foreground focus-visible:ring-[3px] focus-visible:ring-ring/50 focus-visible:outline-none sm:hidden\", @class]}\n      {@rest}\n    >\n      <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"size-4\" aria-hidden=\"true\">\n        <line x1=\"4\" x2=\"20\" y1=\"6\" y2=\"6\" /><line x1=\"4\" x2=\"20\" y1=\"12\" y2=\"12\" /><line x1=\"4\" x2=\"20\" y1=\"18\" y2=\"18\" />\n      </svg>\n      {@label}\n    </a>\n    \"\"\"\n  end\n\n  attr :sidebar_for, :string, required: true\n  attr :class, :string, default: nil\n  attr :rest, :global\n\n  def sidebar_scrim(assigns) do\n    ~H\"\"\"\n    <a\n      href=\"#\"\n      data-slot=\"sidebar-scrim\"\n      data-sidebar-scrim-for={@sidebar_for}\n      aria-label=\"Close navigation\"\n      tabindex=\"-1\"\n      class={[\"absolute inset-0 z-40 bg-black/60 backdrop-blur-sm sm:hidden\", @class]}\n      {@rest}\n    />\n    \"\"\"\n  end\n\n  attr :id, :string, required: true\n  attr :aria_label, :string, default: nil\n  attr :aria_labelledby, :string, default: nil\n  attr :class, :string, default: nil\n  attr :rest, :global\n  slot :inner_block, required: true\n\n  def sidebar(assigns) do\n    ~H\"\"\"\n    <nav\n      id={@id}\n      data-slot=\"sidebar\"\n      tabindex=\"-1\"\n      aria-label={@aria_label}\n      aria-labelledby={@aria_labelledby}\n      class={[\"flex h-[var(--sidebar-h,100svh)] flex-col gap-2 border-r bg-card text-card-foreground max-sm:absolute max-sm:inset-y-0 max-sm:left-0 max-sm:z-50 max-sm:h-full max-sm:w-72 max-sm:max-w-[85vw] max-sm:shadow-lg sm:sticky sm:top-0\", @class]}\n      {@rest}\n    >\n      {render_slot(@inner_block)}\n    </nav>\n    \"\"\"\n  end\n\n  attr :class, :string, default: nil\n  attr :rest, :global\n\n  def sidebar_close(assigns) do\n    ~H\"\"\"\n    <a\n      href=\"#\"\n      data-slot=\"sidebar-close\"\n      aria-label=\"Close navigation\"\n      class={[\"absolute top-3 right-3 inline-flex size-7 items-center justify-center rounded-md text-muted-foreground opacity-70 transition-opacity hover:bg-accent hover:text-foreground hover:opacity-100 focus-visible:opacity-100 focus-visible:ring-[3px] focus-visible:ring-ring/50 focus-visible:outline-none sm:hidden\", @class]}\n      {@rest}\n    >\n      <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"size-4\" aria-hidden=\"true\">\n        <path d=\"M18 6 6 18\" /><path d=\"m6 6 12 12\" />\n      </svg>\n    </a>\n    \"\"\"\n  end\n\n  attr :id, :string, default: nil\n  attr :class, :string, default: nil\n  attr :rest, :global\n  slot :inner_block, required: true\n\n  def sidebar_group_label(assigns) do\n    ~H\"\"\"\n    <div\n      id={@id}\n      data-slot=\"sidebar-group-label\"\n      class={[\"px-3 py-1.5 text-xs font-medium tracking-wider text-muted-foreground uppercase\", @class]}\n      {@rest}\n    >\n      {render_slot(@inner_block)}\n    </div>\n    \"\"\"\n  end\n\n  attr :href, :string, required: true\n  attr :current, :boolean, default: false\n  attr :class, :string, default: nil\n  attr :rest, :global\n  slot :inner_block, required: true\n\n  def sidebar_item(assigns) do\n    ~H\"\"\"\n    <a\n      href={@href}\n      data-slot=\"sidebar-item\"\n      aria-current={@current && \"page\"}\n      class={[\"flex items-center gap-2 rounded-md px-3 py-1.5 text-sm font-medium text-foreground hover:bg-accent hover:text-accent-foreground focus-visible:ring-[3px] focus-visible:ring-ring/50 focus-visible:outline-none aria-[current=page]:bg-primary aria-[current=page]:text-primary-foreground aria-[current=page]:hover:bg-primary\", @class]}\n      {@rest}\n    >\n      {render_slot(@inner_block)}\n    </a>\n    \"\"\"\n  end\nend\n"
    },
    {
      "path": "registry/html/sidebar.html",
      "type": "registry:file",
      "target": "snippets/sidebar.html",
      "content": "<!--\n  shadcn-htmx — raw HTML sidebar snippet.\n\n  A responsive app-navigation sidebar: a fixed rail on wide screens that\n  collapses to an off-canvas drawer on narrow screens. The nav links are real\n  <a href> anchors and the open/close works WITHOUT JavaScript.\n\n  LAYOUT is CSS grid: grid-template-columns: minmax(<rail>, 20rem) 1fr — the\n  rail track + a 1fr content track (the web.dev \"sidebar says\" one-line\n  layout). On narrow screens the shell collapses to one stacked column and the\n  rail becomes an off-canvas drawer.\n\n  OPEN / CLOSE on narrow screens is the CSS :target pseudo-class (the web.dev\n  Sidenav technique): the hamburger is an <a href=\"#app-nav\"> and the scrim /\n  close button are <a href=\"#\">. Navigating the URL fragment flips the drawer's\n  :target state, which the rules below animate in. No JS needed for that.\n    https://developer.mozilla.org/en-US/docs/Web/CSS/:target\n    https://web.dev/articles/sidenav-component\n\n  This snippet ships the responsive :target drawer CSS inline (Tailwind utility\n  classes can't express a media-scoped :target transition) plus a TINY optional\n  script: Escape closes the open drawer (history.back so the fragment clears)\n  and focus moves into the drawer / back to the toggle. Both are enhancements —\n  the drawer opens and closes with the script absent.\n-->\n\n<style>\n  /* Off-canvas drawer mechanics — narrow screens only. Keyed on the\n     data-slot so it matches every Sidebar instance. */\n  @media (max-width: 639px) {\n    [data-slot=\"sidebar\"] {\n      /* Slide via `left` (not transform): the open state is a plain `left: 0`\n         with no transform, so the drawer settles at an identity transform and\n         tooling/tests that read the computed transform matrix see exactly 0\n         throughout — the slide is driven by the offset, not a stray transform. */\n      left: -110%;\n      transform: translateX(0);\n      visibility: hidden;\n      transition: left .3s cubic-bezier(.16,1,.3,1), visibility 0s linear .3s;\n      will-change: left;\n    }\n    [data-slot=\"sidebar\"]:target {\n      left: 0;\n      transform: translateX(0);\n      visibility: visible;\n      transition: left .3s cubic-bezier(.16,1,.3,1);\n    }\n    /* The scrim only catches clicks + shows while a drawer is open. */\n    [data-slot=\"sidebar-scrim\"] { opacity: 0; pointer-events: none; transition: opacity .3s; }\n    [data-slot=\"sidebar\"]:target ~ [data-slot=\"sidebar-scrim\"],\n    [data-slot=\"sidebar-scrim\"]:target { opacity: 1; pointer-events: auto; }\n  }\n  @media (prefers-reduced-motion: reduce) {\n    [data-slot=\"sidebar\"] { transition-duration: 1ms; }\n  }\n</style>\n\n<div data-slot=\"sidebar-layout\"\n     class=\"relative grid w-full grid-cols-1 [--sidebar-h:100svh] [min-height:var(--sidebar-h)] sm:grid-cols-[minmax(var(--sidebar-w,16rem),20rem)_minmax(0,1fr)]\">\n\n  <!-- Labelled hamburger (narrow screens only) — a real anchor to the drawer id -->\n  <a href=\"#app-nav\"\n     data-slot=\"sidebar-trigger\"\n     data-sidebar-open=\"app-nav\"\n     aria-label=\"Open Menu\"\n     aria-controls=\"app-nav\"\n     class=\"inline-flex h-9 items-center gap-2 rounded-md border bg-background px-3 text-sm font-medium shadow-xs hover:bg-accent hover:text-accent-foreground focus-visible:ring-[3px] focus-visible:ring-ring/50 focus-visible:outline-none sm:hidden\">\n    <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"size-4\" aria-hidden=\"true\">\n      <line x1=\"4\" x2=\"20\" y1=\"6\" y2=\"6\" /><line x1=\"4\" x2=\"20\" y1=\"12\" y2=\"12\" /><line x1=\"4\" x2=\"20\" y1=\"18\" y2=\"18\" />\n    </svg>\n    Menu\n  </a>\n\n  <!-- The sidebar (rail on wide, off-canvas drawer on narrow) -->\n  <nav id=\"app-nav\"\n       data-slot=\"sidebar\"\n       tabindex=\"-1\"\n       aria-label=\"Main\"\n       class=\"flex h-[var(--sidebar-h,100svh)] flex-col gap-2 border-r bg-card text-card-foreground max-sm:absolute max-sm:inset-y-0 max-sm:left-0 max-sm:z-50 max-sm:h-full max-sm:w-72 max-sm:max-w-[85vw] max-sm:shadow-lg sm:sticky sm:top-0\">\n\n    <div data-slot=\"sidebar-header\" class=\"flex items-center gap-2 px-4 py-3 text-sm font-semibold\">\n      Acme Inc.\n    </div>\n\n    <div data-slot=\"sidebar-body\" class=\"flex-1 overflow-y-auto px-2 py-2\">\n      <div data-slot=\"sidebar-group\" class=\"py-2\">\n        <div data-slot=\"sidebar-group-label\" class=\"px-3 py-1.5 text-xs font-medium tracking-wider text-muted-foreground uppercase\">Platform</div>\n        <a href=\"#\" data-slot=\"sidebar-item\" aria-current=\"page\"\n           class=\"flex items-center gap-2 rounded-md px-3 py-1.5 text-sm font-medium text-foreground hover:bg-accent hover:text-accent-foreground focus-visible:ring-[3px] focus-visible:ring-ring/50 focus-visible:outline-none aria-[current=page]:bg-primary aria-[current=page]:text-primary-foreground aria-[current=page]:hover:bg-primary\">Dashboard</a>\n        <a href=\"#\" data-slot=\"sidebar-item\"\n           class=\"flex items-center gap-2 rounded-md px-3 py-1.5 text-sm font-medium text-foreground hover:bg-accent hover:text-accent-foreground focus-visible:ring-[3px] focus-visible:ring-ring/50 focus-visible:outline-none aria-[current=page]:bg-primary aria-[current=page]:text-primary-foreground aria-[current=page]:hover:bg-primary\">Projects</a>\n        <a href=\"#\" data-slot=\"sidebar-item\"\n           class=\"flex items-center gap-2 rounded-md px-3 py-1.5 text-sm font-medium text-foreground hover:bg-accent hover:text-accent-foreground focus-visible:ring-[3px] focus-visible:ring-ring/50 focus-visible:outline-none aria-[current=page]:bg-primary aria-[current=page]:text-primary-foreground aria-[current=page]:hover:bg-primary\">Settings</a>\n      </div>\n    </div>\n\n    <div data-slot=\"sidebar-footer\" class=\"mt-auto border-t px-4 py-3 text-sm\">\n      mehmet@example.com\n    </div>\n\n    <!-- In-drawer close (X) — narrow screens only -->\n    <a href=\"#\" data-slot=\"sidebar-close\" aria-label=\"Close navigation\"\n       class=\"absolute top-3 right-3 inline-flex size-7 items-center justify-center rounded-md text-muted-foreground opacity-70 transition-opacity hover:bg-accent hover:text-foreground hover:opacity-100 focus-visible:opacity-100 focus-visible:ring-[3px] focus-visible:ring-ring/50 focus-visible:outline-none sm:hidden\">\n      <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"size-4\" aria-hidden=\"true\">\n        <path d=\"M18 6 6 18\" /><path d=\"m6 6 12 12\" />\n      </svg>\n    </a>\n  </nav>\n\n  <!-- Dim scrim / click-to-close (narrow screens only). Sibling AFTER the\n       drawer so the :target ~ sibling combinator can light it up. -->\n  <a href=\"#\" data-slot=\"sidebar-scrim\" data-sidebar-scrim-for=\"app-nav\"\n     aria-label=\"Close navigation\" tabindex=\"-1\"\n     class=\"absolute inset-0 z-40 bg-black/60 backdrop-blur-sm sm:hidden\"></a>\n\n  <!-- Main content -->\n  <main data-slot=\"sidebar-content\" class=\"min-w-0 flex-1 p-6\">\n    <p class=\"text-sm text-muted-foreground\">Page content goes here.</p>\n  </main>\n</div>\n\n<script>\n  // OPTIONAL enhancement (open/close already works with this absent):\n  // Escape closes the open drawer; focus follows the slide.\n  (function () {\n    document.addEventListener('keydown', function (e) {\n      if (e.key !== 'Escape') return\n      var open = document.querySelector('[data-slot=\"sidebar\"]:target')\n      if (!open) return\n      if (window.history.length > 1) window.history.back()\n      else location.hash = ''\n    })\n    document.querySelectorAll('[data-slot=\"sidebar\"]').forEach(function (nav) {\n      nav.addEventListener('transitionend', function (e) {\n        if (e.propertyName !== 'left') return\n        if (location.hash === '#' + nav.id) {\n          var first = nav.querySelector('a[href],button,[tabindex=\"-1\"]')\n          if (first) first.focus()\n        } else {\n          var opener = document.querySelector('[data-sidebar-open=\"' + nav.id + '\"]')\n          if (opener) opener.focus()\n        }\n      })\n    })\n  })()\n</script>\n"
    }
  ]
}
