context-menu

Context menu surface and items

Use a semantic trigger and content surface. AngularCSS owns right-click and keyboard disclosure, pointer placement, menu focus, and submenu navigation; AngularTS owns actions and checkbox or radio values.

<div ng-context-menu>
  <div>Right click here</div>
  <menu aria-label="Browser actions">
    <button ng-click="reload()">Reload</button>
    <button
      aria-checked="{{ showBookmarks }}"
      ng-click="showBookmarks=!showBookmarks"
    >
      Show Bookmarks Bar
    </button>
  </menu>
</div>

Ordinary click is left to the authored trigger. Right-click, Shift F10, and the Context Menu key open this component.

Basic And Submenu

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 Context 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="lastAction='None'"
    data-example="context-menu-basic context-menu-demo context-menu-submenu"
  >
    <main class="visual-example">
      <div id="context-menu-demo" ng-context-menu>
        <div>Right click here</div>
        <menu aria-label="Browser actions">
          <section>
            <button ng-click="lastAction='Back'">
              Back
              <kbd>Alt+Left</kbd>
            </button>
            <button disabled>
              Forward
              <kbd>Alt+Right</kbd>
            </button>
            <button ng-click="lastAction='Reload'">
              Reload
              <kbd>Ctrl+R</kbd>
            </button>
          </section>
          <hr />
          <section>
            <button ng-click="lastAction='Save'">
              Save
              <kbd>Ctrl+S</kbd>
            </button>
          </section>
          <details>
            <summary>More Tools</summary>
            <menu>
              <section>
                <button ng-click="lastAction='Save Page'">Save Page...</button>
                <button ng-click="lastAction='Create Shortcut'">
                  Create Shortcut...
                </button>
                <button ng-click="lastAction='Name Window'">
                  Name Window...
                </button>
              </section>
              <hr />
              <button ng-click="lastAction='Developer Tools'">
                Developer Tools
              </button>
            </menu>
          </details>
          <hr />
          <button variant="destructive" ng-click="lastAction='Delete'">
            Delete
          </button>
        </menu>
      </div>
      <output class="context-menu-output" aria-live="polite">
        Last action: <span ng-bind="lastAction"></span>
      </output>
    </main>
  </body>
</html>

Content And State

Icons, destructive actions, groups, shortcuts, checkbox values, and radio values are functional packaged scenarios.

View source
<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>AngularCSS Context 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="action='None'; view={bookmarks:true,urls:false}; person='pedro'; theme='light'"
    data-example="context-menu-checkboxes context-menu-destructive context-menu-groups context-menu-icons context-menu-radio context-menu-shortcuts"
  >
    <main class="context-menu-workflow-grid visual-example">
      <section class="context-menu-workflow" aria-labelledby="icons-title">
        <h2 id="icons-title">Icons and destructive action</h2>
        <div ng-context-menu>
          <div>Right click here</div>
          <menu aria-label="Editing actions">
            <section>
              <button ng-click="action='Copy'">
                <svg
                  viewBox="0 0 24 24"
                  fill="none"
                  stroke="currentColor"
                  stroke-width="2"
                  aria-hidden="true"
                >
                  <rect x="9" y="9" width="11" height="11" rx="2"></rect>
                  <path
                    d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"
                  ></path>
                </svg>
                Copy
              </button>
              <button ng-click="action='Cut'">
                <svg
                  viewBox="0 0 24 24"
                  fill="none"
                  stroke="currentColor"
                  stroke-width="2"
                  aria-hidden="true"
                >
                  <circle cx="6" cy="6" r="3"></circle>
                  <circle cx="6" cy="18" r="3"></circle>
                  <path d="m20 4-8.5 8.5"></path>
                  <path d="m14.5 14.5 5.5 5.5"></path>
                </svg>
                Cut
              </button>
              <button ng-click="action='Paste'">
                <svg
                  viewBox="0 0 24 24"
                  fill="none"
                  stroke="currentColor"
                  stroke-width="2"
                  aria-hidden="true"
                >
                  <rect x="5" y="4" width="14" height="18" rx="2"></rect>
                  <path d="M9 4.5V2h6v2.5"></path>
                </svg>
                Paste
              </button>
            </section>
            <hr />
            <button variant="destructive" ng-click="action='Delete'">
              <svg
                viewBox="0 0 24 24"
                fill="none"
                stroke="currentColor"
                stroke-width="2"
                aria-hidden="true"
              >
                <path d="M3 6h18"></path>
                <path d="M8 6V4h8v2"></path>
                <path d="m19 6-1 15H6L5 6"></path>
              </svg>
              Delete
            </button>
          </menu>
        </div>
        <output>Action: <span ng-bind="action"></span></output>
      </section>

      <section class="context-menu-workflow" aria-labelledby="groups-title">
        <h2 id="groups-title">Groups and shortcuts</h2>
        <div ng-context-menu>
          <div>Right click here</div>
          <menu aria-label="File actions">
            <section>
              <h3>File</h3>
              <button>New File <kbd>Ctrl+N</kbd></button>
              <button>Open File <kbd>Ctrl+O</kbd></button>
              <button>Save <kbd>Ctrl+S</kbd></button>
            </section>
            <hr />
            <section>
              <h3>Edit</h3>
              <button>Undo <kbd>Ctrl+Z</kbd></button>
              <button disabled>Redo <kbd>Shift+Ctrl+Z</kbd></button>
            </section>
          </menu>
        </div>
      </section>

      <section class="context-menu-workflow" aria-labelledby="checkbox-title">
        <h2 id="checkbox-title">Checkboxes</h2>
        <div ng-context-menu>
          <div>Right click here</div>
          <menu aria-label="View options">
            <button
              aria-checked="{{ view.bookmarks }}"
              ng-click="view.bookmarks=!view.bookmarks"
            >
              Show Bookmarks Bar
            </button>
            <button
              aria-checked="{{ view.urls }}"
              ng-click="view.urls=!view.urls"
            >
              Show Full URLs
            </button>
          </menu>
        </div>
        <output>
          Bookmarks: <span ng-bind="view.bookmarks ? 'On' : 'Off'"></span>
        </output>
      </section>

      <section class="context-menu-workflow" aria-labelledby="radio-title">
        <h2 id="radio-title">Radio groups</h2>
        <div ng-context-menu>
          <div>Right click here</div>
          <menu aria-label="Profile and theme">
            <section>
              <h3>People</h3>
              <fieldset aria-label="People">
                <button
                  aria-checked="{{ person === 'pedro' }}"
                  ng-click="person='pedro'"
                >
                  Pedro Duarte
                </button>
                <button
                  aria-checked="{{ person === 'colm' }}"
                  ng-click="person='colm'"
                >
                  Colm Tuite
                </button>
              </fieldset>
            </section>
            <hr />
            <section>
              <h3>Theme</h3>
              <fieldset aria-label="Theme">
                <button
                  aria-checked="{{ theme === 'light' }}"
                  ng-click="theme='light'"
                >
                  Light
                </button>
                <button
                  aria-checked="{{ theme === 'dark' }}"
                  ng-click="theme='dark'"
                >
                  Dark
                </button>
                <button
                  aria-checked="{{ theme === 'system' }}"
                  ng-click="theme='system'"
                >
                  System
                </button>
              </fieldset>
            </section>
          </menu>
        </div>
        <output>
          Person: <span ng-bind="person"></span>; Theme:
          <span ng-bind="theme"></span>
        </output>
      </section>
    </main>
  </body>
</html>

Placement

The six physical and logical side options anchor to the invocation point and remain constrained to the viewport.

View source
<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>AngularCSS Context Menu Sides</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 data-example="context-menu-sides">
    <main class="context-menu-sides visual-example">
      <div
        ng-context-menu
        ng-repeat="side in ['inline-start','left','top','bottom','right','inline-end']"
      >
        <div ng-bind="side"></div>
        <menu side="{{ side }}" aria-label="{{ side }} actions">
          <button>Back</button>
          <button>Forward</button>
          <button>Reload</button>
        </menu>
      </div>
    </main>
  </body>
</html>

Right To Left

View source
<!doctype html>
<html lang="ar" dir="rtl">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>AngularCSS Context Menu Rtl</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="lastAction='لا شيء'"
    data-example="context-menu-rtl"
  >
    <main class="visual-example">
      <div ng-context-menu>
        <div>انقر بزر الفأرة الأيمن هنا</div>
        <menu aria-label="إجراءات المتصفح">
          <section>
            <button ng-click="lastAction='رجوع'">
              رجوع
              <kbd>Alt+Right</kbd>
            </button>
            <button disabled>
              للأمام
              <kbd>Alt+Left</kbd>
            </button>
            <button ng-click="lastAction='إعادة تحميل'">
              إعادة تحميل
              <kbd>Ctrl+R</kbd>
            </button>
          </section>
          <hr />
          <details>
            <summary>المزيد من الأدوات</summary>
            <menu>
              <button ng-click="lastAction='حفظ الصفحة'">حفظ الصفحة...</button>
              <button ng-click="lastAction='أدوات المطور'">أدوات المطور</button>
            </menu>
          </details>
        </menu>
      </div>
      <output class="context-menu-output" aria-live="polite">
        الإجراء الأخير: <span ng-bind="lastAction"></span>
      </output>
    </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-context-menu]. Importing the package registers it with the AngularCSS angular.css module; there is no per-component JavaScript registration step.

Anatomy

Directive selectors

  • ng-context-menu

Semantic structure

A context menu root requires one focusable trigger and one menu. The root directive inspects semantic sections, fieldsets, buttons, separators, keyboard hints, and nested details; no child directives or anatomy classes are required.

API

Attributes and state

AttributeAccessPurpose
alignInput/outputCross-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-disabledInput/outputSemantic disabled state.
aria-expandedOutputOpen or expanded state exposed to assistive technology.
aria-haspopupOutputType of popup controlled by the trigger.
aria-hiddenOutputWhether generated or collapsed content is hidden from assistive technology.
dirInputText and interaction direction: ltr or rtl.
disabledInputDisables native or component interaction.
openInputInitial or controlled open state.
roleOutputExplicit semantic role when native HTML does not provide one.
sideInput/outputPhysical placement: left, top, bottom, or right.
side-offsetInputDistance from the invocation point in CSS pixels.
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
--context-menu-available-heightComponent styling variable.
--context-menu-leftComponent styling variable.
--context-menu-topComponent styling variable.

DOM events

  • angularcss:context-menu-select

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 right-click and keyboard disclosure, cursor-relative side placement with viewport collision constraints, menu and submenu focus movement, disabled-item skipping, Escape and outside dismissal, direction-aware submenu keys, semantic roles, and open-state reflection. AngularTS remains responsible for command execution, checkbox and radio values, controlled application state, and structural rendering.

AngularCSS does not replace AngularTS interpolation, bindings, structural directives, form controllers, validation, or application state.

Accessibility

Give the trigger and menu useful accessible names. The trigger exposes aria-haspopup, aria-controls, and expanded state; items receive menuitem, menuitemcheckbox, or menuitemradio roles; groups and separators remain semantic; disabled items are skipped. Shift+F10 or the Context Menu key opens from the keyboard, arrow keys move focus, logical submenu keys follow text direction, and Escape restores focus.

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-context-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.