dropdown-menu

Menu opened from a trigger button

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

AttributeAccessPurpose
alignInputCross-axis alignment: start, center, or end.
align-offsetInputAdditional alignment offset in CSS pixels.
aria-checkedInput/outputARIA relationship or state.
aria-controlsOutputID of the element controlled by a trigger.
aria-expandedOutputOpen or expanded state exposed to assistive technology.
aria-haspopupInput/outputType of popup controlled by the trigger.
aria-hiddenOutputWhether generated or collapsed content is hidden from assistive technology.
aria-labelledbyOutputID of the element that supplies the accessible name.
dirInputText and interaction direction: ltr or rtl.
openInputInitial or controlled open state.
roleInput/outputExplicit semantic role when native HTML does not provide one.
sideInputPhysical placement: left, top, bottom, or right.
side-offsetInputDistance from the trigger in CSS pixels.
sizeInputVisual size token supported by the component stylesheet.
tabindexInput/outputKeyboard 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

VariablePurpose
--dropdown-menu-available-heightComponent 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.