Skip to content

CdsMenu

A menu pops up next to an activator element and shows a list of actions or sections: the "more" button in a table row, the user menu in the header, a context menu on right click. It positions itself — it opens towards the free space and sticks to the activator while the page scrolls.

Storybook GitLab Figma
Test coverage: 87% — partial coverage
API in specs
56 / 56 props, events, slots and exposed members with a test
Test cases
51 unit · 8 browser
Known bugs
5 pinned by an it.fails test
Code coverage
87% lines · 83.8% branches measured in v4.0.0-alpha.13
A11y (axe)
7 recommendations in 12 stories measured in v4.0.0-alpha.11

To pick a value in a form, take CdsSelect — it stays visible and gets submitted. For a short hint on hover use CdsTooltip, and for a dialog that demands an answer use CdsModal. The content of a menu is usually built from CdsList.

Inside the menu works CdsOverlay: it computes the coordinates, flips the panel when there is not enough room up to the edge of the screen, and recalculates the position on scroll. That is why the menu shares its positioning and opening props with the overlay.

Variations ​

Activators ​

A menu is opened by an activator element — you can set it in three ways:

  1. Through the activator slot — the props from the slot (v-bind="props") attach the handlers and the aria attributes to the element. This is the default way: the activator sits next to the menu.
  2. Through the parent — activator="parent" takes the element that contains the CdsMenu. Handy when the activator is already built and you do not want to wrap it.
  3. Through a selector — activator="#id" links the menu to any element on the page, even a distant one in the markup.

Opening ​

By default the menu opens on click. The open-on-hover prop switches it to hover — this is how navigation menus behave; a click then does not open the panel. The open-delay and close-delay delays (300 and 250 ms) keep the menu from flickering when the cursor just passes by. Together with open-on-hover, opening on focus is enabled as well — turn it off with :open-on-focus="false" if the menu should react to the mouse only.

Position ​

The location prop sets the side of the activator the menu appears on: bottom by default, top, end, bottom end — the side and the alignment. If there is not enough room, the menu flips to the opposite edge on its own. The offset prop tunes the distance to the activator, and viewport-margin sets the minimum gap to the edge of the screen.

Sizes ​

The width of the menu equals the width of its content. The min-width, max-width and max-height props set the bounds: the minimum width keeps the panel from collapsing on short items, and a limited height turns on scrolling instead of a full-screen menu.

Closing ​

The menu closes on a click inside the content, on a click outside and on Esc. The :close-on-content-click="false" prop keeps the panel open — you need that when it holds checkboxes, fields or several steps in a row. The persistent prop ignores the outside click and Esc: then the menu can only be closed by your own button through v-model.

Nested menus ​

Menus can be nested into each other: every list item becomes an activator (activator="parent") for a child CdsMenu with the submenu prop — it shifts the panel sideways and moves the navigation to the left and right arrow keys. The open-on-hover prop opens the submenu on hover, and :open-on-focus="false" keeps it from opening while tabbing through.

Animation ​

The transition prop changes the animation the panel appears with.

Slots ​

SlotDescription
activatorThe element that opens the menu. The slot props (props) attach the handlers to it
defaultThe content of the panel. Usually CdsList, CdsCard or CdsSurface

Guidelines ​

Scenarios ​

Actions on a record ​

Rare actions on a table row or a card are collected in a menu behind the "⋮" button: the row stays clean, and the actions do not compete with the main button. location="bottom end" pins the panel to the right edge of the activator, slim on the list removes the extra paddings, and aria-label names the record the menu belongs to.

Context menu ​

A menu can open at the point of the click: the contextmenu handler puts the cursor coordinates into target, and v-model controls the visibility — no activator is needed here. A new click moves the open panel to the new spot. The default animation is meant for an element activator, so for a menu placed by coordinates set a CSS animation — for example, transition="scale-transition".

In the application header a menu solves two tasks: it expands the navigation sections and collects everything about the account behind the avatar. The activator of the user menu is a CdsAvatar inside a button with an aria-label, location="bottom end" pins the panel to the right edge, and min-width keeps it from jumping in width. The first list item shows whose account it is, and CdsListItemDivider separates the sections from signing out.

Filter with several values ​

When several values are picked inside a menu, the panel must not close on the very first click: :close-on-content-click="false" keeps it open, and v-model lets the "Apply" button close the menu. The selection piles up in a draft and is applied as a whole — until the button is pressed, the list of records does not change.

API ​

Props

NameTypeDefaultDescription
activator(string & {}) | Element | "parent" | ComponentPublicInstance | undefined—The element that activates the component
activatorPropsRecord<string, any> | undefined{}Props for the activator element
attachstring | boolean | Element | undefined—Sets the DOM element the overlay content is moved to. It accepts a direct element reference, a querySelector string, or true to disable moving. By default the body element is used. Changing this prop is usually not recommended except for edge cases: the default positioning algorithm handles most scenarios better than it can without moving, and unexpected behavior may appear if the menu ends up being a child of its activator.
captureFocusboolean | undefinedtrueReturns the focus back into the content if it has left it — for example on Tab from the end of the page. A mouse click does not steal the focus.
closeDelaystring | number | undefined250Delay in milliseconds before the component closes. Works only together with open-on-hover.
closeOnBackboolean | undefinedtrueCloses the overlay content when the browser Back button is pressed or $router.back() is called, cancelling the original navigation. persistent overlays cancel the navigation and animate as if they were clicked outside instead of closing.
closeOnContentClickboolean | undefinedtrueHide the component on a click on the content
containedboolean | undefined—Limits the size of the component and of its grid to the offset parent. Implies the absolute and attach props. (Note: the parent element must have position: relative.)
contentClassany—Applies a custom class to the content element. This is handy because the content is moved to the beginning of the cds-app component (unless attach is set) and cannot be selected with classes passed to the component directly.
contentPropsany—Applies custom props to the moved content
disabledboolean | null | undefinednullDisabled state
eagerboolean | undefined—Renders the content of the component right away. Useful when you have content that is not in the DOM but has to be indexed for SEO.
heightstring | number | undefined—Component height
idstring | undefined—Unique identifier that links the menu to its activator. If it is not set, it is generated automatically.
locationAnchor | undefined'bottom'Sets the anchor point used to position the component, aligning it horizontally, vertically or in both directions
locationStrategy"static" | "connected" | LocationStrategyFunction | undefined'connected'Sets the way the overlay content is positioned. By default static is used, which centers the content in its container. Use connected to attach the content to the activator element.
maxHeightstring | number | undefined—Maximum component height
maxWidthstring | number | undefined—Maximum component width
minHeightstring | number | undefined—Minimum component height
minWidthstring | number | undefined—Minimum component width
modelValueboolean | undefined—The v-model value of the component. If the component supports the multiple prop, an empty array is used by default.
noClickAnimationboolean | undefined—Disables the bounce effect on a click outside the content element when persistent is set.
offsetstring | number | number[] | undefined4Increases the distance to the target. If two numbers are passed, the second one shifts the anchor sideways, away from the target.
opacitystring | number | undefined—Opacity of the scrim element. Applies only when scrim=true.
openDelaystring | number | undefined300Delay in milliseconds before the component opens. Works only together with open-on-hover.
openOnClickboolean | undefined—Show the component on a click on the activator
openOnFocusboolean | undefined—Show the component when the activator gets focus
openOnHoverboolean | undefined—Show the component on hover over the activator
originAnchor | "auto" | "overlap" | undefined'auto'Sets the anchor point of the overlay content that is aligned with the location anchor of the target. With "auto" the opposite side of the location point is used. With "overlap" the same anchor point is used, so the overlay overlaps the target. It also sets the CSS transform-origin property for the enter/leave transitions.
persistentboolean | undefined—A click outside the element or the Esc key does not close it.
retainFocusboolean | undefined—Traps Tab navigation inside the content: from the last focusable element the focus moves to the first one and back.
scrimstring | boolean | undefinedfalseBackdrop shown while the overlay is active. Accepts true/false to toggle the backdrop and a string to set its color.
scrollStrategy"none" | "close" | "block" | "reposition" | ScrollStrategyFunction | undefined'none'Strategy applied when the component is active and the user scrolls the page.
stickToTargetboolean | undefined—Lets the overlay content go off screen while scrolling.
submenuboolean | undefined—Opens and closes the menu with the left/right arrow keys instead of up/down (for nested menus).
target(string & {}) | Element | "parent" | ComponentPublicInstance | "cursor" | [x: number, y: number] | undefined—When locationStrategy is set to "connected", specify the element or the array of x, y coordinates the overlay is positioned against. By default the activator element is used.
themestring | undefined—Component theme
transitionstring | boolean | (TransitionProps & { component?: Component | undefined; }) | { component: Component; } | null | undefined{ component: CdsModalTransition }Transition animation
viewportMarginstring | number | undefined12Sets a custom viewport margin for the overlay content
widthstring | number | undefined—Component width
zIndexstring | number | undefined2000The z-index value of the component.

Events

NameTypeDescription
afterEnter[]Fired after the opening animation of the menu has finished
afterLeave[]Fired after the closing animation of the menu has finished
update:modelValue[value: boolean]Fired when the menu is opened or closed. Passes the new visibility state

Slots

NameTypeDescription
activator{ isActive: boolean; props: Record<string, any>; targetRef: TemplateRef; }Slot for the element that acts as the activator of the overlay.
default{ isActive: Ref<boolean, boolean>; }Slot for the overlay content.

Exposed

NameTypeDescription
activatorElHTMLElement | undefinedThe DOM element of the activator.
activatorRefTemplateRefTemplate ref for assigning the activator manually.
animateClick() => voidRuns the bounce animation of the content — the same as on a click outside a persistent overlay.
contentElHTMLElement | undefinedThe DOM element of the overlay content.
globalTopbooleanWhether the overlay is the top one in the global stack of open overlays.
rootElHTMLElement | undefinedThe root DOM element of the overlay.
scrimElHTMLElement | undefinedThe DOM element of the scrim that dims the page.
targetElHTMLElement | undefinedThe DOM element of the positioning target.
targetRefTemplateRefTemplate ref for assigning the positioning target manually.
updateLocation((e: Event) => void) | undefinedRecalculates the position of the content relative to the target.