dropdown-menu
Use ng-dropdown-menu on a wrapper with a trigger button and a native menu.
<div ng-dropdown-menu>
<button type="button">Options</button>
<menu>
<a href="#new">New Task</a>
<a href="#edit">Edit Task</a>
<a href="#delete">Delete Task</a>
</menu>
</div>
The directive adds the required menu roles and manages aria-expanded,
aria-controls, outside-click close, Escape close, and arrow-key focus
movement. It does not publish scope methods or own AngularTS application state.
If the menu needs external control, bind the wrapper’s concise open attribute
from AngularTS state.
Example
View source
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>AngularCSS Dropdown Menu</title>
<link rel="stylesheet" href="../../css/preflight.css" />
<link rel="stylesheet" href="../../css/angular.css" />
<link rel="stylesheet" href="../example.css" />
<script src="../../js/angular-ts.umd.js"></script>
<script src="../../js/angular-css.umd.js"></script>
</head>
<body
ng-app="angular.css"
ng-cloak
ng-init="menuState={toolbar:true,density:'comfortable'}"
data-example="dropdown-menu-complex dropdown-menu-demo"
>
<div ng-dropdown-menu class="visual-example">
<button type="button">Options</button>
<menu style="--menu-width: 11rem">
<section>
<h3>Project</h3>
<button>New task <kbd>Ctrl+N</kbd></button>
<button>Edit task</button>
</section>
<hr />
<button
aria-checked="{{ menuState.toolbar }}"
ng-click="menuState.toolbar=!menuState.toolbar"
>
Show toolbar
</button>
<fieldset aria-label="Density">
<button
aria-checked="{{ menuState.density === 'comfortable' }}"
ng-click="menuState.density='comfortable'"
>
Comfortable
</button>
<button
aria-checked="{{ menuState.density === 'compact' }}"
ng-click="menuState.density='compact'"
>
Compact
</button>
</fieldset>
<details>
<summary>Share</summary>
<menu>
<button>Copy link</button>
<button>Email link</button>
</menu>
</details>
</menu>
</div>
</body>
</html>
Reference workflows
The workflow page covers basic and dynamically inserted items, an avatar trigger, AngularTS-owned checkbox and radio state, shortcuts, destructive items, submenus, right-to-left direction, and disabled triggers.
View source
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>AngularCSS Dropdown Menu Workflows</title>
<link rel="stylesheet" href="../../css/preflight.css" />
<link rel="stylesheet" href="../../css/angular.css" />
<link rel="stylesheet" href="../example.css" />
<script src="../../js/angular-ts.umd.js"></script>
<script src="../../js/angular-css.umd.js"></script>
</head>
<body
ng-app="angular.css"
ng-cloak
ng-init="dropdownDemo={archive:false,email:true,sms:false,position:'bottom',disabled:false}"
data-example="dropdown-menu-avatar dropdown-menu-basic dropdown-menu-checkboxes dropdown-menu-checkboxes-icons dropdown-menu-destructive dropdown-menu-icons dropdown-menu-radio-group dropdown-menu-radio-icons dropdown-menu-rtl dropdown-menu-shortcuts dropdown-menu-submenu"
>
<main class="dropdown-workflow-grid">
<section class="dropdown-workflow" aria-labelledby="dropdown-basic-title">
<h2 id="dropdown-basic-title">Basic and dynamic items</h2>
<div class="dropdown-workflow-actions">
<button
type="button"
variant="outline"
ng-click="dropdownDemo.archive=true"
ng-disabled="dropdownDemo.archive"
>
{{ dropdownDemo.archive ? 'Archive added' : 'Add archive item' }}
</button>
<div ng-dropdown-menu aria-label="Account commands">
<button type="button">Open</button>
<menu>
<section>
<h3>My Account</h3>
<button>Profile</button>
<button>Billing</button>
<button>Settings</button>
</section>
<hr />
<button>Support</button>
<button disabled>API</button>
<button id="dropdown-archive-item" ng-if="dropdownDemo.archive">
Archive
</button>
</menu>
</div>
</div>
</section>
<section
class="dropdown-workflow"
aria-labelledby="dropdown-avatar-title"
>
<h2 id="dropdown-avatar-title">Avatar trigger</h2>
<div ng-dropdown-menu class="dropdown-avatar-demo">
<button
type="button"
size="icon"
variant="ghost"
aria-label="Open account menu"
>
<span class="avatar">
<img src="../../images/avatars/01.png" alt="Jane Doe" />
<span>JD</span>
</span>
</button>
<menu align="end">
<section>
<button>Account</button>
<button>Billing</button>
<button>Notifications</button>
</section>
<hr />
<button variant="destructive">Sign Out</button>
</menu>
</div>
</section>
<section
class="dropdown-workflow dropdown-workflow-menu-tall"
aria-labelledby="dropdown-preferences-title"
>
<h2 id="dropdown-preferences-title">Preferences</h2>
<div ng-dropdown-menu>
<button type="button">Notifications</button>
<menu style="--menu-width: 13rem">
<section>
<h3>Notification Preferences</h3>
<button
aria-checked="{{ dropdownDemo.email }}"
ng-click="dropdownDemo.email=!dropdownDemo.email"
>
Email notifications
</button>
<button
aria-checked="{{ dropdownDemo.sms }}"
ng-click="dropdownDemo.sms=!dropdownDemo.sms"
>
SMS notifications
</button>
</section>
<hr />
<fieldset aria-label="Menu position">
<h3>Position</h3>
<button
aria-checked="{{ dropdownDemo.position === 'top' }}"
ng-click="dropdownDemo.position='top'"
>
Top
</button>
<button
aria-checked="{{ dropdownDemo.position === 'bottom' }}"
ng-click="dropdownDemo.position='bottom'"
>
Bottom
</button>
</fieldset>
</menu>
</div>
</section>
<section
class="dropdown-workflow dropdown-workflow-menu-tall"
aria-labelledby="dropdown-actions-title"
>
<h2 id="dropdown-actions-title">Actions and submenu</h2>
<div ng-dropdown-menu>
<button type="button">Actions</button>
<menu style="--menu-width: 13rem">
<section>
<button>
Edit
<kbd>Ctrl+E</kbd>
</button>
<button>
Share
<kbd>Ctrl+S</kbd>
</button>
<details>
<summary>Invite users</summary>
<menu>
<button>Email</button>
<button>Message</button>
<hr />
<button>More options</button>
</menu>
</details>
</section>
<hr />
<button variant="destructive">Delete</button>
</menu>
</div>
</section>
<section
class="dropdown-workflow dropdown-workflow-wide dropdown-workflow-rtl"
dir="rtl"
lang="ar"
aria-labelledby="dropdown-rtl-title"
>
<div class="dropdown-workflow-heading">
<h2 id="dropdown-rtl-title">اتجاه من اليمين إلى اليسار</h2>
<label>
<input type="checkbox" ng-model="dropdownDemo.disabled" />
تعطيل القائمة
</label>
</div>
<div ng-dropdown-menu aria-label="Arabic account menu">
<button type="button" ng-disabled="dropdownDemo.disabled">
افتح القائمة
</button>
<menu>
<h3>الحساب</h3>
<button>الملف الشخصي</button>
<button>الإعدادات</button>
<hr />
<button variant="destructive">تسجيل الخروج</button>
</menu>
</div>
</section>
</main>
</body>
</html>
Installation
Install AngularCSS, load its stylesheet, and include the angular.css module in your AngularTS application. See Installation for the complete setup.
This component’s root directive is [ng-dropdown-menu]. Importing the package registers it with the AngularCSS angular.css module; there is no per-component JavaScript registration step.
Anatomy
Directive selectors
ng-dropdown-menu
Semantic structure
Use native elements for authored structure. Component classes are optional visual hooks when an HTML relationship is not specific enough.
API
Attributes and state
| Attribute | Access | Purpose |
|---|---|---|
align | Input | Cross-axis alignment: start, center, or end. |
align-offset | Input | Additional alignment offset in CSS pixels. |
aria-checked | Input/output | ARIA relationship or state. |
aria-controls | Output | ID of the element controlled by a trigger. |
aria-expanded | Output | Open or expanded state exposed to assistive technology. |
aria-haspopup | Input/output | Type of popup controlled by the trigger. |
aria-hidden | Output | Whether generated or collapsed content is hidden from assistive technology. |
aria-labelledby | Output | ID of the element that supplies the accessible name. |
dir | Input | Text and interaction direction: ltr or rtl. |
open | Input | Initial or controlled open state. |
role | Input/output | Explicit semantic role when native HTML does not provide one. |
side | Input | Physical placement: left, top, bottom, or right. |
side-offset | Input | Distance from the trigger in CSS pixels. |
size | Input | Visual size token supported by the component stylesheet. |
tabindex | Input/output | Keyboard focus order for composite descendants. |
Input attributes are read from authored HTML. Output attributes are maintained by AngularCSS for CSS and testing. Input/output attributes may be authored for a controlled initial state and are then synchronized by the directive.
CSS custom properties
| Variable | Purpose |
|---|---|
--dropdown-menu-available-height | Component styling variable. |
DOM events
This component does not emit a component-specific custom event.
Native DOM events continue to work normally. AngularTS event directives such as
ng-click and ng-keydown, plus the data-change model callback, remain application-owned.
Behavior
The directive owns menu disclosure, focus movement, escape handling, and outside-click closure. Command execution and checked values remain application-owned.
AngularCSS does not replace AngularTS interpolation, bindings, structural directives, form controllers, validation, or application state.
Accessibility
Triggers expose popup and expanded state. Arrow keys move among enabled items, Escape closes the menu, and focus returns to the invoking control when appropriate.
Authored accessible names and relationships are preserved. Test the final composition with keyboard navigation and assistive technology because labels and content come from the application.
Customization
Target [ng-dropdown-menu], semantic descendants, component classes, and generated state with ordinary CSS. Keep behavior and accessible state in the TypeScript directive; visual choices belong in the application stylesheet.
Read Customization for layer order, design tokens, state variants, and iframe demo isolation.
Feedback
Was this page helpful?
Glad to hear it! Please tell us how we can improve.
Sorry to hear that. Please tell us how we can improve.