<script setup lang="ts">
import {
MenubarCheckboxItem,
MenubarContent,
MenubarItem,
MenubarItemIndicator,
MenubarMenu,
MenubarPortal,
MenubarRadioGroup,
MenubarRadioItem,
MenubarRoot,
MenubarSeparator,
MenubarSub,
MenubarSubContent,
MenubarSubTrigger,
MenubarTrigger,
} from 'akar';
import { ref } from 'vue';
import './styles.css';
const currentMenu = ref('');
const checkboxOne = ref(false);
const checkboxTwo = ref(false);
const person = ref('pedro');
function handleClick() {
// eslint-disable-next-line no-alert
alert('hello!');
}
</script>
<template>
<div class="akar-demo akar-demo-Menubar">
<MenubarRoot
v-model="currentMenu"
class="MenubarRoot"
>
<MenubarMenu value="file">
<MenubarTrigger
class="MenubarTrigger"
>
File
</MenubarTrigger>
<MenubarPortal>
<MenubarContent
class="MenubarContent"
align="start"
:side-offset="5"
:align-offset="-3"
>
<MenubarItem
class="MenubarItem"
>
New Tab
<div class="RightSlot">
⌘ T
</div>
</MenubarItem>
<MenubarItem
class="MenubarItem"
>
New Window
<div class="RightSlot">
⌘ N
</div>
</MenubarItem>
<MenubarItem
class="MenubarItem"
disabled
>
New Incognito Window
</MenubarItem>
<MenubarSeparator class="MenubarSeparator" />
<MenubarSub>
<MenubarSubTrigger
class="MenubarItem"
>
Share
<div
class="RightSlot"
>
<PIcon name="i-lucide-chevron-right" />
</div>
</MenubarSubTrigger>
<MenubarPortal>
<MenubarSubContent
class="MenubarContent"
:align-offset="-5"
>
<MenubarItem
class="MenubarItem"
>
Email Link
</MenubarItem>
<MenubarItem
class="MenubarItem"
>
Messages
</MenubarItem>
<MenubarItem
class="MenubarItem"
>
Notes
</MenubarItem>
</MenubarSubContent>
</MenubarPortal>
</MenubarSub>
<MenubarSeparator class="MenubarSeparator" />
<MenubarItem
class="MenubarItem"
>
Print…
<div class="RightSlot">
⌘ P
</div>
</MenubarItem>
</MenubarContent>
</MenubarPortal>
</MenubarMenu>
<MenubarMenu>
<MenubarTrigger
class="MenubarTrigger"
>
Edit
</MenubarTrigger>
<MenubarPortal>
<MenubarContent
class="MenubarContent"
align="start"
:side-offset="5"
:align-offset="-3"
>
<MenubarItem
class="MenubarItem"
>
Undo
<div class="RightSlot">
⌘ Z
</div>
</MenubarItem>
<MenubarItem
class="MenubarItem"
>
Redo
<div class="RightSlot">
⇧ ⌘ Z
</div>
</MenubarItem>
<MenubarSeparator class="MenubarSeparator" />
<MenubarSub>
<MenubarSubTrigger
class="MenubarItem"
>
Find
<div
class="RightSlot"
>
<PIcon name="i-lucide-chevron-right" />
</div>
</MenubarSubTrigger>
<MenubarPortal>
<MenubarSubContent
class="MenubarContent"
:align-offset="-5"
>
<MenubarItem
class="group MenubarItem"
>
Search the web…
</MenubarItem>
<MenubarSeparator class="MenubarSeparator" />
<MenubarItem
class="MenubarItem"
>
Find…
</MenubarItem>
<MenubarItem
class="MenubarItem"
>
Find Next
</MenubarItem>
<MenubarItem
class="MenubarItem"
>
Find Previous
</MenubarItem>
</MenubarSubContent>
</MenubarPortal>
</MenubarSub>
<MenubarSeparator class="MenubarSeparator" />
<MenubarItem
class="MenubarItem"
>
Cut
</MenubarItem>
<MenubarItem
class="MenubarItem"
>
Copy
</MenubarItem>
<MenubarItem
class="MenubarItem"
>
Paste
</MenubarItem>
</MenubarContent>
</MenubarPortal>
</MenubarMenu>
<MenubarMenu>
<MenubarTrigger
class="MenubarTrigger"
>
View
</MenubarTrigger>
<MenubarPortal>
<MenubarContent
class="MenubarContent"
align="start"
:side-offset="5"
:align-offset="-14"
>
<MenubarCheckboxItem
v-model="checkboxOne"
class="MenubarCheckboxItem"
>
<MenubarItemIndicator class="MenubarItemIndicator">
<PIcon name="i-lucide-check" />
</MenubarItemIndicator>
Show Bookmarks
<div
class="RightSlot"
>
⌘+B
</div>
</MenubarCheckboxItem>
<MenubarCheckboxItem
v-model="checkboxTwo"
class="MenubarCheckboxItem"
>
<MenubarItemIndicator class="MenubarItemIndicator">
<PIcon name="i-lucide-check" />
</MenubarItemIndicator>
Show Full URLs
</MenubarCheckboxItem>
<MenubarSeparator class="MenubarSeparator" />
<MenubarItem
class="MenubarItem"
>
Reload
<div class="RightSlot">
⌘ R
</div>
</MenubarItem>
<MenubarItem
class="MenubarItem"
disabled
>
Force Reload
<div class="RightSlot">
⇧ ⌘ R
</div>
</MenubarItem>
<MenubarSeparator class="MenubarSeparator" />
<MenubarItem
class="MenubarItem"
>
Toggle Fullscreen
</MenubarItem>
<MenubarSeparator class="MenubarSeparator" />
<MenubarItem
class="MenubarItem"
>
Hide Sidebar
</MenubarItem>
</MenubarContent>
</MenubarPortal>
</MenubarMenu>
<MenubarMenu>
<MenubarTrigger
class="MenubarTrigger"
>
Profiles
</MenubarTrigger>
<MenubarPortal>
<MenubarContent
class="MenubarContent"
align="start"
:side-offset="5"
:align-offset="-14"
>
<MenubarRadioGroup v-model="person">
<MenubarRadioItem
class="MenubarCheckboxItem"
value="pedro"
>
<MenubarItemIndicator class="MenubarItemIndicator">
<PIcon name="i-lucide-circle" />
</MenubarItemIndicator>
Pedro Duarte
</MenubarRadioItem>
<MenubarRadioItem
class="MenubarCheckboxItem"
value="colm"
>
<MenubarItemIndicator class="MenubarItemIndicator">
<PIcon name="i-lucide-circle" />
</MenubarItemIndicator>
Colm Tuite
</MenubarRadioItem>
</MenubarRadioGroup>
<MenubarSeparator class="MenubarSeparator" />
<MenubarItem
class="MenubarItem"
@click="handleClick"
>
Edit…
</MenubarItem>
<MenubarSeparator class="MenubarSeparator" />
<MenubarItem
class="MenubarItem"
>
Add Profile…
</MenubarItem>
</MenubarContent>
</MenubarPortal>
</MenubarMenu>
</MenubarRoot>
</div>
</template>@import '../_colors/black-alpha.css';
@import '../_colors/mauve.css';
@import '../_colors/grass.css';
@scope (.akar-demo-Menubar) {
/* reset */
button {
all: unset;
}
.MenubarRoot {
display: flex;
background-color: white;
padding: 3px;
border-radius: 6px;
box-shadow: 0 2px 10px var(--black-a7);
}
.MenubarTrigger {
padding: 8px 12px;
outline: none;
user-select: none;
font-weight: 500;
line-height: 1;
border-radius: 4px;
color: var(--grass-11);
font-size: 13px;
display: flex;
align-items: center;
justify-content: space-between;
gap: 2px;
}
.MenubarTrigger[data-highlighted],
.MenubarTrigger[data-state='open'] {
background-color: var(--grass-4);
}
.MenubarContent,
.MenubarSubContent {
min-width: 220px;
background-color: white;
border-radius: 6px;
padding: 5px;
box-shadow:
0px 10px 38px -10px rgba(22, 23, 24, 0.35),
0px 10px 20px -15px rgba(22, 23, 24, 0.2);
animation-duration: 400ms;
animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
will-change: transform, opacity;
}
.MenubarItem,
.MenubarSubTrigger,
.MenubarCheckboxItem,
.MenubarRadioItem {
all: unset;
font-size: 13px;
line-height: 1;
color: var(--grass-11);
border-radius: 4px;
display: flex;
align-items: center;
height: 25px;
padding: 0 10px;
position: relative;
user-select: none;
}
.MenubarItem.inset,
.MenubarSubTrigger.inset,
.MenubarCheckboxItem.inset,
.MenubarRadioItem.inset {
padding-left: 20px;
}
.MenubarItem[data-state='open'],
.MenubarSubTrigger[data-state='open'] {
background-color: var(--grass-4);
color: var(--grass-11);
}
.MenubarItem[data-highlighted],
.MenubarSubTrigger[data-highlighted],
.MenubarCheckboxItem[data-highlighted],
.MenubarRadioItem[data-highlighted] {
background-image: linear-gradient(135deg, var(--grass-9) 0%, var(--grass-10) 100%);
color: var(--grass-1);
}
.MenubarItem[data-disabled],
.MenubarSubTrigger[data-disabled],
.MenubarCheckboxItem[data-disabled],
.MenubarRadioItem[data-disabled] {
color: var(--mauve-8);
pointer-events: none;
}
.MenubarItemIndicator {
position: absolute;
left: 0;
width: 20px;
display: inline-flex;
align-items: center;
justify-content: center;
}
.MenubarSeparator {
height: 1px;
background-color: var(--grass-6);
margin: 5px;
}
.RightSlot {
margin-left: auto;
padding-left: 20px;
color: var(--mauve-9);
}
[data-highlighted] > .RightSlot {
color: white;
}
[data-disabled] > .RightSlot {
color: var(--mauve-8);
}
}Features
- Can be controlled or uncontrolled.
- Supports submenus with configurable reading direction.
- Supports items, labels, groups of items.
- Supports checkable items (single or multiple).
- Customize side, alignment, offsets, collision handling.
- Optionally render a pointing arrow.
- Focus is fully managed.
- Full keyboard navigation.
- Typeahead support.
Installation
Install the component from your command line.
pnpm add akar
yarn add akar
npm install akar
bun add akar
Anatomy
Import all parts and piece them together.
<script setup lang="ts">
import {
MenubarArrow,
MenubarCheckboxItem,
MenubarContent,
MenubarItem,
MenubarItemIndicator,
MenubarLabel,
MenubarMenu,
MenubarPortal,
MenubarRadioGroup,
MenubarRadioItem,
MenubarRoot,
MenubarSeparator,
MenubarSub,
MenubarSubContent,
MenubarSubTrigger,
MenubarTrigger,
} from './';
</script>
<template>
<MenubarRoot>
<MenubarMenu>
<MenubarTrigger />
<MenubarPortal>
<MenubarContent>
<MenubarLabel />
<MenubarItem />
<MenubarGroup>
<MenubarItem />
</MenubarGroup>
<MenubarCheckboxItem>
<MenubarItemIndicator />
</MenubarCheckboxItem>
<MenubarRadioGroup>
<MenubarRadioItem>
<MenubarItemIndicator />
</MenubarRadioItem>
</MenubarRadioGroup>
<MenubarSub>
<MenubarSubTrigger />
<MenubarPortal>
<MenubarSubContent />
</MenubarPortal>
</MenubarSub>
<MenubarSeparator />
<MenubarArrow />
</MenubarContent>
</MenubarPortal>
</MenubarMenu>
</MenubarRoot>
</template>
API Reference
Root
Contains all the parts of a menubar
| Prop | Default | Type |
|---|---|---|
defaultValue | stringThe value of the menu that should be open when initially rendered. Use when you do not need to control the value state. | |
dir | "ltr" | "rtl"The reading direction of the combobox when applicable. If omitted, inherits globally from | |
loop | false | booleanWhen |
modelValue | stringThe controlled value of the menu to open. Can be used as |
| Event | Payload |
|---|---|
update:modelValue | value: booleanEvent handler called when the value changes. |
| Slot (default) | Payload |
|---|---|
modelValue | stringCurrent input values |
Menu
A top level menu item, contains a trigger with content combination.
| Prop | Default | Type |
|---|---|---|
value | stringA unique value that associates the item with an active value when the navigation menu is controlled. This prop is managed automatically when uncontrolled. |
Trigger
The button that toggles the content. By default, the MenubarContent will position itself against the trigger.
| Prop | Default | Type |
|---|---|---|
as | "button" | AsTag | ComponentThe element or component this component should render as. Can be overwritten by |
asChild | booleanChange the default rendered element for the one passed as a child, merging their props and behavior. Read our Composition guide for more details. | |
disabled | booleanWhen |
| Data attribute | Value |
|---|---|
[data-state] | "open" | "closed" |
[data-highlighted] | Present when highlighted |
[data-disabled] | Present when disabled |
Portal
When used, portals the content part into the body.
| Prop | Default | Type |
|---|---|---|
defer | booleanDefer the resolving of a Teleport target until other parts of the application have mounted (requires Vue 3.5.0+) | |
disabled | booleanDisable teleport and render the component inline | |
forceMount | booleanUsed to force mounting when more control is needed. Useful when controlling animation with Vue animation libraries. | |
to | string | HTMLElementVue native teleport component prop |
Content
The component that pops out when a menu is open.
| Prop | Default | Type |
|---|---|---|
align | "start" | "start" | "center" | "end"The preferred alignment against the trigger. May change when collisions occur. |
alignFlip | true | booleanFlip alignment when colliding with boundary.
May only occur when |
alignOffset | 0 | numberAn offset in pixels from the |
arrowPadding | 0 | numberThe padding between the arrow and the edges of the content. If your content has border-radius, this will prevent it from overflowing the corners. |
as | "div" | AsTag | ComponentThe element or component this component should render as. Can be overwritten by |
asChild | booleanChange the default rendered element for the one passed as a child, merging their props and behavior. Read our Composition guide for more details. | |
avoidCollisions | true | booleanWhen |
collisionBoundary | [] | Element | (Element | null)[] | nullThe element used as the collision boundary. By default this is the viewport, though you can provide additional element(s) to be included in this check. |
collisionPadding | 0 | number | Partial<Record<"top" | "right" | "bottom" | "left", number>>The distance in pixels from the boundary edges where collision detection should occur. Accepts a number (same for all sides), or a partial padding object, for example: . |
disableUpdateOnLayoutShift | false | booleanWhether to disable the update position for the content when the layout shifted. |
forceMount | booleanUsed to force mounting when more control is needed. Useful when controlling animation with Vue animation libraries. | |
hideShiftedArrow | true | booleanWhen |
hideWhenDetached | false | booleanWhether to hide the content when the trigger becomes fully occluded. |
loop | false | booleanWhen |
memoDependencies | unknown[]Reactive dependencies that should invalidate the memoized content subtree. | |
positionStrategy | "fixed" | "absolute"The type of CSS position property to use. | |
prioritizePosition | false | booleanForce content to be position within the viewport. Might overlap the reference element, which may not be desired. |
reference | ReferenceElementThe custom element or virtual element that will be set as the reference to position the floating element. If provided, it will replace the default anchor element. | |
side | "bottom" | "top" | "right" | "bottom" | "left"The preferred side of the trigger to render against when open. Will be reversed when collisions occur and avoidCollisions is enabled. |
sideFlip | true | booleanFlip to the opposite side when colliding with boundary. |
sideOffset | 0 | numberThe distance in pixels from the trigger. |
sticky | "partial" | "partial" | "always"The sticky behavior on the align axis. |
updatePositionStrategy | 'optimized' | "always" | "optimized"Strategy to update the position of the floating element on every animation frame. |
| Event | Payload |
|---|---|
closeAutoFocus | event: EventEvent handler called when auto-focusing on close. Can be prevented. |
escapeKeyDown | event: KeyboardEventEvent handler called when the escape key is down. Can be prevented. |
focusOutside | event: FocusOutsideEventEvent handler called when the focus moves outside of the |
interactOutside | event: PointerDownOutsideEvent | FocusOutsideEventEvent handler called when an interaction happens outside the |
openAutoFocus | event: Event |
pointerDownOutside | event: PointerDownOutsideEventEvent handler called when a |
| Data attribute | Value |
|---|---|
[data-state] | "open" | "closed" |
[data-side] | "left" | "right" | "bottom" | "top" |
[data-align] | "start" | "end" | "center" |
| CSS variable | Description |
|---|---|
--akar-menubar-content-transform-origin | The transform-origin computed from the content and arrow positions/offsets |
--akar-menubar-content-available-width | The remaining width between the trigger and the boundary edge |
--akar-menubar-content-available-height | The remaining height between the trigger and the boundary edge |
--akar-menubar-trigger-width | The width of the trigger |
--akar-menubar-trigger-height | The height of the trigger |
Arrow
An optional arrow element to render alongside a menubar menu. This can be used to help visually link the trigger with the MenubarContent. Must be rendered inside MenubarContent.
| Prop | Default | Type |
|---|---|---|
as | "svg" | AsTag | ComponentThe element or component this component should render as. Can be overwritten by |
asChild | booleanChange the default rendered element for the one passed as a child, merging their props and behavior. Read our Composition guide for more details. | |
height | 5 | numberThe height of the arrow in pixels. |
rounded | false | booleanWhen |
width | 10 | numberThe width of the arrow in pixels. |
Item
The component that contains the menubar items.
| Prop | Default | Type |
|---|---|---|
as | "div" | AsTag | ComponentThe element or component this component should render as. Can be overwritten by |
asChild | booleanChange the default rendered element for the one passed as a child, merging their props and behavior. Read our Composition guide for more details. | |
disabled | booleanWhen | |
textValue | stringOptional text used for typeahead purposes. By default the typeahead behavior will use the Use this when the content is complex, or you have non-textual content inside. |
| Event | Payload |
|---|---|
select | event: EventEvent handler called when the user selects an item (via mouse or keyboard). Calling |
| Data attribute | Value |
|---|---|
[data-highlighted] | Present when highlighted |
[data-disabled] | Present when disabled |
Group
Used to group multiple MenubarItems.
| Prop | Default | Type |
|---|---|---|
as | "div" | AsTag | ComponentThe element or component this component should render as. Can be overwritten by |
asChild | booleanChange the default rendered element for the one passed as a child, merging their props and behavior. Read our Composition guide for more details. |
Label
Used to render a label. It won't be focusable using arrow keys.
| Prop | Default | Type |
|---|---|---|
as | "div" | AsTag | ComponentThe element or component this component should render as. Can be overwritten by |
asChild | booleanChange the default rendered element for the one passed as a child, merging their props and behavior. Read our Composition guide for more details. |
CheckboxItem
An item that can be controlled and rendered like a checkbox.
| Prop | Default | Type |
|---|---|---|
as | "div" | AsTag | ComponentThe element or component this component should render as. Can be overwritten by |
asChild | booleanChange the default rendered element for the one passed as a child, merging their props and behavior. Read our Composition guide for more details. | |
disabled | booleanWhen | |
modelValue | false | true | "indeterminate"The controlled checked state of the item. Can be used as | |
textValue | stringOptional text used for typeahead purposes. By default the typeahead behavior will use the Use this when the content is complex, or you have non-textual content inside. |
| Event | Payload |
|---|---|
select | event: EventEvent handler called when the user selects an item (via mouse or keyboard). Calling |
update:modelValue | payload: booleanEvent handler called when the checked state changes. |
| Slot (default) | Payload |
|---|---|
checked | false | true | "indeterminate"Current checked state |
modelValue | CheckedStateCurrent modelValue state |
| Data attribute | Value |
|---|---|
[data-state] | "checked" | "unchecked" |
[data-highlighted] | Present when highlighted |
[data-disabled] | Present when disabled |
RadioGroup
Used to group multiple MenubarRadioItems.
| Prop | Default | Type |
|---|---|---|
as | "div" | AsTag | ComponentThe element or component this component should render as. Can be overwritten by |
asChild | booleanChange the default rendered element for the one passed as a child, merging their props and behavior. Read our Composition guide for more details. | |
modelValue | AcceptableValueThe value of the selected item in the group. |
| Event | Payload |
|---|---|
update:modelValue | payload: AcceptableValueEvent handler called when the value changes. |
RadioItem
An item that can be controlled and rendered like a radio.
| Prop | Default | Type |
|---|---|---|
as | "div" | AsTag | ComponentThe element or component this component should render as. Can be overwritten by |
asChild | booleanChange the default rendered element for the one passed as a child, merging their props and behavior. Read our Composition guide for more details. | |
disabled | booleanWhen | |
textValue | stringOptional text used for typeahead purposes. By default the typeahead behavior will use the Use this when the content is complex, or you have non-textual content inside. | |
value* | AcceptableValueThe unique value of the item. |
| Event | Payload |
|---|---|
select | event: EventEvent handler called when the user selects an item (via mouse or keyboard). Calling |
| Slot (default) | Payload |
|---|---|
checked | booleanCurrent checked state |
| Data attribute | Value |
|---|---|
[data-state] | "checked" | "unchecked" |
[data-highlighted] | Present when highlighted |
[data-disabled] | Present when disabled |
ItemIndicator
Renders when the parent MenubarCheckboxItem or MenubarRadioItem is checked. You can style this element directly, or you can use it as a wrapper to put an icon into, or both.
| Prop | Default | Type |
|---|---|---|
as | "div" | AsTag | ComponentThe element or component this component should render as. Can be overwritten by |
asChild | booleanChange the default rendered element for the one passed as a child, merging their props and behavior. Read our Composition guide for more details. | |
forceMount | booleanUsed to force mounting when more control is needed. Useful when controlling animation with Vue animation libraries. |
| Data attribute | Value |
|---|---|
[data-state] | "checked" | "unchecked" |
Separator
Used to visually separate items in a menubar menu.
| Prop | Default | Type |
|---|---|---|
as | "div" | AsTag | ComponentThe element or component this component should render as. Can be overwritten by |
asChild | booleanChange the default rendered element for the one passed as a child, merging their props and behavior. Read our Composition guide for more details. |
Sub
Contains all the parts of a submenu.
| Prop | Default | Type |
|---|---|---|
defaultOpen | booleanThe open state of the submenu when it is initially rendered. Use when you do not need to control its open state. | |
open | booleanThe controlled open state of the menu. Can be used as |
| Event | Payload |
|---|---|
update:open | payload: booleanEvent handler called when the open state of the submenu changes. |
| Slot (default) | Payload |
|---|---|
open | booleanCurrent open state |
SubTrigger
An item that opens a submenu. Must be rendered inside MenubarSub.
| Prop | Default | Type |
|---|---|---|
as | "div" | AsTag | ComponentThe element or component this component should render as. Can be overwritten by |
asChild | booleanChange the default rendered element for the one passed as a child, merging their props and behavior. Read our Composition guide for more details. | |
disabled | booleanWhen | |
graceDuration | 300 | numberTime in milliseconds to keep a submenu open while the pointer moves from its trigger to the submenu. |
textValue | stringOptional text used for typeahead purposes. By default the typeahead behavior will use the Use this when the content is complex, or you have non-textual content inside. |
| Data attribute | Value |
|---|---|
[data-state] | "open" | "closed" |
[data-highlighted] | Present when highlighted |
[data-disabled] | Present when disabled |
SubContent
The component that pops out when a submenu is open. Must be rendered inside MenubarSub.
| Prop | Default | Type |
|---|---|---|
alignFlip | true | booleanFlip alignment when colliding with boundary.
May only occur when |
alignOffset | 0 | numberAn offset in pixels from the |
arrowPadding | 0 | numberThe padding between the arrow and the edges of the content. If your content has border-radius, this will prevent it from overflowing the corners. |
as | "div" | AsTag | ComponentThe element or component this component should render as. Can be overwritten by |
asChild | booleanChange the default rendered element for the one passed as a child, merging their props and behavior. Read our Composition guide for more details. | |
avoidCollisions | true | booleanWhen |
collisionBoundary | [] | Element | (Element | null)[] | nullThe element used as the collision boundary. By default this is the viewport, though you can provide additional element(s) to be included in this check. |
collisionPadding | 0 | number | Partial<Record<"top" | "right" | "bottom" | "left", number>>The distance in pixels from the boundary edges where collision detection should occur. Accepts a number (same for all sides), or a partial padding object, for example: . |
disableUpdateOnLayoutShift | false | booleanWhether to disable the update position for the content when the layout shifted. |
forceMount | booleanUsed to force mounting when more control is needed. Useful when controlling animation with Vue animation libraries. | |
hideShiftedArrow | true | booleanWhen |
hideWhenDetached | false | booleanWhether to hide the content when the trigger becomes fully occluded. |
loop | false | booleanWhen |
memoDependencies | unknown[]Reactive dependencies that should invalidate the memoized content subtree. | |
positionStrategy | "fixed" | "absolute"The type of CSS position property to use. | |
prioritizePosition | false | booleanForce content to be position within the viewport. Might overlap the reference element, which may not be desired. |
reference | ReferenceElementThe custom element or virtual element that will be set as the reference to position the floating element. If provided, it will replace the default anchor element. | |
sideFlip | true | booleanFlip to the opposite side when colliding with boundary. |
sideOffset | 0 | numberThe distance in pixels from the trigger. |
sticky | "partial" | "partial" | "always"The sticky behavior on the align axis. |
updatePositionStrategy | 'optimized' | "always" | "optimized"Strategy to update the position of the floating element on every animation frame. |
| Event | Payload |
|---|---|
closeAutoFocus | event: EventEvent handler called when auto-focusing on close. Can be prevented. |
entryFocus | event: Event |
escapeKeyDown | event: KeyboardEventEvent handler called when the escape key is down. Can be prevented. |
focusOutside | event: FocusOutsideEventEvent handler called when the focus moves outside of the |
interactOutside | event: PointerDownOutsideEvent | FocusOutsideEventEvent handler called when an interaction happens outside the |
openAutoFocus | event: Event |
pointerDownOutside | event: PointerDownOutsideEventEvent handler called when a |
| Data attribute | Value |
|---|---|
[data-state] | "open" | "closed" |
[data-side] | "left" | "right" | "bottom" | "top" |
[data-align] | "start" | "end" | "center" |
[data-orientation] | "vertical" | "horizontal" |
| CSS variable | Description |
|---|---|
--akar-menubar-content-transform-origin | The transform-origin computed from the content and arrow positions/offsets |
--akar-menubar-content-available-width | The remaining width between the trigger and the boundary edge |
--akar-menubar-content-available-height | The remaining height between the trigger and the boundary edge |
--akar-menubar-trigger-width | The width of the trigger |
--akar-menubar-trigger-height | The height of the trigger |
Examples
With submenus
You can create submenus by using MenubarSub in combination with its parts.
<script setup lang="ts">
import {
MenubarContent,
MenubarItem,
MenubarMenu,
MenubarPortal,
MenubarRoot,
MenubarSeparator,
MenubarSub,
MenubarSubContent,
MenubarSubTrigger,
MenubarTrigger,
} from 'akar'
</script>
<template>
<MenubarRoot>
<MenubarMenu>
<MenubarTrigger>…</MenubarTrigger>
<MenubarPortal>
<MenubarContent>
<MenubarItem>…</MenubarItem>
<MenubarItem>…</MenubarItem>
<MenubarSeparator />
<MenubarSub>
<MenubarSubTrigger>Sub menu →</MenubarSubTrigger>
<MenubarPortal>
<MenubarSubContent>
<MenubarItem>Sub menu item</MenubarItem>
<MenubarItem>Sub menu item</MenubarItem>
<MenubarArrow />
</MenubarSubContent>
</MenubarPortal>
</MenubarSub>
<MenubarSeparator />
<MenubarItem>…</MenubarItem>
</MenubarContent>
</MenubarPortal>
</MenubarMenu>
</MenubarRoot>
</template>
With disabled items
You can add special styles to disabled items via the data-disabled attribute.
<script setup lang="ts">
import { MenubarContent, MenubarItem, MenubarMenu, MenubarPortal, MenubarRoot, MenubarTrigger } from 'akar'
</script>
<template>
<MenubarRoot>
<MenubarMenu>
<MenubarTrigger>…</MenubarTrigger>
<MenubarPortal>
<MenubarContent>
<MenubarItem
class="MenubarItem"
disabled
>
…
</MenubarItem>
<MenubarItem class="MenubarItem">
…
</MenubarItem>
</MenubarContent>
</MenubarPortal>
</MenubarMenu>
</MenubarRoot>
</template>
/* styles.css */
.MenubarItem[data-disabled] {
color: gainsboro;
}
With separators
Use the Separator part to add a separator between items.
<script setup lang="ts">
import {
MenubarContent,
MenubarItem,
MenubarMenu,
MenubarPortal,
MenubarRoot,
MenubarSeparator,
MenubarTrigger,
} from 'akar'
</script>
<template>
<MenubarRoot>
<MenubarMenu>
<MenubarTrigger>…</MenubarTrigger>
<MenubarPortal>
<MenubarContent>
<MenubarItem>…</MenubarItem>
<MenubarSeparator />
<MenubarItem>…</MenubarItem>
<MenubarSeparator />
<MenubarItem>…</MenubarItem>
</MenubarContent>
</MenubarPortal>
</MenubarMenu>
</MenubarRoot>
</template>
With labels
Use the Label part to help label a section.
<script setup lang="ts">
import {
MenubarContent,
MenubarItem,
MenubarLabel,
MenubarMenu,
MenubarPortal,
MenubarRoot,
MenubarTrigger,
} from 'akar'
</script>
<template>
<MenubarRoot>
<MenubarMenu>
<MenubarTrigger>…</MenubarTrigger>
<MenubarPortal>
<MenubarContent>
<MenubarLabel>Label</MenubarLabel>
<MenubarItem>…</MenubarItem>
<MenubarItem>…</MenubarItem>
<MenubarItem>…</MenubarItem>
</MenubarContent>
</MenubarPortal>
</MenubarMenu>
</MenubarRoot>
</template>
With checkbox items
Use the CheckboxItem part to add an item that can be checked.
<script setup lang="ts">
import { Icon } from '@iconify/vue'
import {
MenubarCheckboxItem,
MenubarContent,
MenubarItem,
MenubarItemIndicator,
MenubarMenu,
MenubarPortal,
MenubarRoot,
MenubarSeparator,
MenubarTrigger,
} from 'akar'
const checked = ref(true)
</script>
<template>
<MenubarRoot>
<MenubarMenu>
<MenubarTrigger>…</MenubarTrigger>
<MenubarPortal>
<MenubarContent>
<MenubarItem>…</MenubarItem>
<MenubarItem>…</MenubarItem>
<MenubarSeparator />
<MenubarCheckboxItem v-model="checked">
<MenubarItemIndicator>
<Icon icon="radix-icons:check" />
</MenubarItemIndicator>
Checkbox item
</MenubarCheckboxItem>
</MenubarContent>
</MenubarPortal>
</MenubarMenu>
</MenubarRoot>
</template>
With radio items
Use the RadioGroup and RadioItem parts to add an item that can be checked amongst others.
<script setup lang="ts">
import { Icon } from '@iconify/vue'
import {
MenubarCheckboxItem,
MenubarContent,
MenubarItem,
MenubarItemIndicator,
MenubarMenu,
MenubarPortal,
MenubarRadioGroup,
MenubarRadioItem,
MenubarRoot,
MenubarSeparator,
MenubarTrigger,
} from 'akar'
const color = ref('blue')
</script>
<template>
<MenubarRoot>
<MenubarMenu>
<MenubarTrigger>…</MenubarTrigger>
<MenubarPortal>
<MenubarContent>
<MenubarRadioGroup v-model="color">
<MenubarRadioItem value="red">
<MenubarItemIndicator>
<Icon icon="radix-icons:check" />
</MenubarItemIndicator>
Red
</MenubarRadioItem>
<MenubarRadioItem value="blue">
<MenubarItemIndicator>
<Icon icon="radix-icons:check" />
</MenubarItemIndicator>
Blue
</MenubarRadioItem>
</MenubarRadioGroup>
</MenubarContent>
</MenubarPortal>
</MenubarMenu>
</MenubarRoot>
</template>
With complex items
You can add extra decorative elements in the Item parts, such as images.
<script setup lang="ts">
import { MenubarContent, MenubarItem, MenubarMenu, MenubarPortal, MenubarRoot, MenubarTrigger } from 'akar'
</script>
<template>
<MenubarRoot>
<MenubarMenu>
<MenubarTrigger>…</MenubarTrigger>
<MenubarPortal>
<MenubarContent>
<MenubarItem>
<img src="…">
Adolfo Hess
</MenubarItem>
<MenubarItem>
<img src="…">
Miyah Myles
</MenubarItem>
</MenubarContent>
</MenubarPortal>
</MenubarMenu>
</MenubarRoot>
</template>
Constrain the content/sub-content size
You may want to constrain the width of the content (or sub-content) so that it matches the trigger (or sub-trigger) width. You may also want to constrain its height to not exceed the viewport.
We expose several CSS custom properties such as --akar-menubar-trigger-width and --akar-menubar-content-available-height to support this. Use them to constrain the content dimensions.
<script setup lang="ts">
import { MenubarContent, MenubarItem, MenubarMenu, MenubarPortal, MenubarRoot, MenubarTrigger } from 'akar'
</script>
<template>
<MenubarRoot>
<MenubarMenu>
<MenubarTrigger> Trigger </MenubarTrigger>
<MenubarPortal>
<MenubarContent
class="MenubarContent"
:side-offset="5"
:align-offset="-3"
>
<MenubarItem> New Tab </MenubarItem>
</MenubarContent>
</MenubarPortal>
</MenubarMenu>
</MenubarRoot>
</template>
/* styles.css */
.MenubarContent {
width: var(--akar-menubar-trigger-width);
max-height: var(--akar-menubar-content-available-height);
}
Origin-aware animations
We expose a CSS custom property --akar-menubar-content-transform-origin. Use it to animate the content from its computed origin based on side, sideOffset, align, alignOffset and any collisions.
<script setup lang="ts">
import { MenubarContent, MenubarMenu, MenubarPortal, MenubarRoot, MenubarTrigger } from 'akar'
</script>
<template>
<MenubarRoot>
<MenubarMenu>
<MenubarTrigger>…</MenubarTrigger>
<MenubarPortal>
<MenubarContent class="MenubarContent">
…
</MenubarContent>
</MenubarPortal>
</MenubarMenu>
</MenubarRoot>
</template>
/* styles.css */
.MenubarContent {
transform-origin: var(--akar-menubar-content-transform-origin);
animation: scaleIn 0.5s ease-out;
}
@keyframes scaleIn {
from {
opacity: 0;
transform: scale(0);
}
to {
opacity: 1;
transform: scale(1);
}
}
Collision-aware animations
We expose data-side and data-align attributes. Their values will change at runtime to reflect collisions. Use them to create collision and direction-aware animations.
<script setup lang="ts">
import { MenubarContent, MenubarMenu, MenubarPortal, MenubarRoot, MenubarTrigger } from 'akar'
</script>
<template>
<MenubarRoot>
<MenubarMenu>
<MenubarTrigger>…</MenubarTrigger>
<MenubarPortal>
<MenubarContent class="MenubarContent">
…
</MenubarContent>
</MenubarPortal>
</MenubarMenu>
</MenubarRoot>
</template>
/* styles.css */
.MenubarContent {
animation-duration: 0.6s;
animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
}
.MenubarContent[data-side="top"] {
animation-name: slideUp;
}
.MenubarContent[data-side="bottom"] {
animation-name: slideDown;
}
@keyframes slideUp {
from {
opacity: 0;
transform: translateY(10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes slideDown {
from {
opacity: 0;
transform: translateY(-10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
Accessibility
Adheres to the Menu Button WAI-ARIA design pattern and uses roving tabindex to manage focus movement among menu items.
Keyboard Interactions
| Key | Description |
|---|
When focus is on an item, activates the focused item.
`,
},
{
keys: ['Enter'],
description: `
When focus is on <Code>MenubarTrigger</Code>, opens the associated menu.
<br />
When focus is on an item, activates the focused item.
`,
},
{
keys: ['ArrowDown'],
description: `
When focus is on <Code>MenubarTrigger</Code>, opens the associated menu.
<br />
When focus is on an item, moves focus to the next item.
`,
},
{
keys: ['ArrowUp'],
description: `When focus is on an item, moves focus to the previous item.`,
},
{
keys: ['ArrowRight', 'ArrowLeft'],
description: `
When focus is on a <Code>MenubarTrigger</Code>, moves focus to the next or previous item. <br /> When focus is on a <Code>MenubarSubTrigger</Code>, opens or closes the submenu depending on reading direction. <br /> When focus is within a <Code>MenubarContent</Code>, opens the next menu in the menubar
`,
},
{
keys: ['Esc'],
description: `
Closes the currently open menu and moves focus to its <Code>MenubarTrigger</Code>.
`,
},
]" />