<c-menu>
Examples
<template>
<div class="example-row">
<c-menu @select="onSelect">
<c-button slot="trigger" outlined>
Device
<c-icon :path="mdiChevronDown" />
</c-button>
<c-menu-label>Type</c-menu-label>
<c-menu-item value="phone">Phone</c-menu-item>
<c-menu-item value="tablet">Tablet</c-menu-item>
<c-menu-item value="desktop">Desktop</c-menu-item>
<c-divider />
<c-menu-item value="forget" danger>Forget this device</c-menu-item>
</c-menu>
<p>Selected: {{ selected ?? '—' }}</p>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { mdiChevronDown } from '@mdi/js';
const selected = ref<null | string>(null);
const onSelect = (event: Event) => {
selected.value = (event as CustomEvent<{ value: string }>).detail.value;
};
</script>
<template>
<div class="example-row">
<c-menu @select="onSelect">
<c-button slot="trigger" ghost>
Export
<c-icon :path="mdiChevronDown" />
</c-button>
<c-menu-item value="documents">
Documents
<c-menu-item slot="submenu" value="pdf">
<c-icon :path="mdiFilePdfBox" />
PDF
</c-menu-item>
<c-menu-item slot="submenu" value="docx">
<c-icon :path="mdiFileDocument" />
Word document
</c-menu-item>
</c-menu-item>
<c-menu-item value="images">
Images
<c-menu-item slot="submenu" value="png">
<c-icon :path="mdiFilePngBox" />
PNG
</c-menu-item>
<c-menu-item slot="submenu" value="jpg">
<c-icon :path="mdiFileJpgBox" />
JPEG
</c-menu-item>
</c-menu-item>
<c-divider />
<c-menu-item value="settings">Export settings…</c-menu-item>
</c-menu>
<p>Selected: {{ selected ?? '—' }}</p>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import {
mdiChevronDown,
mdiFileDocument,
mdiFileJpgBox,
mdiFilePdfBox,
mdiFilePngBox,
} from '@mdi/js';
const selected = ref<null | string>(null);
const onSelect = (event: Event) => {
selected.value = (event as CustomEvent<{ value: string }>).detail.value;
};
</script>
<template>
<div class="example-row">
<c-menu @select="onTheme">
<c-button slot="trigger" text>
<c-icon :path="mdiThemeLightDark" />
Theme: {{ theme }}
<c-icon :path="mdiChevronDown" />
</c-button>
<c-menu-label>Theme</c-menu-label>
<c-menu-item
:active.prop="theme === 'dark'"
:icon="mdiWeatherNight"
value="dark"
>
Dark
</c-menu-item>
<c-menu-item
:active.prop="theme === 'light'"
:icon="mdiWeatherSunny"
value="light"
>
Light
</c-menu-item>
<c-menu-item
:active.prop="theme === 'system'"
:icon="mdiMonitor"
value="system"
>
System
</c-menu-item>
</c-menu>
<c-menu @select="onSort">
<c-button slot="trigger" text>
Sort by: {{ sortBy }}
<c-icon :path="mdiChevronDown" />
</c-button>
<c-menu-item
v-for="key in sortKeys"
:key="key"
:active.prop="sortBy === key"
:active-icon="mdiRadioboxMarked"
:value="key"
>
{{ key }}
</c-menu-item>
</c-menu>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import {
mdiChevronDown,
mdiMonitor,
mdiRadioboxMarked,
mdiThemeLightDark,
mdiWeatherNight,
mdiWeatherSunny,
} from '@mdi/js';
const theme = ref('dark');
const sortKeys = ['name', 'size', 'date'];
const sortBy = ref('name');
const onTheme = (event: Event) => {
theme.value = (event as CustomEvent<{ value: string }>).detail.value;
};
const onSort = (event: Event) => {
sortBy.value = (event as CustomEvent<{ value: string }>).detail.value;
};
</script>
<template>
<div class="example-row">
<c-menu @select="onSelect">
<c-button slot="trigger" text>
<c-icon :path="mdiAccount" />
Account
<c-icon :path="mdiChevronDown" />
</c-button>
<c-menu-item value="profile">View profile</c-menu-item>
<c-menu-item value="billing" disabled>Billing (unavailable)</c-menu-item>
<c-menu-item value="invite">Invite teammate</c-menu-item>
<c-divider />
<c-menu-item value="delete" danger>Delete account</c-menu-item>
</c-menu>
<p>Selected: {{ selected ?? '—' }}</p>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { mdiAccount, mdiChevronDown } from '@mdi/js';
const selected = ref<null | string>(null);
const onSelect = (event: Event) => {
selected.value = (event as CustomEvent<{ value: string }>).detail.value;
};
</script>
<template>
<div class="example-row">
<c-menu @select="onSelect">
<c-button slot="trigger" ghost>
Export
<c-icon :path="mdiChevronDown" />
</c-button>
<c-menu-item value="documents">
Documents
<c-menu-item slot="submenu" value="pdf">
<c-icon :path="mdiFilePdfBox" />
PDF
</c-menu-item>
<c-menu-item slot="submenu" value="docx">
<c-icon :path="mdiFileDocument" />
Word document
</c-menu-item>
</c-menu-item>
<c-menu-item value="settings">Export settings…</c-menu-item>
</c-menu>
<p>Selected: {{ selected ?? '—' }}</p>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { mdiChevronDown, mdiFileDocument, mdiFilePdfBox } from '@mdi/js';
const selected = ref<null | string>(null);
const onSelect = (event: Event) => {
selected.value = (event as CustomEvent<{ value: string }>).detail.value;
};
</script>
<template>
<div class="example-row">
<c-menu @select="onSelect">
<c-button slot="trigger" outlined>
Settings
<c-icon :path="mdiChevronDown" />
</c-button>
<c-menu-label>Account</c-menu-label>
<c-menu-item value="profile">Profile</c-menu-item>
<c-menu-item value="notifications">Notifications</c-menu-item>
<c-divider />
<c-menu-label>Workspace</c-menu-label>
<c-menu-item value="members">Members</c-menu-item>
<c-menu-item value="billing">Billing</c-menu-item>
</c-menu>
<p>Selected: {{ selected ?? '—' }}</p>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { mdiChevronDown } from '@mdi/js';
const selected = ref<null | string>(null);
const onSelect = (event: Event) => {
selected.value = (event as CustomEvent<{ value: string }>).detail.value;
};
</script>
API reference
<c-menu>
Properties
| Property | Attribute | Type | Default | Description |
|---|---|---|---|---|
distance | distance | number | string | 0 | Distance from the trigger to the panel, in pixels. Defaults to `0`. |
open | open | boolean | false | Whether the menu is open. Two-way: emits `change:open`. |
position | position | CPlacement | 'bottom-start' | Preferred placement of the panel relative to the trigger. |
trigger | trigger | HTMLElement | string | — | Designated trigger: an element elsewhere in the document that opens the menu — its document ID, or the element itself. The same trigger concept as the `trigger` slot, supplied by reference: the menu wires click-to-toggle, arrow-key opening, ARIA and focus return onto it and anchors the panel to it. When both routes are supplied, this prop wins over the slot. |
Events
| Event | Detail | Description |
|---|---|---|
change:open | boolean | Fired whenever the menu opens or closes, carrying the new open state. Named `change:open`, not `update:open`: Vue's runtime silently drops `onUpdate:*` listeners on custom elements (`isModelListener`), so a template `@update:open` would never be attached. |
select | { value: unknown } | Fired when a leaf menu item is selected, carrying the item's `value`; bubbles out of the menu so a single listener can handle the whole tree. |
Slots
| Slot | Description |
|---|---|
default | Menu title / activator element (simple variant) |
trigger | The element that opens the menu (e.g. a c-button) |
CSS parts
Style from outside with c-menu::part(name) — parts are the library's only styling customization API.
| Part | Description |
|---|---|
trigger | The inline wrapper around the slotted trigger, serving as the panel's anchor |
panel | The floating popover positioned against the trigger |
list | The menu list surface inside the panel |
Types
Importable from the package root: import type { … } from '@cscfi/csc-ui'
CPlacement shared
Preferred placement of a floating panel relative to its anchor, shared by the anchor-positioned overlay components (`c-menu`, `c-tooltip`, `c-popover`). The side names the panel's position; `-start`/`-end` align the panel's edge with the anchor's on the cross axis.
export type CPlacement =
| 'bottom-end'
| 'bottom-start'
| 'bottom'
| 'left-end'
| 'left-start'
| 'left'
| 'right-end'
| 'right-start'
| 'right'
| 'top-end'
| 'top-start'
| 'top';<c-menu-item>
A single command in a `c-menu`.
Properties
| Property | Attribute | Type | Default | Description |
|---|---|---|---|---|
active | active | boolean | — | Marks the item as the currently selected choice: renders the trailing indicator icon and stamps `role="menuitemradio"` + `aria-checked`. Leave unset for regular command items — only a true/false value marks the item as a selectable choice. The state is consumer-owned: the menu emits `select` as usual and never toggles this itself. Distinct from the keyboard highlight (the `data-active` attribute the controlling menu moves between rows). |
activeIcon | active-icon | string | '' | SVG path for the trailing indicator shown while `active`; defaults to a check mark. |
danger | danger | boolean | false | Marks the action as destructive (renders in the error colour). |
disabled | disabled | boolean | false | Disables the item — it is skipped by keyboard nav and emits no select. |
icon | icon | string | '' | SVG path for a leading icon rendered before the item's content. |
value | value | string | — | Value reported in the menu's `select` event when this item is chosen. |
Methods
| Method | Signature | Description |
|---|---|---|
closeSubmenu | () | |
openSubmenu | () |
Slots
| Slot | Description |
|---|---|
default | The item's content: label text plus optional icons or shortcut hint |
submenu | c-menu-item components for the nested submenu |
CSS parts
Style from outside with c-menu-item::part(name) — parts are the library's only styling customization API.
| Part | Description |
|---|---|
root | The item row containing the content and the submenu chevron |
content | The flex wrapper around the default-slot content |
icon | The leading icon rendered from the `icon` prop |
check | The trailing indicator icon shown while the item is active |
submenu-panel | The floating popover holding the submenu |
submenu | The submenu list surface inside the popover |
<c-menu-label>
Section label for a group of `c-menu-item`s.
Slots
| Slot | Description |
|---|---|
default | The label text for the menu section |
CSS parts
Style from outside with c-menu-label::part(name) — parts are the library's only styling customization API.
| Part | Description |
|---|---|
root | The label element inside the menu list |