combobox

Search input with selectable options

Compose a searchable listbox with one ng-combobox root and semantic child elements identified by combobox part classes. AngularTS ng-model, filters, and event expressions remain the source of truth for query and selected-value state. AngularCSS supplies popup disclosure, active-descendant navigation, disabled handling, collision placement, and visual state.

<div
  ng-combobox
  ng-on-angularcss:combobox-select="selected=$event.detail.value; query=selected"
>
  <header>
    <input
      aria-label="Framework"
      ng-model="query"
      placeholder="Select a framework"
    />
    <button type="button" aria-label="Show frameworks" value="toggle"></button>
  </header>
  <aside aria-label="Framework options">
    <p>No items found.</p>
    <div>
      <ul>
        <li
          ng-repeat="framework in frameworks | filter:query"
          data-value="{{ framework }}"
          ng-attr-aria-selected="{{ selected === framework }}"
        >
          {{ framework }}
        </li>
      </ul>
    </div>
  </aside>
</div>

Add auto-highlight when opening or filtering should highlight the first enabled result. Without it, the popup opens without an active option until the user presses an arrow key. Bind angularcss:combobox-open-change when the root uses controlled open="{{ state.open }}" state.

For multiple selection, add multiple, render chips from AngularTS state, and bind each option’s aria-selected value. Selection events include multiple: true; angularcss:combobox-remove-last only signals Backspace on an empty chip input. AngularCSS never replaces the application’s collection.

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 Combobox</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="frameworks=['Next.js','SvelteKit','Nuxt.js','Remix','Astro']; basic={query:'',selected:''}; automatic={query:'',selected:''}"
    data-example="combobox-auto-highlight combobox-basic combobox-demo"
  >
    <main class="visual-example">
      <div
        id="basic-combobox"
        ng-combobox
        ng-on-angularcss:combobox-select="basic.selected=$event.detail.value; basic.query=$event.detail.value"
      >
        <header>
          <input
            aria-label="Framework"
            ng-model="basic.query"
            placeholder="Select a framework"
          />
          <button
            type="button"
            aria-label="Show frameworks"
            value="toggle"
          ></button>
        </header>
        <aside aria-label="Framework options">
          <p>No items found.</p>
          <div>
            <ul>
              <li
                ng-repeat="framework in frameworks | filter:basic.query"
                ng-attr-aria-selected="{{ basic.selected === framework }}"
              >
                {{ framework }}
              </li>
            </ul>
          </div>
        </aside>
      </div>

      <div
        id="auto-combobox"
        ng-combobox
        auto-highlight
        ng-on-angularcss:combobox-select="automatic.selected=$event.detail.value; automatic.query=$event.detail.value"
      >
        <header>
          <input
            aria-label="Auto-highlight framework"
            ng-model="automatic.query"
            placeholder="Select a framework"
          />
          <button
            type="button"
            aria-label="Show auto-highlight frameworks"
            value="toggle"
          ></button>
        </header>
        <aside aria-label="Auto-highlight framework options">
          <p>No items found.</p>
          <div>
            <ul>
              <li
                ng-repeat="framework in frameworks | filter:automatic.query"
                ng-attr-aria-selected="{{ automatic.selected === framework }}"
              >
                {{ framework }}
              </li>
            </ul>
          </div>
        </aside>
      </div>

      <output class="visually-hidden" aria-live="polite">
        Basic: <span ng-bind="basic.selected || 'none'"></span>. Automatic:
        <span ng-bind="automatic.selected || 'none'"></span>.
      </output>
    </main>
  </body>
</html>

Reference Workflows

Clear, disabled, grouped, input-addon, invalid, and separate popup-trigger compositions are functional in this artifact. Put the selected-value label inside the direct toggle button. When a composition needs a compact button inside an input shell, use a direct native button.

View source
<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>AngularCSS Combobox 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="frameworks=['Next.js','SvelteKit','Nuxt.js','Remix','Astro']; clearState={query:'Next.js',selected:'Next.js'}; grouped={query:'',selected:''}; invalidState={query:'',selected:''}; inputGroupState={query:'',selected:''}; popup={query:'',selected:'Select country'}; timezoneGroups=[{value:'Americas',items:['(GMT-5) New York','(GMT-8) Los Angeles','(GMT-6) Chicago','(GMT-5) Toronto','(GMT-8) Vancouver','(GMT-3) São Paulo']},{value:'Europe',items:['(GMT+0) London','(GMT+1) Paris','(GMT+1) Berlin','(GMT+1) Rome','(GMT+1) Madrid','(GMT+1) Amsterdam']},{value:'Asia/Pacific',items:['(GMT+9) Tokyo','(GMT+8) Shanghai','(GMT+8) Singapore','(GMT+4) Dubai','(GMT+11) Sydney','(GMT+9) Seoul']}]; countries=['Argentina','Australia','Brazil','Canada','China','Colombia','Egypt','France','Germany','Italy','Japan','Kenya','Mexico','New Zealand','Nigeria','South Africa','South Korea','United Kingdom','United States']"
    data-example="combobox-clear combobox-disabled combobox-groups combobox-input-group combobox-invalid combobox-popup"
  >
    <main class="combobox-workflows visual-example">
      <section class="combobox-workflow">
        <div
          id="clear-combobox"
          ng-combobox
          ng-on-angularcss:combobox-select="clearState.selected=$event.detail.value; clearState.query=$event.detail.value"
          ng-on-angularcss:combobox-clear="clearState.selected=''; clearState.query=''"
        >
          <header>
            <input
              aria-label="Clearable framework"
              ng-model="clearState.query"
              placeholder="Select a framework"
            />
            <button type="button" value="clear"></button>
          </header>
          <aside aria-label="Clearable framework options">
            <p>No items found.</p>
            <div>
              <ul>
                <li
                  ng-repeat="framework in frameworks | filter:clearState.query"
                  data-value="{{ framework }}"
                  ng-attr-aria-selected="{{ clearState.selected === framework }}"
                >
                  {{ framework }}
                </li>
              </ul>
            </div>
          </aside>
        </div>
      </section>

      <section class="combobox-workflow">
        <div id="disabled-combobox" ng-combobox>
          <header>
            <input
              aria-label="Disabled framework"
              placeholder="Select a framework"
              disabled
            />
            <button
              type="button"
              aria-label="Show disabled frameworks"
              disabled
              value="toggle"
            ></button>
          </header>
          <aside aria-label="Disabled framework options">
            <div>
              <ul>
                <li
                  ng-repeat="framework in frameworks"
                  data-value="{{ framework }}"
                >
                  {{ framework }}
                </li>
              </ul>
            </div>
          </aside>
        </div>
      </section>

      <section class="combobox-workflow">
        <div
          id="groups-combobox"
          ng-combobox
          ng-on-angularcss:combobox-select="grouped.selected=$event.detail.value; grouped.query=$event.detail.value"
        >
          <header>
            <input
              aria-label="Grouped timezone"
              ng-model="grouped.query"
              placeholder="Select a timezone"
            />
            <button
              type="button"
              aria-label="Show grouped timezones"
              value="toggle"
            ></button>
          </header>
          <aside aria-label="Grouped timezone options">
            <p>No timezones found.</p>
            <div>
              <section
                ng-repeat="group in timezoneGroups"
                ng-if="(group.items | filter:grouped.query).length"
              >
                <h3>{{ group.value }}</h3>
                <ul>
                  <li
                    ng-repeat="zone in group.items | filter:grouped.query"
                    data-value="{{ zone }}"
                    ng-attr-aria-selected="{{ grouped.selected === zone }}"
                  >
                    {{ zone }}
                  </li>
                </ul>
                <hr />
              </section>
            </div>
          </aside>
        </div>
      </section>

      <section class="combobox-workflow">
        <div
          id="input-group-combobox"
          ng-combobox
          ng-on-angularcss:combobox-select="inputGroupState.selected=$event.detail.value; inputGroupState.query=$event.detail.value"
        >
          <header>
            <span class="combobox-globe" aria-hidden="true"></span>
            <input
              aria-label="Timezone with icon"
              ng-model="inputGroupState.query"
              placeholder="Select a timezone"
            />
            <button
              type="button"
              aria-label="Show icon timezones"
              value="toggle"
            ></button>
          </header>
          <aside
            aria-label="Icon timezone options"
            style="--combobox-options-width: 15rem"
          >
            <p>No timezones found.</p>
            <div>
              <section
                ng-repeat="group in timezoneGroups"
                ng-if="(group.items | filter:inputGroupState.query).length"
              >
                <h3>{{ group.value }}</h3>
                <ul>
                  <li
                    ng-repeat="zone in group.items | filter:inputGroupState.query"
                    data-value="{{ zone }}"
                  >
                    {{ zone }}
                  </li>
                </ul>
              </section>
            </div>
          </aside>
        </div>
      </section>

      <section class="combobox-workflow">
        <div
          id="invalid-combobox"
          ng-combobox
          ng-on-angularcss:combobox-select="invalidState.selected=$event.detail.value; invalidState.query=$event.detail.value"
        >
          <header>
            <input
              aria-label="Invalid framework"
              aria-invalid="true"
              ng-model="invalidState.query"
              placeholder="Select a framework"
            />
            <button
              type="button"
              aria-label="Show invalid frameworks"
              value="toggle"
            ></button>
          </header>
          <aside aria-label="Invalid framework options">
            <p>No items found.</p>
            <div>
              <ul>
                <li
                  ng-repeat="framework in frameworks | filter:invalidState.query"
                  data-value="{{ framework }}"
                >
                  {{ framework }}
                </li>
              </ul>
            </div>
          </aside>
        </div>
      </section>

      <section class="combobox-workflow combobox-popup-workflow">
        <div
          id="popup-combobox"
          ng-combobox
          ng-on-angularcss:combobox-select="popup.selected=$event.detail.value; popup.query=''"
        >
          <button
            type="button"
            variant="outline"
            aria-label="Country"
            value="toggle"
          >
            <span ng-bind="popup.selected"></span>
          </button>
          <aside aria-label="Country options">
            <header>
              <input
                aria-label="Search countries"
                ng-model="popup.query"
                placeholder="Search"
              />
            </header>
            <p>No items found.</p>
            <div>
              <ul>
                <li
                  ng-repeat="country in countries | filter:popup.query"
                  data-value="{{ country }}"
                  ng-attr-aria-selected="{{ popup.selected === country }}"
                >
                  {{ country }}
                </li>
              </ul>
            </div>
          </aside>
        </div>
      </section>

      <output class="visually-hidden" aria-live="polite">
        Clear: <span ng-bind="clearState.selected || 'none'"></span>. Grouped:
        <span ng-bind="grouped.selected || 'none'"></span>. Popup:
        <span ng-bind="popup.selected"></span>.
      </output>
    </main>
  </body>
</html>

Custom, Multiple, And RTL

Custom option content and chip collections remain ordinary authored HTML and AngularTS application state.

View source
<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>AngularCSS Combobox Compositions</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="frameworks=['Next.js','SvelteKit','Nuxt.js','Remix','Astro']; custom={query:'',selected:''}; multi={query:'',next:true,svelte:false,nuxt:false,remix:false,astro:false}; rtl={query:'',technology:true,design:false,business:false,marketing:false,education:false,health:false}; countries=[{code:'ar',label:'Argentina',continent:'South America'},{code:'au',label:'Australia',continent:'Oceania'},{code:'br',label:'Brazil',continent:'South America'},{code:'ca',label:'Canada',continent:'North America'},{code:'cn',label:'China',continent:'Asia'},{code:'co',label:'Colombia',continent:'South America'},{code:'eg',label:'Egypt',continent:'Africa'},{code:'fr',label:'France',continent:'Europe'},{code:'de',label:'Germany',continent:'Europe'},{code:'it',label:'Italy',continent:'Europe'},{code:'jp',label:'Japan',continent:'Asia'},{code:'ke',label:'Kenya',continent:'Africa'},{code:'mx',label:'Mexico',continent:'North America'},{code:'nz',label:'New Zealand',continent:'Oceania'},{code:'ng',label:'Nigeria',continent:'Africa'},{code:'za',label:'South Africa',continent:'Africa'},{code:'kr',label:'South Korea',continent:'Asia'},{code:'gb',label:'United Kingdom',continent:'Europe'},{code:'us',label:'United States',continent:'North America'}]; categories=['التكنولوجيا','التصميم','الأعمال','التسويق','التعليم','الصحة']"
    data-example="combobox-custom combobox-multiple combobox-rtl"
  >
    <main class="combobox-compositions visual-example">
      <section class="combobox-composition">
        <div
          id="custom-combobox"
          ng-combobox
          ng-on-angularcss:combobox-select="custom.selected=$event.detail.value; custom.query=$event.detail.value"
        >
          <header>
            <input
              aria-label="Country with details"
              ng-model="custom.query"
              placeholder="Search countries..."
            />
            <button
              type="button"
              aria-label="Show detailed countries"
              value="toggle"
            ></button>
          </header>
          <aside aria-label="Detailed country options">
            <p>No countries found.</p>
            <div>
              <ul>
                <li
                  ng-repeat="country in countries | filter:custom.query"
                  data-value="{{ country.label }}"
                  ng-attr-aria-selected="{{ custom.selected === country.label }}"
                >
                  <article size="xs" class="combobox-custom-item item">
                    <section>
                      <h3>{{ country.label }}</h3>
                      <p>{{ country.continent }} ({{ country.code }})</p>
                    </section>
                  </article>
                </li>
              </ul>
            </div>
          </aside>
        </div>
      </section>

      <section class="combobox-composition">
        <div
          id="multiple-combobox"
          ng-combobox
          multiple
          auto-highlight
          ng-on-angularcss:combobox-select="$event.detail.value === 'Next.js' ? multi.next=!multi.next : $event.detail.value === 'SvelteKit' ? multi.svelte=!multi.svelte : $event.detail.value === 'Nuxt.js' ? multi.nuxt=!multi.nuxt : $event.detail.value === 'Remix' ? multi.remix=!multi.remix : multi.astro=!multi.astro"
          ng-on-angularcss:combobox-remove-last="multi.astro ? multi.astro=false : multi.remix ? multi.remix=false : multi.nuxt ? multi.nuxt=false : multi.svelte ? multi.svelte=false : multi.next=false"
        >
          <fieldset>
            <span ng-if="multi.next" animate>
              Next.js
              <button
                type="button"
                aria-label="Remove Next.js"
                ng-click="multi.next=false"
                value="remove"
              ></button>
            </span>
            <span ng-if="multi.svelte" animate>
              SvelteKit
              <button
                type="button"
                aria-label="Remove SvelteKit"
                ng-click="multi.svelte=false"
                value="remove"
              ></button>
            </span>
            <span ng-if="multi.nuxt" animate>
              Nuxt.js
              <button
                type="button"
                aria-label="Remove Nuxt.js"
                ng-click="multi.nuxt=false"
                value="remove"
              ></button>
            </span>
            <span ng-if="multi.remix" animate>
              Remix
              <button
                type="button"
                aria-label="Remove Remix"
                ng-click="multi.remix=false"
                value="remove"
              ></button>
            </span>
            <span ng-if="multi.astro" animate>
              Astro
              <button
                type="button"
                aria-label="Remove Astro"
                ng-click="multi.astro=false"
                value="remove"
              ></button>
            </span>
            <input
              aria-label="Frameworks"
              ng-model="multi.query"
              placeholder="Add framework"
            />
          </fieldset>
          <aside aria-label="Multiple framework options">
            <p>No items found.</p>
            <div>
              <ul>
                <li
                  ng-repeat="framework in frameworks | filter:multi.query"
                  data-value="{{ framework }}"
                  ng-attr-aria-selected="{{ framework === 'Next.js' ? multi.next : framework === 'SvelteKit' ? multi.svelte : framework === 'Nuxt.js' ? multi.nuxt : framework === 'Remix' ? multi.remix : multi.astro }}"
                >
                  {{ framework }}
                </li>
              </ul>
            </div>
          </aside>
        </div>
      </section>

      <section
        class="combobox-composition combobox-rtl-composition"
        lang="ar"
        dir="rtl"
      >
        <div class="field">
          <label for="rtl-combobox-input">الفئات</label>
          <div
            id="rtl-combobox"
            ng-combobox
            multiple
            auto-highlight
            ng-on-angularcss:combobox-select="$event.detail.value === 'التكنولوجيا' ? rtl.technology=!rtl.technology : $event.detail.value === 'التصميم' ? rtl.design=!rtl.design : $event.detail.value === 'الأعمال' ? rtl.business=!rtl.business : $event.detail.value === 'التسويق' ? rtl.marketing=!rtl.marketing : $event.detail.value === 'التعليم' ? rtl.education=!rtl.education : rtl.health=!rtl.health"
            ng-on-angularcss:combobox-remove-last="rtl.health ? rtl.health=false : rtl.education ? rtl.education=false : rtl.marketing ? rtl.marketing=false : rtl.business ? rtl.business=false : rtl.design ? rtl.design=false : rtl.technology=false"
          >
            <fieldset>
              <span ng-if="rtl.technology" animate>
                التكنولوجيا
                <button
                  type="button"
                  aria-label="إزالة التكنولوجيا"
                  ng-click="rtl.technology=false"
                  value="remove"
                ></button>
              </span>
              <span ng-if="rtl.design" animate>
                التصميم
                <button
                  type="button"
                  aria-label="إزالة التصميم"
                  ng-click="rtl.design=false"
                  value="remove"
                ></button>
              </span>
              <span ng-if="rtl.business" animate>
                الأعمال
                <button
                  type="button"
                  aria-label="إزالة الأعمال"
                  ng-click="rtl.business=false"
                  value="remove"
                ></button>
              </span>
              <span ng-if="rtl.marketing" animate>
                التسويق
                <button
                  type="button"
                  aria-label="إزالة التسويق"
                  ng-click="rtl.marketing=false"
                  value="remove"
                ></button>
              </span>
              <span ng-if="rtl.education" animate>
                التعليم
                <button
                  type="button"
                  aria-label="إزالة التعليم"
                  ng-click="rtl.education=false"
                  value="remove"
                ></button>
              </span>
              <span ng-if="rtl.health" animate>
                الصحة
                <button
                  type="button"
                  aria-label="إزالة الصحة"
                  ng-click="rtl.health=false"
                  value="remove"
                ></button>
              </span>
              <input
                id="rtl-combobox-input"
                aria-label="الفئات"
                ng-model="rtl.query"
                placeholder="أضف فئات"
              />
            </fieldset>
            <aside lang="ar" aria-label="خيارات الفئات">
              <p>لم يتم العثور على فئات.</p>
              <div>
                <ul>
                  <li
                    ng-repeat="category in categories | filter:rtl.query"
                    data-value="{{ category }}"
                    ng-attr-aria-selected="{{ category === 'التكنولوجيا' ? rtl.technology : category === 'التصميم' ? rtl.design : category === 'الأعمال' ? rtl.business : category === 'التسويق' ? rtl.marketing : category === 'التعليم' ? rtl.education : rtl.health }}"
                  >
                    {{ category }}
                  </li>
                </ul>
              </div>
            </aside>
          </div>
        </div>
      </section>

      <output class="visually-hidden" aria-live="polite">
        Country: <span ng-bind="custom.selected || 'none'"></span>. Frameworks:
        <span ng-bind="multi.next ? 'Next.js' : 'none'"></span>. Categories:
        <span ng-bind="rtl.technology ? 'التكنولوجيا' : 'none'"></span>.
      </output>
    </main>
  </body>
</html>

Controlled State

The state artifact covers controlled disclosure, disabled-option skipping, Home and End navigation, dynamic options, and outside dismissal.

View source
<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>AngularCSS Combobox State 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="state={query:'',selected:'Next.js',open:false,showAstro:false}; stateOptions=[{label:'Next.js',disabled:false},{label:'SvelteKit',disabled:true},{label:'Nuxt.js',disabled:false},{label:'Remix',disabled:false}]"
  >
    <main class="combobox-state-workflows visual-example">
      <div class="combobox-state-controls">
        <button
          type="button"
          variant="outline"
          ng-click="state.open=!state.open"
        >
          Toggle popup
        </button>
        <button
          type="button"
          variant="outline"
          ng-click="state.showAstro=!state.showAstro"
        >
          Toggle Astro option
        </button>
      </div>

      <div
        id="state-combobox"
        ng-combobox
        auto-highlight
        open="{{ state.open }}"
        ng-on-angularcss:combobox-open-change="state.open=$event.detail.open"
        ng-on-angularcss:combobox-select="state.selected=$event.detail.value; state.query=$event.detail.value; state.open=false"
      >
        <header>
          <input
            aria-label="State framework"
            ng-model="state.query"
            placeholder="Select a framework"
          />
          <button
            type="button"
            aria-label="Show state frameworks"
            value="toggle"
          ></button>
        </header>
        <aside aria-label="State framework options">
          <p>No items found.</p>
          <div>
            <ul>
              <li
                ng-repeat="option in stateOptions | filter:state.query"
                data-value="{{ option.label }}"
                ng-attr-aria-disabled="{{ option.disabled }}"
                ng-attr-aria-selected="{{ state.selected === option.label }}"
              >
                {{ option.label }}
              </li>
              <li
                ng-if="state.showAstro"
                data-value="Astro"
                ng-attr-aria-selected="{{ state.selected === 'Astro' }}"
              >
                Astro
              </li>
            </ul>
          </div>
        </aside>
      </div>

      <output class="combobox-state-output" aria-live="polite">
        Selected: <span ng-bind="state.selected"></span>. Popup:
        <span ng-bind="state.open ? 'open' : 'closed'"></span>. Astro:
        <span ng-bind="state.showAstro ? 'shown' : 'hidden'"></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-combobox]. Importing the package registers it with the AngularCSS angular.css module; there is no per-component JavaScript registration step.

Anatomy

Directive selectors

  • ng-combobox

Semantic structure

A combobox root requires one input and one options surface. The root directive inspects semantic headers, sections, lists, options, fieldsets, and buttons; no child directives or anatomy classes are required.

API

Attributes and state

AttributeAccessPurpose
aria-activedescendantOutputID of the active option while focus remains on the composite control.
aria-autocompleteInput/outputHow a text control presents completion suggestions.
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-hiddenInput/outputWhether generated or collapsed content is hidden from assistive technology.
aria-invalidInputValidation state exposed to assistive technology and CSS.
aria-labelInput/outputAccessible name when visible text is insufficient.
aria-labelledbyOutputID of the element that supplies the accessible name.
aria-multiselectableOutputWhether the composite allows multiple selected items.
aria-orientationOutputInteraction axis exposed to assistive technology.
aria-selectedInput/outputSelected item state.
auto-highlightInputHighlights the first enabled result when the popup opens or filters change.
data-highlightedOutputCurrent option highlighted for keyboard selection.
data-valueInputApplication value reported when the corresponding option is selected.
dirInputText and interaction direction: ltr or rtl.
disabledInputDisables native or component interaction.
hiddenInputNative visibility state observed when finding available items.
multipleInputKeeps the popup open and reports selections for an application-owned collection.
openInput/outputInitial or externally synchronized disclosure state.
requiredInputMarks a native form value as required.
roleOutputExplicit semantic role when native HTML does not provide one.
sideOutputPhysical placement: left, top, bottom, or right.
tabindexOutputKeyboard 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
--combobox-anchor-widthComponent styling variable.
--combobox-content-topComponent styling variable.

DOM events

  • angularcss:combobox-clear
  • angularcss:combobox-open-change
  • angularcss:combobox-remove-last
  • angularcss:combobox-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 disclosure, collision-aware placement, active-descendant navigation, enabled-option boundaries, Escape and outside dismissal, and selection, clear, remove-last, and open-change signaling. AngularTS remains responsible for query filtering, selected values and collections, controlled open state, validation, and structural bindings such as ng-repeat, ng-if, and ng-model.

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

Accessibility

Give the input an accessible name. The input exposes combobox, expanded, controls, autocomplete, invalid, disabled, and active-descendant state connected to a listbox. Arrow keys, Home, End, Enter, Escape, and Tab operate on enabled visible options; labeled groups retain group relationships, multiple listboxes expose aria-multiselectable, and text direction is mirrored to the popup.

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-combobox], 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.