{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "menubar",
  "type": "registry:ui",
  "title": "Menubar",
  "description": "A visually-persistent, app-style horizontal bar of menus built on the native Popover API and the WAI-ARIA menu/menubar keyboard contract (roving tabindex, arrow navigation, type-ahead, ESC).",
  "registryDependencies": [
    "utils"
  ],
  "files": [
    {
      "path": "registry/ui/menubar.tsx",
      "type": "registry:ui",
      "target": "components/ui/menubar.tsx",
      "content": "/** @jsxImportSource hono/jsx */\nimport type { PropsWithChildren } from \"hono/jsx\"\nimport { cn, type ClassValue } from \"@/registry/lib/cn\"\n\n// Menubar — shadcn-htmx, htmx v4 + Tailwind v4.\n//\n// An app-style, visually-persistent horizontal bar of menus (File / Edit /\n// View …). Each top-level item is a button that opens a submenu.\n//\n// shadcn-ui ships @radix-ui/react-menubar; we rebuild the SAME semantics on\n// web standards instead of a JS-driven overlay:\n//   - The bar is role=\"menubar\" (default aria-orientation horizontal).\n//   - Each trigger is role=\"menuitem\" with aria-haspopup=\"menu\" +\n//     aria-expanded, and opens its submenu via the native Popover API\n//     (popovertarget + popover=\"auto\"). The platform then gives us light\n//     dismiss, ESC, top-layer rendering, and focus restoration for free —\n//     exactly like dropdown-menu.tsx.\n//   - Submenu items are role=\"menuitem\" and reuse the dropdown-menu item\n//     styling/contract.\n//\n// The APG composite-widget keyboard contract (roving tabindex on the bar,\n// ArrowLeft/Right along the bar, ArrowDown to open + focus first item,\n// ArrowUp/Down inside a menu, Home/End, type-ahead, ESC) lives in\n// public/site.js keyed on data-slot=\"menubar\" — the platform does not\n// provide composite menu navigation.\n//\n// Refs:\n//   repos/aria-practices/content/patterns/menubar/menu-and-menubar-pattern.html\n//     (Menu and Menubar pattern: roles/states + full keyboard contract)\n//   repos/aria-practices/content/patterns/menubar/examples/menubar-navigation.html\n//     (roving tabindex: first menuitem tabindex=0, parents carry\n//      aria-haspopup + aria-expanded)\n//   repos/mdn/files/en-us/web/api/popover_api/\n//   repos/mdn/files/en-us/web/accessibility/aria/reference/roles/menubar_role/\n//   registry/ui/dropdown-menu.tsx (submenu/popover + item styling reused here)\n\n// --- Root --------------------------------------------------------------\n\nconst menubarBase =\n  \"inline-flex h-9 items-center gap-0.5 rounded-md border bg-background p-1 shadow-xs\"\n\nexport function Menubar(\n  props: PropsWithChildren<{\n    ariaLabel?: string\n    // Id of a visible element (e.g. a heading) that names the menubar.\n    // APG prefers aria-labelledby over aria-label when a visible label\n    // exists (menu-and-menubar-pattern.html:220-222; MDN menubar_role:32).\n    ariaLabelledby?: string\n    class?: ClassValue\n  }>,\n) {\n  const { ariaLabel, ariaLabelledby, class: className, children } = props\n  return (\n    <div\n      role=\"menubar\"\n      data-slot=\"menubar\"\n      aria-label={ariaLabel}\n      aria-labelledby={ariaLabelledby}\n      class={cn(menubarBase, className)}\n    >\n      {children}\n    </div>\n  )\n}\n\n// --- Menu (trigger + popover submenu) ----------------------------------\n\nconst menubarTriggerBase =\n  \"flex select-none items-center rounded-sm px-3 py-1 text-sm font-medium outline-none \" +\n  \"focus:bg-accent focus:text-accent-foreground \" +\n  \"aria-[expanded=true]:bg-accent aria-[expanded=true]:text-accent-foreground \" +\n  \"data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50\"\n\nconst menubarContentBase =\n  \"z-50 m-0 min-w-[12rem] rounded-md border bg-popover p-1 text-popover-foreground shadow-md outline-none \" +\n  \"[&:not(:popover-open)]:hidden \" +\n  \"[&:popover-open]:animate-[scn-popover-in_120ms_ease-out] \" +\n  \"anchor-popover-bottom\"\n\n// MenubarMenu pairs a top-level trigger with its submenu popover. The\n// trigger is the parent menuitem (aria-haspopup + aria-expanded); the\n// popover is role=\"menu\". `id` wires popovertarget → popover.\nexport function MenubarMenu(\n  props: PropsWithChildren<{\n    // Visible label of the top-level menu (e.g. \"File\").\n    label: string\n    // Unique id; the trigger's popovertarget and the menu's id.\n    id: string\n    disabled?: boolean\n    triggerClass?: ClassValue\n    contentClass?: ClassValue\n  }>,\n) {\n  const { label, id, disabled, triggerClass, contentClass, children } = props\n  return (\n    <div data-slot=\"menubar-menu\" class=\"contents\">\n      <button\n        type=\"button\"\n        role=\"menuitem\"\n        // Roving tabindex: site.js promotes the first enabled trigger to\n        // tabindex=\"0\" on boot; the rest stay at -1.\n        tabindex={-1}\n        popovertarget={id}\n        popovertargetaction=\"toggle\"\n        aria-haspopup=\"menu\"\n        aria-expanded=\"false\"\n        data-slot=\"menubar-trigger\"\n        data-menu-for={id}\n        data-disabled={disabled ? \"true\" : undefined}\n        disabled={disabled || undefined}\n        class={cn(menubarTriggerBase, triggerClass)}\n      >\n        {label}\n      </button>\n      <div\n        id={id}\n        popover=\"auto\"\n        role=\"menu\"\n        aria-label={label}\n        data-slot=\"menubar-content\"\n        data-side=\"bottom\"\n        class={cn(menubarContentBase, contentClass)}\n      >\n        {children}\n      </div>\n    </div>\n  )\n}\n\n// --- Items (mirrors dropdown-menu item contract) -----------------------\n\nconst itemBase =\n  \"relative flex w-full cursor-pointer items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none select-none \" +\n  \"focus:bg-accent focus:text-accent-foreground \" +\n  \"data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50 \" +\n  \"[&_svg]:size-4 [&_svg]:shrink-0\"\n\nexport type MenubarItemVariant = \"default\" | \"destructive\"\n\nconst variantMap: Record<MenubarItemVariant, string> = {\n  default: \"\",\n  destructive: \"text-destructive focus:bg-destructive/10 focus:text-destructive\",\n}\n\ntype MenubarItemProps = PropsWithChildren<{\n  onclick?: string\n  href?: string\n  disabled?: boolean\n  variant?: MenubarItemVariant\n  class?: ClassValue\n  \"hx-get\"?: string\n  \"hx-post\"?: string\n  \"hx-target\"?: string\n  \"hx-swap\"?: string\n  \"hx-trigger\"?: string\n}>\n\nexport function MenubarItem(props: MenubarItemProps) {\n  const { children, onclick, href, disabled, variant = \"default\", class: className, ...rest } = props\n  const Tag: any = href ? \"a\" : \"button\"\n  return (\n    <Tag\n      role=\"menuitem\"\n      type={href ? undefined : \"button\"}\n      tabindex={-1}\n      href={href}\n      onclick={onclick}\n      data-slot=\"menubar-item\"\n      data-disabled={disabled ? \"true\" : undefined}\n      class={cn(itemBase, variantMap[variant], className)}\n      {...rest}\n    >\n      {children}\n    </Tag>\n  )\n}\n\nexport function MenubarSeparator(props: { class?: ClassValue }) {\n  return (\n    <div\n      role=\"separator\"\n      aria-orientation=\"horizontal\"\n      data-slot=\"menubar-separator\"\n      class={cn(\"-mx-1 my-1 h-px bg-border\", props.class)}\n    />\n  )\n}\n\nexport function MenubarLabel(props: PropsWithChildren<{ class?: ClassValue }>) {\n  return (\n    <div\n      data-slot=\"menubar-label\"\n      class={cn(\"px-2 py-1.5 text-xs font-semibold tracking-wider text-muted-foreground uppercase\", props.class)}\n    >\n      {props.children}\n    </div>\n  )\n}\n\n// MenubarShortcut — presentational accelerator hint (e.g. \"⌘S\") shown at\n// the trailing edge of a MenubarItem. aria-hidden so AT does not announce\n// the visual glyph; expose the real shortcut to AT via aria-keyshortcuts on\n// the MenubarItem itself (passed through with the rest spread). See\n// repos/mdn/files/en-us/web/accessibility/aria/reference/attributes/aria-keyshortcuts/index.md\nexport function MenubarShortcut(props: PropsWithChildren<{ class?: ClassValue }>) {\n  return (\n    <span\n      aria-hidden=\"true\"\n      data-slot=\"menubar-shortcut\"\n      class={cn(\"ml-auto text-xs tracking-widest text-muted-foreground\", props.class)}\n    >\n      {props.children}\n    </span>\n  )\n}\n"
    },
    {
      "path": "registry/jinja2/menubar.html",
      "type": "registry:file",
      "target": "templates/components/menubar.html",
      "content": "{# Menubar macros — shadcn-htmx, htmx v4 + Tailwind v4.\n   App-style horizontal menubar. role=\"menubar\" > role=\"menuitem\" triggers\n   open native [popover] submenus (role=\"menu\"). The APG composite keyboard\n   contract (roving tabindex, ArrowLeft/Right, ArrowDown to open, etc.) is\n   wired up in public/site.js keyed on data-slot=\"menubar\". Mirrors\n   registry/ui/menubar.tsx exactly. #}\n\n{# aria_labelledby: id of a visible element naming the menubar. APG prefers\n   aria-labelledby over aria-label when a visible label exists\n   (menu-and-menubar-pattern.html:220-222; MDN menubar_role:32). #}\n{% macro menubar_open(aria_label=none, aria_labelledby=none, extra_class=\"\") %}\n<div role=\"menubar\" data-slot=\"menubar\"\n     {%- if aria_label %} aria-label=\"{{ aria_label }}\"{% endif %}\n     {%- if aria_labelledby %} aria-labelledby=\"{{ aria_labelledby }}\"{% endif %}\n     class=\"inline-flex h-9 items-center gap-0.5 rounded-md border bg-background p-1 shadow-xs {{ extra_class }}\">\n{% endmacro %}\n\n{% macro menubar_close() %}</div>{% endmacro %}\n\n{# menu_open / menu_close wrap one top-level menu: trigger + popover submenu.\n   `id` is the popovertarget + popover id. Put items between them. #}\n{% macro menu_open(label, id, disabled=false, trigger_class=\"\", content_class=\"\") %}\n<div data-slot=\"menubar-menu\" class=\"contents\">\n  <button type=\"button\" role=\"menuitem\" tabindex=\"-1\"\n          popovertarget=\"{{ id }}\" popovertargetaction=\"toggle\"\n          aria-haspopup=\"menu\" aria-expanded=\"false\"\n          data-slot=\"menubar-trigger\" data-menu-for=\"{{ id }}\"\n          {%- if disabled %} data-disabled=\"true\" disabled{% endif %}\n          class=\"flex select-none items-center rounded-sm px-3 py-1 text-sm font-medium outline-none focus:bg-accent focus:text-accent-foreground aria-[expanded=true]:bg-accent aria-[expanded=true]:text-accent-foreground data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50 {{ trigger_class }}\">{{ label }}</button>\n  <div id=\"{{ id }}\" popover=\"auto\" role=\"menu\" aria-label=\"{{ label }}\"\n       data-slot=\"menubar-content\" data-side=\"bottom\"\n       class=\"z-50 m-0 min-w-[12rem] rounded-md border bg-popover p-1 text-popover-foreground shadow-md outline-none [&:not(:popover-open)]:hidden [&:popover-open]:animate-[scn-popover-in_120ms_ease-out] anchor-popover-bottom {{ content_class }}\">\n{% endmacro %}\n\n{% macro menu_close() %}</div></div>{% endmacro %}\n\n{% macro menubar_item(label, href=none, onclick=none, disabled=false, variant=\"default\", extra_class=\"\", **attrs) %}\n{%- set base -%}\nrelative flex w-full cursor-pointer items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none select-none focus:bg-accent focus:text-accent-foreground data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50 [&_svg]:size-4 [&_svg]:shrink-0\n{%- endset -%}\n{%- set destructive -%}\n{% if variant == \"destructive\" %}text-destructive focus:bg-destructive/10 focus:text-destructive{% endif %}\n{%- endset -%}\n{% if href %}\n<a role=\"menuitem\" tabindex=\"-1\" href=\"{{ href }}\"\n   data-slot=\"menubar-item\"\n   {%- if disabled %} data-disabled=\"true\"{% endif %}\n   class=\"{{ base }} {{ destructive }} {{ extra_class }}\"\n   {%- for k, v in attrs.items() %} {{ k|replace('_', '-') }}=\"{{ v }}\"{% endfor -%}\n>{{ label }}</a>\n{% else %}\n<button type=\"button\" role=\"menuitem\" tabindex=\"-1\"\n        {% if onclick %}onclick=\"{{ onclick }}\"{% endif %}\n        data-slot=\"menubar-item\"\n        {%- if disabled %} data-disabled=\"true\"{% endif %}\n        class=\"{{ base }} {{ destructive }} {{ extra_class }}\"\n        {%- for k, v in attrs.items() %} {{ k|replace('_', '-') }}=\"{{ v }}\"{% endfor -%}\n>{{ label }}</button>\n{% endif %}\n{% endmacro %}\n\n{% macro menubar_separator() %}\n<div role=\"separator\" aria-orientation=\"horizontal\" data-slot=\"menubar-separator\" class=\"-mx-1 my-1 h-px bg-border\"></div>\n{% endmacro %}\n\n{% macro menubar_label(text) %}\n<div data-slot=\"menubar-label\" class=\"px-2 py-1.5 text-xs font-semibold tracking-wider text-muted-foreground uppercase\">{{ text }}</div>\n{% endmacro %}\n\n{# menubar_shortcut: presentational accelerator hint (e.g. \"⌘S\") at the\n   trailing edge of an item. aria-hidden so AT does not announce the glyph;\n   expose the real shortcut via aria-keyshortcuts on menubar_item.\n   repos/.../attributes/aria-keyshortcuts/index.md #}\n{% macro menubar_shortcut(text) %}\n<span aria-hidden=\"true\" data-slot=\"menubar-shortcut\" class=\"ml-auto text-xs tracking-widest text-muted-foreground\">{{ text }}</span>\n{% endmacro %}\n"
    },
    {
      "path": "registry/go-templates/menubar.tmpl",
      "type": "registry:file",
      "target": "components/menubar.tmpl",
      "content": "{{/* Menubar templates — shadcn-htmx, htmx v4 + Tailwind v4.\n     App-style horizontal menubar. role=\"menubar\" > role=\"menuitem\" triggers\n     open native [popover] submenus (role=\"menu\"). The APG composite keyboard\n     contract lives in public/site.js keyed on data-slot=\"menubar\".\n     Mirrors registry/ui/menubar.tsx exactly. */}}\n\n{{/* AriaLabelledby: id of a visible element naming the menubar. APG prefers\n     aria-labelledby over aria-label when a visible label exists\n     (menu-and-menubar-pattern.html:220-222; MDN menubar_role:32). */}}\n{{define \"menubar\"}}\n<div role=\"menubar\" data-slot=\"menubar\"\n     {{if .AriaLabel}}aria-label=\"{{.AriaLabel}}\"{{end}}\n     {{if .AriaLabelledby}}aria-labelledby=\"{{.AriaLabelledby}}\"{{end}}\n     class=\"inline-flex h-9 items-center gap-0.5 rounded-md border bg-background p-1 shadow-xs {{.Class}}\">\n  {{.Body}}\n</div>\n{{end}}\n\n{{/* menubar_menu renders one top-level menu: trigger + popover submenu.\n     ID is the popovertarget + popover id; Body holds the items. */}}\n{{define \"menubar_menu\"}}\n<div data-slot=\"menubar-menu\" class=\"contents\">\n  <button type=\"button\" role=\"menuitem\" tabindex=\"-1\"\n          popovertarget=\"{{.ID}}\" popovertargetaction=\"toggle\"\n          aria-haspopup=\"menu\" aria-expanded=\"false\"\n          data-slot=\"menubar-trigger\" data-menu-for=\"{{.ID}}\"\n          {{if .Disabled}}data-disabled=\"true\" disabled{{end}}\n          class=\"flex select-none items-center rounded-sm px-3 py-1 text-sm font-medium outline-none focus:bg-accent focus:text-accent-foreground aria-[expanded=true]:bg-accent aria-[expanded=true]:text-accent-foreground data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50 {{.TriggerClass}}\">{{.Label}}</button>\n  <div id=\"{{.ID}}\" popover=\"auto\" role=\"menu\" aria-label=\"{{.Label}}\"\n       data-slot=\"menubar-content\" data-side=\"bottom\"\n       class=\"z-50 m-0 min-w-[12rem] rounded-md border bg-popover p-1 text-popover-foreground shadow-md outline-none [&:not(:popover-open)]:hidden [&:popover-open]:animate-[scn-popover-in_120ms_ease-out] anchor-popover-bottom {{.ContentClass}}\">\n    {{.Body}}\n  </div>\n</div>\n{{end}}\n\n{{define \"menubar_item\"}}\n{{- $variant := or .Variant \"default\" -}}\n{{- $base := \"relative flex w-full cursor-pointer items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none select-none focus:bg-accent focus:text-accent-foreground data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50 [&_svg]:size-4 [&_svg]:shrink-0\" -}}\n{{- $destr := \"\" -}}{{- if eq $variant \"destructive\" -}}{{- $destr = \"text-destructive focus:bg-destructive/10 focus:text-destructive\" -}}{{- end -}}\n{{if .Href}}\n<a role=\"menuitem\" tabindex=\"-1\" href=\"{{.Href}}\" data-slot=\"menubar-item\"\n   {{if .Disabled}}data-disabled=\"true\"{{end}}\n   class=\"{{$base}} {{$destr}}\"\n   {{- range $k, $v := .Attrs}} {{$k}}=\"{{$v}}\"{{end -}}\n>{{.Label}}</a>\n{{else}}\n<button type=\"button\" role=\"menuitem\" tabindex=\"-1\" data-slot=\"menubar-item\"\n        {{if .Disabled}}data-disabled=\"true\"{{end}}\n        {{- if .Onclick}} onclick=\"{{.Onclick}}\"{{end}}\n        class=\"{{$base}} {{$destr}}\"\n        {{- range $k, $v := .Attrs}} {{$k}}=\"{{$v}}\"{{end -}}\n>{{.Label}}</button>\n{{end}}\n{{end}}\n\n{{define \"menubar_separator\"}}\n<div role=\"separator\" aria-orientation=\"horizontal\" data-slot=\"menubar-separator\" class=\"-mx-1 my-1 h-px bg-border\"></div>\n{{end}}\n\n{{define \"menubar_label\"}}\n<div data-slot=\"menubar-label\" class=\"px-2 py-1.5 text-xs font-semibold tracking-wider text-muted-foreground uppercase\">{{.Text}}</div>\n{{end}}\n\n{{/* menubar_shortcut: presentational accelerator hint (e.g. \"⌘S\") at the\n     trailing edge of an item. aria-hidden so AT does not announce the glyph;\n     expose the real shortcut via aria-keyshortcuts on menubar_item.\n     repos/.../attributes/aria-keyshortcuts/index.md */}}\n{{define \"menubar_shortcut\"}}\n<span aria-hidden=\"true\" data-slot=\"menubar-shortcut\" class=\"ml-auto text-xs tracking-widest text-muted-foreground\">{{.Text}}</span>\n{{end}}\n"
    },
    {
      "path": "registry/phoenix/menubar.ex",
      "type": "registry:file",
      "target": "lib/my_app_web/components/menubar.ex",
      "content": "defmodule ShadcnHtmx.Components.Menubar do\n  @moduledoc \"\"\"\n  Menubar — shadcn-htmx, htmx v4 + Tailwind v4 for Phoenix.\n\n  An app-style, visually-persistent horizontal bar of menus. The bar is\n  `role=\"menubar\"`; each top-level item is a `role=\"menuitem\"` button with\n  `aria-haspopup=\"menu\"` + `aria-expanded` that opens its submenu via the\n  native Popover API (`popovertarget` + `popover=\"auto\"`). The APG composite\n  keyboard contract (roving tabindex, ArrowLeft/Right, ArrowDown to open,\n  ArrowUp/Down inside a menu, Home/End, type-ahead, ESC) is wired up in\n  public/site.js keyed on `data-slot=\"menubar\"`.\n\n  ## Examples\n\n      <.menubar aria_label=\"Application\">\n        <.menubar_menu label=\"File\" id=\"mb-file\">\n          <.menubar_item>New File</.menubar_item>\n          <.menubar_item>Open…</.menubar_item>\n          <.menubar_separator />\n          <.menubar_item variant=\"destructive\">Delete project</.menubar_item>\n        </.menubar_menu>\n        <.menubar_menu label=\"Edit\" id=\"mb-edit\">\n          <.menubar_item>Undo</.menubar_item>\n          <.menubar_item>Redo</.menubar_item>\n        </.menubar_menu>\n      </.menubar>\n  \"\"\"\n\n  use Phoenix.Component\n\n  attr :aria_label, :string, default: nil\n  # aria_labelledby: id of a visible element naming the menubar. APG prefers\n  # aria-labelledby over aria-label when a visible label exists\n  # (menu-and-menubar-pattern.html:220-222; MDN menubar_role:32).\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 menubar(assigns) do\n    ~H\"\"\"\n    <div\n      role=\"menubar\"\n      data-slot=\"menubar\"\n      aria-label={@aria_label}\n      aria-labelledby={@aria_labelledby}\n      class={[\n        \"inline-flex h-9 items-center gap-0.5 rounded-md border bg-background p-1 shadow-xs\",\n        @class\n      ]}\n      {@rest}\n    >\n      {render_slot(@inner_block)}\n    </div>\n    \"\"\"\n  end\n\n  attr :label, :string, required: true\n  attr :id, :string, required: true\n  attr :disabled, :boolean, default: false\n  attr :trigger_class, :string, default: nil\n  attr :content_class, :string, default: nil\n  slot :inner_block, required: true\n\n  def menubar_menu(assigns) do\n    ~H\"\"\"\n    <div data-slot=\"menubar-menu\" class=\"contents\">\n      <button\n        type=\"button\"\n        role=\"menuitem\"\n        tabindex=\"-1\"\n        popovertarget={@id}\n        popovertargetaction=\"toggle\"\n        aria-haspopup=\"menu\"\n        aria-expanded=\"false\"\n        data-slot=\"menubar-trigger\"\n        data-menu-for={@id}\n        data-disabled={@disabled && \"true\"}\n        disabled={@disabled}\n        class={[\n          \"flex select-none items-center rounded-sm px-3 py-1 text-sm font-medium outline-none\",\n          \"focus:bg-accent focus:text-accent-foreground\",\n          \"aria-[expanded=true]:bg-accent aria-[expanded=true]:text-accent-foreground\",\n          \"data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50\",\n          @trigger_class\n        ]}\n      >\n        {@label}\n      </button>\n      <div\n        id={@id}\n        popover=\"auto\"\n        role=\"menu\"\n        aria-label={@label}\n        data-slot=\"menubar-content\"\n        data-side=\"bottom\"\n        class={[\n          \"z-50 m-0 min-w-[12rem] rounded-md border bg-popover p-1 text-popover-foreground shadow-md outline-none\",\n          \"[&:not(:popover-open)]:hidden\",\n          \"[&:popover-open]:animate-[scn-popover-in_120ms_ease-out]\",\n          \"anchor-popover-bottom\",\n          @content_class\n        ]}\n      >\n        {render_slot(@inner_block)}\n      </div>\n    </div>\n    \"\"\"\n  end\n\n  attr :href, :string, default: nil\n  attr :disabled, :boolean, default: false\n  attr :variant, :string, default: \"default\", values: ~w(default destructive)\n  attr :class, :string, default: nil\n  attr :rest, :global\n  slot :inner_block, required: true\n\n  def menubar_item(assigns) do\n    base =\n      \"relative flex w-full cursor-pointer items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none select-none \" <>\n        \"focus:bg-accent focus:text-accent-foreground \" <>\n        \"data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50 \" <>\n        \"[&_svg]:size-4 [&_svg]:shrink-0\"\n\n    destr =\n      if assigns.variant == \"destructive\",\n        do: \"text-destructive focus:bg-destructive/10 focus:text-destructive\",\n        else: \"\"\n\n    assigns = assign(assigns, base: base, destr: destr)\n\n    cond do\n      assigns.href ->\n        ~H\"\"\"\n        <a\n          role=\"menuitem\"\n          tabindex=\"-1\"\n          href={@href}\n          data-slot=\"menubar-item\"\n          data-disabled={@disabled && \"true\"}\n          class={[@base, @destr, @class]}\n          {@rest}\n        >\n          {render_slot(@inner_block)}\n        </a>\n        \"\"\"\n\n      true ->\n        ~H\"\"\"\n        <button\n          type=\"button\"\n          role=\"menuitem\"\n          tabindex=\"-1\"\n          data-slot=\"menubar-item\"\n          data-disabled={@disabled && \"true\"}\n          class={[@base, @destr, @class]}\n          {@rest}\n        >\n          {render_slot(@inner_block)}\n        </button>\n        \"\"\"\n    end\n  end\n\n  def menubar_separator(assigns) do\n    ~H\"\"\"\n    <div\n      role=\"separator\"\n      aria-orientation=\"horizontal\"\n      data-slot=\"menubar-separator\"\n      class=\"-mx-1 my-1 h-px bg-border\"\n    />\n    \"\"\"\n  end\n\n  attr :class, :string, default: nil\n  slot :inner_block, required: true\n\n  def menubar_label(assigns) do\n    ~H\"\"\"\n    <div\n      data-slot=\"menubar-label\"\n      class={[\n        \"px-2 py-1.5 text-xs font-semibold tracking-wider text-muted-foreground uppercase\",\n        @class\n      ]}\n    >\n      {render_slot(@inner_block)}\n    </div>\n    \"\"\"\n  end\n\n  attr :class, :string, default: nil\n  slot :inner_block, required: true\n\n  @doc \"\"\"\n  Presentational accelerator hint (e.g. \"⌘S\") at the trailing edge of an\n  item. aria-hidden so AT does not announce the glyph; expose the real\n  shortcut via aria-keyshortcuts on menubar_item.\n  repos/.../attributes/aria-keyshortcuts/index.md\n  \"\"\"\n  def menubar_shortcut(assigns) do\n    ~H\"\"\"\n    <span\n      aria-hidden=\"true\"\n      data-slot=\"menubar-shortcut\"\n      class={[\"ml-auto text-xs tracking-widest text-muted-foreground\", @class]}\n    >\n      {render_slot(@inner_block)}\n    </span>\n    \"\"\"\n  end\nend\n"
    },
    {
      "path": "registry/html/menubar.html",
      "type": "registry:file",
      "target": "snippets/menubar.html",
      "content": "<!--\n  shadcn-htmx — raw HTML menubar snippet.\n\n  App-style horizontal menubar. role=\"menubar\" > role=\"menuitem\" triggers\n  open native [popover] submenus (role=\"menu\"). Native [popover] gives\n  open/close + light dismiss + ESC + top-layer; the inline script below adds\n  the APG composite keyboard contract (roving tabindex, ArrowLeft/Right along\n  the bar, ArrowDown to open a menu, ArrowUp/Down inside a menu, Home/End,\n  type-ahead). Relies only on the theme tokens in styles.css.\n\n  Naming: this example uses aria-label. When a VISIBLE heading names the bar,\n  prefer aria-labelledby=\"<heading-id>\" instead (APG menu-and-menubar-pattern\n  :220-222; MDN menubar_role:32).\n-->\n\n<div role=\"menubar\" data-slot=\"menubar\" aria-label=\"Application\"\n     class=\"inline-flex h-9 items-center gap-0.5 rounded-md border bg-background p-1 shadow-xs\">\n\n  <div data-slot=\"menubar-menu\" class=\"contents\">\n    <button type=\"button\" role=\"menuitem\" tabindex=\"-1\"\n            popovertarget=\"mb-file\" popovertargetaction=\"toggle\"\n            aria-haspopup=\"menu\" aria-expanded=\"false\"\n            data-slot=\"menubar-trigger\" data-menu-for=\"mb-file\"\n            class=\"flex select-none items-center rounded-sm px-3 py-1 text-sm font-medium outline-none focus:bg-accent focus:text-accent-foreground aria-[expanded=true]:bg-accent aria-[expanded=true]:text-accent-foreground data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50\">File</button>\n    <div id=\"mb-file\" popover=\"auto\" role=\"menu\" aria-label=\"File\"\n         data-slot=\"menubar-content\" data-side=\"bottom\"\n         class=\"z-50 m-0 min-w-[12rem] rounded-md border bg-popover p-1 text-popover-foreground shadow-md outline-none [&:not(:popover-open)]:hidden [&:popover-open]:animate-[scn-popover-in_120ms_ease-out] anchor-popover-bottom\">\n      <!-- aria-keyshortcuts exposes the accelerator to AT; the trailing\n           data-slot=\"menubar-shortcut\" span is the aria-hidden visual hint.\n           repos/.../attributes/aria-keyshortcuts/index.md -->\n      <button type=\"button\" role=\"menuitem\" tabindex=\"-1\" data-slot=\"menubar-item\" aria-keyshortcuts=\"Control+N\" class=\"relative flex w-full cursor-pointer items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none select-none focus:bg-accent focus:text-accent-foreground data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50 [&_svg]:size-4 [&_svg]:shrink-0\">New File<span aria-hidden=\"true\" data-slot=\"menubar-shortcut\" class=\"ml-auto text-xs tracking-widest text-muted-foreground\">⌃N</span></button>\n      <button type=\"button\" role=\"menuitem\" tabindex=\"-1\" data-slot=\"menubar-item\" class=\"relative flex w-full cursor-pointer items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none select-none focus:bg-accent focus:text-accent-foreground data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50 [&_svg]:size-4 [&_svg]:shrink-0\">Open…</button>\n      <div role=\"separator\" aria-orientation=\"horizontal\" data-slot=\"menubar-separator\" class=\"-mx-1 my-1 h-px bg-border\"></div>\n      <button type=\"button\" role=\"menuitem\" tabindex=\"-1\" data-slot=\"menubar-item\" class=\"relative flex w-full cursor-pointer items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none select-none focus:bg-accent focus:text-accent-foreground data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50 [&_svg]:size-4 [&_svg]:shrink-0 text-destructive focus:bg-destructive/10 focus:text-destructive\">Delete project</button>\n    </div>\n  </div>\n\n  <div data-slot=\"menubar-menu\" class=\"contents\">\n    <button type=\"button\" role=\"menuitem\" tabindex=\"-1\"\n            popovertarget=\"mb-edit\" popovertargetaction=\"toggle\"\n            aria-haspopup=\"menu\" aria-expanded=\"false\"\n            data-slot=\"menubar-trigger\" data-menu-for=\"mb-edit\"\n            class=\"flex select-none items-center rounded-sm px-3 py-1 text-sm font-medium outline-none focus:bg-accent focus:text-accent-foreground aria-[expanded=true]:bg-accent aria-[expanded=true]:text-accent-foreground data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50\">Edit</button>\n    <div id=\"mb-edit\" popover=\"auto\" role=\"menu\" aria-label=\"Edit\"\n         data-slot=\"menubar-content\" data-side=\"bottom\"\n         class=\"z-50 m-0 min-w-[12rem] rounded-md border bg-popover p-1 text-popover-foreground shadow-md outline-none [&:not(:popover-open)]:hidden [&:popover-open]:animate-[scn-popover-in_120ms_ease-out] anchor-popover-bottom\">\n      <button type=\"button\" role=\"menuitem\" tabindex=\"-1\" data-slot=\"menubar-item\" class=\"relative flex w-full cursor-pointer items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none select-none focus:bg-accent focus:text-accent-foreground data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50 [&_svg]:size-4 [&_svg]:shrink-0\">Undo</button>\n      <button type=\"button\" role=\"menuitem\" tabindex=\"-1\" data-slot=\"menubar-item\" class=\"relative flex w-full cursor-pointer items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none select-none focus:bg-accent focus:text-accent-foreground data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50 [&_svg]:size-4 [&_svg]:shrink-0\">Redo</button>\n    </div>\n  </div>\n</div>\n\n<!-- Keyboard contract — copy once per page -->\n<script>\n  document.querySelectorAll('[data-slot=\"menubar\"]').forEach(function (bar) {\n    var triggers = function () {\n      return [].slice.call(bar.querySelectorAll('[data-slot=\"menubar-trigger\"]:not([disabled])'))\n    }\n    // Roving tabindex: first enabled trigger is the single tab stop.\n    var ts = triggers()\n    ts.forEach(function (t, i) { t.setAttribute('tabindex', i === 0 ? '0' : '-1') })\n    var roll = function (target) {\n      triggers().forEach(function (t) { t.setAttribute('tabindex', '-1') })\n      target.setAttribute('tabindex', '0')\n      target.focus()\n    }\n    var menuOf = function (trig) { return document.getElementById(trig.getAttribute('data-menu-for')) }\n    bar.addEventListener('keydown', function (e) {\n      var trig = e.target.closest && e.target.closest('[data-slot=\"menubar-trigger\"]')\n      if (!trig) return\n      var list = triggers(); var i = list.indexOf(trig)\n      if (e.key === 'ArrowRight') { e.preventDefault(); roll(list[(i + 1) % list.length]) }\n      else if (e.key === 'ArrowLeft') { e.preventDefault(); roll(list[(i - 1 + list.length) % list.length]) }\n      else if (e.key === 'Home') { e.preventDefault(); roll(list[0]) }\n      else if (e.key === 'End') { e.preventDefault(); roll(list[list.length - 1]) }\n      else if (e.key === 'ArrowDown' || e.key === 'Enter' || e.key === ' ' || e.key === 'ArrowUp') {\n        var menu = menuOf(trig); if (!menu || typeof menu.showPopover !== 'function') return\n        e.preventDefault()\n        if (!menu.matches(':popover-open')) menu.showPopover()\n        var items = [].slice.call(menu.querySelectorAll('[role=\"menuitem\"]:not([data-disabled=\"true\"])'))\n        if (items.length) setTimeout(function () { items[e.key === 'ArrowUp' ? items.length - 1 : 0].focus() }, 0)\n      }\n    })\n    // Reflect open/closed state onto aria-expanded.\n    bar.querySelectorAll('[data-slot=\"menubar-content\"]').forEach(function (menu) {\n      menu.addEventListener('toggle', function (e) {\n        var trig = bar.querySelector('[data-menu-for=\"' + CSS.escape(menu.id) + '\"]')\n        if (trig) trig.setAttribute('aria-expanded', e.newState === 'open' ? 'true' : 'false')\n      })\n    })\n  })\n  // Inside a submenu: ArrowUp/Down/Home/End/type-ahead + ArrowLeft/Right hop bars.\n  document.addEventListener('keydown', function (e) {\n    var item = e.target.closest && e.target.closest('[data-slot=\"menubar-content\"] [role=\"menuitem\"]')\n    if (!item) return\n    var menu = item.closest('[data-slot=\"menubar-content\"]'); if (!menu) return\n    var bar = menu.closest('[data-slot=\"menubar\"]'); if (!bar) return\n    var items = [].slice.call(menu.querySelectorAll('[role=\"menuitem\"]:not([data-disabled=\"true\"])'))\n    var i = items.indexOf(item)\n    var bars = [].slice.call(bar.querySelectorAll('[data-slot=\"menubar-trigger\"]:not([disabled])'))\n    var trig = bar.querySelector('[data-menu-for=\"' + CSS.escape(menu.id) + '\"]')\n    var bi = bars.indexOf(trig)\n    var hop = function (dir) {\n      menu.hidePopover()\n      var next = bars[(bi + dir + bars.length) % bars.length]\n      bars.forEach(function (t) { t.setAttribute('tabindex', '-1') })\n      next.setAttribute('tabindex', '0')\n      var nm = document.getElementById(next.getAttribute('data-menu-for'))\n      if (nm && typeof nm.showPopover === 'function') {\n        nm.showPopover()\n        var ni = [].slice.call(nm.querySelectorAll('[role=\"menuitem\"]:not([data-disabled=\"true\"])'))\n        if (ni.length) setTimeout(function () { ni[0].focus() }, 0)\n      } else next.focus()\n    }\n    if (e.key === 'ArrowDown') { e.preventDefault(); items[(i + 1) % items.length].focus() }\n    else if (e.key === 'ArrowUp') { e.preventDefault(); items[(i - 1 + items.length) % items.length].focus() }\n    else if (e.key === 'Home') { e.preventDefault(); items[0].focus() }\n    else if (e.key === 'End') { e.preventDefault(); items[items.length - 1].focus() }\n    else if (e.key === 'ArrowRight') { e.preventDefault(); hop(1) }\n    else if (e.key === 'ArrowLeft') { e.preventDefault(); hop(-1) }\n    else if (e.key.length === 1 && /\\S/.test(e.key) && !e.ctrlKey && !e.metaKey && !e.altKey) {\n      var ch = e.key.toLowerCase()\n      for (var k = 1; k <= items.length; k++) {\n        var c = items[(i + k) % items.length]\n        if ((c.textContent || '').trim().toLowerCase().startsWith(ch)) { c.focus(); break }\n      }\n    }\n  })\n  // Click a leaf item closes its menu (APG: items perform an action + close).\n  document.addEventListener('click', function (e) {\n    var item = e.target.closest && e.target.closest('[data-slot=\"menubar-item\"]')\n    if (!item || item.getAttribute('data-disabled') === 'true') return\n    var menu = item.closest('[data-slot=\"menubar-content\"]')\n    if (menu && typeof menu.hidePopover === 'function') menu.hidePopover()\n  })\n</script>\n"
    }
  ]
}
