command
Build a searchable command menu with one ng-command root and semantic child
elements identified by command part classes. AngularTS owns query filtering and
command execution. Command follows the rendered result DOM and supplies listbox
relationships, active-descendant navigation, disabled skipping, and
scroll-to-active behavior.
<section ng-command variant="surface" aria-label="Command menu">
<header>
<label>
<span aria-hidden="true"><!-- search icon --></span>
<input
aria-label="Search commands"
ng-model="query"
placeholder="Type a command or search..."
/>
</label>
</header>
<div>
<p>No results found.</p>
<section>
<h2>Suggestions</h2>
<button
type="button"
ng-repeat="command in commands | filter:query"
ng-click="selected=command.label"
>
<span ng-bind="command.label"></span>
<kbd ng-bind="command.shortcut"></kbd>
</button>
</section>
</div>
</section>
Use aria-disabled="true" or native disabled state for unavailable options.
Arrow keys wrap through enabled rendered options; Home and End move to the
boundaries; Enter activates the current option through its ordinary click
handler. Pointer movement updates the same active state.
For a modal palette, place the ng-command root inside a native dialog within
a .dialog wrapper. Use native invoker and close commands to open and close it.
The browser handles modal focus, Escape, configured light dismissal, and focus
restoration. Application
shortcuts such as Ctrl J remain AngularTS ng-keydown expressions.
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 Command</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="commandState={query:'',selected:''}; suggestions=[{label:'Calendar',icon:'calendar',disabled:false},{label:'Search Emoji',icon:'smile',disabled:false},{label:'Calculator',icon:'calculator',disabled:true}]; settings=[{label:'Profile',shortcut:'⌘P',icon:'user'},{label:'Billing',shortcut:'⌘B',icon:'credit-card'},{label:'Settings',shortcut:'⌘S',icon:'settings'}]"
data-example="command-demo"
>
<main class="visual-example">
<section
id="command-demo"
ng-command
variant="surface"
aria-label="Command menu"
>
<header>
<label>
<span aria-hidden="true">
<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<circle cx="11" cy="11" r="8"></circle>
<path d="m21 21-4.3-4.3"></path>
</svg>
</span>
<input
aria-label="Search commands"
placeholder="Type a command or search..."
ng-model="commandState.query"
/>
</label>
</header>
<div>
<p>No results found.</p>
<section>
<h2>Suggestions</h2>
<button
type="button"
ng-repeat="item in suggestions | filter:commandState.query"
ng-attr-aria-disabled="{{ item.disabled }}"
ng-click="commandState.selected=item.label"
>
<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
aria-hidden="true"
>
<circle cx="12" cy="12" r="8"></circle>
<path d="M8 12h8"></path>
</svg>
<span ng-bind="item.label"></span>
</button>
</section>
<hr />
<section>
<h2>Settings</h2>
<button
type="button"
ng-repeat="item in settings | filter:commandState.query"
ng-click="commandState.selected=item.label"
>
<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
aria-hidden="true"
>
<circle cx="12" cy="8" r="4"></circle>
<path d="M4 21a8 8 0 0 1 16 0"></path>
</svg>
<span ng-bind="item.label"></span>
<kbd ng-bind="item.shortcut"></kbd>
</button>
</section>
</div>
</section>
<output class="visually-hidden" aria-live="polite">
Selected:
<span ng-bind="commandState.selected || 'None'"></span>
</output>
</main>
</body>
</html>
Dialog Workflows
Basic, grouped, shortcut-label, and application-owned Ctrl J dialog references are functional packaged examples.
View source
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>AngularCSS Command Dialog 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>
<script src="../../js/command-demo.umd.js"></script>
</head>
<body
ng-app="angular.css"
ng-cloak
ng-init="basic={query:'',selected:''}; grouped={query:'',selected:''}; shortcuts={query:'',selected:''}; shortcutDialog={query:'',selected:''}; suggestions=['Calendar','Search Emoji','Calculator']; settings=[{label:'Profile',shortcut:'⌘P'},{label:'Billing',shortcut:'⌘B'},{label:'Settings',shortcut:'⌘S'}]"
data-example="command-basic command-dialog command-groups command-shortcuts"
>
<main class="command-dialog-workflows visual-example">
<section class="command-dialog-workflow">
<button
type="button"
variant="outline"
aria-controls="basic-command-dialog-content"
commandfor="basic-command-dialog-content"
command="show-modal"
>
Open Menu
</button>
<section id="basic-command-dialog" class="command-dialog-shell dialog">
<dialog
id="basic-command-dialog-content"
closedby="any"
aria-labelledby="basic-command-dialog-title"
aria-describedby="basic-command-dialog-description"
>
<header class="visually-hidden">
<h2 id="basic-command-dialog-title">Command Palette</h2>
<p id="basic-command-dialog-description">
Search for a command to run.
</p>
</header>
<section ng-command aria-label="Basic command menu">
<header>
<label>
<span aria-hidden="true">
<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
>
<circle cx="11" cy="11" r="8"></circle>
<path d="m21 21-4.3-4.3"></path>
</svg>
</span>
<input
aria-label="Search basic commands"
ng-model="basic.query"
placeholder="Type a command or search..."
/>
</label>
</header>
<div>
<p>No results found.</p>
<section>
<h3>Suggestions</h3>
<button
ng-repeat="item in suggestions | filter:basic.query"
type="button"
ng-click="basic.selected=item"
commandfor="basic-command-dialog-content"
command="close"
>
<span ng-bind="item"></span>
</button>
</section>
</div>
</section>
</dialog>
</section>
<output class="command-workflow-output" aria-live="polite">
Basic: <span ng-bind="basic.selected || 'None'"></span>
</output>
</section>
<section class="command-dialog-workflow">
<button
type="button"
variant="outline"
aria-controls="groups-command-dialog-content"
commandfor="groups-command-dialog-content"
command="show-modal"
>
Open Grouped Menu
</button>
<section id="groups-command-dialog" class="command-dialog-shell dialog">
<dialog
id="groups-command-dialog-content"
closedby="any"
aria-labelledby="groups-command-dialog-title"
aria-describedby="groups-command-dialog-description"
>
<header class="visually-hidden">
<h2 id="groups-command-dialog-title">Grouped Command Palette</h2>
<p id="groups-command-dialog-description">
Search grouped application commands.
</p>
</header>
<section ng-command aria-label="Grouped command menu">
<header>
<label>
<span aria-hidden="true">
<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
>
<circle cx="11" cy="11" r="8"></circle>
<path d="m21 21-4.3-4.3"></path>
</svg>
</span>
<input
aria-label="Search grouped commands"
ng-model="grouped.query"
placeholder="Type a command or search..."
/>
</label>
</header>
<div>
<p>No results found.</p>
<section>
<h3>Suggestions</h3>
<button
ng-repeat="item in suggestions | filter:grouped.query"
type="button"
ng-click="grouped.selected=item"
commandfor="groups-command-dialog-content"
command="close"
>
<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
aria-hidden="true"
>
<circle cx="12" cy="12" r="8"></circle>
</svg>
<span ng-bind="item"></span>
</button>
</section>
<hr />
<section>
<h3>Settings</h3>
<button
ng-repeat="item in settings | filter:grouped.query"
type="button"
ng-click="grouped.selected=item.label"
commandfor="groups-command-dialog-content"
command="close"
>
<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
aria-hidden="true"
>
<circle cx="12" cy="8" r="4"></circle>
<path d="M4 21a8 8 0 0 1 16 0"></path>
</svg>
<span ng-bind="item.label"></span>
<kbd ng-bind="item.shortcut"></kbd>
</button>
</section>
</div>
</section>
</dialog>
</section>
<output class="command-workflow-output" aria-live="polite">
Grouped: <span ng-bind="grouped.selected || 'None'"></span>
</output>
</section>
<section class="command-dialog-workflow">
<button
type="button"
variant="outline"
aria-controls="shortcuts-command-dialog-content"
commandfor="shortcuts-command-dialog-content"
command="show-modal"
>
Open Shortcuts
</button>
<section
id="shortcuts-command-dialog"
class="command-dialog-shell dialog"
>
<dialog
id="shortcuts-command-dialog-content"
closedby="any"
aria-labelledby="shortcuts-command-dialog-title"
aria-describedby="shortcuts-command-dialog-description"
>
<header class="visually-hidden">
<h2 id="shortcuts-command-dialog-title">Command Shortcuts</h2>
<p id="shortcuts-command-dialog-description">
Search settings commands.
</p>
</header>
<section ng-command aria-label="Shortcut command menu">
<header>
<label>
<span aria-hidden="true">
<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
>
<circle cx="11" cy="11" r="8"></circle>
<path d="m21 21-4.3-4.3"></path>
</svg>
</span>
<input
aria-label="Search shortcut commands"
ng-model="shortcuts.query"
placeholder="Type a command or search..."
/>
</label>
</header>
<div>
<p>No results found.</p>
<section>
<h3>Settings</h3>
<button
ng-repeat="item in settings | filter:shortcuts.query"
type="button"
ng-click="shortcuts.selected=item.label"
commandfor="shortcuts-command-dialog-content"
command="close"
>
<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
aria-hidden="true"
>
<circle cx="12" cy="8" r="4"></circle>
<path d="M4 21a8 8 0 0 1 16 0"></path>
</svg>
<span ng-bind="item.label"></span>
<kbd ng-bind="item.shortcut"></kbd>
</button>
</section>
</div>
</section>
</dialog>
</section>
<output class="command-workflow-output" aria-live="polite">
Shortcut: <span ng-bind="shortcuts.selected || 'None'"></span>
</output>
</section>
<section class="command-dialog-workflow command-shortcut-workflow">
<p>Press <kbd>Ctrl J</kbd></p>
<section
id="keyboard-command-dialog"
class="command-dialog-shell dialog"
>
<dialog
id="keyboard-command-dialog-content"
closedby="any"
aria-labelledby="keyboard-command-dialog-title"
aria-describedby="keyboard-command-dialog-description"
>
<header class="visually-hidden">
<h2 id="keyboard-command-dialog-title">
Keyboard Command Palette
</h2>
<p id="keyboard-command-dialog-description">
Search for a command to run.
</p>
</header>
<section ng-command aria-label="Keyboard command menu">
<header>
<label>
<span aria-hidden="true">
<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
>
<circle cx="11" cy="11" r="8"></circle>
<path d="m21 21-4.3-4.3"></path>
</svg>
</span>
<input
aria-label="Search keyboard commands"
ng-model="shortcutDialog.query"
placeholder="Type a command or search..."
/>
</label>
</header>
<div>
<p>No results found.</p>
<section>
<h3>Suggestions</h3>
<button
ng-repeat="item in suggestions | filter:shortcutDialog.query"
type="button"
ng-click="shortcutDialog.selected=item"
commandfor="keyboard-command-dialog-content"
command="close"
>
<span ng-bind="item"></span>
</button>
</section>
<hr />
<section>
<h3>Settings</h3>
<button
ng-repeat="item in settings | filter:shortcutDialog.query"
type="button"
ng-click="shortcutDialog.selected=item.label"
commandfor="keyboard-command-dialog-content"
command="close"
>
<span ng-bind="item.label"></span>
<kbd ng-bind="item.shortcut"></kbd>
</button>
</section>
</div>
</section>
</dialog>
</section>
<output class="command-workflow-output" aria-live="polite">
Keyboard:
<span ng-bind="shortcutDialog.selected || 'None'"></span>
</output>
</section>
</main>
</body>
</html>
Scrollable
The full 23-item reference inventory demonstrates the 288px list constraint, keyboard scroll-to-active behavior, filtering, and selection.
View source
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>AngularCSS Command Scrollable</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="scroll={query:'',selected:''}; commandGroups=[{label:'Navigation',items:[{label:'Home',shortcut:'⌘H'},{label:'Inbox',shortcut:'⌘I'},{label:'Documents',shortcut:'⌘D'},{label:'Folders',shortcut:'⌘F'}]},{label:'Actions',items:[{label:'New File',shortcut:'⌘N'},{label:'New Folder',shortcut:'⇧⌘N'},{label:'Copy',shortcut:'⌘C'},{label:'Cut',shortcut:'⌘X'},{label:'Paste',shortcut:'⌘V'},{label:'Delete',shortcut:'⌫'}]},{label:'View',items:[{label:'Grid View',shortcut:''},{label:'List View',shortcut:''},{label:'Zoom In',shortcut:'⌘+'},{label:'Zoom Out',shortcut:'⌘-'}]},{label:'Account',items:[{label:'Profile',shortcut:'⌘P'},{label:'Billing',shortcut:'⌘B'},{label:'Settings',shortcut:'⌘S'},{label:'Notifications',shortcut:''},{label:'Help & Support',shortcut:''}]},{label:'Tools',items:[{label:'Calculator',shortcut:''},{label:'Calendar',shortcut:''},{label:'Image Editor',shortcut:''},{label:'Code Editor',shortcut:''}]}]"
data-example="command-scrollable"
>
<main class="command-scrollable-demo visual-example">
<button
type="button"
variant="outline"
aria-controls="scrollable-command-dialog-content"
commandfor="scrollable-command-dialog-content"
command="show-modal"
>
Open Menu
</button>
<section
id="scrollable-command-dialog"
class="command-dialog-shell dialog"
>
<dialog
id="scrollable-command-dialog-content"
closedby="any"
aria-labelledby="scrollable-command-dialog-title"
aria-describedby="scrollable-command-dialog-description"
>
<header class="visually-hidden">
<h2 id="scrollable-command-dialog-title">
Scrollable Command Palette
</h2>
<p id="scrollable-command-dialog-description">
Search all application commands.
</p>
</header>
<section ng-command aria-label="Scrollable command menu">
<header>
<label>
<span aria-hidden="true">
<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
>
<circle cx="11" cy="11" r="8"></circle>
<path d="m21 21-4.3-4.3"></path>
</svg>
</span>
<input
aria-label="Search all commands"
ng-model="scroll.query"
placeholder="Type a command or search..."
/>
</label>
</header>
<div>
<p>No results found.</p>
<section ng-repeat="group in commandGroups">
<h3 ng-bind="group.label"></h3>
<button
ng-repeat="item in group.items | filter:scroll.query"
type="button"
ng-click="scroll.selected=item.label"
commandfor="scrollable-command-dialog-content"
command="close"
>
<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
aria-hidden="true"
>
<path d="M6 2h9l3 3v17H6z"></path>
<path d="M14 2v4h4"></path>
</svg>
<span ng-bind="item.label"></span>
<kbd ng-if="item.shortcut" ng-bind="item.shortcut"></kbd>
</button>
<hr />
</section>
</div>
</section>
</dialog>
</section>
<output class="command-workflow-output" aria-live="polite">
Selected: <span ng-bind="scroll.selected || 'None'"></span>
</output>
</main>
</body>
</html>
RTL
Logical icon, text, shortcut, active-item, and keyboard order are preserved in an Arabic command surface.
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 Command 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="rtlCommand={query:'',selected:''}; rtlSuggestions=[{label:'التقويم',disabled:false},{label:'البحث عن الرموز التعبيرية',disabled:false},{label:'الآلة الحاسبة',disabled:true}]; rtlSettings=[{label:'الملف الشخصي',shortcut:'⌘P'},{label:'الفوترة',shortcut:'⌘B'},{label:'الإعدادات',shortcut:'⌘S'}]"
data-example="command-rtl"
>
<main class="visual-example">
<section
id="rtl-command"
ng-command
variant="surface"
aria-label="قائمة الأوامر"
>
<header>
<label>
<span aria-hidden="true">
<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
>
<circle cx="11" cy="11" r="8"></circle>
<path d="m21 21-4.3-4.3"></path>
</svg>
</span>
<input
dir="rtl"
aria-label="البحث في الأوامر"
ng-model="rtlCommand.query"
placeholder="اكتب أمرًا أو ابحث..."
/>
</label>
</header>
<div>
<p>لم يتم العثور على نتائج.</p>
<section>
<h2>اقتراحات</h2>
<button
type="button"
ng-repeat="item in rtlSuggestions | filter:rtlCommand.query"
ng-attr-aria-disabled="{{ item.disabled }}"
ng-click="rtlCommand.selected=item.label"
>
<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
aria-hidden="true"
>
<circle cx="12" cy="12" r="8"></circle>
</svg>
<span ng-bind="item.label"></span>
</button>
</section>
<hr />
<section>
<h2>الإعدادات</h2>
<button
type="button"
ng-repeat="item in rtlSettings | filter:rtlCommand.query"
ng-click="rtlCommand.selected=item.label"
>
<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
aria-hidden="true"
>
<circle cx="12" cy="8" r="4"></circle>
<path d="M4 21a8 8 0 0 1 16 0"></path>
</svg>
<span ng-bind="item.label"></span>
<kbd ng-bind="item.shortcut"></kbd>
</button>
</section>
</div>
</section>
<output class="visually-hidden" aria-live="polite">
المحدد: <span ng-bind="rtlCommand.selected || 'لا شيء'"></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-command]. Importing the package registers it with the AngularCSS angular.css module; there is no per-component JavaScript registration step.
Anatomy
Directive selectors
ng-command
Semantic structure
A command root requires one input and a result container. The root directive inspects semantic headers, sections, headings, buttons, separators, and keyboard hints; no child directives or anatomy classes are required. Compose modal palettes from Dialog.
API
Attributes and state
| Attribute | Access | Purpose |
|---|---|---|
aria-activedescendant | Output | ID of the active option while focus remains on the composite control. |
aria-autocomplete | Input/output | How a text control presents completion suggestions. |
aria-controls | Output | ID of the element controlled by a trigger. |
aria-disabled | Input/output | Semantic disabled state. |
aria-expanded | Output | Open or expanded state exposed to assistive technology. |
aria-hidden | Input/output | Whether generated or collapsed content is hidden from assistive technology. |
aria-labelledby | Output | ID of the element that supplies the accessible name. |
aria-orientation | Output | Interaction axis exposed to assistive technology. |
aria-selected | Input/output | Selected item state. |
dir | Input | Text and interaction direction: ltr or rtl. |
disabled | Input | Disables native or component interaction. |
hidden | Input | Native visibility state observed when finding available items. |
role | Output | Explicit semantic role when native HTML does not provide one. |
tabindex | 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
This directive does not write component-specific CSS custom properties.
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 follows the application-rendered result DOM and owns active-descendant navigation, enabled-option wrapping and boundaries, pointer synchronization, Enter activation through the authored click handler, semantic group and empty state, and scroll-to-active behavior. AngularTS remains responsible for query filtering, command execution, result data, structural bindings, and application keyboard shortcuts. Dialog remains responsible for modal disclosure, focus trapping, Escape, outside dismissal, and focus restoration.
AngularCSS does not replace AngularTS interpolation, bindings, structural directives, form controllers, validation, or application state.
Accessibility
Give the search input and command surface useful accessible names. The input is connected to a listbox through aria-controls and aria-activedescendant; rendered options expose selected and disabled state, labeled groups retain group relationships, separators are decorative structure, and shortcut labels are hidden from repeated announcement. A modal composition must include an accessible Dialog title and description.
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-command], 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.