Menubar
A persistent row of menus, like a desktop app menu bar.
import { Menubar, MenubarCheckboxItem, MenubarContent, MenubarGroup, MenubarItem, MenubarMenu, MenubarSeparator, MenubarShortcut, MenubarTrigger,} from "@/components/ui/menubar";
export default function Demo() { return ( <Menubar> <MenubarMenu> <MenubarTrigger>File</MenubarTrigger> <MenubarContent> <MenubarGroup> <MenubarItem> New index <MenubarShortcut>⌘N</MenubarShortcut> </MenubarItem> <MenubarItem>Open…</MenubarItem> </MenubarGroup> <MenubarSeparator /> <MenubarItem variant="destructive">Discard draft</MenubarItem> </MenubarContent> </MenubarMenu> <MenubarMenu> <MenubarTrigger>View</MenubarTrigger> <MenubarContent> <MenubarGroup> <MenubarCheckboxItem defaultChecked> Show sidebar </MenubarCheckboxItem> <MenubarCheckboxItem>Show grid</MenubarCheckboxItem> </MenubarGroup> <MenubarSeparator /> <MenubarItem> Zoom in <MenubarShortcut>⌘+</MenubarShortcut> </MenubarItem> <MenubarItem> Zoom out <MenubarShortcut>⌘-</MenubarShortcut> </MenubarItem> </MenubarContent> </MenubarMenu> <MenubarMenu> <MenubarTrigger>Help</MenubarTrigger> <MenubarContent> <MenubarItem>Docs</MenubarItem> </MenubarContent> </MenubarMenu> </Menubar> );}<script setup lang="ts">import { ref } from "vue";import { Menubar, MenubarCheckboxItem, MenubarContent, MenubarGroup, MenubarItem, MenubarMenu, MenubarSeparator, MenubarShortcut, MenubarTrigger,} from "@/components/ui/menubar";
const sidebar = ref(true);const grid = ref(false);</script>
<template> <Menubar> <MenubarMenu> <MenubarTrigger>File</MenubarTrigger> <MenubarContent> <MenubarGroup> <MenubarItem> New index <MenubarShortcut>⌘N</MenubarShortcut> </MenubarItem> <MenubarItem>Open…</MenubarItem> </MenubarGroup> <MenubarSeparator /> <MenubarItem variant="destructive">Discard draft</MenubarItem> </MenubarContent> </MenubarMenu> <MenubarMenu> <MenubarTrigger>View</MenubarTrigger> <MenubarContent> <MenubarGroup> <MenubarCheckboxItem v-model="sidebar">Show sidebar</MenubarCheckboxItem> <MenubarCheckboxItem v-model="grid">Show grid</MenubarCheckboxItem> </MenubarGroup> <MenubarSeparator /> <MenubarItem> Zoom in <MenubarShortcut>⌘+</MenubarShortcut> </MenubarItem> <MenubarItem> Zoom out <MenubarShortcut>⌘-</MenubarShortcut> </MenubarItem> </MenubarContent> </MenubarMenu> <MenubarMenu> <MenubarTrigger>Help</MenubarTrigger> <MenubarContent> <MenubarItem>Docs</MenubarItem> </MenubarContent> </MenubarMenu> </Menubar></template><script lang="ts"> import * as Menubar from "$lib/components/ui/menubar";
let showSidebar = $state(true); let showGrid = $state(false);</script>
<Menubar.Root> <Menubar.Menu> <Menubar.Trigger>File</Menubar.Trigger> <Menubar.Content> <Menubar.Group> <Menubar.Item> New index <Menubar.Shortcut>⌘N</Menubar.Shortcut> </Menubar.Item> <Menubar.Item>Open…</Menubar.Item> </Menubar.Group> <Menubar.Separator /> <Menubar.Item variant="destructive">Discard draft</Menubar.Item> </Menubar.Content> </Menubar.Menu> <Menubar.Menu> <Menubar.Trigger>View</Menubar.Trigger> <Menubar.Content> <Menubar.Group> <Menubar.CheckboxItem bind:checked={showSidebar}>Show sidebar</Menubar.CheckboxItem> <Menubar.CheckboxItem bind:checked={showGrid}>Show grid</Menubar.CheckboxItem> </Menubar.Group> <Menubar.Separator /> <Menubar.Item> Zoom in <Menubar.Shortcut>⌘+</Menubar.Shortcut> </Menubar.Item> <Menubar.Item> Zoom out <Menubar.Shortcut>⌘-</Menubar.Shortcut> </Menubar.Item> </Menubar.Content> </Menubar.Menu> <Menubar.Menu> <Menubar.Trigger>Help</Menubar.Trigger> <Menubar.Content> <Menubar.Item>Docs</Menubar.Item> </Menubar.Content> </Menubar.Menu></Menubar.Root>Installation
Section titled “Installation”npx shadcn@latest add @edmi-ui/menubarpnpm dlx shadcn@latest add @edmi-ui/menubaryarn dlx shadcn@latest add @edmi-ui/menubarbunx shadcn@latest add @edmi-ui/menubarPick your package manager above; the choice is remembered. Add the @edmi-ui registry first, see Getting started.
Install the dependencies:
Terminal window npm install @base-ui/reactTerminal window pnpm add @base-ui/reactTerminal window yarn add @base-ui/reactTerminal window bun add @base-ui/reactAdd the Edmi components this one builds on:
dropdown-menu,elevation.Copy the code from Component source into your project and update the import paths to match your aliases.
npx shadcn-vue@latest add @edmi-ui/menubarpnpm dlx shadcn-vue@latest add @edmi-ui/menubaryarn dlx shadcn-vue@latest add @edmi-ui/menubarbunx shadcn-vue@latest add @edmi-ui/menubarPick your package manager above; the choice is remembered. Add the @edmi-ui registry first, see Getting started.
Install the dependencies:
Terminal window npm install reka-ui @vueuse/coreTerminal window pnpm add reka-ui @vueuse/coreTerminal window yarn add reka-ui @vueuse/coreTerminal window bun add reka-ui @vueuse/coreCopy the code from Component source into your project and update the import paths to match your aliases.
npx shadcn-svelte@latest add https://viandwi24.github.io/edmi-ui/r/svelte/menubar.jsonpnpm dlx shadcn-svelte@latest add https://viandwi24.github.io/edmi-ui/r/svelte/menubar.jsonyarn dlx shadcn-svelte@latest add https://viandwi24.github.io/edmi-ui/r/svelte/menubar.jsonbunx --bun shadcn-svelte@latest add https://viandwi24.github.io/edmi-ui/r/svelte/menubar.jsonPick your package manager above; the choice is remembered. Add the @edmi-ui registry first, see Getting started.
Copy the code from Component source into your project and update the import paths to match your aliases.
import { Menubar, MenubarContent, MenubarGroup, MenubarItem, MenubarMenu, MenubarSeparator, MenubarShortcut, MenubarTrigger,} from "@/components/ui/menubar";
<Menubar> <MenubarMenu> <MenubarTrigger>File</MenubarTrigger> <MenubarContent> <MenubarGroup> <MenubarItem> New index <MenubarShortcut>⌘N</MenubarShortcut> </MenubarItem> <MenubarItem>Open…</MenubarItem> </MenubarGroup> <MenubarSeparator /> <MenubarItem variant="destructive">Discard draft</MenubarItem> </MenubarContent> </MenubarMenu> {/* … */}</Menubar><script setup lang="ts">import { Menubar, MenubarContent, MenubarGroup, MenubarItem, MenubarMenu, MenubarSeparator, MenubarShortcut, MenubarTrigger,} from "@/components/ui/menubar";</script>
<template> <Menubar> <MenubarMenu> <MenubarTrigger>File</MenubarTrigger> <MenubarContent> <MenubarGroup> <MenubarItem> New index <MenubarShortcut>⌘N</MenubarShortcut> </MenubarItem> <MenubarItem>Open…</MenubarItem> </MenubarGroup> <MenubarSeparator /> <MenubarItem variant="destructive">Discard draft</MenubarItem> </MenubarContent> </MenubarMenu> <!-- … --> </Menubar></template><script lang="ts"> import * as Menubar from "$lib/components/ui/menubar";</script>
<Menubar.Root> <Menubar.Menu> <Menubar.Trigger>File</Menubar.Trigger> <Menubar.Content> <Menubar.Group> <Menubar.Item> New index <Menubar.Shortcut>⌘N</Menubar.Shortcut> </Menubar.Item> <Menubar.Item>Open…</Menubar.Item> </Menubar.Group> <Menubar.Separator /> <Menubar.Item variant="destructive">Discard draft</Menubar.Item> </Menubar.Content> </Menubar.Menu> <!-- … --></Menubar.Root>Component source
Section titled “Component source”"use client";
import { Menu as MenuPrimitive } from "@base-ui/react/menu";import { Menubar as MenubarPrimitive } from "@base-ui/react/menubar";import { cn } from "cn";import type * as React from "react";import { CheckIcon } from "@phosphor-icons/react";import { DropdownMenu, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger,} from "@/registry/edmi/ui/dropdown-menu";import { type Elevation, useElevation } from "@/registry/edmi/ui/elevation";
// ✦ depth (v4): the bar itself rises.const barElevation = { sunken: "", flat: "", raised: "border-transparent shadow-raised", floating: "border-transparent shadow-floating",};
function Menubar({ className, elevation, ...props}: MenubarPrimitive.Props & { /** ✦ depth for the bar: flat 0, raised +1 (bevel), floating +2. */ elevation?: Elevation;}) { const level = useElevation(elevation, "control"); return ( <MenubarPrimitive data-slot="menubar" className={cn( "flex items-center gap-0.5 rounded-[10px] border border-border bg-card p-[3px]", barElevation[level], className, )} {...props} /> );}
function MenubarMenu({ ...props }: React.ComponentProps<typeof DropdownMenu>) { return <DropdownMenu data-slot="menubar-menu" {...props} />;}
function MenubarGroup({ ...props}: React.ComponentProps<typeof DropdownMenuGroup>) { return <DropdownMenuGroup data-slot="menubar-group" {...props} />;}
function MenubarPortal({ ...props}: React.ComponentProps<typeof DropdownMenuPortal>) { return <DropdownMenuPortal data-slot="menubar-portal" {...props} />;}
function MenubarTrigger({ className, ...props}: React.ComponentProps<typeof DropdownMenuTrigger>) { return ( <DropdownMenuTrigger data-slot="menubar-trigger" className={cn( "flex h-[30px] items-center rounded-[7px] px-3 text-[13.5px] font-medium outline-none select-none hover:bg-accent data-[popup-open]:bg-accent aria-expanded:bg-accent", className, )} {...props} /> );}
function MenubarContent({ className, align = "start", alignOffset = -4, sideOffset = 8, ...props}: React.ComponentProps<typeof DropdownMenuContent>) { return ( <DropdownMenuContent data-slot="menubar-content" align={align} alignOffset={alignOffset} sideOffset={sideOffset} className={cn( "w-auto min-w-36 rounded-xl border border-border bg-popover p-1.5 text-popover-foreground duration-100 data-[side=bottom]:slide-in-from-top-2 data-[side=inline-end]:slide-in-from-left-2 data-[side=inline-start]:slide-in-from-right-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[open]:animate-in data-[open]:fade-in-0 data-[open]:zoom-in-95 data-[closed]:animate-out data-[closed]:fade-out-0 data-[closed]:zoom-out-95", className, )} {...props} /> );}
function MenubarItem({ className, inset, variant = "default", ...props}: React.ComponentProps<typeof DropdownMenuItem>) { return ( <DropdownMenuItem data-slot="menubar-item" data-inset={inset} data-variant={variant} className={cn( "group/menu-item relative flex h-8 cursor-default items-center gap-2.5 rounded-[7px] px-2 text-[13.5px] outline-none select-none data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground data-[inset]:pl-8 data-[disabled]:pointer-events-none data-[disabled]:opacity-45 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-[15px] [&_svg:not([class*='text-'])]:text-muted-foreground data-[variant=destructive]:text-destructive-text data-[variant=destructive]:data-[highlighted]:bg-destructive-soft data-[variant=destructive]:data-[highlighted]:text-destructive-text data-[variant=destructive]:*:[svg]:text-destructive-text", className, )} {...props} /> );}
function MenubarCheckboxItem({ className, children, checked, inset, ...props}: MenuPrimitive.CheckboxItem.Props & { inset?: boolean;}) { return ( <MenuPrimitive.CheckboxItem data-slot="menubar-checkbox-item" data-inset={inset} className={cn( "relative flex h-8 cursor-default items-center gap-2.5 rounded-[7px] px-2 text-[13.5px] outline-none select-none data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground data-[inset]:pl-8 data-[disabled]:pointer-events-none data-[disabled]:opacity-45 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-[15px] [&_svg:not([class*='text-'])]:text-muted-foreground pl-8", className, )} checked={checked} {...props} > <span className="pointer-events-none absolute left-1.5 flex size-4 items-center justify-center [&_svg:not([class*='size-'])]:size-4"> <MenuPrimitive.CheckboxItemIndicator> <CheckIcon /> </MenuPrimitive.CheckboxItemIndicator> </span> {children} </MenuPrimitive.CheckboxItem> );}
function MenubarRadioGroup({ ...props}: React.ComponentProps<typeof DropdownMenuRadioGroup>) { return <DropdownMenuRadioGroup data-slot="menubar-radio-group" {...props} />;}
function MenubarRadioItem({ className, children, inset, ...props}: MenuPrimitive.RadioItem.Props & { inset?: boolean;}) { return ( <MenuPrimitive.RadioItem data-slot="menubar-radio-item" data-inset={inset} className={cn( "relative flex h-8 cursor-default items-center gap-2.5 rounded-[7px] px-2 text-[13.5px] outline-none select-none data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground data-[inset]:pl-8 data-[disabled]:pointer-events-none data-[disabled]:opacity-45 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-[15px] [&_svg:not([class*='text-'])]:text-muted-foreground pl-8", className, )} {...props} > <span className="pointer-events-none absolute left-1.5 flex size-4 items-center justify-center [&_svg:not([class*='size-'])]:size-4"> <MenuPrimitive.RadioItemIndicator> <span className="block size-1.5 rounded-full bg-current" /> </MenuPrimitive.RadioItemIndicator> </span> {children} </MenuPrimitive.RadioItem> );}
function MenubarLabel({ className, inset, ...props}: React.ComponentProps<typeof DropdownMenuLabel> & { inset?: boolean;}) { return ( <DropdownMenuLabel data-slot="menubar-label" data-inset={inset} className={cn( "px-2 pt-1.5 pb-1 text-xs font-semibold text-muted-foreground data-[inset]:pl-8", className, )} {...props} /> );}
function MenubarSeparator({ className, ...props}: React.ComponentProps<typeof DropdownMenuSeparator>) { return ( <DropdownMenuSeparator data-slot="menubar-separator" className={cn("-mx-1 my-1 h-px bg-border", className)} {...props} /> );}
function MenubarShortcut({ className, ...props}: React.ComponentProps<typeof DropdownMenuShortcut>) { return ( <DropdownMenuShortcut data-slot="menubar-shortcut" className={cn( "ml-auto font-mono text-[11.5px] tracking-wide text-muted-foreground", className, )} {...props} /> );}
function MenubarSub({ ...props}: React.ComponentProps<typeof DropdownMenuSub>) { return <DropdownMenuSub data-slot="menubar-sub" {...props} />;}
function MenubarSubTrigger({ className, inset, ...props}: React.ComponentProps<typeof DropdownMenuSubTrigger> & { inset?: boolean;}) { return ( <DropdownMenuSubTrigger data-slot="menubar-sub-trigger" data-inset={inset} className={cn( "relative flex h-8 cursor-default items-center gap-2.5 rounded-[7px] px-2 text-[13.5px] outline-none select-none data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground data-[inset]:pl-8 data-[disabled]:pointer-events-none data-[disabled]:opacity-45 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-[15px] [&_svg:not([class*='text-'])]:text-muted-foreground data-[popup-open]:bg-accent data-[popup-open]:text-accent-foreground data-[open]:bg-accent data-[open]:text-accent-foreground", className, )} {...props} /> );}
function MenubarSubContent({ className, ...props}: React.ComponentProps<typeof DropdownMenuSubContent>) { return ( <DropdownMenuSubContent data-slot="menubar-sub-content" className={cn( "w-auto min-w-36 rounded-xl border border-border bg-popover p-1.5 text-popover-foreground duration-100 data-[side=bottom]:slide-in-from-top-2 data-[side=inline-end]:slide-in-from-left-2 data-[side=inline-start]:slide-in-from-right-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[open]:animate-in data-[open]:fade-in-0 data-[open]:zoom-in-95 data-[closed]:animate-out data-[closed]:fade-out-0 data-[closed]:zoom-out-95", className, )} {...props} /> );}
export { Menubar, MenubarCheckboxItem, MenubarContent, MenubarGroup, MenubarItem, MenubarLabel, MenubarMenu, MenubarPortal, MenubarRadioGroup, MenubarRadioItem, MenubarSeparator, MenubarShortcut, MenubarSub, MenubarSubContent, MenubarSubTrigger, MenubarTrigger,};<script setup lang="ts">import type { MenubarRootEmits, MenubarRootProps } from "reka-ui"import type { HTMLAttributes } from "vue"import { reactiveOmit } from "@vueuse/core"import { MenubarRoot, useForwardPropsEmits,} from "reka-ui"import { cn } from "@/registry/edmi/lib/utils"import { type Elevation, useElevation } from "@/registry/edmi/ui/elevation"
const props = withDefaults(defineProps<MenubarRootProps & { class?: HTMLAttributes["class"], elevation?: Elevation }>(), { elevation: undefined })const emits = defineEmits<MenubarRootEmits>()
const delegatedProps = reactiveOmit(props, "class", "elevation")
// ✦ depth (v4): the bar itself rises.const barElevation = { sunken: '', flat: '', raised: 'border-transparent shadow-raised', floating: 'border-transparent shadow-floating',}const level = useElevation(() => props.elevation, 'control')
const forwarded = useForwardPropsEmits(delegatedProps, emits)</script>
<template> <MenubarRoot v-slot="slotProps" data-slot="menubar" v-bind="forwarded" :class=" cn( `flex items-center gap-0.5 rounded-[10px] border border-border bg-card p-[3px]`, barElevation[level], props.class, ) " > <slot v-bind="slotProps" /> </MenubarRoot></template><script setup lang="ts">import type { MenubarCheckboxItemEmits, MenubarCheckboxItemProps } from "reka-ui"import type { HTMLAttributes } from "vue"import { Check } from "@lucide/vue"import { reactiveOmit } from "@vueuse/core"import { MenubarCheckboxItem, MenubarItemIndicator, useForwardPropsEmits,} from "reka-ui"import { cn } from "@/registry/edmi/lib/utils"
const props = defineProps<MenubarCheckboxItemProps & { class?: HTMLAttributes["class"], closeOnSelect?: boolean }>()const emits = defineEmits<MenubarCheckboxItemEmits>()
const delegatedProps = reactiveOmit(props, "class", "closeOnSelect")
const forwarded = useForwardPropsEmits(delegatedProps, emits)
// Checkbox and radio items keep the menu open (like Base UI and Bits); pass close-on-select to close it.function keepOpen(event: Event) { if (!props.closeOnSelect) event.preventDefault()}</script>
<template> <MenubarCheckboxItem data-slot="menubar-checkbox-item" v-bind="forwarded" @select="keepOpen" :class="cn( `relative flex h-8 cursor-default items-center gap-2.5 rounded-[7px] px-2 text-[13.5px] outline-none select-none data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground data-[inset]:pl-8 data-[disabled]:pointer-events-none data-[disabled]:opacity-45 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-[15px] [&_svg:not([class*='text-'])]:text-muted-foreground pl-8`, props.class, )" > <span class="pointer-events-none absolute left-1.5 flex size-4 items-center justify-center [&_svg:not([class*='size-'])]:size-4"> <MenubarItemIndicator> <slot name="indicator-icon"> <Check /> </slot> </MenubarItemIndicator> </span> <slot /> </MenubarCheckboxItem></template><script setup lang="ts">import type { MenubarContentProps } from "reka-ui"import type { HTMLAttributes } from "vue"import { reactiveOmit } from "@vueuse/core"import { MenubarContent, MenubarPortal, useForwardProps,} from "reka-ui"import { cn } from "@/registry/edmi/lib/utils"
defineOptions({ inheritAttrs: false,})
const props = withDefaults( defineProps<MenubarContentProps & { class?: HTMLAttributes["class"] }>(), { align: "start", alignOffset: -4, sideOffset: 8, },)
const delegatedProps = reactiveOmit(props, "class")
const forwardedProps = useForwardProps(delegatedProps)</script>
<template> <MenubarPortal> <MenubarContent data-slot="menubar-content" v-bind="{ ...$attrs, ...forwardedProps }" :class=" cn( `z-50 max-h-(--reka-menubar-content-available-height) min-w-40 origin-(--reka-menubar-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-xl border border-border bg-popover p-1.5 text-popover-foreground duration-100 outline-none data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=closed]:overflow-hidden`, props.class, ) " > <slot /> </MenubarContent> </MenubarPortal></template><script setup lang="ts">import type { MenubarGroupProps } from "reka-ui"import { MenubarGroup } from "reka-ui"
const props = defineProps<MenubarGroupProps>()</script>
<template> <MenubarGroup data-slot="menubar-group" v-bind="props" > <slot /> </MenubarGroup></template><script setup lang="ts">import type { MenubarItemEmits, MenubarItemProps } from "reka-ui"import type { HTMLAttributes } from "vue"import { reactiveOmit } from "@vueuse/core"import { MenubarItem, useForwardPropsEmits,} from "reka-ui"import { cn } from "@/registry/edmi/lib/utils"
const props = defineProps<MenubarItemProps & { class?: HTMLAttributes["class"] inset?: boolean variant?: "default" | "destructive"}>()
const emits = defineEmits<MenubarItemEmits>()
const delegatedProps = reactiveOmit(props, "class", "inset", "variant")const forwarded = useForwardPropsEmits(delegatedProps, emits)</script>
<template> <MenubarItem data-slot="menubar-item" :data-inset="inset ? '' : undefined" :data-variant="variant" v-bind="forwarded" :class="cn( `group/menu-item relative flex h-8 cursor-default items-center gap-2.5 rounded-[7px] px-2 text-[13.5px] outline-none select-none data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground data-[inset]:pl-8 data-[disabled]:pointer-events-none data-[disabled]:opacity-45 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-[15px] [&_svg:not([class*='text-'])]:text-muted-foreground data-[variant=destructive]:text-destructive-text data-[variant=destructive]:data-[highlighted]:bg-destructive-soft data-[variant=destructive]:data-[highlighted]:text-destructive-text data-[variant=destructive]:*:[svg]:text-destructive-text`, props.class, )" > <slot /> </MenubarItem></template><script setup lang="ts">import type { MenubarLabelProps } from "reka-ui"import type { HTMLAttributes } from "vue"import { reactiveOmit } from "@vueuse/core"import { MenubarLabel } from "reka-ui"import { cn } from "@/registry/edmi/lib/utils"
const props = defineProps<MenubarLabelProps & { class?: HTMLAttributes["class"], inset?: boolean }>()const delegatedProps = reactiveOmit(props, "class", "inset")</script>
<template> <MenubarLabel :data-inset="inset ? '' : undefined" v-bind="delegatedProps" :class="cn( `px-2 pt-1.5 pb-1 text-xs font-semibold text-muted-foreground data-[inset]:pl-8`, props.class)" > <slot /> </MenubarLabel></template><script setup lang="ts">import type { MenubarMenuProps } from "reka-ui"import { MenubarMenu } from "reka-ui"
const props = defineProps<MenubarMenuProps>()</script>
<template> <MenubarMenu data-slot="menubar-menu" v-bind="props" > <slot /> </MenubarMenu></template><script setup lang="ts">import type { MenubarRadioGroupEmits, MenubarRadioGroupProps } from "reka-ui"import { MenubarRadioGroup, useForwardPropsEmits,} from "reka-ui"
const props = defineProps<MenubarRadioGroupProps>()const emits = defineEmits<MenubarRadioGroupEmits>()
const forwarded = useForwardPropsEmits(props, emits)</script>
<template> <MenubarRadioGroup data-slot="menubar-radio-group" v-bind="forwarded" > <slot /> </MenubarRadioGroup></template><script setup lang="ts">import type { MenubarRadioItemEmits, MenubarRadioItemProps } from "reka-ui"import type { HTMLAttributes } from "vue"import { reactiveOmit } from "@vueuse/core"import { MenubarItemIndicator, MenubarRadioItem, useForwardPropsEmits,} from "reka-ui"import { cn } from "@/registry/edmi/lib/utils"
const props = defineProps<MenubarRadioItemProps & { class?: HTMLAttributes["class"], closeOnSelect?: boolean }>()const emits = defineEmits<MenubarRadioItemEmits>()
const delegatedProps = reactiveOmit(props, "class", "closeOnSelect")
const forwarded = useForwardPropsEmits(delegatedProps, emits)
// Checkbox and radio items keep the menu open (like Base UI and Bits); pass close-on-select to close it.function keepOpen(event: Event) { if (!props.closeOnSelect) event.preventDefault()}</script>
<template> <MenubarRadioItem data-slot="menubar-radio-item" v-bind="forwarded" @select="keepOpen" :class="cn( `relative flex h-8 cursor-default items-center gap-2.5 rounded-[7px] px-2 text-[13.5px] outline-none select-none data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground data-[inset]:pl-8 data-[disabled]:pointer-events-none data-[disabled]:opacity-45 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-[15px] [&_svg:not([class*='text-'])]:text-muted-foreground pl-8`, props.class, )" > <span class="pointer-events-none absolute left-1.5 flex size-4 items-center justify-center [&_svg:not([class*='size-'])]:size-4"> <MenubarItemIndicator> <slot name="indicator-icon"> <span class="block size-1.5 rounded-full bg-current" /> </slot> </MenubarItemIndicator> </span> <slot /> </MenubarRadioItem></template><script setup lang="ts">import type { MenubarSeparatorProps } from "reka-ui"import type { HTMLAttributes } from "vue"import { reactiveOmit } from "@vueuse/core"import { MenubarSeparator, useForwardProps } from "reka-ui"import { cn } from "@/registry/edmi/lib/utils"
const props = defineProps<MenubarSeparatorProps & { class?: HTMLAttributes["class"] }>()
const delegatedProps = reactiveOmit(props, "class")
const forwardedProps = useForwardProps(delegatedProps)</script>
<template> <MenubarSeparator data-slot="menubar-separator" :class=" cn( `-mx-1 my-1 h-px bg-border`, props.class)" v-bind="forwardedProps" /></template><script setup lang="ts">import type { HTMLAttributes } from "vue"import { cn } from "@/registry/edmi/lib/utils"
const props = defineProps<{ class?: HTMLAttributes["class"]}>()</script>
<template> <span data-slot="menubar-shortcut" :class="cn( `ml-auto font-mono text-[11.5px] tracking-wide text-muted-foreground`, props.class)" > <slot /> </span></template><script setup lang="ts">import type { MenubarSubEmits } from "reka-ui"import { MenubarSub, useForwardPropsEmits } from "reka-ui"
interface MenubarSubRootProps { defaultOpen?: boolean open?: boolean}
const props = defineProps<MenubarSubRootProps>()const emits = defineEmits<MenubarSubEmits>()
const forwarded = useForwardPropsEmits(props, emits)</script>
<template> <MenubarSub v-slot="slotProps" data-slot="menubar-sub" v-bind="forwarded" > <slot v-bind="slotProps" /> </MenubarSub></template><script setup lang="ts">import type { MenubarSubContentEmits, MenubarSubContentProps } from "reka-ui"import type { HTMLAttributes } from "vue"import { reactiveOmit } from "@vueuse/core"import { MenubarPortal, MenubarSubContent, useForwardPropsEmits,} from "reka-ui"import { cn } from "@/registry/edmi/lib/utils"
defineOptions({ inheritAttrs: false,})
const props = defineProps<MenubarSubContentProps & { class?: HTMLAttributes["class"] }>()const emits = defineEmits<MenubarSubContentEmits>()
const delegatedProps = reactiveOmit(props, "class")
const forwarded = useForwardPropsEmits(delegatedProps, emits)</script>
<template> <MenubarPortal> <MenubarSubContent data-slot="menubar-sub-content" v-bind="{ ...$attrs, ...forwarded }" :class=" cn( `z-50 w-auto min-w-36 origin-(--reka-menubar-content-transform-origin) overflow-hidden rounded-xl border border-border bg-popover p-1.5 text-popover-foreground duration-100 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95`, props.class, ) " > <slot /> </MenubarSubContent> </MenubarPortal></template><script setup lang="ts">import type { MenubarSubTriggerProps } from "reka-ui"import type { HTMLAttributes } from "vue"import { ChevronRight } from "@lucide/vue"import { reactiveOmit } from "@vueuse/core"import { MenubarSubTrigger, useForwardProps } from "reka-ui"import { cn } from "@/registry/edmi/lib/utils"
const props = defineProps<MenubarSubTriggerProps & { class?: HTMLAttributes["class"], inset?: boolean }>()
const delegatedProps = reactiveOmit(props, "class", "inset")const forwardedProps = useForwardProps(delegatedProps)</script>
<template> <MenubarSubTrigger data-slot="menubar-sub-trigger" :data-inset="inset ? '' : undefined" v-bind="forwardedProps" :class="cn( `relative flex h-8 cursor-default items-center gap-2.5 rounded-[7px] px-2 text-[13.5px] outline-none select-none data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground data-[inset]:pl-8 data-[disabled]:pointer-events-none data-[disabled]:opacity-45 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-[15px] [&_svg:not([class*='text-'])]:text-muted-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground`, props.class, )" > <slot /> <ChevronRight class="ml-auto rtl:rotate-180" /> </MenubarSubTrigger></template><script setup lang="ts">import type { MenubarTriggerProps } from "reka-ui"import type { HTMLAttributes } from "vue"import { reactiveOmit } from "@vueuse/core"import { MenubarTrigger, useForwardProps } from "reka-ui"import { cn } from "@/registry/edmi/lib/utils"
const props = defineProps<MenubarTriggerProps & { class?: HTMLAttributes["class"] }>()
const delegatedProps = reactiveOmit(props, "class")
const forwardedProps = useForwardProps(delegatedProps)</script>
<template> <MenubarTrigger data-slot="menubar-trigger" v-bind="forwardedProps" :class=" cn( `flex h-[30px] items-center rounded-[7px] px-3 text-[13.5px] font-medium outline-none select-none hover:bg-accent data-[state=open]:bg-accent`, props.class, ) " > <slot /> </MenubarTrigger></template>export { default as Menubar } from "./Menubar.vue";export { default as MenubarCheckboxItem } from "./MenubarCheckboxItem.vue";export { default as MenubarContent } from "./MenubarContent.vue";export { default as MenubarGroup } from "./MenubarGroup.vue";export { default as MenubarItem } from "./MenubarItem.vue";export { default as MenubarLabel } from "./MenubarLabel.vue";export { default as MenubarMenu } from "./MenubarMenu.vue";export { default as MenubarRadioGroup } from "./MenubarRadioGroup.vue";export { default as MenubarRadioItem } from "./MenubarRadioItem.vue";export { default as MenubarSeparator } from "./MenubarSeparator.vue";export { default as MenubarShortcut } from "./MenubarShortcut.vue";export { default as MenubarSub } from "./MenubarSub.vue";export { default as MenubarSubContent } from "./MenubarSubContent.vue";export { default as MenubarSubTrigger } from "./MenubarSubTrigger.vue";export { default as MenubarTrigger } from "./MenubarTrigger.vue";<script lang="ts"> import { Menubar as MenubarPrimitive } from "bits-ui"; import MinusIcon from "phosphor-svelte/lib/Minus"; import CheckIcon from "phosphor-svelte/lib/Check"; import { cn, type WithoutChildrenOrChild } from "$lib/utils.js"; import type { Snippet } from "svelte";
let { ref = $bindable(null), class: className, checked = $bindable(false), indeterminate = $bindable(false), inset, children: childrenProp, closeOnSelect = false, ...restProps }: WithoutChildrenOrChild<MenubarPrimitive.CheckboxItemProps> & { inset?: boolean; children?: Snippet; } = $props();</script>
<MenubarPrimitive.CheckboxItem bind:ref {closeOnSelect} bind:checked bind:indeterminate data-slot="menubar-checkbox-item" data-inset={inset} class={cn( "relative flex h-8 cursor-default items-center gap-2.5 rounded-[7px] px-2 text-[13.5px] outline-none select-none data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground data-[inset]:pl-8 data-[disabled]:pointer-events-none data-[disabled]:opacity-45 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-[15px] [&_svg:not([class*='text-'])]:text-muted-foreground pl-8", className )} {...restProps}> {#snippet children({ checked: checked, indeterminate: indeterminate })} <span class="pointer-events-none absolute left-1.5 flex size-4 items-center justify-center" > {#if indeterminate} <MinusIcon /> {:else if checked} <CheckIcon /> {/if} </span> {@render childrenProp?.()} {/snippet}</MenubarPrimitive.CheckboxItem><script lang="ts"> import { Menubar as MenubarPrimitive } from "bits-ui"; import { cn, type WithoutChildrenOrChild } from "$lib/utils.js"; import MenubarPortal from "./menubar-portal.svelte"; import type { ComponentProps } from "svelte";
let { ref = $bindable(null), class: className, sideOffset = 8, alignOffset = -4, align = "start", side = "bottom", portalProps, ...restProps }: MenubarPrimitive.ContentProps & { portalProps?: WithoutChildrenOrChild<ComponentProps<typeof MenubarPortal>>; } = $props();</script>
<MenubarPortal {...portalProps}> <MenubarPrimitive.Content bind:ref data-slot="menubar-content" {align} {alignOffset} {side} {sideOffset} class={cn( "z-50 w-auto min-w-36 origin-(--bits-menubar-content-transform-origin) overflow-hidden rounded-xl border border-border bg-popover p-1.5 text-popover-foreground duration-100 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95", className )} {...restProps} /></MenubarPortal><script lang="ts"> import { Menubar as MenubarPrimitive } from "bits-ui"; import { cn } from "$lib/utils.js"; import type { ComponentProps } from "svelte";
let { ref = $bindable(null), inset, class: className, ...restProps }: ComponentProps<typeof MenubarPrimitive.GroupHeading> & { inset?: boolean; } = $props();</script>
<MenubarPrimitive.GroupHeading bind:ref data-slot="menubar-group-heading" data-inset={inset} class={cn( "px-2 pt-1.5 pb-1 text-xs font-semibold text-muted-foreground data-[inset]:pl-8", className)} {...restProps}/><script lang="ts"> import { Menubar as MenubarPrimitive } from "bits-ui";
let { ref = $bindable(null), ...restProps }: MenubarPrimitive.GroupProps & { inset?: boolean; } = $props();</script>
<MenubarPrimitive.Group bind:ref data-slot="menubar-group" {...restProps} /><script lang="ts"> import { Menubar as MenubarPrimitive } from "bits-ui"; import { cn } from "$lib/utils.js";
let { ref = $bindable(null), class: className, inset = undefined, variant = "default", ...restProps }: MenubarPrimitive.ItemProps & { inset?: boolean; variant?: "default" | "destructive"; } = $props();</script>
<MenubarPrimitive.Item bind:ref data-slot="menubar-item" data-inset={inset} data-variant={variant} class={cn( "group/menu-item relative flex h-8 cursor-default items-center gap-2.5 rounded-[7px] px-2 text-[13.5px] outline-none select-none data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground data-[inset]:pl-8 data-[disabled]:pointer-events-none data-[disabled]:opacity-45 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-[15px] [&_svg:not([class*='text-'])]:text-muted-foreground data-[variant=destructive]:text-destructive-text data-[variant=destructive]:data-[highlighted]:bg-destructive-soft data-[variant=destructive]:data-[highlighted]:text-destructive-text data-[variant=destructive]:*:[svg]:text-destructive-text", className )} {...restProps}/><script lang="ts"> import { type WithElementRef } from "bits-ui"; import { cn } from "$lib/utils.js"; import type { HTMLAttributes } from "svelte/elements";
let { ref = $bindable(null), inset, children, class: className, ...restProps }: WithElementRef<HTMLAttributes<HTMLElement>> & { inset?: boolean; } = $props();</script>
<div bind:this={ref} data-slot="menubar-label" data-inset={inset} class={cn( "px-2 pt-1.5 pb-1 text-xs font-semibold text-muted-foreground data-[inset]:pl-8", className)} {...restProps}> {@render children?.()}</div><script lang="ts"> import { Menubar as MenubarPrimitive } from "bits-ui";
let { ...restProps }: MenubarPrimitive.MenuProps = $props();</script>
<MenubarPrimitive.Menu {...restProps} /><script lang="ts"> import { Menubar as MenubarPrimitive } from "bits-ui";
let { ...restProps }: MenubarPrimitive.PortalProps = $props();</script>
<MenubarPrimitive.Portal {...restProps} /><script lang="ts"> import { Menubar as MenubarPrimitive } from "bits-ui";
let { ref = $bindable(null), value = $bindable(""), ...restProps }: MenubarPrimitive.RadioGroupProps = $props();</script>
<MenubarPrimitive.RadioGroup bind:ref bind:value data-slot="menubar-radio-group" {...restProps} /><script lang="ts"> import { Menubar as MenubarPrimitive } from "bits-ui"; import { cn, type WithoutChild } from "$lib/utils.js";
let { ref = $bindable(null), class: className, inset, children: childrenProp, closeOnSelect = false, ...restProps }: WithoutChild<MenubarPrimitive.RadioItemProps> & { inset?: boolean; } = $props();</script>
<MenubarPrimitive.RadioItem bind:ref {closeOnSelect} data-slot="menubar-radio-item" data-inset={inset} class={cn( "relative flex h-8 cursor-default items-center gap-2.5 rounded-[7px] px-2 text-[13.5px] outline-none select-none data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground data-[inset]:pl-8 data-[disabled]:pointer-events-none data-[disabled]:opacity-45 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-[15px] [&_svg:not([class*='text-'])]:text-muted-foreground pl-8", className )} {...restProps}> {#snippet children({ checked })} <span class="pointer-events-none absolute left-1.5 flex size-4 items-center justify-center" > {#if checked} <span class="size-1.5 rounded-full bg-current"></span> {/if} </span> {@render childrenProp?.({ checked })} {/snippet}</MenubarPrimitive.RadioItem><script lang="ts"> import { Menubar as MenubarPrimitive } from "bits-ui"; import { cn } from "$lib/utils.js";
let { ref = $bindable(null), class: className, ...restProps }: MenubarPrimitive.SeparatorProps = $props();</script>
<MenubarPrimitive.Separator bind:ref data-slot="menubar-separator" class={cn( "-mx-1 my-1 h-px bg-border", className)} {...restProps}/><script lang="ts"> import { cn, type WithElementRef } from "$lib/utils.js"; import type { HTMLAttributes } from "svelte/elements";
let { ref = $bindable(null), class: className, children, ...restProps }: WithElementRef<HTMLAttributes<HTMLSpanElement>> = $props();</script>
<span bind:this={ref} data-slot="menubar-shortcut" class={cn( "ml-auto font-mono text-[11.5px] tracking-wide text-muted-foreground", className)} {...restProps}> {@render children?.()}</span><script lang="ts"> import { Menubar as MenubarPrimitive } from "bits-ui"; import { cn, type WithoutChildrenOrChild } from "$lib/utils.js"; import MenubarPortal from "./menubar-portal.svelte"; import type { ComponentProps } from "svelte";
let { ref = $bindable(null), class: className, align = "start", alignOffset = -3, portalProps, ...restProps }: MenubarPrimitive.SubContentProps & { portalProps?: WithoutChildrenOrChild<ComponentProps<typeof MenubarPortal>>; } = $props();</script>
<MenubarPortal {...portalProps}> <MenubarPrimitive.SubContent bind:ref data-slot="menubar-sub-content" {align} {alignOffset} class={cn( "z-50 w-auto min-w-36 origin-(--bits-menubar-content-transform-origin) overflow-hidden rounded-xl border border-border bg-popover p-1.5 text-popover-foreground duration-100 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95", className )} {...restProps} /></MenubarPortal><script lang="ts"> import { Menubar as MenubarPrimitive } from "bits-ui"; import CaretRightIcon from "phosphor-svelte/lib/CaretRight"; import { cn, type WithoutChild } from "$lib/utils.js";
let { ref = $bindable(null), class: className, inset = undefined, children, ...restProps }: WithoutChild<MenubarPrimitive.SubTriggerProps> & { inset?: boolean; } = $props();</script>
<MenubarPrimitive.SubTrigger bind:ref data-slot="menubar-sub-trigger" data-inset={inset} class={cn( "relative flex h-8 cursor-default items-center gap-2.5 rounded-[7px] px-2 text-[13.5px] outline-none select-none data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground data-[inset]:pl-8 data-[disabled]:pointer-events-none data-[disabled]:opacity-45 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-[15px] [&_svg:not([class*='text-'])]:text-muted-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground", className )} {...restProps}> {@render children?.()} <CaretRightIcon class="ml-auto rtl:rotate-180" /></MenubarPrimitive.SubTrigger><script lang="ts"> import { Menubar as MenubarPrimitive } from "bits-ui";
let { open = $bindable(false), ...restProps }: MenubarPrimitive.SubProps = $props();</script>
<MenubarPrimitive.Sub bind:open {...restProps} /><script lang="ts"> import { Menubar as MenubarPrimitive } from "bits-ui"; import { cn } from "$lib/utils.js";
let { ref = $bindable(null), class: className, ...restProps }: MenubarPrimitive.TriggerProps = $props();</script>
<MenubarPrimitive.Trigger bind:ref data-slot="menubar-trigger" class={cn( "flex h-[30px] items-center rounded-[7px] px-3 text-[13.5px] font-medium outline-none select-none hover:bg-accent data-[state=open]:bg-accent", className)} {...restProps}/><script lang="ts"> import { Menubar as MenubarPrimitive } from "bits-ui"; import { cn } from "$lib/utils.js"; import { type Elevation, useElevation } from "$lib/registry/ui/elevation/index.js";
let { ref = $bindable(null), class: className, elevation = "auto", ...restProps }: MenubarPrimitive.RootProps & { /** ✦ depth for the bar: flat 0, raised +1 (bevel), floating +2. */ elevation?: Elevation; } = $props();
// ✦ depth (v4): the bar itself rises. const barElevation = { sunken: "", flat: "", raised: "border-transparent shadow-raised", floating: "border-transparent shadow-floating", }; const level = useElevation(() => elevation, "control");</script>
<MenubarPrimitive.Root bind:ref data-slot="menubar" class={cn( "flex items-center gap-0.5 rounded-[10px] border border-border bg-card p-[3px]", barElevation[level.current], className )} {...restProps}/>import Root from "./menubar.svelte";import CheckboxItem from "./menubar-checkbox-item.svelte";import Content from "./menubar-content.svelte";import Group from "./menubar-group.svelte";import GroupHeading from "./menubar-group-heading.svelte";import Item from "./menubar-item.svelte";import Label from "./menubar-label.svelte";import Menu from "./menubar-menu.svelte";import Portal from "./menubar-portal.svelte";import RadioGroup from "./menubar-radio-group.svelte";import RadioItem from "./menubar-radio-item.svelte";import Separator from "./menubar-separator.svelte";import Shortcut from "./menubar-shortcut.svelte";import Sub from "./menubar-sub.svelte";import SubContent from "./menubar-sub-content.svelte";import SubTrigger from "./menubar-sub-trigger.svelte";import Trigger from "./menubar-trigger.svelte";
export { CheckboxItem, CheckboxItem as MenubarCheckboxItem, Content, Content as MenubarContent, Group, Group as MenubarGroup, GroupHeading, GroupHeading as MenubarGroupHeading, Item, Item as MenubarItem, Label, Label as MenubarLabel, Menu, Menu as MenubarMenu, Portal, Portal as MenubarPortal, RadioGroup, RadioGroup as MenubarRadioGroup, RadioItem, RadioItem as MenubarRadioItem, Root, // Root as Menubar, Separator, Separator as MenubarSeparator, Shortcut, Shortcut as MenubarShortcut, Sub, Sub as MenubarSub, SubContent, SubContent as MenubarSubContent, SubTrigger, SubTrigger as MenubarSubTrigger, Trigger, Trigger as MenubarTrigger,};Elevation ✦
Section titled “Elevation ✦”See the Elevation guide for the levels, roles and layered mode.
Flat by default. On Menubar, raised (+1) adds the bevel to the bar and floating (+2) a soft drop. The menus below it follow DropdownMenuContent (flat, or elevation on the content).
Flat (0)
Raised (+1)
Flat (0)
Raised (+1)
Flat (0)
Raised (+1)
import { Menubar, MenubarCheckboxItem, MenubarContent, MenubarGroup, MenubarItem, MenubarMenu, MenubarSeparator, MenubarShortcut, MenubarTrigger,} from "@/components/ui/menubar";
const levels = [ { value: "flat", label: "Flat (0)" }, { value: "raised", label: "Raised (+1)" },] as const;
function Bar({ elevation }: { elevation: "flat" | "raised" }) { return ( <Menubar elevation={elevation}> <MenubarMenu> <MenubarTrigger>File</MenubarTrigger> <MenubarContent> <MenubarGroup> <MenubarItem> New index <MenubarShortcut>⌘N</MenubarShortcut> </MenubarItem> <MenubarItem>Open…</MenubarItem> </MenubarGroup> <MenubarSeparator /> <MenubarItem variant="destructive">Discard draft</MenubarItem> </MenubarContent> </MenubarMenu> <MenubarMenu> <MenubarTrigger>View</MenubarTrigger> <MenubarContent> <MenubarGroup> <MenubarCheckboxItem defaultChecked> Show sidebar </MenubarCheckboxItem> <MenubarCheckboxItem>Show grid</MenubarCheckboxItem> </MenubarGroup> <MenubarSeparator /> <MenubarItem> Zoom in <MenubarShortcut>⌘+</MenubarShortcut> </MenubarItem> <MenubarItem> Zoom out <MenubarShortcut>⌘-</MenubarShortcut> </MenubarItem> </MenubarContent> </MenubarMenu> <MenubarMenu> <MenubarTrigger>Help</MenubarTrigger> <MenubarContent> <MenubarItem>Docs</MenubarItem> </MenubarContent> </MenubarMenu> </Menubar> );}
export default function Demo() { return ( <div className="flex flex-col gap-5"> {levels.map(({ value, label }) => ( <div key={value} className="flex flex-col gap-2"> <p className="text-xs font-medium text-muted-foreground">{label}</p> <Bar elevation={value} /> </div> ))} </div> );}<script setup lang="ts">import { ref } from "vue";import { Menubar, MenubarCheckboxItem, MenubarContent, MenubarGroup, MenubarItem, MenubarMenu, MenubarSeparator, MenubarShortcut, MenubarTrigger,} from "@/components/ui/menubar";
const levels = [{ value: "flat", label: "Flat (0)" }, { value: "raised", label: "Raised (+1)" }] as const;const sidebar = ref(true);const grid = ref(false);</script>
<template> <div class="flex flex-col gap-5"> <div v-for="level in levels" :key="level.value" class="flex flex-col gap-2"> <p class="text-xs font-medium text-muted-foreground">{{ level.label }}</p> <Menubar :elevation="level.value"> <MenubarMenu> <MenubarTrigger>File</MenubarTrigger> <MenubarContent> <MenubarGroup> <MenubarItem> New index <MenubarShortcut>⌘N</MenubarShortcut> </MenubarItem> <MenubarItem>Open…</MenubarItem> </MenubarGroup> <MenubarSeparator /> <MenubarItem variant="destructive">Discard draft</MenubarItem> </MenubarContent> </MenubarMenu> <MenubarMenu> <MenubarTrigger>View</MenubarTrigger> <MenubarContent> <MenubarGroup> <MenubarCheckboxItem v-model="sidebar">Show sidebar</MenubarCheckboxItem> <MenubarCheckboxItem v-model="grid">Show grid</MenubarCheckboxItem> </MenubarGroup> <MenubarSeparator /> <MenubarItem> Zoom in <MenubarShortcut>⌘+</MenubarShortcut> </MenubarItem> <MenubarItem> Zoom out <MenubarShortcut>⌘-</MenubarShortcut> </MenubarItem> </MenubarContent> </MenubarMenu> <MenubarMenu> <MenubarTrigger>Help</MenubarTrigger> <MenubarContent> <MenubarItem>Docs</MenubarItem> </MenubarContent> </MenubarMenu> </Menubar> </div> </div></template><script lang="ts"> import * as Menubar from "$lib/components/ui/menubar";
const levels = [{ value: "flat", label: "Flat (0)" }, { value: "raised", label: "Raised (+1)" }] as const; let showSidebar = $state(true); let showGrid = $state(false);</script>
<div class="flex flex-col gap-5"> {#each levels as level (level.value)} <div class="flex flex-col gap-2"> <p class="text-xs font-medium text-muted-foreground">{level.label}</p> <Menubar.Root elevation={level.value}> <Menubar.Menu> <Menubar.Trigger>File</Menubar.Trigger> <Menubar.Content> <Menubar.Group> <Menubar.Item> New index <Menubar.Shortcut>⌘N</Menubar.Shortcut> </Menubar.Item> <Menubar.Item>Open…</Menubar.Item> </Menubar.Group> <Menubar.Separator /> <Menubar.Item variant="destructive">Discard draft</Menubar.Item> </Menubar.Content> </Menubar.Menu> <Menubar.Menu> <Menubar.Trigger>View</Menubar.Trigger> <Menubar.Content> <Menubar.Group> <Menubar.CheckboxItem bind:checked={showSidebar}>Show sidebar</Menubar.CheckboxItem> <Menubar.CheckboxItem bind:checked={showGrid}>Show grid</Menubar.CheckboxItem> </Menubar.Group> <Menubar.Separator /> <Menubar.Item> Zoom in <Menubar.Shortcut>⌘+</Menubar.Shortcut> </Menubar.Item> <Menubar.Item> Zoom out <Menubar.Shortcut>⌘-</Menubar.Shortcut> </Menubar.Item> </Menubar.Content> </Menubar.Menu> <Menubar.Menu> <Menubar.Trigger>Help</Menubar.Trigger> <Menubar.Content> <Menubar.Item>Docs</Menubar.Item> </Menubar.Content> </Menubar.Menu> </Menubar.Root> </div> {/each}</div>| Prop | Type | Default |
|---|---|---|
MenubarItem variant |
default | destructive |
default |
MenubarItem inset |
boolean |
false |
MenubarContent align |
start | center | end |
start |
elevation |
"auto" | "sunken" | "flat" | "raised" | "floating" |
"auto" |
Parts: Menubar, MenubarMenu, MenubarTrigger, MenubarContent, MenubarGroup, MenubarLabel, MenubarItem, MenubarCheckboxItem, MenubarRadioGroup, MenubarRadioItem, MenubarSeparator, MenubarShortcut, MenubarSub, MenubarSubTrigger, MenubarSubContent.
✦ Edmi additions
Section titled “✦ Edmi additions”- Menus reuse the Dropdown Menu recipe (
data-[highlighted]); the open trigger stays highlighted.