A menu offers a list of actions or options to users, typically appearing in response to a button click or right-click. Menus support keyboard navigation with arrow keys, submenus, checkboxes, radio buttons, and disabled items.
import {Component, viewChild} from '@angular/core';
import {Menu, MenuContent, MenuItem, MenuTrigger} from '@angular/aria/menu';
import {OverlayModule} from '@angular/cdk/overlay';
@Component({
selector: 'app-root',
templateUrl: 'app.html',
styleUrl: 'app.css',
imports: [Menu, MenuContent, MenuItem, MenuTrigger, OverlayModule],
})
export class App {
formatMenu = viewChild<Menu<string>>('formatMenu');
categorizeMenu = viewChild<Menu<string>>('categorizeMenu');
}
<button ngMenuTrigger #origin #trigger="ngMenuTrigger" [menu]="formatMenu()">Open Menu</button>
<ng-template
[cdkConnectedOverlayOpen]="trigger.expanded()"
[cdkConnectedOverlay]="{origin, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top', offsetY: 4},
]"
cdkAttachPopoverAsChild
>
<div ngMenu class="menu" #formatMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="Mark as read">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>mark_email_read</span
>
<span class="label">Mark as read</span>
</div>
<div ngMenuItem value="Snooze">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true">snooze</span>
<span class="label">Snooze</span>
</div>
<div role="separator" aria-orientation="horizontal" class="separator"></div>
<div
ngMenuItem
class="menu-item"
value="Categorize"
#categorizeItem
[submenu]="categorizeMenu()"
>
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>category</span
>
<span class="label">Categorize</span>
<span class="icon material-symbols-outlined arrow" translate="no" aria-hidden="true"
>arrow_right</span
>
</div>
<ng-template
[cdkConnectedOverlayOpen]="formatMenu.visible()"
[cdkConnectedOverlay]="{origin: categorizeItem, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'end', originY: 'top', overlayY: 'top', overlayX: 'start', offsetX: 6},
]"
cdkAttachPopoverAsChild
>
<div ngMenu class="menu" #categorizeMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="Mark as important">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>label_important</span
>
<span class="label">Mark as important</span>
</div>
<div ngMenuItem value="Star">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>star</span
>
<span class="label">Star</span>
</div>
<div ngMenuItem value="Label">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>label</span
>
<span class="label">Label</span>
</div>
</ng-template>
</div>
</ng-template>
<div role="separator" aria-orientation="horizontal" class="separator"></div>
<div ngMenuItem value="Archive">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>archive</span
>
<span class="label">Archive</span>
</div>
<div ngMenuItem value="Report spam">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true">report</span>
<span class="label">Report spam</span>
</div>
<div ngMenuItem value="Delete">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true">delete</span>
<span class="label">Delete</span>
</div>
</ng-template>
</div>
</ng-template>
@import url('https://fonts.googleapis.com/icon?family=Material+Symbols+Outlined');
:host {
display: flex;
justify-content: center;
font-family: var(--inter-font);
--border-color: color-mix(in srgb, var(--full-contrast) 20%, var(--page-background));
}
[ngMenuTrigger] {
display: flex;
cursor: pointer;
align-items: center;
padding: 0.6rem 2rem;
border-radius: 0.5rem;
color: var(--primary-contrast);
border: 1px solid var(--border-color);
background-color: var(--page-background);
}
[ngMenuTrigger] .icon {
font-size: 1.5rem;
opacity: 0.875;
}
[ngMenu] {
margin: 0;
width: 15rem;
padding: 0.25rem;
border-radius: 0.5rem;
border: 1px solid var(--border-color);
background-color: var(--page-background);
}
[ngMenu][data-visible='false'] {
display: none;
}
[ngMenuItem] {
outline: none;
display: flex;
cursor: pointer;
align-items: center;
gap: 0.5rem;
padding: 0.5rem;
font-size: 0.875rem;
border-radius: 0.25rem;
}
[ngMenuTrigger]:hover,
[ngMenuItem][data-active='true'] {
background: color-mix(in srgb, var(--border-color) 10%, transparent);
}
[ngMenuItem]:focus,
[ngMenuTrigger]:focus {
outline: 2px solid var(--vivid-pink);
}
[ngMenuItem] .icon {
opacity: 0.875;
font-size: 1.25rem;
}
[ngMenuItem] .label {
flex: 1;
opacity: 0.875;
font-size: 0.875rem;
}
[ngMenuItem]:not([aria-expanded='true']) .arrow {
opacity: 0.5;
}
[ngMenu] .separator {
border-top: 1px solid var(--border-color);
margin: 0.25rem 0;
opacity: 0.25;
}
import {Component, viewChild} from '@angular/core';
import {Menu, MenuContent, MenuItem, MenuTrigger} from '@angular/aria/menu';
import {OverlayModule} from '@angular/cdk/overlay';
@Component({
selector: 'app-root',
templateUrl: 'app.html',
styleUrl: 'app.css',
imports: [Menu, MenuContent, MenuItem, MenuTrigger, OverlayModule],
})
export class App {
formatMenu = viewChild<Menu<string>>('formatMenu');
categorizeMenu = viewChild<Menu<string>>('categorizeMenu');
}
<button ngMenuTrigger #origin #trigger="ngMenuTrigger" [menu]="formatMenu()">Open Menu</button>
<ng-template
[cdkConnectedOverlayOpen]="trigger.expanded()"
[cdkConnectedOverlay]="{origin, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top', offsetY: 4},
]"
cdkAttachPopoverAsChild
>
<div ngMenu class="menu" #formatMenu="ngMenu">
<ng-template ngMenuContent>
<div class="group">
<div ngMenuItem value="Mark as read">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>mark_email_read</span
>
<span class="label">Mark as read</span>
</div>
<div ngMenuItem value="Snooze">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>snooze</span
>
<span class="label">Snooze</span>
</div>
<div ngMenuItem value="Delete">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>delete</span
>
<span class="label">Delete</span>
</div>
</div>
<div class="group">
<div
ngMenuItem
class="menu-item"
value="Categorize"
#categorizeItem
[submenu]="categorizeMenu()"
>
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>category</span
>
<span class="label">Categorize</span>
<span class="icon material-symbols-outlined arrow" translate="no" aria-hidden="true"
>arrow_right</span
>
</div>
</div>
<ng-template
[cdkConnectedOverlayOpen]="formatMenu.visible()"
[cdkConnectedOverlay]="{origin: categorizeItem, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'end', originY: 'top', overlayY: 'top', overlayX: 'start', offsetX: 6},
]"
cdkAttachPopoverAsChild
>
<div ngMenu class="menu" #categorizeMenu="ngMenu">
<ng-template ngMenuContent>
<div class="group">
<div ngMenuItem value="Mark as important">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>label_important</span
>
<span class="label">Mark as important</span>
</div>
<div ngMenuItem value="Star">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>star</span
>
<span class="label">Star</span>
</div>
<div ngMenuItem value="Label">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>label</span
>
<span class="label">Label</span>
</div>
</div>
</ng-template>
</div>
</ng-template>
</ng-template>
</div>
</ng-template>
@import url('https://fonts.googleapis.com/icon?family=Material+Symbols+Outlined');
:host {
display: flex;
justify-content: center;
font-family: var(--inter-font);
}
[ngMenuTrigger] {
display: flex;
cursor: pointer;
align-items: center;
padding: 0.6rem 2rem;
border-radius: 0.5rem;
border: 1px solid transparent;
background-color: color-mix(in srgb, var(--vivid-pink) 5%, transparent);
color: color-mix(in srgb, var(--vivid-pink) 70%, var(--primary-contrast));
}
[ngMenuTrigger] .icon {
font-size: 1.5rem;
opacity: 0.875;
}
[ngMenu] {
gap: 3px;
width: 15rem;
display: flex;
flex-direction: column;
}
[ngMenu] .group {
padding: 0.25rem;
border-radius: 0.25rem;
background-color: var(--page-background);
box-shadow: 0 1px 2px 1px color-mix(in srgb, var(--primary-contrast) 25%, transparent);
}
[ngMenu] .group:first-of-type {
border-top-left-radius: 1rem;
border-top-right-radius: 1rem;
}
[ngMenu] .group:last-of-type {
border-bottom-left-radius: 1rem;
border-bottom-right-radius: 1rem;
}
[ngMenu][data-visible='false'] {
display: none;
}
[ngMenuItem] {
outline: none;
display: flex;
cursor: pointer;
align-items: center;
gap: 0.5rem;
padding: 0.75rem;
font-size: 0.875rem;
border-radius: 0.75rem;
}
[ngMenuTrigger]:hover,
[ngMenuTrigger][aria-expanded='true'] {
background: color-mix(in srgb, var(--vivid-pink) 10%, transparent);
}
[ngMenuItem][data-active='true'] {
color: color-mix(in srgb, var(--vivid-pink) 70%, var(--primary-contrast));
background: color-mix(in srgb, var(--vivid-pink) 5%, transparent);
}
[ngMenuItem]:focus,
[ngMenuTrigger]:focus {
outline: 2px solid var(--vivid-pink);
}
[ngMenuItem] .icon {
opacity: 0.875;
font-size: 1.25rem;
}
[ngMenuItem] .label {
flex: 1;
opacity: 0.875;
font-size: 0.875rem;
}
[ngMenuItem]:not([aria-expanded='true']) .arrow {
opacity: 0.5;
}
[ngMenu] .separator {
border-top: 1px solid var(--quaternary-contrast);
margin: 0.25rem 0;
opacity: 0.25;
}
import {Component, viewChild} from '@angular/core';
import {Menu, MenuContent, MenuItem, MenuTrigger} from '@angular/aria/menu';
import {OverlayModule} from '@angular/cdk/overlay';
@Component({
selector: 'app-root',
templateUrl: 'app.html',
styleUrl: 'app.css',
imports: [Menu, MenuContent, MenuItem, MenuTrigger, OverlayModule],
})
export class App {
formatMenu = viewChild<Menu<string>>('formatMenu');
categorizeMenu = viewChild<Menu<string>>('categorizeMenu');
}
<button class="retro-trigger" ngMenuTrigger #trigger="ngMenuTrigger" #origin [menu]="formatMenu()">
Open Menu
</button>
<ng-template
[cdkConnectedOverlayOpen]="trigger.expanded()"
[cdkConnectedOverlay]="{origin, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top', offsetY: 4},
]"
cdkAttachPopoverAsChild
>
<div ngMenu class="menu" #formatMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="Mark as read">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>mark_email_read</span
>
<span class="label">Mark as read</span>
</div>
<div ngMenuItem value="Snooze">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true">snooze</span>
<span class="label">Snooze</span>
</div>
<div role="separator" aria-orientation="horizontal" class="separator"></div>
<div
ngMenuItem
class="menu-item"
value="Categorize"
#categorizeItem
[submenu]="categorizeMenu()"
>
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>category</span
>
<span class="label">Categorize</span>
<span class="icon material-symbols-outlined arrow" translate="no" aria-hidden="true"
>arrow_right</span
>
</div>
<ng-template
[cdkConnectedOverlayOpen]="formatMenu.visible()"
[cdkConnectedOverlay]="{origin: categorizeItem, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'end', originY: 'top', overlayY: 'top', overlayX: 'start', offsetX: 6},
]"
cdkAttachPopoverAsChild
>
<div ngMenu class="menu" #categorizeMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="Mark as important">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>label_important</span
>
<span class="label">Mark as important</span>
</div>
<div ngMenuItem value="Star">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>star</span
>
<span class="label">Star</span>
</div>
<div ngMenuItem value="Label">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>label</span
>
<span class="label">Label</span>
</div>
</ng-template>
</div>
</ng-template>
<div role="separator" aria-orientation="horizontal" class="separator"></div>
<div ngMenuItem value="Archive">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>archive</span
>
<span class="label">Archive</span>
</div>
<div ngMenuItem value="Report spam">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true">report</span>
<span class="label">Report spam</span>
</div>
<div ngMenuItem value="Delete">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true">delete</span>
<span class="label">Delete</span>
</div>
</ng-template>
</div>
</ng-template>
@import url('https://fonts.googleapis.com/css2?family=Press+Start+2P&display=swap');
@import url('https://fonts.googleapis.com/icon?family=Material+Symbols+Outlined');
:host {
display: flex;
justify-content: center;
font-size: 0.8rem;
--border-color: color-mix(in srgb, var(--full-contrast) 20%, var(--page-background));
font-family: 'Press Start 2P';
--retro-button-color: var(--vivid-pink);
--retro-shadow-light: color-mix(in srgb, #fff 20%, transparent);
--retro-shadow-dark: color-mix(in srgb, #000 20%, transparent);
--retro-elevated-shadow:
inset 4px 4px 0px 0px var(--retro-shadow-light),
inset -4px -4px 0px 0px var(--retro-shadow-dark), 4px 0px 0px 0px var(--tertiary-contrast),
0px 4px 0px 0px var(--tertiary-contrast), -4px 0px 0px 0px var(--tertiary-contrast),
0px -4px 0px 0px var(--tertiary-contrast);
--retro-flat-shadow:
4px 0px 0px 0px var(--tertiary-contrast), 0px 4px 0px 0px var(--tertiary-contrast),
-4px 0px 0px 0px var(--tertiary-contrast), 0px -4px 0px 0px var(--tertiary-contrast);
--retro-clickable-shadow:
inset 4px 4px 0px 0px var(--retro-shadow-light),
inset -4px -4px 0px 0px var(--retro-shadow-dark), 4px 0px 0px 0px var(--tertiary-contrast),
0px 4px 0px 0px var(--tertiary-contrast), -4px 0px 0px 0px var(--tertiary-contrast),
0px -4px 0px 0px var(--tertiary-contrast), 8px 8px 0px 0px var(--tertiary-contrast);
--retro-pressed-shadow:
inset 4px 4px 0px 0px var(--retro-shadow-dark),
inset -4px -4px 0px 0px var(--retro-shadow-light), 4px 0px 0px 0px var(--tertiary-contrast),
0px 4px 0px 0px var(--tertiary-contrast), -4px 0px 0px 0px var(--tertiary-contrast),
0px -4px 0px 0px var(--tertiary-contrast), 0px 0px 0px 0px var(--tertiary-contrast);
}
[ngMenuTrigger] {
display: flex;
cursor: pointer;
align-items: center;
padding: 0.6rem 2rem;
font-family: 'Press Start 2P';
color: #000;
background-color: var(--vivid-pink);
box-shadow: var(--retro-clickable-shadow);
transition:
transform 0.1s,
box-shadow 0.1s;
}
[ngMenuTrigger]:hover {
transform: translate(1px, 1px);
}
[ngMenuTrigger]:active {
background-color: color-mix(in srgb, var(--vivid-pink) 80%, #fff);
box-shadow: var(--retro-pressed-shadow);
transform: translate(4px, 4px);
}
[ngMenuTrigger] .icon {
font-size: 1.5rem;
opacity: 0.875;
}
[ngMenu] {
margin-top: 8px;
width: 15rem;
padding: 0.25rem;
background-color: var(--page-background);
box-shadow: var(--retro-flat-shadow);
}
[ngMenu][data-visible='false'] {
display: none;
}
[ngMenuItem] {
outline: none;
display: flex;
cursor: pointer;
align-items: center;
gap: 0.5rem;
padding: 0.5rem;
font-size: 0.875rem;
}
[ngMenuItem][data-active='true'] {
background: color-mix(in srgb, var(--border-color) 10%, transparent);
}
[ngMenuTrigger]:focus {
outline: 4px dashed var(--vivid-pink);
outline-offset: 8px;
}
[ngMenuItem]:focus {
outline: 4px dashed var(--vivid-pink);
outline-offset: -4px;
}
[ngMenuItem] .icon {
opacity: 0.875;
font-size: 1.25rem;
}
[ngMenuItem] .label {
flex: 1;
opacity: 0.875;
font-size: 0.875rem;
}
[ngMenuItem]:not([aria-expanded='true']) .arrow {
opacity: 0.5;
}
[ngMenu] .separator {
border-top: 4px solid #000;
margin: 0.25rem 0;
opacity: 0.25;
}
Menus work well for presenting lists of actions or commands that users can choose from.
Use menus when:
Avoid menus when:
Create a dropdown menu by pairing a trigger button with a menu. The trigger opens and closes the menu.
import {Component, viewChild} from '@angular/core';
import {Menu, MenuContent, MenuItem, MenuTrigger} from '@angular/aria/menu';
import {OverlayModule} from '@angular/cdk/overlay';
@Component({
selector: 'app-root',
templateUrl: 'app.html',
styleUrl: 'app.css',
imports: [Menu, MenuContent, MenuItem, MenuTrigger, OverlayModule],
})
export class App {
formatMenu = viewChild<Menu<string>>('formatMenu');
categorizeMenu = viewChild<Menu<string>>('categorizeMenu');
}
<button ngMenuTrigger #origin #trigger="ngMenuTrigger" [menu]="formatMenu()">Open Menu</button>
<ng-template
[cdkConnectedOverlayOpen]="trigger.expanded()"
[cdkConnectedOverlay]="{origin, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top', offsetY: 4},
]"
cdkAttachPopoverAsChild
>
<div ngMenu class="menu" #formatMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="Mark as read">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>mark_email_read</span
>
<span class="label">Mark as read</span>
</div>
<div ngMenuItem value="Snooze">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true">snooze</span>
<span class="label">Snooze</span>
</div>
<div role="separator" aria-orientation="horizontal" class="separator"></div>
<div
ngMenuItem
class="menu-item"
value="Categorize"
#categorizeItem
[submenu]="categorizeMenu()"
>
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>category</span
>
<span class="label">Categorize</span>
<span class="icon material-symbols-outlined arrow" translate="no" aria-hidden="true"
>arrow_right</span
>
</div>
<ng-template
[cdkConnectedOverlayOpen]="formatMenu.visible()"
[cdkConnectedOverlay]="{origin: categorizeItem, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'end', originY: 'top', overlayY: 'top', overlayX: 'start', offsetX: 6},
]"
cdkAttachPopoverAsChild
>
<div ngMenu class="menu" #categorizeMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="Mark as important">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>label_important</span
>
<span class="label">Mark as important</span>
</div>
<div ngMenuItem value="Star">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>star</span
>
<span class="label">Star</span>
</div>
<div ngMenuItem value="Label">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>label</span
>
<span class="label">Label</span>
</div>
</ng-template>
</div>
</ng-template>
<div role="separator" aria-orientation="horizontal" class="separator"></div>
<div ngMenuItem value="Archive">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>archive</span
>
<span class="label">Archive</span>
</div>
<div ngMenuItem value="Report spam">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true">report</span>
<span class="label">Report spam</span>
</div>
<div ngMenuItem value="Delete">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true">delete</span>
<span class="label">Delete</span>
</div>
</ng-template>
</div>
</ng-template>
@import url('https://fonts.googleapis.com/icon?family=Material+Symbols+Outlined');
:host {
display: flex;
justify-content: center;
font-family: var(--inter-font);
--border-color: color-mix(in srgb, var(--full-contrast) 20%, var(--page-background));
}
[ngMenuTrigger] {
display: flex;
cursor: pointer;
align-items: center;
padding: 0.6rem 2rem;
border-radius: 0.5rem;
color: var(--primary-contrast);
border: 1px solid var(--border-color);
background-color: var(--page-background);
}
[ngMenuTrigger] .icon {
font-size: 1.5rem;
opacity: 0.875;
}
[ngMenu] {
margin: 0;
width: 15rem;
padding: 0.25rem;
border-radius: 0.5rem;
border: 1px solid var(--border-color);
background-color: var(--page-background);
}
[ngMenu][data-visible='false'] {
display: none;
}
[ngMenuItem] {
outline: none;
display: flex;
cursor: pointer;
align-items: center;
gap: 0.5rem;
padding: 0.5rem;
font-size: 0.875rem;
border-radius: 0.25rem;
}
[ngMenuTrigger]:hover,
[ngMenuItem][data-active='true'] {
background: color-mix(in srgb, var(--border-color) 10%, transparent);
}
[ngMenuItem]:focus,
[ngMenuTrigger]:focus {
outline: 2px solid var(--vivid-pink);
}
[ngMenuItem] .icon {
opacity: 0.875;
font-size: 1.25rem;
}
[ngMenuItem] .label {
flex: 1;
opacity: 0.875;
font-size: 0.875rem;
}
[ngMenuItem]:not([aria-expanded='true']) .arrow {
opacity: 0.5;
}
[ngMenu] .separator {
border-top: 1px solid var(--border-color);
margin: 0.25rem 0;
opacity: 0.25;
}
import {Component, viewChild} from '@angular/core';
import {Menu, MenuContent, MenuItem, MenuTrigger} from '@angular/aria/menu';
import {OverlayModule} from '@angular/cdk/overlay';
@Component({
selector: 'app-root',
templateUrl: 'app.html',
styleUrl: 'app.css',
imports: [Menu, MenuContent, MenuItem, MenuTrigger, OverlayModule],
})
export class App {
formatMenu = viewChild<Menu<string>>('formatMenu');
categorizeMenu = viewChild<Menu<string>>('categorizeMenu');
}
<button ngMenuTrigger #origin #trigger="ngMenuTrigger" [menu]="formatMenu()">Open Menu</button>
<ng-template
[cdkConnectedOverlayOpen]="trigger.expanded()"
[cdkConnectedOverlay]="{origin, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top', offsetY: 4},
]"
cdkAttachPopoverAsChild
>
<div ngMenu class="menu" #formatMenu="ngMenu">
<ng-template ngMenuContent>
<div class="group">
<div ngMenuItem value="Mark as read">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>mark_email_read</span
>
<span class="label">Mark as read</span>
</div>
<div ngMenuItem value="Snooze">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>snooze</span
>
<span class="label">Snooze</span>
</div>
<div ngMenuItem value="Delete">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>delete</span
>
<span class="label">Delete</span>
</div>
</div>
<div class="group">
<div
ngMenuItem
class="menu-item"
value="Categorize"
#categorizeItem
[submenu]="categorizeMenu()"
>
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>category</span
>
<span class="label">Categorize</span>
<span class="icon material-symbols-outlined arrow" translate="no" aria-hidden="true"
>arrow_right</span
>
</div>
</div>
<ng-template
[cdkConnectedOverlayOpen]="formatMenu.visible()"
[cdkConnectedOverlay]="{origin: categorizeItem, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'end', originY: 'top', overlayY: 'top', overlayX: 'start', offsetX: 6},
]"
cdkAttachPopoverAsChild
>
<div ngMenu class="menu" #categorizeMenu="ngMenu">
<ng-template ngMenuContent>
<div class="group">
<div ngMenuItem value="Mark as important">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>label_important</span
>
<span class="label">Mark as important</span>
</div>
<div ngMenuItem value="Star">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>star</span
>
<span class="label">Star</span>
</div>
<div ngMenuItem value="Label">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>label</span
>
<span class="label">Label</span>
</div>
</div>
</ng-template>
</div>
</ng-template>
</ng-template>
</div>
</ng-template>
@import url('https://fonts.googleapis.com/icon?family=Material+Symbols+Outlined');
:host {
display: flex;
justify-content: center;
font-family: var(--inter-font);
}
[ngMenuTrigger] {
display: flex;
cursor: pointer;
align-items: center;
padding: 0.6rem 2rem;
border-radius: 0.5rem;
border: 1px solid transparent;
background-color: color-mix(in srgb, var(--vivid-pink) 5%, transparent);
color: color-mix(in srgb, var(--vivid-pink) 70%, var(--primary-contrast));
}
[ngMenuTrigger] .icon {
font-size: 1.5rem;
opacity: 0.875;
}
[ngMenu] {
gap: 3px;
width: 15rem;
display: flex;
flex-direction: column;
}
[ngMenu] .group {
padding: 0.25rem;
border-radius: 0.25rem;
background-color: var(--page-background);
box-shadow: 0 1px 2px 1px color-mix(in srgb, var(--primary-contrast) 25%, transparent);
}
[ngMenu] .group:first-of-type {
border-top-left-radius: 1rem;
border-top-right-radius: 1rem;
}
[ngMenu] .group:last-of-type {
border-bottom-left-radius: 1rem;
border-bottom-right-radius: 1rem;
}
[ngMenu][data-visible='false'] {
display: none;
}
[ngMenuItem] {
outline: none;
display: flex;
cursor: pointer;
align-items: center;
gap: 0.5rem;
padding: 0.75rem;
font-size: 0.875rem;
border-radius: 0.75rem;
}
[ngMenuTrigger]:hover,
[ngMenuTrigger][aria-expanded='true'] {
background: color-mix(in srgb, var(--vivid-pink) 10%, transparent);
}
[ngMenuItem][data-active='true'] {
color: color-mix(in srgb, var(--vivid-pink) 70%, var(--primary-contrast));
background: color-mix(in srgb, var(--vivid-pink) 5%, transparent);
}
[ngMenuItem]:focus,
[ngMenuTrigger]:focus {
outline: 2px solid var(--vivid-pink);
}
[ngMenuItem] .icon {
opacity: 0.875;
font-size: 1.25rem;
}
[ngMenuItem] .label {
flex: 1;
opacity: 0.875;
font-size: 0.875rem;
}
[ngMenuItem]:not([aria-expanded='true']) .arrow {
opacity: 0.5;
}
[ngMenu] .separator {
border-top: 1px solid var(--quaternary-contrast);
margin: 0.25rem 0;
opacity: 0.25;
}
import {Component, viewChild} from '@angular/core';
import {Menu, MenuContent, MenuItem, MenuTrigger} from '@angular/aria/menu';
import {OverlayModule} from '@angular/cdk/overlay';
@Component({
selector: 'app-root',
templateUrl: 'app.html',
styleUrl: 'app.css',
imports: [Menu, MenuContent, MenuItem, MenuTrigger, OverlayModule],
})
export class App {
formatMenu = viewChild<Menu<string>>('formatMenu');
categorizeMenu = viewChild<Menu<string>>('categorizeMenu');
}
<button class="retro-trigger" ngMenuTrigger #trigger="ngMenuTrigger" #origin [menu]="formatMenu()">
Open Menu
</button>
<ng-template
[cdkConnectedOverlayOpen]="trigger.expanded()"
[cdkConnectedOverlay]="{origin, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top', offsetY: 4},
]"
cdkAttachPopoverAsChild
>
<div ngMenu class="menu" #formatMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="Mark as read">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>mark_email_read</span
>
<span class="label">Mark as read</span>
</div>
<div ngMenuItem value="Snooze">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true">snooze</span>
<span class="label">Snooze</span>
</div>
<div role="separator" aria-orientation="horizontal" class="separator"></div>
<div
ngMenuItem
class="menu-item"
value="Categorize"
#categorizeItem
[submenu]="categorizeMenu()"
>
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>category</span
>
<span class="label">Categorize</span>
<span class="icon material-symbols-outlined arrow" translate="no" aria-hidden="true"
>arrow_right</span
>
</div>
<ng-template
[cdkConnectedOverlayOpen]="formatMenu.visible()"
[cdkConnectedOverlay]="{origin: categorizeItem, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'end', originY: 'top', overlayY: 'top', overlayX: 'start', offsetX: 6},
]"
cdkAttachPopoverAsChild
>
<div ngMenu class="menu" #categorizeMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="Mark as important">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>label_important</span
>
<span class="label">Mark as important</span>
</div>
<div ngMenuItem value="Star">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>star</span
>
<span class="label">Star</span>
</div>
<div ngMenuItem value="Label">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>label</span
>
<span class="label">Label</span>
</div>
</ng-template>
</div>
</ng-template>
<div role="separator" aria-orientation="horizontal" class="separator"></div>
<div ngMenuItem value="Archive">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>archive</span
>
<span class="label">Archive</span>
</div>
<div ngMenuItem value="Report spam">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true">report</span>
<span class="label">Report spam</span>
</div>
<div ngMenuItem value="Delete">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true">delete</span>
<span class="label">Delete</span>
</div>
</ng-template>
</div>
</ng-template>
@import url('https://fonts.googleapis.com/css2?family=Press+Start+2P&display=swap');
@import url('https://fonts.googleapis.com/icon?family=Material+Symbols+Outlined');
:host {
display: flex;
justify-content: center;
font-size: 0.8rem;
--border-color: color-mix(in srgb, var(--full-contrast) 20%, var(--page-background));
font-family: 'Press Start 2P';
--retro-button-color: var(--vivid-pink);
--retro-shadow-light: color-mix(in srgb, #fff 20%, transparent);
--retro-shadow-dark: color-mix(in srgb, #000 20%, transparent);
--retro-elevated-shadow:
inset 4px 4px 0px 0px var(--retro-shadow-light),
inset -4px -4px 0px 0px var(--retro-shadow-dark), 4px 0px 0px 0px var(--tertiary-contrast),
0px 4px 0px 0px var(--tertiary-contrast), -4px 0px 0px 0px var(--tertiary-contrast),
0px -4px 0px 0px var(--tertiary-contrast);
--retro-flat-shadow:
4px 0px 0px 0px var(--tertiary-contrast), 0px 4px 0px 0px var(--tertiary-contrast),
-4px 0px 0px 0px var(--tertiary-contrast), 0px -4px 0px 0px var(--tertiary-contrast);
--retro-clickable-shadow:
inset 4px 4px 0px 0px var(--retro-shadow-light),
inset -4px -4px 0px 0px var(--retro-shadow-dark), 4px 0px 0px 0px var(--tertiary-contrast),
0px 4px 0px 0px var(--tertiary-contrast), -4px 0px 0px 0px var(--tertiary-contrast),
0px -4px 0px 0px var(--tertiary-contrast), 8px 8px 0px 0px var(--tertiary-contrast);
--retro-pressed-shadow:
inset 4px 4px 0px 0px var(--retro-shadow-dark),
inset -4px -4px 0px 0px var(--retro-shadow-light), 4px 0px 0px 0px var(--tertiary-contrast),
0px 4px 0px 0px var(--tertiary-contrast), -4px 0px 0px 0px var(--tertiary-contrast),
0px -4px 0px 0px var(--tertiary-contrast), 0px 0px 0px 0px var(--tertiary-contrast);
}
[ngMenuTrigger] {
display: flex;
cursor: pointer;
align-items: center;
padding: 0.6rem 2rem;
font-family: 'Press Start 2P';
color: #000;
background-color: var(--vivid-pink);
box-shadow: var(--retro-clickable-shadow);
transition:
transform 0.1s,
box-shadow 0.1s;
}
[ngMenuTrigger]:hover {
transform: translate(1px, 1px);
}
[ngMenuTrigger]:active {
background-color: color-mix(in srgb, var(--vivid-pink) 80%, #fff);
box-shadow: var(--retro-pressed-shadow);
transform: translate(4px, 4px);
}
[ngMenuTrigger] .icon {
font-size: 1.5rem;
opacity: 0.875;
}
[ngMenu] {
margin-top: 8px;
width: 15rem;
padding: 0.25rem;
background-color: var(--page-background);
box-shadow: var(--retro-flat-shadow);
}
[ngMenu][data-visible='false'] {
display: none;
}
[ngMenuItem] {
outline: none;
display: flex;
cursor: pointer;
align-items: center;
gap: 0.5rem;
padding: 0.5rem;
font-size: 0.875rem;
}
[ngMenuItem][data-active='true'] {
background: color-mix(in srgb, var(--border-color) 10%, transparent);
}
[ngMenuTrigger]:focus {
outline: 4px dashed var(--vivid-pink);
outline-offset: 8px;
}
[ngMenuItem]:focus {
outline: 4px dashed var(--vivid-pink);
outline-offset: -4px;
}
[ngMenuItem] .icon {
opacity: 0.875;
font-size: 1.25rem;
}
[ngMenuItem] .label {
flex: 1;
opacity: 0.875;
font-size: 0.875rem;
}
[ngMenuItem]:not([aria-expanded='true']) .arrow {
opacity: 0.5;
}
[ngMenu] .separator {
border-top: 4px solid #000;
margin: 0.25rem 0;
opacity: 0.25;
}
The menu automatically closes when a user selects an item or presses Escape.
Context menus appear at the cursor position when users right-click an element.
import {Component} from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.html',
styleUrl: './app.css',
})
export class App {}
<h1>Coming Soon</h1>
Position the menu using the contextmenu event coordinates.
A standalone menu doesn't require a trigger and remains visible in the interface.
import {Component, signal, viewChild} from '@angular/core';
import {Menu, MenuContent, MenuItem} from '@angular/aria/menu';
import {OverlayModule} from '@angular/cdk/overlay';
@Component({
selector: 'app-root',
templateUrl: 'app.html',
styleUrl: 'app.css',
imports: [Menu, MenuContent, MenuItem, OverlayModule],
})
export class App {
hasInteracted = signal(false);
updateMenu = viewChild<Menu<string>>('updateMenu');
}
<div ngMenu (mouseover)="hasInteracted.set(true)" (focusin)="hasInteracted.set(true)">
<ng-template ngMenuContent>
<span id="security-label" class="heading">SECURITY</span>
<div role="group" aria-labelledby="security-label">
<div ngMenuItem value="Change password">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>lock_open</span
>
<span class="label">Change password</span>
</div>
<div ngMenuItem value="Two-factor authentication">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>security_key</span
>
<span class="label">Two-factor authentication</span>
</div>
<div ngMenuItem value="Reset" #resetItem [submenu]="updateMenu()">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>refresh</span
>
<span class="label">Reset</span>
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>arrow_right</span
>
</div>
<ng-template
[cdkConnectedOverlayOpen]="hasInteracted()"
[cdkConnectedOverlay]="{origin: resetItem, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'end', originY: 'top', overlayY: 'top', overlayX: 'start', offsetX: 6},
]"
cdkAttachPopoverAsChild
>
<div ngMenu #updateMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="Email address">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>email</span
>
<span class="label">Email address</span>
</div>
<div ngMenuItem value="Phone number">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>phone</span
>
<span class="label">Phone number</span>
</div>
<div ngMenuItem value="Password">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>vpn_key</span
>
<span class="label">Password</span>
</div>
</ng-template>
</div>
</ng-template>
</div>
<div role="separator" class="separator"></div>
<span id="help-label" class="heading">HELP</span>
<div role="group" aria-labelledby="help-label">
<div ngMenuItem value="Support">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true">help</span>
<span class="label">Support</span>
</div>
<div ngMenuItem value="Feedback">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>feedback</span
>
<span class="label">Feedback</span>
</div>
</div>
<div role="separator" class="separator"></div>
<div ngMenuItem value="Logout">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true">logout</span>
<span class="label">Logout</span>
</div>
</ng-template>
</div>
@import url('https://fonts.googleapis.com/icon?family=Material+Symbols+Outlined');
:host {
display: flex;
justify-content: center;
font-family: var(--inter-font);
--border-color: color-mix(in srgb, var(--full-contrast) 20%, var(--page-background));
}
[ngMenu] {
margin: 0;
width: 30rem;
padding: 0.25rem;
border-radius: 0.5rem;
border: 1px solid var(--border-color);
background-color: var(--page-background);
}
[ngMenu] [ngMenu] {
width: 15rem;
}
[ngMenu][data-visible='false'] {
display: none;
}
[ngMenuItem] {
outline: none;
display: flex;
cursor: pointer;
align-items: center;
gap: 0.5rem;
padding: 0.5rem;
font-size: 0.875rem;
border-radius: 0.25rem;
}
[ngMenuItem][data-active='true'] {
background: color-mix(in srgb, var(--border-color) 10%, transparent);
}
[ngMenuItem]:focus {
outline: 2px solid var(--vivid-pink);
}
[ngMenuItem] .icon {
opacity: 0.875;
font-size: 1.25rem;
}
[ngMenuItem] .label {
flex: 1;
opacity: 0.875;
font-size: 0.875rem;
}
[ngMenuItem]:not([aria-expanded='true']) .arrow {
opacity: 0.5;
}
[ngMenu] .separator {
border-top: 1px solid var(--border-color);
margin: 0.25rem 0;
opacity: 0.25;
}
[ngMenu] .heading {
display: block;
font-weight: bold;
opacity: 0.6;
font-size: 0.75rem;
padding: 0.75rem;
letter-spacing: 0.05rem;
}
import {Component, signal, viewChild} from '@angular/core';
import {Menu, MenuContent, MenuItem} from '@angular/aria/menu';
import {OverlayModule} from '@angular/cdk/overlay';
@Component({
selector: 'app-root',
templateUrl: 'app.html',
styleUrl: 'app.css',
imports: [Menu, MenuContent, MenuItem, OverlayModule],
})
export class App {
hasInteracted = signal(false);
updateMenu = viewChild<Menu<string>>('updateMenu');
}
<div
ngMenu
class="material-menu"
(mouseover)="hasInteracted.set(true)"
(focusin)="hasInteracted.set(true)"
>
<ng-template ngMenuContent>
<span id="security-label" class="heading">SECURITY</span>
<div role="group" aria-labelledby="security-label">
<div ngMenuItem value="Change password">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>lock_open</span
>
<span class="label">Change password</span>
</div>
<div ngMenuItem value="Two-factor authentication">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>security_key</span
>
<span class="label">Two-factor authentication</span>
</div>
<div ngMenuItem value="Reset" #resetItem [submenu]="updateMenu()">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>refresh</span
>
<span class="label">Reset</span>
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>arrow_right</span
>
</div>
<ng-template
[cdkConnectedOverlayOpen]="hasInteracted()"
[cdkConnectedOverlay]="{origin: resetItem, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'end', originY: 'top', overlayY: 'top', overlayX: 'start', offsetX: 6},
]"
cdkAttachPopoverAsChild
>
<div ngMenu #updateMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="Email address">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>email</span
>
<span class="label">Email address</span>
</div>
<div ngMenuItem value="Phone number">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>phone</span
>
<span class="label">Phone number</span>
</div>
<div ngMenuItem value="Password">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>vpn_key</span
>
<span class="label">Password</span>
</div>
</ng-template>
</div>
</ng-template>
</div>
<div role="separator" class="separator"></div>
<span id="help-label" class="heading">HELP</span>
<div role="group" aria-labelledby="help-label">
<div ngMenuItem value="Support">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true">help</span>
<span class="label">Support</span>
</div>
<div ngMenuItem value="Feedback">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>feedback</span
>
<span class="label">Feedback</span>
</div>
</div>
<div role="separator" class="separator"></div>
<div ngMenuItem value="Logout">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true">logout</span>
<span class="label">Logout</span>
</div>
</ng-template>
</div>
@import url('https://fonts.googleapis.com/icon?family=Material+Symbols+Outlined');
:host {
display: flex;
justify-content: center;
font-family: var(--inter-font);
}
[ngMenu] {
gap: 3px;
width: 30rem;
padding: 1rem;
border-radius: 1rem;
display: flex;
flex-direction: column;
background-color: color-mix(in srgb, var(--vivid-pink) 5%, var(--page-background));
border: 1px solid color-mix(in srgb, var(--full-contrast) 10%, var(--page-background));
}
[ngMenu] [ngMenu] {
width: 15rem;
}
[ngMenu] .group {
padding: 0.25rem;
border-radius: 0.25rem;
background-color: var(--page-background);
box-shadow: 0 1px 2px 1px color-mix(in srgb, var(--primary-contrast) 25%, transparent);
}
[ngMenu] .group:first-of-type {
border-top-left-radius: 1rem;
border-top-right-radius: 1rem;
}
[ngMenu] .group:last-of-type {
border-bottom-left-radius: 1rem;
border-bottom-right-radius: 1rem;
}
[ngMenu][data-visible='false'] {
display: none;
}
[ngMenuItem] {
outline: none;
display: flex;
cursor: pointer;
align-items: center;
gap: 0.5rem;
padding: 0.75rem;
font-size: 0.875rem;
border-radius: 0.75rem;
}
[ngMenuItem][data-active='true'] {
color: color-mix(in srgb, var(--vivid-pink) 70%, var(--primary-contrast));
background: color-mix(in srgb, var(--vivid-pink) 5%, transparent);
}
[ngMenuItem]:focus {
outline: 2px solid var(--vivid-pink);
}
[ngMenuItem] .icon {
opacity: 0.875;
font-size: 1.25rem;
}
[ngMenuItem] .label {
flex: 1;
opacity: 0.875;
font-size: 0.875rem;
}
[ngMenuItem]:not([aria-expanded='true']) .arrow {
opacity: 0.5;
}
[ngMenu] .separator {
border-top: 1px solid var(--quaternary-contrast);
margin: 0.25rem 0;
opacity: 0.25;
}
[ngMenu] .heading {
display: block;
font-weight: bold;
opacity: 0.6;
font-size: 0.75rem;
padding: 0.75rem;
letter-spacing: 0.05rem;
}
import {Component, signal, viewChild} from '@angular/core';
import {Menu, MenuContent, MenuItem} from '@angular/aria/menu';
import {OverlayModule} from '@angular/cdk/overlay';
@Component({
selector: 'app-root',
templateUrl: 'app.html',
styleUrl: 'app.css',
imports: [Menu, MenuContent, MenuItem, OverlayModule],
})
export class App {
hasInteracted = signal(false);
updateMenu = viewChild<Menu<string>>('updateMenu');
}
<div
ngMenu
class="retro-menu"
(mouseover)="hasInteracted.set(true)"
(focusin)="hasInteracted.set(true)"
>
<ng-template ngMenuContent>
<span id="security-label" class="heading">SECURITY</span>
<div role="group" aria-labelledby="security-label">
<div ngMenuItem value="Change password">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>lock_open</span
>
<span class="label">Change password</span>
</div>
<div ngMenuItem value="Two-factor authentication">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>security_key</span
>
<span class="label">Two-factor authentication</span>
</div>
<div ngMenuItem value="Reset" #resetItem [submenu]="updateMenu()">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>refresh</span
>
<span class="label">Reset</span>
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>arrow_right</span
>
</div>
<ng-template
[cdkConnectedOverlayOpen]="hasInteracted()"
[cdkConnectedOverlay]="{origin: resetItem, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'end', originY: 'top', overlayY: 'top', overlayX: 'start', offsetX: 6},
]"
cdkAttachPopoverAsChild
>
<div ngMenu #updateMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="Email address">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>email</span
>
<span class="label">Email address</span>
</div>
<div ngMenuItem value="Phone number">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>phone</span
>
<span class="label">Phone number</span>
</div>
<div ngMenuItem value="Password">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>vpn_key</span
>
<span class="label">Password</span>
</div>
</ng-template>
</div>
</ng-template>
</div>
<div role="separator" class="separator"></div>
<span id="help-label" class="heading">HELP</span>
<div role="group" aria-labelledby="help-label">
<div ngMenuItem value="Support">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true">help</span>
<span class="label">Support</span>
</div>
<div ngMenuItem value="Feedback">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>feedback</span
>
<span class="label">Feedback</span>
</div>
</div>
<div role="separator" class="separator"></div>
<div ngMenuItem value="Logout">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true">logout</span>
<span class="label">Logout</span>
</div>
</ng-template>
</div>
@import url('https://fonts.googleapis.com/css2?family=Press+Start+2P&display=swap');
@import url('https://fonts.googleapis.com/icon?family=Material+Symbols+Outlined');
:host {
display: flex;
justify-content: center;
font-size: 0.8rem;
--border-color: color-mix(in srgb, var(--full-contrast) 20%, var(--page-background));
font-family: 'Press Start 2P';
--retro-button-color: var(--vivid-pink);
--retro-shadow-light: color-mix(in srgb, #fff 20%, transparent);
--retro-shadow-dark: color-mix(in srgb, #000 20%, transparent);
--retro-elevated-shadow:
inset 4px 4px 0px 0px var(--retro-shadow-light),
inset -4px -4px 0px 0px var(--retro-shadow-dark), 4px 0px 0px 0px var(--tertiary-contrast),
0px 4px 0px 0px var(--tertiary-contrast), -4px 0px 0px 0px var(--tertiary-contrast),
0px -4px 0px 0px var(--tertiary-contrast);
--retro-flat-shadow:
4px 0px 0px 0px var(--tertiary-contrast), 0px 4px 0px 0px var(--tertiary-contrast),
-4px 0px 0px 0px var(--tertiary-contrast), 0px -4px 0px 0px var(--tertiary-contrast);
--retro-clickable-shadow:
inset 4px 4px 0px 0px var(--retro-shadow-light),
inset -4px -4px 0px 0px var(--retro-shadow-dark), 4px 0px 0px 0px var(--tertiary-contrast),
0px 4px 0px 0px var(--tertiary-contrast), -4px 0px 0px 0px var(--tertiary-contrast),
0px -4px 0px 0px var(--tertiary-contrast), 8px 8px 0px 0px var(--tertiary-contrast);
--retro-pressed-shadow:
inset 4px 4px 0px 0px var(--retro-shadow-dark),
inset -4px -4px 0px 0px var(--retro-shadow-light), 4px 0px 0px 0px var(--tertiary-contrast),
0px 4px 0px 0px var(--tertiary-contrast), -4px 0px 0px 0px var(--tertiary-contrast),
0px -4px 0px 0px var(--tertiary-contrast), 0px 0px 0px 0px var(--tertiary-contrast);
}
[ngMenu] {
margin-top: 8px;
width: 30rem;
padding: 0.25rem;
background-color: var(--page-background);
box-shadow: var(--retro-flat-shadow);
}
[ngMenu] [ngMenu] {
width: 15rem;
}
[ngMenu][data-visible='false'] {
display: none;
}
[ngMenuItem] {
outline: none;
display: flex;
cursor: pointer;
align-items: center;
gap: 0.5rem;
padding: 0.5rem;
font-size: 0.875rem;
}
[ngMenuItem][data-active='true'] {
background: color-mix(in srgb, var(--border-color) 10%, transparent);
}
[ngMenuItem]:focus {
outline: 4px dashed var(--vivid-pink);
outline-offset: -4px;
}
[ngMenuItem] .icon {
opacity: 0.875;
font-size: 1.25rem;
}
[ngMenuItem] .label {
flex: 1;
opacity: 0.875;
font-size: 0.875rem;
}
[ngMenuItem]:not([aria-expanded='true']) .arrow {
opacity: 0.5;
}
[ngMenu] .separator {
border-top: 4px solid #000;
margin: 0.25rem 0;
opacity: 0.25;
}
[ngMenu] .heading {
display: block;
font-weight: bold;
opacity: 0.6;
font-size: 0.75rem;
padding: 0.75rem;
letter-spacing: 0.05rem;
}
Standalone menus work well for always-visible action lists or navigation.
Disable specific menu items using the disabled input. Control focus behavior with softDisabled.
import {Component, viewChild} from '@angular/core';
import {Menu, MenuContent, MenuItem, MenuTrigger} from '@angular/aria/menu';
import {OverlayModule} from '@angular/cdk/overlay';
@Component({
selector: 'app-root',
templateUrl: 'app.html',
styleUrl: 'app.css',
imports: [Menu, MenuContent, MenuItem, MenuTrigger, OverlayModule],
})
export class App {
formatMenu = viewChild<Menu<string>>('formatMenu');
categorizeMenu = viewChild<Menu<string>>('categorizeMenu');
}
<button ngMenuTrigger #origin #trigger="ngMenuTrigger" [menu]="formatMenu()">Open Menu</button>
<ng-template
[cdkConnectedOverlayOpen]="trigger.expanded()"
[cdkConnectedOverlay]="{origin, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top', offsetY: 4},
]"
cdkAttachPopoverAsChild
>
<div ngMenu class="menu" #formatMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="Mark as read">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>mark_email_read</span
>
<span class="label">Mark as read</span>
</div>
<div ngMenuItem value="Snooze" [disabled]="true">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true">snooze</span>
<span class="label">Snooze</span>
</div>
<div role="separator" aria-orientation="horizontal" class="separator"></div>
<div
ngMenuItem
class="menu-item"
value="Categorize"
#categorizeItem
[submenu]="categorizeMenu()"
[disabled]="true"
>
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>category</span
>
<span class="label">Categorize</span>
<span class="icon material-symbols-outlined arrow" translate="no" aria-hidden="true"
>arrow_right</span
>
</div>
<ng-template
[cdkConnectedOverlayOpen]="formatMenu.visible()"
[cdkConnectedOverlay]="{origin: categorizeItem, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'end', originY: 'top', overlayY: 'top', overlayX: 'start', offsetX: 6},
]"
cdkAttachPopoverAsChild
>
<div ngMenu class="menu" #categorizeMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="Mark as important">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>label_important</span
>
<span class="label">Mark as important</span>
</div>
<div ngMenuItem value="Star">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>star</span
>
<span class="label">Star</span>
</div>
<div ngMenuItem value="Label">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>label</span
>
<span class="label">Label</span>
</div>
</ng-template>
</div>
</ng-template>
<div role="separator" aria-orientation="horizontal" class="separator"></div>
<div ngMenuItem value="Archive">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>archive</span
>
<span class="label">Archive</span>
</div>
<div ngMenuItem value="Report spam">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true">report</span>
<span class="label">Report spam</span>
</div>
<div ngMenuItem value="Delete">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true">delete</span>
<span class="label">Delete</span>
</div>
</ng-template>
</div>
</ng-template>
@import url('https://fonts.googleapis.com/icon?family=Material+Symbols+Outlined');
:host {
display: flex;
justify-content: center;
font-family: var(--inter-font);
--border-color: color-mix(in srgb, var(--full-contrast) 20%, var(--page-background));
}
[ngMenuTrigger] {
display: flex;
cursor: pointer;
align-items: center;
padding: 0.6rem 2rem;
border-radius: 0.5rem;
color: var(--primary-contrast);
border: 1px solid var(--border-color);
background-color: var(--page-background);
}
[ngMenuTrigger] .icon {
font-size: 1.5rem;
opacity: 0.875;
}
[ngMenu] {
margin: 0;
width: 15rem;
padding: 0.25rem;
border-radius: 0.5rem;
border: 1px solid var(--border-color);
background-color: var(--page-background);
}
[ngMenu][data-visible='false'] {
display: none;
}
[ngMenuItem] {
outline: none;
display: flex;
cursor: pointer;
align-items: center;
gap: 0.5rem;
padding: 0.5rem;
font-size: 0.875rem;
border-radius: 0.25rem;
}
[ngMenuItem][aria-disabled='true'] {
opacity: 0.5;
cursor: default;
}
[ngMenuTrigger]:hover,
[ngMenuItem][data-active='true'] {
background: color-mix(in srgb, var(--border-color) 10%, transparent);
}
[ngMenuItem]:focus,
[ngMenuTrigger]:focus {
outline: 2px solid var(--vivid-pink);
}
[ngMenuItem] .icon {
opacity: 0.875;
font-size: 1.25rem;
}
[ngMenuItem] .label {
flex: 1;
opacity: 0.875;
font-size: 0.875rem;
}
[ngMenuItem]:not([aria-expanded='true']) .arrow {
opacity: 0.5;
}
[ngMenu] .separator {
border-top: 1px solid var(--border-color);
margin: 0.25rem 0;
opacity: 0.25;
}
import {Component, viewChild} from '@angular/core';
import {Menu, MenuContent, MenuItem, MenuTrigger} from '@angular/aria/menu';
import {OverlayModule} from '@angular/cdk/overlay';
@Component({
selector: 'app-root',
templateUrl: 'app.html',
styleUrl: 'app.css',
imports: [Menu, MenuContent, MenuItem, MenuTrigger, OverlayModule],
})
export class App {
formatMenu = viewChild<Menu<string>>('formatMenu');
categorizeMenu = viewChild<Menu<string>>('categorizeMenu');
}
<button ngMenuTrigger #origin #trigger="ngMenuTrigger" [menu]="formatMenu()">Open Menu</button>
<ng-template
[cdkConnectedOverlayOpen]="trigger.expanded()"
[cdkConnectedOverlay]="{origin, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top', offsetY: 4},
]"
cdkAttachPopoverAsChild
>
<div ngMenu class="menu" #formatMenu="ngMenu">
<ng-template ngMenuContent>
<div class="group">
<div ngMenuItem value="Mark as read">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>mark_email_read</span
>
<span class="label">Mark as read</span>
</div>
<div ngMenuItem value="Snooze">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>snooze</span
>
<span class="label">Snooze</span>
</div>
<div ngMenuItem value="Delete" [disabled]="true">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>delete</span
>
<span class="label">Delete</span>
</div>
</div>
<div class="group">
<div
ngMenuItem
class="menu-item"
value="Categorize"
#categorizeItem
[submenu]="categorizeMenu()"
>
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>category</span
>
<span class="label">Categorize</span>
<span class="icon material-symbols-outlined arrow" translate="no" aria-hidden="true"
>arrow_right</span
>
</div>
</div>
<ng-template
[cdkConnectedOverlayOpen]="formatMenu.visible()"
[cdkConnectedOverlay]="{origin: categorizeItem, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'end', originY: 'top', overlayY: 'top', overlayX: 'start', offsetX: 6},
]"
cdkAttachPopoverAsChild
>
<div ngMenu class="menu" #categorizeMenu="ngMenu">
<ng-template ngMenuContent>
<div class="group">
<div ngMenuItem value="Mark as important">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>label_important</span
>
<span class="label">Mark as important</span>
</div>
<div ngMenuItem value="Star">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>star</span
>
<span class="label">Star</span>
</div>
<div ngMenuItem value="Label" [disabled]="true">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>label</span
>
<span class="label">Label</span>
</div>
</div>
</ng-template>
</div>
</ng-template>
</ng-template>
</div>
</ng-template>
@import url('https://fonts.googleapis.com/icon?family=Material+Symbols+Outlined');
:host {
display: flex;
justify-content: center;
font-family: var(--inter-font);
}
[ngMenuTrigger] {
display: flex;
cursor: pointer;
align-items: center;
padding: 0.6rem 2rem;
border-radius: 0.5rem;
border: 1px solid transparent;
background-color: color-mix(in srgb, var(--vivid-pink) 5%, transparent);
color: color-mix(in srgb, var(--vivid-pink) 70%, var(--primary-contrast));
}
[ngMenuTrigger] .icon {
font-size: 1.5rem;
opacity: 0.875;
}
[ngMenu] {
gap: 3px;
width: 15rem;
display: flex;
flex-direction: column;
}
[ngMenu] .group {
padding: 0.25rem;
border-radius: 0.25rem;
background-color: var(--page-background);
box-shadow: 0 1px 2px 1px color-mix(in srgb, var(--primary-contrast) 25%, transparent);
}
[ngMenu] .group:first-of-type {
border-top-left-radius: 1rem;
border-top-right-radius: 1rem;
}
[ngMenu] .group:last-of-type {
border-bottom-left-radius: 1rem;
border-bottom-right-radius: 1rem;
}
[ngMenu][data-visible='false'] {
display: none;
}
[ngMenuItem] {
outline: none;
display: flex;
cursor: pointer;
align-items: center;
gap: 0.5rem;
padding: 0.75rem;
font-size: 0.875rem;
border-radius: 0.75rem;
}
[ngMenuTrigger]:hover,
[ngMenuTrigger][aria-expanded='true'] {
background: color-mix(in srgb, var(--vivid-pink) 10%, transparent);
}
[ngMenuItem][data-active='true'] {
color: color-mix(in srgb, var(--vivid-pink) 70%, var(--primary-contrast));
background: color-mix(in srgb, var(--vivid-pink) 5%, transparent);
}
[ngMenuItem]:focus,
[ngMenuTrigger]:focus {
outline: 2px solid var(--vivid-pink);
}
[ngMenuItem] .icon {
opacity: 0.875;
font-size: 1.25rem;
}
[ngMenuItem] .label {
flex: 1;
opacity: 0.875;
font-size: 0.875rem;
}
[ngMenuItem]:not([aria-expanded='true']) .arrow {
opacity: 0.5;
}
[ngMenu] .separator {
border-top: 1px solid var(--quaternary-contrast);
margin: 0.25rem 0;
opacity: 0.25;
}
[ngMenuItem][aria-disabled='true'] {
opacity: 0.5;
cursor: default;
}
import {Component, viewChild} from '@angular/core';
import {Menu, MenuContent, MenuItem, MenuTrigger} from '@angular/aria/menu';
import {OverlayModule} from '@angular/cdk/overlay';
@Component({
selector: 'app-root',
templateUrl: 'app.html',
styleUrl: 'app.css',
imports: [Menu, MenuContent, MenuItem, MenuTrigger, OverlayModule],
})
export class App {
formatMenu = viewChild<Menu<string>>('formatMenu');
categorizeMenu = viewChild<Menu<string>>('categorizeMenu');
}
<button class="retro-trigger" ngMenuTrigger #trigger="ngMenuTrigger" #origin [menu]="formatMenu()">
Open Menu
</button>
<ng-template
[cdkConnectedOverlayOpen]="trigger.expanded()"
[cdkConnectedOverlay]="{origin, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top', offsetY: 4},
]"
cdkAttachPopoverAsChild
>
<div ngMenu class="menu" #formatMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="Mark as read" [disabled]="true">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>mark_email_read</span
>
<span class="label">Mark as read</span>
</div>
<div ngMenuItem value="Snooze">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true">snooze</span>
<span class="label">Snooze</span>
</div>
<div role="separator" aria-orientation="horizontal" class="separator"></div>
<div
ngMenuItem
class="menu-item"
value="Categorize"
#categorizeItem
[submenu]="categorizeMenu()"
>
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>category</span
>
<span class="label">Categorize</span>
<span class="icon material-symbols-outlined arrow" translate="no" aria-hidden="true"
>arrow_right</span
>
</div>
<ng-template
[cdkConnectedOverlayOpen]="formatMenu.visible()"
[cdkConnectedOverlay]="{origin: categorizeItem, usePopover: 'inline'}"
[cdkConnectedOverlayPositions]="[
{originX: 'end', originY: 'top', overlayY: 'top', overlayX: 'start', offsetX: 6},
]"
cdkAttachPopoverAsChild
>
<div ngMenu class="menu" #categorizeMenu="ngMenu">
<ng-template ngMenuContent>
<div ngMenuItem value="Mark as important">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>label_important</span
>
<span class="label">Mark as important</span>
</div>
<div ngMenuItem value="Star">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>star</span
>
<span class="label">Star</span>
</div>
<div ngMenuItem value="Label">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>label</span
>
<span class="label">Label</span>
</div>
</ng-template>
</div>
</ng-template>
<div role="separator" aria-orientation="horizontal" class="separator"></div>
<div ngMenuItem value="Archive">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true"
>archive</span
>
<span class="label">Archive</span>
</div>
<div ngMenuItem value="Report spam">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true">report</span>
<span class="label">Report spam</span>
</div>
<div ngMenuItem value="Delete">
<span class="icon material-symbols-outlined" translate="no" aria-hidden="true">delete</span>
<span class="label">Delete</span>
</div>
</ng-template>
</div>
</ng-template>
@import url('https://fonts.googleapis.com/css2?family=Press+Start+2P&display=swap');
@import url('https://fonts.googleapis.com/icon?family=Material+Symbols+Outlined');
:host {
display: flex;
justify-content: center;
font-size: 0.8rem;
--border-color: color-mix(in srgb, var(--full-contrast) 20%, var(--page-background));
font-family: 'Press Start 2P';
--retro-button-color: var(--vivid-pink);
--retro-shadow-light: color-mix(in srgb, #fff 20%, transparent);
--retro-shadow-dark: color-mix(in srgb, #000 20%, transparent);
--retro-elevated-shadow:
inset 4px 4px 0px 0px var(--retro-shadow-light),
inset -4px -4px 0px 0px var(--retro-shadow-dark), 4px 0px 0px 0px var(--tertiary-contrast),
0px 4px 0px 0px var(--tertiary-contrast), -4px 0px 0px 0px var(--tertiary-contrast),
0px -4px 0px 0px var(--tertiary-contrast);
--retro-flat-shadow:
4px 0px 0px 0px var(--tertiary-contrast), 0px 4px 0px 0px var(--tertiary-contrast),
-4px 0px 0px 0px var(--tertiary-contrast), 0px -4px 0px 0px var(--tertiary-contrast);
--retro-clickable-shadow:
inset 4px 4px 0px 0px var(--retro-shadow-light),
inset -4px -4px 0px 0px var(--retro-shadow-dark), 4px 0px 0px 0px var(--tertiary-contrast),
0px 4px 0px 0px var(--tertiary-contrast), -4px 0px 0px 0px var(--tertiary-contrast),
0px -4px 0px 0px var(--tertiary-contrast), 8px 8px 0px 0px var(--tertiary-contrast);
--retro-pressed-shadow:
inset 4px 4px 0px 0px var(--retro-shadow-dark),
inset -4px -4px 0px 0px var(--retro-shadow-light), 4px 0px 0px 0px var(--tertiary-contrast),
0px 4px 0px 0px var(--tertiary-contrast), -4px 0px 0px 0px var(--tertiary-contrast),
0px -4px 0px 0px var(--tertiary-contrast), 0px 0px 0px 0px var(--tertiary-contrast);
}
[ngMenuTrigger] {
display: flex;
cursor: pointer;
align-items: center;
padding: 0.6rem 2rem;
font-family: 'Press Start 2P';
color: #000;
background-color: var(--vivid-pink);
box-shadow: var(--retro-clickable-shadow);
transition:
transform 0.1s,
box-shadow 0.1s;
}
[ngMenuTrigger]:hover {
transform: translate(1px, 1px);
}
[ngMenuTrigger]:active {
background-color: color-mix(in srgb, var(--vivid-pink) 80%, #fff);
box-shadow: var(--retro-pressed-shadow);
transform: translate(4px, 4px);
}
[ngMenuTrigger] .icon {
font-size: 1.5rem;
opacity: 0.875;
}
[ngMenu] {
margin-top: 8px;
width: 15rem;
padding: 0.25rem;
background-color: var(--page-background);
box-shadow: var(--retro-flat-shadow);
}
[ngMenu][data-visible='false'] {
display: none;
}
[ngMenuItem] {
outline: none;
display: flex;
cursor: pointer;
align-items: center;
gap: 0.5rem;
padding: 0.5rem;
font-size: 0.875rem;
}
[ngMenuItem][data-active='true'] {
background: color-mix(in srgb, var(--border-color) 10%, transparent);
}
[ngMenuTrigger]:focus {
outline: 4px dashed var(--vivid-pink);
outline-offset: 8px;
}
[ngMenuItem]:focus {
outline: 4px dashed var(--vivid-pink);
outline-offset: -4px;
}
[ngMenuItem] .icon {
opacity: 0.875;
font-size: 1.25rem;
}
[ngMenuItem] .label {
flex: 1;
opacity: 0.875;
font-size: 0.875rem;
}
[ngMenuItem]:not([aria-expanded='true']) .arrow {
opacity: 0.5;
}
[ngMenu] .separator {
border-top: 4px solid #000;
margin: 0.25rem 0;
opacity: 0.25;
}
[ngMenuItem][aria-disabled='true'] {
opacity: 0.5;
cursor: default;
}
When [softDisabled]="true", disabled items can receive focus but cannot be activated. When [softDisabled]="false", disabled items are skipped during keyboard navigation.
The container directive for menu items.
| Property | Type | Default | Description |
|---|---|---|---|
disabled | boolean | false | Disables all items in the menu |
wrap | boolean | true | Whether keyboard navigation wraps at edges |
softDisabled | boolean | true | When true, disabled items are focusable but not interactive |
| Method | Parameters | Description |
|---|---|---|
close | none | Closes the menu |
A horizontal container for multiple menus.
| Property | Type | Default | Description |
|---|---|---|---|
disabled | boolean | false | Disables the entire menubar |
wrap | boolean | true | Whether keyboard navigation wraps at edges |
softDisabled | boolean | true | When true, disabled items are focusable but not interactive |
An individual item within a menu.
| Property | Type | Default | Description |
|---|---|---|---|
value | any | — | Required. Value for this item |
disabled | boolean | false | Disables this menu item |
submenu | Menu | — | Reference to a submenu |
searchTerm | string | '' | Search term for typeahead (supports two-way binding) |
| Property | Type | Description |
|---|---|---|
active | Signal<boolean> | Whether the item currently has focus |
expanded | Signal<boolean> | Whether the submenu is expanded |
hasPopup | Signal<boolean> | Whether the item has an associated submenu |
NOTE: MenuItem does not expose public methods. Use the submenu input to associate submenus with menu items.
A button or element that opens a menu.
| Property | Type | Default | Description |
|---|---|---|---|
menu | Menu | — | Required. The menu to trigger |
disabled | boolean | false | Disables the trigger |
softDisabled | boolean | true | When true, disabled trigger is focusable |
| Property | Type | Description |
|---|---|---|
expanded | Signal<boolean> | Whether the menu is currently open |
hasPopup | Signal<boolean> | Whether the trigger has an associated menu |
| Method | Parameters | Description |
|---|---|---|
open | none | Opens the menu |
close | none | Closes the menu |
toggle | none | Toggles the menu open/closed |
Super-powered by Google ©2010–2025.
Code licensed under an MIT-style License. Documentation licensed under CC BY 4.0.
https://angular.dev/guide/aria/menu