{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "menubar",
  "title": "Menubar",
  "description": "A menubar component.",
  "dependencies": [
    "class-variance-authority"
  ],
  "registryDependencies": [
    "@cubby-ui/button",
    "@cubby-ui/dropdown-menu",
    "@cubby-ui/elevated"
  ],
  "files": [
    {
      "path": "registry/default/menubar/menubar.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport { Menu as BaseMenu } from \"@base-ui/react/menu\";\nimport { Menubar as BaseMenubar } from \"@base-ui/react/menubar\";\n\nimport { cn } from \"@/lib/utils\";\nimport { buttonVariants } from \"@/registry/default/button/button\";\nimport {\n  DropdownMenuCheckboxItem,\n  DropdownMenuClear,\n  DropdownMenuEmpty,\n  DropdownMenuFilter,\n  DropdownMenuGroup,\n  DropdownMenuGroupLabel,\n  DropdownMenuInput,\n  DropdownMenuItem,\n  DropdownMenuLabel,\n  DropdownMenuLinkItem,\n  DropdownMenuList,\n  DropdownMenuPortal,\n  DropdownMenuRadioGroup,\n  DropdownMenuRadioItem,\n  DropdownMenuSeparator,\n  DropdownMenuShortcut,\n  DropdownMenuSub,\n  DropdownMenuSubContent,\n  DropdownMenuSubTrigger,\n  useDropdownMenuFilter,\n} from \"@/registry/default/dropdown-menu/dropdown-menu\";\nimport {\n  solidSurface,\n  type SurfaceLevel,\n} from \"@/registry/default/lib/elevated\";\nimport \"./menubar.css\";\n\n// A shell, as in Button Group and Toolbar. Trigger insets are borders, 2px\n// facing the shell's edge and 1px facing a neighbour; `of` counts triggers\n// only, so Base UI's guard spans can't move the ends. Hit areas, the gliding\n// highlight and focus rings are in menubar.css.\nconst menubarVariants = cva(\n  [\n    \"group/menubar relative isolate flex w-fit items-stretch rounded-(--menubar-radius) data-[orientation=vertical]:flex-col\",\n    // Never narrower than its triggers; overflow is the container's to handle.\n    \"min-w-max\",\n    // Paint corners are the shell's minus the 2px edge border.\n    \"[--menubar-item-radius:max(0px,calc(var(--menubar-radius)-2px))]\",\n    // Label-color tints: hover 10%, press 14%\n    \"[--menubar-highlight:color-mix(in_oklab,currentColor_10%,transparent)] [--menubar-press:color-mix(in_oklab,currentColor_14%,transparent)]\",\n    \"data-[orientation=horizontal]:h-(--menubar-h)\",\n    \"data-[orientation=horizontal]:[&>[data-menubar-item]]:border-y-2 data-[orientation=horizontal]:[&>:nth-child(1_of_[data-menubar-item])]:border-s-2 data-[orientation=horizontal]:[&>:nth-last-child(1_of_[data-menubar-item])]:border-e-2\",\n    \"data-[orientation=vertical]:[&>[data-menubar-item]]:border-x-2 data-[orientation=vertical]:[&>:nth-child(1_of_[data-menubar-item])]:border-t-2 data-[orientation=vertical]:[&>:nth-last-child(1_of_[data-menubar-item])]:border-b-2\",\n    // Triggers dim with the shell, not twice.\n    \"data-disabled:opacity-60 data-disabled:**:data-menubar-item:opacity-100\",\n  ],\n  {\n    variants: {\n      variant: {\n        elevated: cn(solidSurface(2, 2), \"text-foreground\"),\n        outline: \"bg-card text-foreground outline outline-border\",\n        soft: \"bg-secondary text-secondary-foreground\",\n        ghost: \"text-foreground\",\n      },\n      // Button's height ladder (one step taller below sm); corners step with it.\n      size: {\n        sm: \"[--menubar-h:--spacing(9)] sm:not-pointer-coarse:[--menubar-h:--spacing(8)] [--menubar-radius:calc(var(--radius)-2px)]\",\n        default:\n          \"[--menubar-h:--spacing(10)] sm:not-pointer-coarse:[--menubar-h:--spacing(9)] [--menubar-radius:var(--radius)]\",\n        lg: \"[--menubar-h:--spacing(11)] sm:not-pointer-coarse:[--menubar-h:--spacing(10)] [--menubar-radius:calc(var(--radius)+2px)]\",\n      },\n    },\n  },\n);\n\ntype MenubarVariant = NonNullable<\n  VariantProps<typeof menubarVariants>[\"variant\"]\n>;\ntype MenubarSize = NonNullable<VariantProps<typeof menubarVariants>[\"size\"]>;\n\nexport type MenubarProps = BaseMenubar.Props & {\n  /** Shell style. */\n  variant?: MenubarVariant;\n  /** Outer height, on Button's ladder. */\n  size?: MenubarSize;\n  /** Glide a shared highlight between triggers. */\n  highlight?: boolean;\n};\n\nfunction Menubar({\n  className,\n  variant = \"elevated\",\n  size = \"default\",\n  highlight = true,\n  disabled,\n  children,\n  ...props\n}: MenubarProps) {\n  return (\n    <BaseMenubar\n      data-slot=\"menubar\"\n      data-variant={variant}\n      data-size={size}\n      // Base UI's Menubar state has no disabled flag, so it sets no attribute.\n      data-disabled={disabled ? \"\" : undefined}\n      data-glide={highlight ? \"\" : undefined}\n      disabled={disabled}\n      className={cn(menubarVariants({ variant, size }), className)}\n      {...props}\n    >\n      {children}\n      {highlight && (\n        <span\n          aria-hidden\n          data-slot=\"menubar-highlight\"\n          data-menubar-highlight\n        />\n      )}\n    </BaseMenubar>\n  );\n}\n\nfunction MenubarMenu(props: BaseMenu.Root.Props) {\n  return <BaseMenu.Root data-slot=\"menubar-menu\" {...props} />;\n}\n\n// A quiet Button: hover and the open menu paint the label tint. Menu triggers\n// skip Button's press, as everywhere else.\nconst triggerClasses = cn(\n  buttonVariants({ variant: \"ghost\", size: null }),\n  \"shrink-0 border border-transparent px-2.5 text-sm font-medium text-current hover:text-current data-popup-open:text-current\",\n  \"[--btn-bg-hover:var(--menubar-highlight)] [--btn-bg-active:var(--menubar-press)]\",\n  // Instant hover, so a sweep never shows two triggers half-lit.\n  \"before:[transition-duration:0ms,100ms,100ms] active:before:[transition-duration:100ms]\",\n  // Box corners are the plate's + 2px.\n  \"rounded-[calc(var(--menubar-item-radius)+2px)] before:rounded-(--menubar-item-radius) focus-visible:z-10\",\n  // In a column every trigger gets the row's paint height.\n  \"h-auto self-stretch group-data-[orientation=vertical]/menubar:box-content group-data-[orientation=vertical]/menubar:h-[calc(var(--menubar-h)-4px)] group-data-[orientation=vertical]/menubar:min-w-[calc(var(--menubar-h)-4px)] group-data-[orientation=vertical]/menubar:py-0 group-data-[orientation=vertical]/menubar:justify-start\",\n);\n\nfunction MenubarTrigger({\n  className,\n  delay = 0,\n  closeDelay = 0,\n  ...props\n}: BaseMenu.Trigger.Props) {\n  return (\n    <BaseMenu.Trigger\n      data-slot=\"menubar-trigger\"\n      data-menubar-item=\"\"\n      delay={delay}\n      closeDelay={closeDelay}\n      className={cn(triggerClasses, className)}\n      {...props}\n    />\n  );\n}\n\nfunction MenubarContent({\n  className,\n  positionerClassName,\n  viewportClassName,\n  children,\n  align = \"start\",\n  alignOffset = 0,\n  sideOffset = 4,\n  level = 3,\n  shadowLevel = 3,\n  ...props\n}: React.ComponentProps<typeof BaseMenu.Popup> & {\n  /** Classes for the positioner (the element Base UI moves into place), merged last. */\n  positionerClassName?: string;\n  /** Classes for the padded box around the items, e.g. `p-0` for a filterable menu. */\n  viewportClassName?: string;\n  align?: BaseMenu.Positioner.Props[\"align\"];\n  alignOffset?: BaseMenu.Positioner.Props[\"alignOffset\"];\n  sideOffset?: BaseMenu.Positioner.Props[\"sideOffset\"];\n  /** Surface elevation level for the popup bg (1-8). Bump when nesting inside a Dialog or other elevated container. Defaults to 3. */\n  level?: SurfaceLevel;\n  /** Shadow weight (1-8). Pinned to 3 by default so the menu reads the same regardless of nesting depth. */\n  shadowLevel?: SurfaceLevel;\n}) {\n  return (\n    <BaseMenu.Portal>\n      {/* No guard here, unlike DropdownMenu: there one positioner is shared by\n          detached triggers and physically travels between them. Each MenubarMenu\n          mounts its own, with no previous position to animate from. */}\n      <BaseMenu.Positioner\n        className={cn(\n          \"z-50 h-(--positioner-height) max-h-(--available-height) w-(--positioner-width) max-w-(--available-width) transition-[top,left,right,bottom,transform] duration-150 ease-[cubic-bezier(0.22,1,0.36,1)] motion-reduce:transition-none\",\n          positionerClassName,\n        )}\n        align={align}\n        alignOffset={alignOffset}\n        sideOffset={sideOffset}\n      >\n        <BaseMenu.Popup\n          data-slot=\"menubar-content\"\n          data-level={level}\n          className={cn(\n            \"text-popover-foreground relative max-h-(--available-height) max-w-(--available-width) min-w-[12rem] overflow-hidden rounded-xl outline-none\",\n            solidSurface(level, shadowLevel),\n            \"h-(--popup-height,auto) w-(--popup-width,auto)\",\n            \"origin-(--transform-origin) transition-[width,height,scale,opacity] duration-[150ms,150ms,100ms,100ms] ease-[cubic-bezier(0.22,1,0.36,1),cubic-bezier(0.22,1,0.36,1),var(--ease-out-expo),var(--ease-out-expo)]\",\n            \"data-starting-style:scale-95 data-starting-style:opacity-0\",\n            \"data-ending-style:scale-95 data-ending-style:opacity-0\",\n            // Own compositor layer while mounted. width/height in the transition\n            // list stops Chrome compositing the scale, so rows re-raster every\n            // frame and the differential stretch reads as a ripple. ContextMenu\n            // escapes it by transitioning scale and opacity alone.\n            \"will-change-transform\",\n            \"motion-reduce:transition-none motion-reduce:will-change-auto\",\n            // 'group' (a switch to another menu, or a tab-out close) is\n            // instant, as in native menu bars. Start and end styles are pinned\n            // too, or the swap frame shows both popups at opacity 0.\n            \"data-[instant=group]:transition-none data-[instant=group]:data-ending-style:scale-100 data-[instant=group]:data-ending-style:opacity-100 data-[instant=group]:data-starting-style:scale-100 data-[instant=group]:data-starting-style:opacity-100\",\n            className,\n          )}\n          {...props}\n        >\n          {/*\n            Kept for sizing, not for content transitions. Base UI runs the popup\n            auto-resize from inside the Viewport, and that is what writes\n            --positioner-width / --positioner-height (plus --popup-width /\n            --popup-height, which sit at `auto` between resizes). The positioner\n            above sizes off those, so dropping the Viewport leaves them unset\n            and its explicit box collapses.\n\n            Deliberately carries no morph styling. A menubar can't morph: each\n            MenubarMenu is its own Menu.Root, so moving from File to Edit mounts\n            a new popup rather than swapping content within one, and\n            data-current / data-previous / data-activation-direction never\n            appear. (Verified: two distinct popup elements across a switch.)\n          */}\n          <BaseMenu.Viewport\n            data-slot=\"menubar-viewport\"\n            // No `max-w`: `w-full` resolves against the popup's already-capped\n            // content box, so the width bound is inherited for free.\n            className={cn(\n              \"relative max-h-(--available-height) w-full overflow-clip overflow-y-auto p-1\",\n              // Lets a filterable list shrink to fit; off during swaps (see DropdownMenu).\n              \"flex flex-col *:data-current:flex *:data-current:flex-col not-data-transitioning:*:data-current:min-h-0\",\n              viewportClassName,\n            )}\n          >\n            {children}\n          </BaseMenu.Viewport>\n        </BaseMenu.Popup>\n      </BaseMenu.Positioner>\n    </BaseMenu.Portal>\n  );\n}\n\n// The menu's contents are Dropdown Menu's parts: a menubar menu is the same\n// Base UI Menu.\nconst MenubarPortal = DropdownMenuPortal;\nconst MenubarGroup = DropdownMenuGroup;\nconst MenubarLabel = DropdownMenuLabel;\nconst MenubarGroupLabel = DropdownMenuGroupLabel;\nconst MenubarItem = DropdownMenuItem;\nconst MenubarLinkItem = DropdownMenuLinkItem;\nconst MenubarCheckboxItem = DropdownMenuCheckboxItem;\nconst MenubarRadioGroup = DropdownMenuRadioGroup;\nconst MenubarRadioItem = DropdownMenuRadioItem;\nconst MenubarSeparator = DropdownMenuSeparator;\nconst MenubarShortcut = DropdownMenuShortcut;\nconst MenubarSub = DropdownMenuSub;\nconst MenubarSubTrigger = DropdownMenuSubTrigger;\nconst MenubarSubContent = DropdownMenuSubContent;\n// Filtering: wrap a MenubarMenu (or MenubarSub) in MenubarFilter.\nconst MenubarFilter = DropdownMenuFilter;\nconst MenubarInput = DropdownMenuInput;\nconst MenubarClear = DropdownMenuClear;\nconst MenubarList = DropdownMenuList;\nconst MenubarEmpty = DropdownMenuEmpty;\nconst useMenubarFilter = useDropdownMenuFilter;\n\nexport {\n  Menubar,\n  MenubarPortal,\n  MenubarMenu,\n  MenubarTrigger,\n  MenubarContent,\n  MenubarGroup,\n  MenubarSeparator,\n  MenubarGroupLabel,\n  MenubarLabel,\n  MenubarItem,\n  MenubarLinkItem,\n  MenubarShortcut,\n  MenubarCheckboxItem,\n  MenubarRadioGroup,\n  MenubarRadioItem,\n  MenubarSub,\n  MenubarSubTrigger,\n  MenubarSubContent,\n  MenubarFilter,\n  MenubarInput,\n  MenubarClear,\n  MenubarList,\n  MenubarEmpty,\n  useMenubarFilter,\n  menubarVariants,\n};\n",
      "type": "registry:ui",
      "target": "components/ui/cubby-ui/menubar/menubar.tsx"
    },
    {
      "path": "registry/default/menubar/menubar.css",
      "content": "/* Hit areas: a square ::after over the whole box, corners included, so the\n * notch between two triggers isn't dead. */\n[data-slot=\"menubar\"] [data-menubar-item]::after {\n  content: \"\";\n  position: absolute;\n  inset-block: -2px;\n  inset-inline: -1px;\n}\n\n[data-slot=\"menubar\"][data-orientation=\"vertical\"] [data-menubar-item]::after {\n  inset-block: -1px;\n  inset-inline: -2px;\n}\n\n/* -------------------------------------------------------------------------------------------------\n * Gliding highlight. The lit trigger's plate (::before) is the anchor, so\n * the pill matches it whatever the borders. Without anchor positioning, each\n * trigger paints its own hover.\n * -------------------------------------------------------------------------------------------------*/\n\n[data-menubar-highlight] {\n  display: none;\n}\n\n/* All anchor features or none: a partial implementation leaves the pill\n * unplaced or tied to another menubar. */\n@supports (anchor-name: --menubar-item) and (anchor-scope: --menubar-item) and\n  (inset: anchor(inside)) {\n  [data-slot=\"menubar\"][data-glide] {\n    anchor-scope: --menubar-item;\n  }\n\n  /* The pill stands in for the triggers' own hover. */\n  [data-slot=\"menubar\"][data-glide] [data-menubar-item] {\n    --btn-bg-hover: transparent;\n    --btn-bg-active: transparent;\n  }\n\n  /* Focus, press, and the open menu's trigger hold the pill. Hover is gated\n   * to real hover (:active covers taps). */\n  [data-slot=\"menubar\"][data-glide]\n    [data-menubar-item]:not(:disabled, [data-disabled]):is(\n      :focus-visible,\n      :active,\n      [data-popup-open]\n    )::before {\n    anchor-name: --menubar-item;\n  }\n\n  /* While a menu is open, hovering another trigger opens it, so the pill\n   * leads on hover instead of waiting for the mount. The open trigger lets\n   * go and keeps its own tint until it closes, so an arrow-key switch still\n   * reads as open. Keyboard focus outranks hover. */\n  @media (hover: hover) {\n    [data-slot=\"menubar\"][data-glide]:not(\n        :has([data-menubar-item]:is(:active, :focus-visible))\n      )\n      [data-menubar-item]:not(:disabled, [data-disabled]):hover::before {\n      anchor-name: --menubar-item;\n    }\n\n    [data-slot=\"menubar\"][data-glide]:has(\n        [data-menubar-item]:not(:disabled, [data-disabled]):hover\n      ):not(:has([data-menubar-item]:is(:active, :focus-visible)))\n      [data-menubar-item][data-popup-open]:not(:hover, :focus-visible)::before {\n      anchor-name: none;\n      background-color: var(--menubar-highlight);\n    }\n  }\n\n  [data-slot=\"menubar\"][data-glide] > [data-menubar-highlight] {\n    display: block;\n    position: absolute;\n    position-anchor: --menubar-item;\n    inset: anchor(inside);\n    border-radius: var(--menubar-item-radius);\n    background-color: var(--menubar-highlight);\n    /* Rendered last, so it sits under the triggers inside the isolated menubar. */\n    z-index: -1;\n    pointer-events: none;\n    opacity: 0;\n    /* Glides between triggers; appears and goes instantly. */\n    transition:\n      inset 80ms var(--ease-out-expo, cubic-bezier(0.19, 1, 0.22, 1)),\n      background-color 100ms var(--ease-out, cubic-bezier(0, 0, 0.2, 1));\n  }\n\n  [data-slot=\"menubar\"][data-glide]:has(\n      [data-menubar-item]:not(:disabled, [data-disabled]):is(\n          :focus-visible,\n          :active,\n          [data-popup-open]\n        )\n    )\n    > [data-menubar-highlight] {\n    opacity: 1;\n  }\n\n  @media (hover: hover) {\n    [data-slot=\"menubar\"][data-glide]:has(\n        [data-menubar-item]:not(:disabled, [data-disabled]):hover\n      )\n      > [data-menubar-highlight] {\n      opacity: 1;\n    }\n  }\n\n  /* While a menu is open, moving across the bar opens the next one, and that\n   * mount blocks the main thread for a few frames. The inset transition runs\n   * there too, so it would stutter; the pill jumps with the menu instead. */\n  [data-slot=\"menubar\"][data-glide]:has([data-menubar-item][data-popup-open])\n    > [data-menubar-highlight] {\n    transition-property: background-color;\n  }\n\n  @media (prefers-reduced-motion: reduce) {\n    [data-slot=\"menubar\"][data-glide] > [data-menubar-highlight] {\n      transition: background-color 100ms\n        var(--ease-out, cubic-bezier(0, 0, 0.2, 1));\n    }\n  }\n}\n\n/* -------------------------------------------------------------------------------------------------\n * Focus ring: 2px inside the fill's edge, on the plate (::before), following\n * its corners.\n * -------------------------------------------------------------------------------------------------*/\n\n[data-slot=\"menubar\"] [data-menubar-item]:focus-visible {\n  outline-color: transparent;\n}\n\n[data-slot=\"menubar\"] [data-menubar-item]:focus-visible::before {\n  outline: 2px solid color-mix(in oklab, var(--color-ring) 50%, transparent);\n  outline-offset: -2px;\n}\n\n/* Forced colors drop the fills, so the open trigger gets a Highlight border. */\n@media (forced-colors: active) {\n  [data-slot=\"menubar\"] [data-menubar-item][data-popup-open] {\n    border-color: Highlight;\n  }\n}\n",
      "type": "registry:file",
      "target": "components/ui/cubby-ui/menubar/menubar.css"
    }
  ],
  "type": "registry:ui"
}