This is the multi-page printable view of this section. Click here to print.

Return to the regular view of this page.

Components

Focused AngularCSS runtime components for coordinated interaction.

Components are the small runtime layer used only when native HTML cannot coordinate the interaction. Every component includes a locally bundled, non-scrollable iframe demo and its complete TypeScript-derived contract.

Each reference page answers:

  1. What the component does and where to use it.
  2. What semantic HTML and directive selectors it requires.
  3. Which parts, attributes, state hooks, CSS variables, and events it exposes.
  4. Which behavior belongs to AngularCSS, AngularTS, native HTML, or application code.
  5. What accessibility and customization constraints apply.

Action

Command Palette

Date Input

Feedback

Form

Form Overlay

Layout

Media

Menu

Navigation

Overlay

1 - calendar

Calendar date grid structure

Use data-calendar-generated with data-month="YYYY-MM" to render a complete month, including outside days. Previous and next controls update the visible month, while selecting a date updates data-value and emits angularcss:calendar-select. Bind that event with AngularTS ng-on-* so application state remains responsible for the selected value.

<section
  ng-calendar
  data-calendar-generated
  data-month="2026-05"
  data-value="{{ selectedDate }}"
  ng-on-angularcss:calendar-select="selectedDate = $event.detail.value"
>
  <header>
    <button type="button">Previous</button>
    <h2></h2>
    <button type="button">Next</button>
  </header>
  <div></div>
</section>

Use data-week-start="0" through "6" to choose the first weekday and data-show-outside-days="false" to hide outside dates. Set --calendar-cell-size in CSS to resize cells. Day cells support arrow keys, Home, End, Page Up, and Page Down. Month changes emit angularcss:calendar-month-change.

Omit data-calendar-generated to author every weekday and day cell yourself. This mode supports selected, today, outside-month, disabled, booked, range, and week-number attributes without replacing application markup.

Date pickers are compositions rather than a second model implementation: use a native date or text input with ng-model, a field and label, a popover, and this calendar grid. Use native input[type="time"] for time values and ordinary buttons for presets. Booked dates use disabled plus data-booked="true"; custom day content can be nested inside a date button, and generated week numbers use native data elements.

The generated calendar is a Gregorian local-date UI backed by date-fns. Non-Gregorian engines, natural-language parsing, and IANA time-zone conversion are explicit application adapters: they provide localized labels and stable YYYY-MM-DD values to the calendar, or bind native inputs and zone selectors with AngularTS ng-model. AngularCSS does not silently convert calendar systems or instants because those conversions require application locale and time-zone policy.

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 Calendar</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="selectedDate='2026-05-14'"
    data-example="calendar-demo"
  >
    <div class="stack-sm">
      <section
        ng-calendar
        data-calendar-generated
        data-month="2026-05"
        data-value="{{ selectedDate }}"
        data-caption-layout="dropdown"
        data-start-year="2024"
        data-end-year="2028"
        ng-on-angularcss:calendar-select="selectedDate = $event.detail.value"
        class="calendar-demo visual-example"
      >
        <header>
          <button
            type="button"
            size="icon"
            variant="ghost"
            aria-label="Previous month"
          >
            <svg
              viewBox="0 0 24 24"
              fill="none"
              stroke="currentColor"
              stroke-width="2"
              aria-hidden="true"
            >
              <path d="m15 18-6-6 6-6" />
            </svg>
          </button>
          <h3></h3>
          <button
            type="button"
            size="icon"
            variant="ghost"
            aria-label="Next month"
          >
            <svg
              viewBox="0 0 24 24"
              fill="none"
              stroke="currentColor"
              stroke-width="2"
              aria-hidden="true"
            >
              <path d="m9 18 6-6-6-6" />
            </svg>
          </button>
        </header>
        <div></div>
      </section>
      <output class="output" aria-live="polite">
        Selected: {{ selectedDate }}
      </output>
    </div>
  </body>
</html>

Selection And Options

View source
<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>AngularCSS Calendar 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="calendar={basic:'',booked:'2026-09-08',caption:'2026-09-12',multiple:'2026-09-10,2026-09-12',rangeStart:'2026-09-10',rangeEnd:'2026-09-15',week:'2026-09-12'}"
    data-example="calendar-basic calendar-booked-dates calendar-caption calendar-multiple calendar-range calendar-week-numbers"
  >
    <main
      class="calendar-workflow-grid"
      aria-label="Calendar selection workflows"
    >
      <section
        class="calendar-workflow"
        aria-labelledby="calendar-basic-heading"
      >
        <h2 id="calendar-basic-heading">Basic</h2>
        <section
          ng-calendar
          data-calendar-generated
          data-month="2026-09"
          data-value="{{ calendar.basic }}"
          ng-on-angularcss:calendar-select="calendar.basic=$event.detail.value"
          class="calendar-demo"
        >
          <header>
            <button
              type="button"
              size="icon"
              variant="ghost"
              aria-label="Previous basic month"
            >
              <svg
                viewBox="0 0 24 24"
                fill="none"
                stroke="currentColor"
                stroke-width="2"
                aria-hidden="true"
              >
                <path d="m15 18-6-6 6-6" />
              </svg>
            </button>
            <h3></h3>
            <button
              type="button"
              size="icon"
              variant="ghost"
              aria-label="Next basic month"
            >
              <svg
                viewBox="0 0 24 24"
                fill="none"
                stroke="currentColor"
                stroke-width="2"
                aria-hidden="true"
              >
                <path d="m9 18 6-6-6-6" />
              </svg>
            </button>
          </header>
          <div></div>
        </section>
        <output class="calendar-workflow-output" aria-live="polite">
          Selected: {{ calendar.basic || 'None' }}
        </output>
      </section>

      <section
        class="calendar-workflow"
        aria-labelledby="calendar-booked-heading"
      >
        <h2 id="calendar-booked-heading">Booked dates</h2>
        <article class="calendar-card card">
          <section class="calendar-card-content">
            <section
              ng-calendar
              data-calendar-generated
              data-month="2026-09"
              data-value="{{ calendar.booked }}"
              data-booked-dates="2026-09-10,2026-09-11,2026-09-12,2026-09-13,2026-09-14,2026-09-15,2026-09-16,2026-09-17,2026-09-18,2026-09-19,2026-09-20,2026-09-21,2026-09-22,2026-09-23,2026-09-24"
              ng-on-angularcss:calendar-select="calendar.booked=$event.detail.value"
            >
              <header>
                <button
                  type="button"
                  size="icon"
                  variant="ghost"
                  aria-label="Previous availability month"
                >
                  <svg
                    viewBox="0 0 24 24"
                    fill="none"
                    stroke="currentColor"
                    stroke-width="2"
                    aria-hidden="true"
                  >
                    <path d="m15 18-6-6 6-6" />
                  </svg>
                </button>
                <h3></h3>
                <button
                  type="button"
                  size="icon"
                  variant="ghost"
                  aria-label="Next availability month"
                >
                  <svg
                    viewBox="0 0 24 24"
                    fill="none"
                    stroke="currentColor"
                    stroke-width="2"
                    aria-hidden="true"
                  >
                    <path d="m9 18 6-6-6-6" />
                  </svg>
                </button>
              </header>
              <div></div>
            </section>
          </section>
        </article>
        <output class="calendar-workflow-output" aria-live="polite">
          Available date: {{ calendar.booked }}
        </output>
      </section>

      <section
        class="calendar-workflow"
        aria-labelledby="calendar-caption-heading"
      >
        <h2 id="calendar-caption-heading">Dropdown caption</h2>
        <section
          ng-calendar
          data-calendar-generated
          data-month="2026-09"
          data-value="{{ calendar.caption }}"
          data-caption-layout="dropdown"
          data-start-year="2024"
          data-end-year="2028"
          ng-on-angularcss:calendar-select="calendar.caption=$event.detail.value"
          class="calendar-demo"
        >
          <header>
            <button
              type="button"
              size="icon"
              variant="ghost"
              aria-label="Previous caption month"
            >
              <svg
                viewBox="0 0 24 24"
                fill="none"
                stroke="currentColor"
                stroke-width="2"
                aria-hidden="true"
              >
                <path d="m15 18-6-6 6-6" />
              </svg>
            </button>
            <h3></h3>
            <button
              type="button"
              size="icon"
              variant="ghost"
              aria-label="Next caption month"
            >
              <svg
                viewBox="0 0 24 24"
                fill="none"
                stroke="currentColor"
                stroke-width="2"
                aria-hidden="true"
              >
                <path d="m9 18 6-6-6-6" />
              </svg>
            </button>
          </header>
          <div></div>
        </section>
        <output class="calendar-workflow-output" aria-live="polite">
          Selected: {{ calendar.caption }}
        </output>
      </section>

      <section
        class="calendar-workflow"
        aria-labelledby="calendar-multiple-heading"
      >
        <h2 id="calendar-multiple-heading">Multiple dates</h2>
        <article class="calendar-card card">
          <section class="calendar-card-content">
            <section
              ng-calendar
              data-calendar-generated
              data-month="2026-09"
              data-selection-mode="multiple"
              data-values="{{ calendar.multiple }}"
              ng-on-angularcss:calendar-select="calendar.multiple=$event.detail.values"
            >
              <header>
                <button
                  type="button"
                  size="icon"
                  variant="ghost"
                  aria-label="Previous multiple month"
                >
                  <svg
                    viewBox="0 0 24 24"
                    fill="none"
                    stroke="currentColor"
                    stroke-width="2"
                    aria-hidden="true"
                  >
                    <path d="m15 18-6-6 6-6" />
                  </svg>
                </button>
                <h3></h3>
                <button
                  type="button"
                  size="icon"
                  variant="ghost"
                  aria-label="Next multiple month"
                >
                  <svg
                    viewBox="0 0 24 24"
                    fill="none"
                    stroke="currentColor"
                    stroke-width="2"
                    aria-hidden="true"
                  >
                    <path d="m9 18 6-6-6-6" />
                  </svg>
                </button>
              </header>
              <div></div>
            </section>
          </section>
        </article>
        <output class="calendar-workflow-output" aria-live="polite">
          Selected dates: {{ calendar.multiple || 'None' }}
        </output>
      </section>

      <section
        class="calendar-workflow calendar-workflow-wide"
        aria-labelledby="calendar-range-heading"
      >
        <h2 id="calendar-range-heading">Date range</h2>
        <section
          ng-calendar
          data-calendar-generated
          data-month="2026-09"
          data-number-of-months="2"
          data-selection-mode="range"
          data-range-start-value="{{ calendar.rangeStart }}"
          data-range-end-value="{{ calendar.rangeEnd }}"
          ng-on-angularcss:calendar-select="calendar.rangeStart=$event.detail.range.start; calendar.rangeEnd=$event.detail.range.end"
          class="calendar-demo"
        >
          <header>
            <button
              type="button"
              size="icon"
              variant="ghost"
              aria-label="Previous range month"
            >
              <svg
                viewBox="0 0 24 24"
                fill="none"
                stroke="currentColor"
                stroke-width="2"
                aria-hidden="true"
              >
                <path d="m15 18-6-6 6-6" />
              </svg>
            </button>
            <h3></h3>
            <button
              type="button"
              size="icon"
              variant="ghost"
              aria-label="Next range month"
            >
              <svg
                viewBox="0 0 24 24"
                fill="none"
                stroke="currentColor"
                stroke-width="2"
                aria-hidden="true"
              >
                <path d="m9 18 6-6-6-6" />
              </svg>
            </button>
          </header>
          <div></div>
        </section>
        <output class="calendar-workflow-output" aria-live="polite">
          Range: {{ calendar.rangeStart || 'None' }} to {{ calendar.rangeEnd ||
          'None' }}
        </output>
      </section>

      <section
        class="calendar-workflow"
        aria-labelledby="calendar-week-heading"
      >
        <h2 id="calendar-week-heading">Week numbers</h2>
        <article class="calendar-card card">
          <section class="calendar-card-content">
            <section
              ng-calendar
              data-calendar-generated
              data-month="2026-09"
              data-value="{{ calendar.week }}"
              data-show-week-numbers="true"
              ng-on-angularcss:calendar-select="calendar.week=$event.detail.value"
            >
              <header>
                <button
                  type="button"
                  size="icon"
                  variant="ghost"
                  aria-label="Previous week-number month"
                >
                  <svg
                    viewBox="0 0 24 24"
                    fill="none"
                    stroke="currentColor"
                    stroke-width="2"
                    aria-hidden="true"
                  >
                    <path d="m15 18-6-6 6-6" />
                  </svg>
                </button>
                <h3></h3>
                <button
                  type="button"
                  size="icon"
                  variant="ghost"
                  aria-label="Next week-number month"
                >
                  <svg
                    viewBox="0 0 24 24"
                    fill="none"
                    stroke="currentColor"
                    stroke-width="2"
                    aria-hidden="true"
                  >
                    <path d="m9 18 6-6-6-6" />
                  </svg>
                </button>
              </header>
              <div></div>
            </section>
          </section>
        </article>
        <output class="calendar-workflow-output" aria-live="polite">
          Selected: {{ calendar.week }}
        </output>
      </section>
    </main>
  </body>
</html>

Reference Compositions

Custom day content remains authored HTML, while presets and time values use ordinary buttons and native inputs bound by AngularTS. The Persian example is an explicit application-provided calendar-system adapter: it supplies localized labels and stable Gregorian interchange values without adding a second calendar engine to AngularCSS.

View source
<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>AngularCSS Calendar 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="custom={start:'2026-09-10',end:'2026-09-15'}; presetDate='2026-09-12'; time={date:'2026-09-12',start:'09:00',end:'17:00'}; rtlDate='2026-09-12'; hijriDate='2025-06-12'; customDays=[{v:'2026-08-30',d:'30',p:'',o:true},{v:'2026-08-31',d:'31',p:'',o:true},{v:'2026-09-01',d:'1',p:'$100',o:false},{v:'2026-09-02',d:'2',p:'$100',o:false},{v:'2026-09-03',d:'3',p:'$100',o:false},{v:'2026-09-04',d:'4',p:'$100',o:false},{v:'2026-09-05',d:'5',p:'$120',o:false},{v:'2026-09-06',d:'6',p:'$120',o:false},{v:'2026-09-07',d:'7',p:'$100',o:false},{v:'2026-09-08',d:'8',p:'$100',o:false},{v:'2026-09-09',d:'9',p:'$100',o:false},{v:'2026-09-10',d:'10',p:'$100',o:false},{v:'2026-09-11',d:'11',p:'$100',o:false},{v:'2026-09-12',d:'12',p:'$120',o:false},{v:'2026-09-13',d:'13',p:'$120',o:false},{v:'2026-09-14',d:'14',p:'$100',o:false},{v:'2026-09-15',d:'15',p:'$100',o:false},{v:'2026-09-16',d:'16',p:'$100',o:false},{v:'2026-09-17',d:'17',p:'$100',o:false},{v:'2026-09-18',d:'18',p:'$100',o:false},{v:'2026-09-19',d:'19',p:'$120',o:false},{v:'2026-09-20',d:'20',p:'$120',o:false},{v:'2026-09-21',d:'21',p:'$100',o:false},{v:'2026-09-22',d:'22',p:'$100',o:false},{v:'2026-09-23',d:'23',p:'$100',o:false},{v:'2026-09-24',d:'24',p:'$100',o:false},{v:'2026-09-25',d:'25',p:'$100',o:false},{v:'2026-09-26',d:'26',p:'$120',o:false},{v:'2026-09-27',d:'27',p:'$120',o:false},{v:'2026-09-28',d:'28',p:'$100',o:false},{v:'2026-09-29',d:'29',p:'$100',o:false},{v:'2026-09-30',d:'30',p:'$100',o:false},{v:'2026-10-01',d:'1',p:'',o:true},{v:'2026-10-02',d:'2',p:'',o:true},{v:'2026-10-03',d:'3',p:'',o:true},{v:'2026-10-04',d:'4',p:'',o:true},{v:'2026-10-05',d:'5',p:'',o:true},{v:'2026-10-06',d:'6',p:'',o:true},{v:'2026-10-07',d:'7',p:'',o:true},{v:'2026-10-08',d:'8',p:'',o:true},{v:'2026-10-09',d:'9',p:'',o:true},{v:'2026-10-10',d:'10',p:'',o:true}]; hijriDays=[{v:'2025-05-17',d:'۲۷',o:true},{v:'2025-05-18',d:'۲۸',o:true},{v:'2025-05-19',d:'۲۹',o:true},{v:'2025-05-20',d:'۳۰',o:true},{v:'2025-05-21',d:'۳۱',o:true},{v:'2025-05-22',d:'۱',o:false},{v:'2025-05-23',d:'۲',o:false},{v:'2025-05-24',d:'۳',o:false},{v:'2025-05-25',d:'۴',o:false},{v:'2025-05-26',d:'۵',o:false},{v:'2025-05-27',d:'۶',o:false},{v:'2025-05-28',d:'۷',o:false},{v:'2025-05-29',d:'۸',o:false},{v:'2025-05-30',d:'۹',o:false},{v:'2025-05-31',d:'۱۰',o:false},{v:'2025-06-01',d:'۱۱',o:false},{v:'2025-06-02',d:'۱۲',o:false},{v:'2025-06-03',d:'۱۳',o:false},{v:'2025-06-04',d:'۱۴',o:false},{v:'2025-06-05',d:'۱۵',o:false},{v:'2025-06-06',d:'۱۶',o:false},{v:'2025-06-07',d:'۱۷',o:false},{v:'2025-06-08',d:'۱۸',o:false},{v:'2025-06-09',d:'۱۹',o:false},{v:'2025-06-10',d:'۲۰',o:false},{v:'2025-06-11',d:'۲۱',o:false},{v:'2025-06-12',d:'۲۲',o:false},{v:'2025-06-13',d:'۲۳',o:false},{v:'2025-06-14',d:'۲۴',o:false},{v:'2025-06-15',d:'۲۵',o:false},{v:'2025-06-16',d:'۲۶',o:false},{v:'2025-06-17',d:'۲۷',o:false},{v:'2025-06-18',d:'۲۸',o:false},{v:'2025-06-19',d:'۲۹',o:false},{v:'2025-06-20',d:'۳۰',o:false},{v:'2025-06-21',d:'۳۱',o:false},{v:'2025-06-22',d:'۱',o:true},{v:'2025-06-23',d:'۲',o:true},{v:'2025-06-24',d:'۳',o:true},{v:'2025-06-25',d:'۴',o:true},{v:'2025-06-26',d:'۵',o:true},{v:'2025-06-27',d:'۶',o:true}]"
    data-example="calendar-custom-days calendar-presets calendar-rtl calendar-time calendar-hijri"
  >
    <main class="calendar-composition-grid" aria-label="Calendar compositions">
      <section
        class="calendar-composition calendar-composition-wide"
        aria-labelledby="custom-days-heading"
      >
        <h2 id="custom-days-heading">Custom day prices</h2>
        <article class="calendar-card card">
          <section class="calendar-card-content">
            <section
              ng-calendar
              data-selection-mode="range"
              data-range-start-value="{{ custom.start }}"
              data-range-end-value="{{ custom.end }}"
              ng-on-angularcss:calendar-select="custom.start=$event.detail.range.start; custom.end=$event.detail.range.end"
              class="calendar-custom-days"
            >
              <header>
                <h3>September 2026</h3>
              </header>
              <div>
                <abbr>Su</abbr><abbr>Mo</abbr><abbr>Tu</abbr><abbr>We</abbr
                ><abbr>Th</abbr><abbr>Fr</abbr><abbr>Sa</abbr>
                <button
                  type="button"
                  ng-repeat="day in customDays"
                  value="{{ day.v }}"
                  data-outside="{{ day.o }}"
                  aria-label="{{ day.v }} {{ day.p }}"
                >
                  <strong>{{ day.d }}</strong><span>{{ day.p }}</span>
                </button>
              </div>
            </section>
          </section>
        </article>
        <output class="calendar-workflow-output" aria-live="polite">
          Range: {{ custom.start }} to {{ custom.end }}
        </output>
      </section>

      <section class="calendar-composition" aria-labelledby="preset-heading">
        <h2 id="preset-heading">Presets</h2>
        <article size="sm" class="calendar-preset-card card">
          <section class="calendar-preset-content">
            <section
              ng-calendar
              data-calendar-generated
              data-month="2026-09"
              data-value="{{ presetDate }}"
              ng-on-angularcss:calendar-select="presetDate=$event.detail.value"
              class="calendar-flush calendar-preset-grid"
            >
              <header>
                <button
                  type="button"
                  size="icon"
                  variant="ghost"
                  aria-label="Previous preset month"
                >
                  <svg
                    viewBox="0 0 24 24"
                    fill="none"
                    stroke="currentColor"
                    stroke-width="2"
                    aria-hidden="true"
                  >
                    <path d="m15 18-6-6 6-6" />
                  </svg>
                </button>
                <h3></h3>
                <button
                  type="button"
                  size="icon"
                  variant="ghost"
                  aria-label="Next preset month"
                >
                  <svg
                    viewBox="0 0 24 24"
                    fill="none"
                    stroke="currentColor"
                    stroke-width="2"
                    aria-hidden="true"
                  >
                    <path d="m9 18 6-6-6-6" />
                  </svg>
                </button>
              </header>
              <div></div>
            </section>
          </section>
          <footer class="calendar-preset-footer">
            <button
              type="button"
              variant="outline"
              size="sm"
              ng-click="presetDate='2026-09-12'"
            >
              Today
            </button>
            <button
              type="button"
              variant="outline"
              size="sm"
              ng-click="presetDate='2026-09-13'"
            >
              Tomorrow
            </button>
            <button
              type="button"
              variant="outline"
              size="sm"
              ng-click="presetDate='2026-09-15'"
            >
              In 3 days
            </button>
            <button
              type="button"
              variant="outline"
              size="sm"
              ng-click="presetDate='2026-09-19'"
            >
              In a week
            </button>
            <button
              type="button"
              variant="outline"
              size="sm"
              ng-click="presetDate='2026-09-26'"
            >
              In 2 weeks
            </button>
          </footer>
        </article>
        <output class="calendar-workflow-output" aria-live="polite">
          Selected: {{ presetDate }}
        </output>
      </section>

      <section class="calendar-composition" aria-labelledby="time-heading">
        <h2 id="time-heading">Date and time</h2>
        <article size="sm" class="calendar-time-card card">
          <section class="calendar-time-content">
            <section
              ng-calendar
              data-calendar-generated
              data-month="2026-09"
              data-value="{{ time.date }}"
              ng-on-angularcss:calendar-select="time.date=$event.detail.value"
              class="calendar-flush"
            >
              <header>
                <button
                  type="button"
                  size="icon"
                  variant="ghost"
                  aria-label="Previous time month"
                >
                  <svg
                    viewBox="0 0 24 24"
                    fill="none"
                    stroke="currentColor"
                    stroke-width="2"
                    aria-hidden="true"
                  >
                    <path d="m15 18-6-6 6-6" />
                  </svg>
                </button>
                <h3></h3>
                <button
                  type="button"
                  size="icon"
                  variant="ghost"
                  aria-label="Next time month"
                >
                  <svg
                    viewBox="0 0 24 24"
                    fill="none"
                    stroke="currentColor"
                    stroke-width="2"
                    aria-hidden="true"
                  >
                    <path d="m9 18 6-6-6-6" />
                  </svg>
                </button>
              </header>
              <div></div>
            </section>
          </section>
          <footer class="calendar-time-footer">
            <label
              ><span>Start</span>
              <div class="input-group">
                <input
                  type="time"
                  ng-model="time.start"
                  aria-label="Start time"
                /><span
                  ><svg
                    viewBox="0 0 24 24"
                    fill="none"
                    stroke="currentColor"
                    stroke-width="2"
                    aria-hidden="true"
                  >
                    <circle cx="12" cy="12" r="9" />
                    <path d="M12 7v5l3 2" /></svg
                ></span></div
            ></label>
            <label
              ><span>End</span>
              <div class="input-group">
                <input
                  type="time"
                  ng-model="time.end"
                  aria-label="End time"
                /><span
                  ><svg
                    viewBox="0 0 24 24"
                    fill="none"
                    stroke="currentColor"
                    stroke-width="2"
                    aria-hidden="true"
                  >
                    <circle cx="12" cy="12" r="9" />
                    <path d="M12 7v5l3 2" /></svg
                ></span></div
            ></label>
          </footer>
        </article>
        <output class="calendar-workflow-output" aria-live="polite">
          {{ time.date }} · {{ time.start }}–{{ time.end }}
        </output>
      </section>

      <section
        class="calendar-composition"
        aria-labelledby="rtl-heading"
        dir="rtl"
        lang="ar-SA"
      >
        <h2 id="rtl-heading">تقويم من اليمين إلى اليسار</h2>
        <section
          ng-calendar
          data-calendar-generated
          data-month="2026-09"
          data-value="{{ rtlDate }}"
          data-caption-layout="dropdown"
          data-start-year="2024"
          data-end-year="2028"
          ng-on-angularcss:calendar-select="rtlDate=$event.detail.value"
          class="calendar-demo calendar-rtl-grid"
        >
          <header>
            <button
              type="button"
              size="icon"
              variant="ghost"
              aria-label="الشهر السابق"
            >
              <svg
                viewBox="0 0 24 24"
                fill="none"
                stroke="currentColor"
                stroke-width="2"
                aria-hidden="true"
              >
                <path d="m15 18-6-6 6-6" />
              </svg>
            </button>
            <h3></h3>
            <button
              type="button"
              size="icon"
              variant="ghost"
              aria-label="الشهر التالي"
            >
              <svg
                viewBox="0 0 24 24"
                fill="none"
                stroke="currentColor"
                stroke-width="2"
                aria-hidden="true"
              >
                <path d="m9 18 6-6-6-6" />
              </svg>
            </button>
          </header>
          <div></div>
        </section>
        <output class="calendar-workflow-output" aria-live="polite">
          التاريخ: {{ rtlDate }}
        </output>
      </section>

      <section
        class="calendar-composition"
        aria-labelledby="hijri-heading"
        dir="rtl"
        lang="fa"
      >
        <h2 id="hijri-heading">تقویم هجری شمسی</h2>
        <section
          ng-calendar
          data-value="{{ hijriDate }}"
          ng-on-angularcss:calendar-select="hijriDate=$event.detail.value"
          class="calendar-demo calendar-hijri-grid"
          aria-label="خرداد ۱۴۰۴"
        >
          <header>
            <h3>خرداد ۱۴۰۴</h3>
          </header>
          <div>
            <abbr>ش</abbr><abbr>ی</abbr><abbr>د</abbr><abbr>س</abbr
            ><abbr>چ</abbr><abbr>پ</abbr><abbr>ج</abbr>
            <button
              type="button"
              ng-repeat="day in hijriDays"
              value="{{ day.v }}"
              data-outside="{{ day.o }}"
              aria-label="روز {{ day.d }}"
            >
              {{ day.d }}
            </button>
          </div>
        </section>
        <output class="calendar-workflow-output" aria-live="polite">
          روز انتخاب‌شده: {{ hijriDate }}
        </output>
      </section>
    </main>
  </body>
</html>

Date Picker Compositions

Date Picker is a composition of Field, Input Group, Popover, Calendar, Button, and native time input primitives. The artifact below implements all eight checked-in reference workflows: basic, standalone demo, date of birth, parsed text input, natural-language input, range, RTL, and date with time.

The page loads a locally bundled TypeScript application adapter. AngularTS owns the values and commands, while chrono-node parses natural-language input into the Calendar’s stable YYYY-MM-DD interchange value. No CDN or source TypeScript module is loaded by the 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 Date Picker 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/date-picker-demo.umd.js"></script>
  </head>
  <body
    ng-app="datePickerDemo"
    ng-controller="DatePickerDemoController as picker"
    ng-cloak
    data-example="date-picker-basic date-picker-demo date-picker-dob date-picker-input date-picker-natural-language date-picker-range date-picker-rtl date-picker-time"
  >
    <main class="date-picker-grid" aria-label="Date picker workflows">
      <section class="date-picker-workflow">
        <h2>Basic</h2>
        <div class="date-picker-field-compact field">
          <label for="date-picker-basic">Date</label>
          <span id="date-picker-basic-popover">
            <button
              id="date-picker-basic"
              type="button"
              variant="outline"
              class="date-picker-trigger"
              popovertarget="popover-date-picker-workflows-1-content"
            >
              {{ picker.basicDate ? picker.format(picker.basicDate) : 'Pick a
              date' }}
            </button>
            <aside
              class="calendar-popover-content"
              aria-label="Choose a basic date"
              id="popover-date-picker-workflows-1-content"
              side="bottom"
              align="start"
              popover
            >
              <section
                ng-calendar
                data-calendar-generated
                data-month="2026-09"
                ng-attr-data-value="{{ picker.basicDate }}"
                ng-on-angularcss:calendar-select="picker.selectBasic($event.detail.value)"
              >
                <header>
                  <button
                    type="button"
                    size="icon"
                    variant="ghost"
                    aria-label="Previous basic month"
                  >
                    <svg
                      viewBox="0 0 24 24"
                      fill="none"
                      stroke="currentColor"
                      stroke-width="2"
                      aria-hidden="true"
                    >
                      <path d="m15 18-6-6 6-6"></path>
                    </svg>
                  </button>
                  <h3></h3>
                  <button
                    type="button"
                    size="icon"
                    variant="ghost"
                    aria-label="Next basic month"
                  >
                    <svg
                      viewBox="0 0 24 24"
                      fill="none"
                      stroke="currentColor"
                      stroke-width="2"
                      aria-hidden="true"
                    >
                      <path d="m9 18 6-6-6-6"></path>
                    </svg>
                  </button>
                </header>
                <div></div>
              </section>
            </aside>
          </span>
        </div>
      </section>

      <section class="date-picker-workflow">
        <h2>Demo</h2>
        <span id="date-picker-demo-popover">
          <button
            type="button"
            variant="outline"
            class="date-picker-trigger date-picker-trigger-between"
            ng-attr-empty="{{ picker.demoDate ? undefined : '' }}"
            popovertarget="popover-date-picker-workflows-2-content"
          >
            <span
              >{{ picker.demoDate ? picker.format(picker.demoDate) : 'Pick a
              date' }}</span
            >
            <svg
              viewBox="0 0 24 24"
              fill="none"
              stroke="currentColor"
              stroke-width="2"
              aria-hidden="true"
            >
              <path d="m6 9 6 6 6-6"></path>
            </svg>
          </button>
          <aside
            class="calendar-popover-content"
            aria-label="Choose a demo date"
            id="popover-date-picker-workflows-2-content"
            side="bottom"
            align="start"
            popover
          >
            <section
              ng-calendar
              data-calendar-generated
              data-month="2026-09"
              ng-attr-data-value="{{ picker.demoDate }}"
              ng-on-angularcss:calendar-select="picker.selectDemo($event.detail.value)"
            >
              <header>
                <button
                  type="button"
                  size="icon"
                  variant="ghost"
                  aria-label="Previous demo month"
                >
                  <svg
                    viewBox="0 0 24 24"
                    fill="none"
                    stroke="currentColor"
                    stroke-width="2"
                    aria-hidden="true"
                  >
                    <path d="m15 18-6-6 6-6"></path>
                  </svg>
                </button>
                <h3></h3>
                <button
                  type="button"
                  size="icon"
                  variant="ghost"
                  aria-label="Next demo month"
                >
                  <svg
                    viewBox="0 0 24 24"
                    fill="none"
                    stroke="currentColor"
                    stroke-width="2"
                    aria-hidden="true"
                  >
                    <path d="m9 18 6-6-6-6"></path>
                  </svg>
                </button>
              </header>
              <div></div>
            </section>
          </aside>
        </span>
      </section>

      <section class="date-picker-workflow">
        <h2>Date of birth</h2>
        <div class="date-picker-field-compact field">
          <label for="date-picker-dob">Date of birth</label>
          <span id="date-picker-dob-popover">
            <button
              id="date-picker-dob"
              type="button"
              variant="outline"
              class="date-picker-trigger"
              popovertarget="popover-date-picker-workflows-3-content"
            >
              {{ picker.dobDate ? picker.format(picker.dobDate) : 'Select date'
              }}
            </button>
            <aside
              class="calendar-popover-content"
              aria-label="Choose a birth date"
              id="popover-date-picker-workflows-3-content"
              side="bottom"
              align="start"
              popover
            >
              <section
                ng-calendar
                data-calendar-generated
                ng-attr-data-month="{{ picker.month(picker.dobDate) }}"
                ng-attr-data-value="{{ picker.dobDate }}"
                data-caption-layout="dropdown"
                data-start-year="1900"
                data-end-year="2026"
                ng-on-angularcss:calendar-select="picker.selectDob($event.detail.value)"
              >
                <header>
                  <button
                    type="button"
                    size="icon"
                    variant="ghost"
                    aria-label="Previous birth month"
                  >
                    <svg
                      viewBox="0 0 24 24"
                      fill="none"
                      stroke="currentColor"
                      stroke-width="2"
                      aria-hidden="true"
                    >
                      <path d="m15 18-6-6 6-6"></path>
                    </svg>
                  </button>
                  <h3></h3>
                  <button
                    type="button"
                    size="icon"
                    variant="ghost"
                    aria-label="Next birth month"
                  >
                    <svg
                      viewBox="0 0 24 24"
                      fill="none"
                      stroke="currentColor"
                      stroke-width="2"
                      aria-hidden="true"
                    >
                      <path d="m9 18 6-6-6-6"></path>
                    </svg>
                  </button>
                </header>
                <div></div>
              </section>
            </aside>
          </span>
        </div>
      </section>

      <section class="date-picker-workflow">
        <h2>Input</h2>
        <div class="date-picker-input-field field">
          <label for="date-picker-input">Subscription Date</label>
          <div class="input-group">
            <input
              id="date-picker-input"
              ng-model="picker.inputValue"
              data-change="picker.updateInput()"
              ng-keydown="picker.openPopover('date-picker-input-popover', $event)"
              placeholder="June 01, 2025"
            />
            <div align="inline-end">
              <span id="date-picker-input-popover">
                <button
                  type="button"
                  variant="ghost"
                  size="icon-xs"
                  aria-label="Select subscription date"
                  popovertarget="popover-date-picker-workflows-4-content"
                >
                  <svg
                    viewBox="0 0 24 24"
                    fill="none"
                    stroke="currentColor"
                    stroke-width="2"
                    aria-hidden="true"
                  >
                    <rect x="3" y="5" width="18" height="16" rx="2"></rect>
                    <path d="M16 3v4M8 3v4M3 10h18"></path>
                  </svg>
                </button>
                <aside
                  class="calendar-popover-content"
                  aria-label="Choose a subscription date"
                  id="popover-date-picker-workflows-4-content"
                  side="bottom"
                  align="end"
                  popover
                >
                  <section
                    ng-calendar
                    data-calendar-generated
                    ng-attr-data-month="{{ picker.month(picker.inputDate) }}"
                    ng-attr-data-value="{{ picker.inputDate }}"
                    ng-on-angularcss:calendar-select="picker.selectInput($event.detail.value)"
                  >
                    <header>
                      <button
                        type="button"
                        size="icon"
                        variant="ghost"
                        aria-label="Previous subscription month"
                      >
                        <svg
                          viewBox="0 0 24 24"
                          fill="none"
                          stroke="currentColor"
                          stroke-width="2"
                          aria-hidden="true"
                        >
                          <path d="m15 18-6-6 6-6"></path>
                        </svg>
                      </button>
                      <h3></h3>
                      <button
                        type="button"
                        size="icon"
                        variant="ghost"
                        aria-label="Next subscription month"
                      >
                        <svg
                          viewBox="0 0 24 24"
                          fill="none"
                          stroke="currentColor"
                          stroke-width="2"
                          aria-hidden="true"
                        >
                          <path d="m9 18 6-6-6-6"></path>
                        </svg>
                      </button>
                    </header>
                    <div></div>
                  </section>
                </aside>
              </span>
            </div>
          </div>
        </div>
      </section>

      <section class="date-picker-workflow date-picker-workflow-wide">
        <h2>Natural language</h2>
        <div class="date-picker-natural-field field">
          <label for="date-picker-natural">Schedule Date</label>
          <div class="input-group">
            <input
              id="date-picker-natural"
              ng-model="picker.naturalValue"
              data-change="picker.updateNatural()"
              ng-keydown="picker.openPopover('date-picker-natural-popover', $event)"
              placeholder="Tomorrow or next week"
            />
            <div align="inline-end">
              <span id="date-picker-natural-popover">
                <button
                  type="button"
                  variant="ghost"
                  size="icon-xs"
                  aria-label="Select schedule date"
                  popovertarget="popover-date-picker-workflows-5-content"
                >
                  <svg
                    viewBox="0 0 24 24"
                    fill="none"
                    stroke="currentColor"
                    stroke-width="2"
                    aria-hidden="true"
                  >
                    <rect x="3" y="5" width="18" height="16" rx="2"></rect>
                    <path d="M16 3v4M8 3v4M3 10h18"></path>
                  </svg>
                </button>
                <aside
                  class="calendar-popover-content"
                  aria-label="Choose a schedule date"
                  id="popover-date-picker-workflows-5-content"
                  side="bottom"
                  align="end"
                  popover
                >
                  <section
                    ng-calendar
                    data-calendar-generated
                    ng-attr-data-month="{{ picker.month(picker.naturalDate) }}"
                    ng-attr-data-value="{{ picker.naturalDate }}"
                    data-caption-layout="dropdown"
                    data-start-year="2024"
                    data-end-year="2028"
                    ng-on-angularcss:calendar-select="picker.selectNatural($event.detail.value)"
                  >
                    <header>
                      <button
                        type="button"
                        size="icon"
                        variant="ghost"
                        aria-label="Previous schedule month"
                      >
                        <svg
                          viewBox="0 0 24 24"
                          fill="none"
                          stroke="currentColor"
                          stroke-width="2"
                          aria-hidden="true"
                        >
                          <path d="m15 18-6-6 6-6"></path>
                        </svg>
                      </button>
                      <h3></h3>
                      <button
                        type="button"
                        size="icon"
                        variant="ghost"
                        aria-label="Next schedule month"
                      >
                        <svg
                          viewBox="0 0 24 24"
                          fill="none"
                          stroke="currentColor"
                          stroke-width="2"
                          aria-hidden="true"
                        >
                          <path d="m9 18 6-6-6-6"></path>
                        </svg>
                      </button>
                    </header>
                    <div></div>
                  </section>
                </aside>
              </span>
            </div>
          </div>
          <output class="calendar-workflow-output">
            Your post will be published on
            <strong>{{ picker.format(picker.naturalDate) }}</strong>.
          </output>
        </div>
      </section>

      <section class="date-picker-workflow date-picker-workflow-wide">
        <h2>Range</h2>
        <div class="date-picker-range-field field">
          <label for="date-picker-range">Date Picker Range</label>
          <span id="date-picker-range-popover">
            <button
              id="date-picker-range"
              type="button"
              variant="outline"
              class="date-picker-trigger"
              popovertarget="popover-date-picker-workflows-6-content"
            >
              <svg
                viewBox="0 0 24 24"
                fill="none"
                stroke="currentColor"
                stroke-width="2"
                aria-hidden="true"
              >
                <rect x="3" y="5" width="18" height="16" rx="2"></rect>
                <path d="M16 3v4M8 3v4M3 10h18"></path>
              </svg>
              <span
                >{{ picker.formatShort(picker.rangeStart) }} - {{
                picker.formatShort(picker.rangeEnd) }}</span
              >
            </button>
            <aside
              class="calendar-popover-content"
              aria-label="Choose a date range"
              id="popover-date-picker-workflows-6-content"
              side="bottom"
              align="start"
              popover
            >
              <section
                ng-calendar
                data-calendar-generated
                ng-attr-data-month="{{ picker.month(picker.rangeStart) }}"
                data-number-of-months="2"
                data-selection-mode="range"
                ng-attr-data-range-start-value="{{ picker.rangeStart }}"
                ng-attr-data-range-end-value="{{ picker.rangeEnd }}"
                ng-on-angularcss:calendar-select="picker.selectRange($event.detail.range)"
              >
                <header>
                  <button
                    type="button"
                    size="icon"
                    variant="ghost"
                    aria-label="Previous range month"
                  >
                    <svg
                      viewBox="0 0 24 24"
                      fill="none"
                      stroke="currentColor"
                      stroke-width="2"
                      aria-hidden="true"
                    >
                      <path d="m15 18-6-6 6-6"></path>
                    </svg>
                  </button>
                  <h3></h3>
                  <button
                    type="button"
                    size="icon"
                    variant="ghost"
                    aria-label="Next range month"
                  >
                    <svg
                      viewBox="0 0 24 24"
                      fill="none"
                      stroke="currentColor"
                      stroke-width="2"
                      aria-hidden="true"
                    >
                      <path d="m9 18 6-6-6-6"></path>
                    </svg>
                  </button>
                </header>
                <div></div>
              </section>
            </aside>
          </span>
        </div>
      </section>

      <section class="date-picker-workflow" dir="rtl" lang="ar">
        <h2>من اليمين إلى اليسار</h2>
        <span id="date-picker-rtl-popover">
          <button
            type="button"
            variant="outline"
            class="date-picker-trigger date-picker-trigger-between"
            ng-attr-empty="{{ picker.rtlDate ? undefined : '' }}"
            popovertarget="popover-date-picker-workflows-7-content"
          >
            <span
              >{{ picker.rtlDate ? picker.formatRtl(picker.rtlDate) : 'اختر
              تاريخًا' }}</span
            >
            <svg
              viewBox="0 0 24 24"
              fill="none"
              stroke="currentColor"
              stroke-width="2"
              aria-hidden="true"
            >
              <path d="m6 9 6 6 6-6"></path>
            </svg>
          </button>
          <aside
            class="calendar-popover-content"
            aria-label="اختر تاريخًا"
            id="popover-date-picker-workflows-7-content"
            side="bottom"
            align="start"
            popover
          >
            <section
              ng-calendar
              data-calendar-generated
              data-month="2026-09"
              ng-attr-data-value="{{ picker.rtlDate }}"
              ng-on-angularcss:calendar-select="picker.selectRtl($event.detail.value)"
            >
              <header>
                <button
                  type="button"
                  size="icon"
                  variant="ghost"
                  aria-label="الشهر السابق"
                >
                  <svg
                    viewBox="0 0 24 24"
                    fill="none"
                    stroke="currentColor"
                    stroke-width="2"
                    aria-hidden="true"
                  >
                    <path d="m15 18-6-6 6-6"></path>
                  </svg>
                </button>
                <h3></h3>
                <button
                  type="button"
                  size="icon"
                  variant="ghost"
                  aria-label="الشهر التالي"
                >
                  <svg
                    viewBox="0 0 24 24"
                    fill="none"
                    stroke="currentColor"
                    stroke-width="2"
                    aria-hidden="true"
                  >
                    <path d="m9 18 6-6-6-6"></path>
                  </svg>
                </button>
              </header>
              <div></div>
            </section>
          </aside>
        </span>
      </section>

      <section class="date-picker-workflow date-picker-workflow-wide">
        <h2>Date and time</h2>
        <div class="date-picker-time-fields">
          <div class="field">
            <label for="date-picker-time-date">Date</label>
            <span id="date-picker-time-popover">
              <button
                id="date-picker-time-date"
                type="button"
                variant="outline"
                class="date-picker-trigger date-picker-trigger-between"
                popovertarget="popover-date-picker-workflows-8-content"
              >
                <span
                  >{{ picker.timeDate ? picker.formatShort(picker.timeDate) :
                  'Select date' }}</span
                >
                <svg
                  viewBox="0 0 24 24"
                  fill="none"
                  stroke="currentColor"
                  stroke-width="2"
                  aria-hidden="true"
                >
                  <path d="m6 9 6 6 6-6"></path>
                </svg>
              </button>
              <aside
                class="calendar-popover-content"
                aria-label="Choose an appointment date"
                id="popover-date-picker-workflows-8-content"
                side="bottom"
                align="start"
                popover
              >
                <section
                  ng-calendar
                  data-calendar-generated
                  ng-attr-data-month="{{ picker.month(picker.timeDate) }}"
                  ng-attr-data-value="{{ picker.timeDate }}"
                  data-caption-layout="dropdown"
                  data-start-year="2024"
                  data-end-year="2028"
                  ng-on-angularcss:calendar-select="picker.selectTimeDate($event.detail.value)"
                >
                  <header>
                    <button
                      type="button"
                      size="icon"
                      variant="ghost"
                      aria-label="Previous appointment month"
                    >
                      <svg
                        viewBox="0 0 24 24"
                        fill="none"
                        stroke="currentColor"
                        stroke-width="2"
                        aria-hidden="true"
                      >
                        <path d="m15 18-6-6 6-6"></path>
                      </svg>
                    </button>
                    <h3></h3>
                    <button
                      type="button"
                      size="icon"
                      variant="ghost"
                      aria-label="Next appointment month"
                    >
                      <svg
                        viewBox="0 0 24 24"
                        fill="none"
                        stroke="currentColor"
                        stroke-width="2"
                        aria-hidden="true"
                      >
                        <path d="m9 18 6-6-6-6"></path>
                      </svg>
                    </button>
                  </header>
                  <div></div>
                </section>
              </aside>
            </span>
          </div>
          <div class="field">
            <label for="date-picker-time-value">Time</label>
            <input
              id="date-picker-time-value"
              type="time"
              step="1"
              ng-model="picker.time"
            />
          </div>
        </div>
      </section>
    </main>
  </body>
</html>

Date Picker With Dropdowns

This focused composition keeps the Popover open after selecting a date so the user can change the month or year and confirm with Done. Selection and the Done command are implemented in the locally bundled TypeScript adapter.

View source
<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>AngularCSS Date Picker With Dropdowns</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/date-picker-demo.umd.js"></script>
  </head>
  <body
    ng-app="datePickerDemo"
    ng-controller="DatePickerDemoController as picker"
    ng-cloak
    data-example="data-picker-with-dropdowns"
  >
    <main class="date-picker-dropdown-demo visual-example">
      <div class="date-picker-dropdown-field field">
        <label for="date-picker-with-dropdowns">Date</label>
        <span id="date-picker-dropdown-popover">
          <button
            id="date-picker-with-dropdowns"
            type="button"
            variant="outline"
            class="date-picker-trigger date-picker-trigger-between"
            popovertarget="popover-date-picker-with-dropdowns-1-content"
          >
            <span
              >{{ picker.dropdownDate ? picker.format(picker.dropdownDate) :
              'Pick a date' }}</span
            >
            <svg
              viewBox="0 0 24 24"
              fill="none"
              stroke="currentColor"
              stroke-width="2"
              aria-hidden="true"
            >
              <path d="m6 9 6 6 6-6"></path>
            </svg>
          </button>
          <aside
            class="calendar-popover-content date-picker-dropdown-content"
            aria-label="Choose a date with dropdowns"
            id="popover-date-picker-with-dropdowns-1-content"
            side="bottom"
            align="start"
            popover
          >
            <section
              ng-calendar
              data-calendar-generated
              data-month="2026-09"
              data-caption-layout="dropdown"
              data-start-year="2020"
              data-end-year="2030"
              ng-attr-data-value="{{ picker.dropdownDate }}"
              ng-on-angularcss:calendar-select="picker.selectDropdown($event.detail.value)"
            >
              <header>
                <button
                  type="button"
                  size="icon"
                  variant="ghost"
                  aria-label="Previous dropdown month"
                >
                  <svg
                    viewBox="0 0 24 24"
                    fill="none"
                    stroke="currentColor"
                    stroke-width="2"
                    aria-hidden="true"
                  >
                    <path d="m15 18-6-6 6-6"></path>
                  </svg>
                </button>
                <h3></h3>
                <button
                  type="button"
                  size="icon"
                  variant="ghost"
                  aria-label="Next dropdown month"
                >
                  <svg
                    viewBox="0 0 24 24"
                    fill="none"
                    stroke="currentColor"
                    stroke-width="2"
                    aria-hidden="true"
                  >
                    <path d="m9 18 6-6-6-6"></path>
                  </svg>
                </button>
              </header>
              <div></div>
            </section>
            <footer class="date-picker-dropdown-footer">
              <button
                type="button"
                variant="outline"
                size="sm"
                ng-click="picker.finishDropdown()"
              >
                Done
              </button>
            </footer>
          </aside>
        </span>
      </div>
    </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-calendar]. Importing the package registers it with the AngularCSS angular.css module; there is no per-component JavaScript registration step.

Anatomy

Directive selectors

  • ng-calendar

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
aria-currentInput/outputCurrent item or date state.
aria-disabledOutputSemantic disabled state.
aria-labelInput/outputAccessible name when visible text is insufficient.
aria-labelledbyOutputID of the element that supplies the accessible name.
aria-liveOutputAnnouncement priority for updates to a live region.
aria-pressedInput/outputPressed state of a toggle control.
data-bookedOutputWhether a generated calendar day is booked.
data-booked-datesInputComma-separated ISO dates styled as booked.
data-calendar-generatedInputEnables generated month markup inside the authored Calendar shell.
data-calendar-presetInputGenerated calendar preset: single, multiple, or range.
data-caption-layoutInputCaption controls: label or dropdown.
data-columnsInputNumber of columns used for calendar grid keyboard movement; defaults to 7.
data-disabled-afterInputLast selectable date as an ISO date.
data-disabled-beforeInputFirst selectable date as an ISO date.
data-disabled-datesInputComma-separated ISO dates that cannot be selected.
data-end-yearInputFinal year offered by a dropdown caption.
data-min-nightsInputMinimum number of nights accepted by range selection.
data-monthInput/outputDisplayed month in YYYY-MM form.
data-monthsOutputNumber of month grids currently rendered.
data-number-of-monthsInputNumber of consecutive months to render.
data-outsideInput/outputWhether a day belongs to an adjacent month.
data-range-endOutputMarks the final day in the selected range.
data-range-end-valueInput/outputSelected range end as an ISO date.
data-range-invalidOutputWhether the pending range violates the minimum-night constraint.
data-range-middleOutputMarks a day between the selected range boundaries.
data-range-startOutputMarks the first day in the selected range.
data-range-start-valueInput/outputSelected range start as an ISO date.
data-selection-modeInputSelection behavior: single, multiple, or range.
data-show-outside-daysInputShows dates from adjacent months when true.
data-show-week-numbersInput/outputShows ISO-style week numbers when true.
data-start-yearInputFirst year offered by a dropdown caption.
data-valueInput/outputSelected ISO date for single selection.
data-valuesInput/outputComma-separated selected ISO dates for multiple selection.
data-week-startInputFirst weekday as an integer from 0 (Sunday) to 6 (Saturday).
dirInputText and interaction direction: ltr or rtl.
langInputLanguage used for generated labels and localized text.
selectedOutputSelected value reflected by the component.
tabindexInput/outputKeyboard focus order for composite descendants.
valueOutputNative value or authored component value.

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
--calendar-cell-sizeWidth and height of calendar controls and day cells.

DOM events

  • angularcss:calendar-month-change
  • angularcss:calendar-range-invalid
  • angularcss:calendar-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 generated Gregorian month grids, local-date navigation, selectable day state, range and multiple-selection signaling, disabled and booked constraints, caption controls, week numbers, keyboard grid movement, and synchronized authored attributes. AngularTS remains responsible for application models, parsed text, commands, validation, and composed popover state. Natural-language and non-Gregorian conversion remain explicit application adapters; the packaged Date Picker demo uses locally bundled chrono-node without adding a second model implementation.

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

Accessibility

Give the calendar or its visible title a useful accessible name. Generated weekdays are column headers; week numbers are row headers; day buttons expose selected, current, disabled, outside, booked, and range state. Arrow keys, Home, End, Page Up, and Page Down move through the date grid, while RTL reverses horizontal movement. Date Picker triggers must keep an accessible name and preserve focus through the composed Popover.

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

2 - carousel

Accessible drag and snap carousel powered by Embla

Use a required content viewport and track around authored slide items. The TypeScript directive initializes the locally bundled Embla engine, synchronizes controls and accessible state, and supports pointer dragging and keyboard navigation.

<section
  ng-carousel
  align="start"
  style="--carousel-item-size: 50%; --carousel-gap: 0.5rem"
>
  <div>
    <ul>
      <li>Slide</li>
    </ul>
  </div>
</section>

Use orientation="vertical" for vertical movement and dir="rtl" for RTL. --carousel-item-size controls single or multi-item layouts and --carousel-gap controls spacing. Add autoplay and optionally autoplay-delay="2000" for the same locally bundled plugin behavior as the checked-in reference. The root mirrors snap index, snap count, item count, and boundary state.

angularcss:carousel-ready and angularcss:carousel-change expose the Embla API, zero-based snap index, snap count, selected item index, selected item, and total item count. Bind those events with AngularTS when the application needs a counter; AngularCSS does not create or replace an AngularTS model.

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 Carousel</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="carousel-demo">
    <main class="visual-example">
      <section ng-carousel class="carousel-demo" aria-label="Numbered slides">
        <div>
          <ul>
            <li>
              <div class="carousel-item-padding">
                <article class="card">
                  <section class="carousel-square">
                    <span>1</span>
                  </section>
                </article>
              </div>
            </li>
            <li>
              <div class="carousel-item-padding">
                <article class="card">
                  <section class="carousel-square">
                    <span>2</span>
                  </section>
                </article>
              </div>
            </li>
            <li>
              <div class="carousel-item-padding">
                <article class="card">
                  <section class="carousel-square">
                    <span>3</span>
                  </section>
                </article>
              </div>
            </li>
            <li>
              <div class="carousel-item-padding">
                <article class="card">
                  <section class="carousel-square">
                    <span>4</span>
                  </section>
                </article>
              </div>
            </li>
            <li>
              <div class="carousel-item-padding">
                <article class="card">
                  <section class="carousel-square">
                    <span>5</span>
                  </section>
                </article>
              </div>
            </li>
          </ul>
        </div>
        <button
          size="icon-sm"
          variant="outline"
          type="button"
          aria-label="Previous slide"
        >
          <svg
            viewBox="0 0 24 24"
            fill="none"
            stroke="currentColor"
            stroke-width="2"
            aria-hidden="true"
          >
            <path d="m15 18-6-6 6-6" />
          </svg>
        </button>
        <button
          size="icon-sm"
          variant="outline"
          type="button"
          aria-label="Next slide"
        >
          <svg
            viewBox="0 0 24 24"
            fill="none"
            stroke="currentColor"
            stroke-width="2"
            aria-hidden="true"
          >
            <path d="m9 18 6-6-6-6" />
          </svg>
        </button>
      </section>
    </main>
  </body>
</html>

Behavior workflows

The following functional page covers API state, multiple visible items, vertical orientation, and autoplay.

View source
<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>AngularCSS Carousel 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="apiState={index:0,count:5}; autoplayState={index:0,count:5}"
    data-example="carousel-api carousel-multiple carousel-orientation carousel-plugin"
  >
    <main
      class="carousel-workflow-grid"
      aria-label="Carousel behavior workflows"
    >
      <section class="carousel-workflow" aria-labelledby="carousel-api-heading">
        <h2 id="carousel-api-heading">API</h2>
        <section
          ng-carousel
          class="carousel-api-demo"
          aria-label="API numbered slides"
          ng-on-angularcss:carousel-ready="apiState=$event.detail"
          ng-on-angularcss:carousel-change="apiState=$event.detail"
        >
          <div>
            <ul>
              <li>
                <article class="card">
                  <section class="carousel-square">
                    <span>1</span>
                  </section>
                </article>
              </li>
              <li>
                <article class="card">
                  <section class="carousel-square">
                    <span>2</span>
                  </section>
                </article>
              </li>
              <li>
                <article class="card">
                  <section class="carousel-square">
                    <span>3</span>
                  </section>
                </article>
              </li>
              <li>
                <article class="card">
                  <section class="carousel-square">
                    <span>4</span>
                  </section>
                </article>
              </li>
              <li>
                <article class="card">
                  <section class="carousel-square">
                    <span>5</span>
                  </section>
                </article>
              </li>
            </ul>
          </div>
          <button
            size="icon-sm"
            variant="outline"
            type="button"
            aria-label="Previous API slide"
          >
            <svg
              viewBox="0 0 24 24"
              fill="none"
              stroke="currentColor"
              stroke-width="2"
              aria-hidden="true"
            >
              <path d="m15 18-6-6 6-6" />
            </svg>
          </button>
          <button
            size="icon-sm"
            variant="outline"
            type="button"
            aria-label="Next API slide"
          >
            <svg
              viewBox="0 0 24 24"
              fill="none"
              stroke="currentColor"
              stroke-width="2"
              aria-hidden="true"
            >
              <path d="m9 18 6-6-6-6" />
            </svg>
          </button>
        </section>
        <output class="carousel-status" aria-live="polite">
          Slide {{ apiState.index + 1 }} of {{ apiState.count }}
        </output>
      </section>

      <section
        class="carousel-workflow"
        aria-labelledby="carousel-multiple-heading"
      >
        <h2 id="carousel-multiple-heading">Multiple items</h2>
        <section
          ng-carousel
          align="start"
          class="carousel-multiple-demo"
          aria-label="Multiple numbered slides"
        >
          <div>
            <ul>
              <li>
                <div class="carousel-item-padding">
                  <article class="card">
                    <section class="carousel-square carousel-square-medium">
                      <span>1</span>
                    </section>
                  </article>
                </div>
              </li>
              <li>
                <div class="carousel-item-padding">
                  <article class="card">
                    <section class="carousel-square carousel-square-medium">
                      <span>2</span>
                    </section>
                  </article>
                </div>
              </li>
              <li>
                <div class="carousel-item-padding">
                  <article class="card">
                    <section class="carousel-square carousel-square-medium">
                      <span>3</span>
                    </section>
                  </article>
                </div>
              </li>
              <li>
                <div class="carousel-item-padding">
                  <article class="card">
                    <section class="carousel-square carousel-square-medium">
                      <span>4</span>
                    </section>
                  </article>
                </div>
              </li>
              <li>
                <div class="carousel-item-padding">
                  <article class="card">
                    <section class="carousel-square carousel-square-medium">
                      <span>5</span>
                    </section>
                  </article>
                </div>
              </li>
            </ul>
          </div>
          <button
            size="icon-sm"
            variant="outline"
            type="button"
            aria-label="Previous group"
          >
            <svg
              viewBox="0 0 24 24"
              fill="none"
              stroke="currentColor"
              stroke-width="2"
              aria-hidden="true"
            >
              <path d="m15 18-6-6 6-6" />
            </svg>
          </button>
          <button
            size="icon-sm"
            variant="outline"
            type="button"
            aria-label="Next group"
          >
            <svg
              viewBox="0 0 24 24"
              fill="none"
              stroke="currentColor"
              stroke-width="2"
              aria-hidden="true"
            >
              <path d="m9 18 6-6-6-6" />
            </svg>
          </button>
        </section>
      </section>

      <section
        class="carousel-workflow"
        aria-labelledby="carousel-orientation-heading"
      >
        <h2 id="carousel-orientation-heading">Vertical orientation</h2>
        <section
          ng-carousel
          orientation="vertical"
          align="start"
          class="carousel-vertical-demo"
          aria-label="Vertical numbered slides"
        >
          <div>
            <ul>
              <li>
                <div class="carousel-item-padding">
                  <article class="card">
                    <section class="carousel-vertical-card">
                      <span>1</span>
                    </section>
                  </article>
                </div>
              </li>
              <li>
                <div class="carousel-item-padding">
                  <article class="card">
                    <section class="carousel-vertical-card">
                      <span>2</span>
                    </section>
                  </article>
                </div>
              </li>
              <li>
                <div class="carousel-item-padding">
                  <article class="card">
                    <section class="carousel-vertical-card">
                      <span>3</span>
                    </section>
                  </article>
                </div>
              </li>
              <li>
                <div class="carousel-item-padding">
                  <article class="card">
                    <section class="carousel-vertical-card">
                      <span>4</span>
                    </section>
                  </article>
                </div>
              </li>
              <li>
                <div class="carousel-item-padding">
                  <article class="card">
                    <section class="carousel-vertical-card">
                      <span>5</span>
                    </section>
                  </article>
                </div>
              </li>
            </ul>
          </div>
          <button
            size="icon-sm"
            variant="outline"
            type="button"
            aria-label="Previous vertical slide"
          >
            <svg
              viewBox="0 0 24 24"
              fill="none"
              stroke="currentColor"
              stroke-width="2"
              aria-hidden="true"
            >
              <path d="m15 18-6-6 6-6" />
            </svg>
          </button>
          <button
            size="icon-sm"
            variant="outline"
            type="button"
            aria-label="Next vertical slide"
          >
            <svg
              viewBox="0 0 24 24"
              fill="none"
              stroke="currentColor"
              stroke-width="2"
              aria-hidden="true"
            >
              <path d="m9 18 6-6-6-6" />
            </svg>
          </button>
        </section>
      </section>

      <section
        class="carousel-workflow"
        aria-labelledby="carousel-plugin-heading"
      >
        <h2 id="carousel-plugin-heading">Autoplay plugin</h2>
        <section
          ng-carousel
          autoplay
          autoplay-delay="2000"
          class="carousel-api-demo"
          aria-label="Autoplay numbered slides"
          ng-on-angularcss:carousel-ready="autoplayState=$event.detail"
          ng-on-angularcss:carousel-change="autoplayState=$event.detail"
        >
          <div>
            <ul>
              <li>
                <div class="carousel-item-padding">
                  <article class="card">
                    <section class="carousel-square">
                      <span>1</span>
                    </section>
                  </article>
                </div>
              </li>
              <li>
                <div class="carousel-item-padding">
                  <article class="card">
                    <section class="carousel-square">
                      <span>2</span>
                    </section>
                  </article>
                </div>
              </li>
              <li>
                <div class="carousel-item-padding">
                  <article class="card">
                    <section class="carousel-square">
                      <span>3</span>
                    </section>
                  </article>
                </div>
              </li>
              <li>
                <div class="carousel-item-padding">
                  <article class="card">
                    <section class="carousel-square">
                      <span>4</span>
                    </section>
                  </article>
                </div>
              </li>
              <li>
                <div class="carousel-item-padding">
                  <article class="card">
                    <section class="carousel-square">
                      <span>5</span>
                    </section>
                  </article>
                </div>
              </li>
            </ul>
          </div>
          <button
            size="icon-sm"
            variant="outline"
            type="button"
            aria-label="Previous autoplay slide"
          >
            <svg
              viewBox="0 0 24 24"
              fill="none"
              stroke="currentColor"
              stroke-width="2"
              aria-hidden="true"
            >
              <path d="m15 18-6-6 6-6" />
            </svg>
          </button>
          <button
            size="icon-sm"
            variant="outline"
            type="button"
            aria-label="Next autoplay slide"
          >
            <svg
              viewBox="0 0 24 24"
              fill="none"
              stroke="currentColor"
              stroke-width="2"
              aria-hidden="true"
            >
              <path d="m9 18 6-6-6-6" />
            </svg>
          </button>
        </section>
        <output class="carousel-status" aria-live="polite">
          Autoplay slide {{ autoplayState.index + 1 }} of {{ autoplayState.count
          }}
        </output>
      </section>
    </main>
  </body>
</html>

Layout workflows

RTL direction, responsive item sizing, and custom spacing remain semantic HTML and authored CSS composition.

View source
<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>AngularCSS Carousel 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
    data-example="carousel-rtl carousel-size carousel-spacing"
  >
    <main
      class="carousel-composition-grid"
      aria-label="Carousel layout compositions"
    >
      <section
        class="carousel-workflow"
        aria-labelledby="carousel-rtl-heading"
        dir="rtl"
        lang="ar"
      >
        <h2 id="carousel-rtl-heading">من اليمين إلى اليسار</h2>
        <section
          ng-carousel
          dir="rtl"
          class="carousel-demo"
          aria-label="شرائح مرقمة"
        >
          <div>
            <ul>
              <li>
                <div class="carousel-item-padding">
                  <article class="card">
                    <section class="carousel-square">
                      <span>١</span>
                    </section>
                  </article>
                </div>
              </li>
              <li>
                <div class="carousel-item-padding">
                  <article class="card">
                    <section class="carousel-square">
                      <span>٢</span>
                    </section>
                  </article>
                </div>
              </li>
              <li>
                <div class="carousel-item-padding">
                  <article class="card">
                    <section class="carousel-square">
                      <span>٣</span>
                    </section>
                  </article>
                </div>
              </li>
              <li>
                <div class="carousel-item-padding">
                  <article class="card">
                    <section class="carousel-square">
                      <span>٤</span>
                    </section>
                  </article>
                </div>
              </li>
              <li>
                <div class="carousel-item-padding">
                  <article class="card">
                    <section class="carousel-square">
                      <span>٥</span>
                    </section>
                  </article>
                </div>
              </li>
            </ul>
          </div>
          <button
            size="icon-sm"
            variant="outline"
            type="button"
            aria-label="الشريحة السابقة"
          >
            <svg
              viewBox="0 0 24 24"
              fill="none"
              stroke="currentColor"
              stroke-width="2"
              aria-hidden="true"
            >
              <path d="m15 18-6-6 6-6" />
            </svg>
          </button>
          <button
            size="icon-sm"
            variant="outline"
            type="button"
            aria-label="الشريحة التالية"
          >
            <svg
              viewBox="0 0 24 24"
              fill="none"
              stroke="currentColor"
              stroke-width="2"
              aria-hidden="true"
            >
              <path d="m9 18 6-6-6-6" />
            </svg>
          </button>
        </section>
      </section>

      <section
        class="carousel-workflow"
        aria-labelledby="carousel-size-heading"
      >
        <h2 id="carousel-size-heading">Responsive size</h2>
        <section
          ng-carousel
          align="start"
          class="carousel-size-demo"
          aria-label="Sized numbered slides"
        >
          <div>
            <ul>
              <li>
                <div class="carousel-item-padding">
                  <article class="card">
                    <section class="carousel-square carousel-square-medium">
                      <span>1</span>
                    </section>
                  </article>
                </div>
              </li>
              <li>
                <div class="carousel-item-padding">
                  <article class="card">
                    <section class="carousel-square carousel-square-medium">
                      <span>2</span>
                    </section>
                  </article>
                </div>
              </li>
              <li>
                <div class="carousel-item-padding">
                  <article class="card">
                    <section class="carousel-square carousel-square-medium">
                      <span>3</span>
                    </section>
                  </article>
                </div>
              </li>
              <li>
                <div class="carousel-item-padding">
                  <article class="card">
                    <section class="carousel-square carousel-square-medium">
                      <span>4</span>
                    </section>
                  </article>
                </div>
              </li>
              <li>
                <div class="carousel-item-padding">
                  <article class="card">
                    <section class="carousel-square carousel-square-medium">
                      <span>5</span>
                    </section>
                  </article>
                </div>
              </li>
            </ul>
          </div>
          <button
            size="icon-sm"
            variant="outline"
            type="button"
            aria-label="Previous sized slide"
          >
            <svg
              viewBox="0 0 24 24"
              fill="none"
              stroke="currentColor"
              stroke-width="2"
              aria-hidden="true"
            >
              <path d="m15 18-6-6 6-6" />
            </svg>
          </button>
          <button
            size="icon-sm"
            variant="outline"
            type="button"
            aria-label="Next sized slide"
          >
            <svg
              viewBox="0 0 24 24"
              fill="none"
              stroke="currentColor"
              stroke-width="2"
              aria-hidden="true"
            >
              <path d="m9 18 6-6-6-6" />
            </svg>
          </button>
        </section>
      </section>

      <section
        class="carousel-workflow carousel-composition-wide"
        aria-labelledby="carousel-spacing-heading"
      >
        <h2 id="carousel-spacing-heading">Custom spacing</h2>
        <section
          ng-carousel
          align="start"
          class="carousel-spacing-demo"
          aria-label="Tightly spaced numbered slides"
        >
          <div>
            <ul>
              <li>
                <div class="carousel-item-padding">
                  <article class="card">
                    <section class="carousel-square carousel-square-small">
                      <span>1</span>
                    </section>
                  </article>
                </div>
              </li>
              <li>
                <div class="carousel-item-padding">
                  <article class="card">
                    <section class="carousel-square carousel-square-small">
                      <span>2</span>
                    </section>
                  </article>
                </div>
              </li>
              <li>
                <div class="carousel-item-padding">
                  <article class="card">
                    <section class="carousel-square carousel-square-small">
                      <span>3</span>
                    </section>
                  </article>
                </div>
              </li>
              <li>
                <div class="carousel-item-padding">
                  <article class="card">
                    <section class="carousel-square carousel-square-small">
                      <span>4</span>
                    </section>
                  </article>
                </div>
              </li>
              <li>
                <div class="carousel-item-padding">
                  <article class="card">
                    <section class="carousel-square carousel-square-small">
                      <span>5</span>
                    </section>
                  </article>
                </div>
              </li>
            </ul>
          </div>
          <button
            size="icon-sm"
            variant="outline"
            type="button"
            aria-label="Previous tightly spaced slide"
          >
            <svg
              viewBox="0 0 24 24"
              fill="none"
              stroke="currentColor"
              stroke-width="2"
              aria-hidden="true"
            >
              <path d="m15 18-6-6 6-6" />
            </svg>
          </button>
          <button
            size="icon-sm"
            variant="outline"
            type="button"
            aria-label="Next tightly spaced slide"
          >
            <svg
              viewBox="0 0 24 24"
              fill="none"
              stroke="currentColor"
              stroke-width="2"
              aria-hidden="true"
            >
              <path d="m9 18 6-6-6-6" />
            </svg>
          </button>
        </section>
      </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-carousel]. Importing the package registers it with the AngularCSS angular.css module; there is no per-component JavaScript registration step.

Anatomy

Directive selectors

  • ng-carousel

Semantic structure

The content viewport and its direct track child are required. Items must be direct track children. Navigation controls and dots are optional.

API

Attributes and state

AttributeAccessPurpose
alignInputCross-axis alignment: start, center, or end.
aria-currentOutputCurrent item or date state.
aria-disabledOutputSemantic disabled state.
aria-hiddenOutputWhether generated or collapsed content is hidden from assistive technology.
aria-labelInput/outputAccessible name when visible text is insufficient.
aria-roledescriptionOutputHuman-readable description of the component role.
autoplayInputEnables the locally bundled Embla autoplay plugin.
autoplay-delayInputAutoplay delay in milliseconds.
contain-scrollInputEmbla scroll containment mode.
dirInputText and interaction direction: ltr or rtl.
drag-freeInputAllows free dragging between snap points.
draggableInputSet to false to disable pointer dragging.
loopInputAllows navigation to wrap from the final item to the first.
orientationInput/outputLayout direction: horizontal or vertical.
roleInput/outputExplicit semantic role when native HTML does not provide one.
skip-snapsInputAllows momentum to skip snap points.
slides-to-scrollInputNumber of slides advanced as one snap group.
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
--carousel-gapGap between slides; defaults to four spacing units.
--carousel-item-sizeSlide basis; defaults to 100%.

DOM events

  • angularcss:carousel-change
  • angularcss:carousel-ready

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 uses Embla to own drag gestures, snap selection, orientation, loop boundaries, control availability, and optional autoplay. AngularTS remains responsible for counters, business actions, and other application state consumed from the carousel DOM events.

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

Accessibility

The root is an accessible carousel region, every authored item is exposed as a labeled slide, and unavailable previous or next controls are disabled. Give the region a useful accessible name and keep authored slide content semantic.

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

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

4 - command

Command palette layout

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

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-hiddenInput/outputWhether generated or collapsed content is hidden from assistive technology.
aria-labelledbyOutputID of the element that supplies the accessible name.
aria-orientationOutputInteraction axis exposed to assistive technology.
aria-selectedInput/outputSelected item state.
dirInputText and interaction direction: ltr or rtl.
disabledInputDisables native or component interaction.
hiddenInputNative visibility state observed when finding available items.
roleOutputExplicit semantic role when native HTML does not provide one.
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

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.

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

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

7 - hover-card

Rich hover preview cards

Use ng-hover-card with a keyboard-focusable trigger and preview content. Pointer disclosure honors optional open-delay and close-delay values in milliseconds. Set side on the content to left, top, bottom, or right.

<span ng-hover-card open-delay="100" close-delay="100">
  <a href="#">@angularcss</a>
  <aside side="bottom">Preview</aside>
</span>

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 Hover Card</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="hover-card-demo">
    <main class="visual-example">
      <span ng-hover-card open-delay="10" close-delay="100">
        <button type="button" variant="link">Hover Here</button>
        <aside>
          <h3>@nextjs</h3>
          <p>The React Framework - created and maintained by @vercel.</p>
          <footer>Joined December 2021</footer>
        </aside>
      </span>
    </main>
  </body>
</html>

Physical sides

View source
<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>AngularCSS Hover Card 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 data-example="hover-card-sides">
    <main class="hover-card-workflow">
      <header class="hover-card-workflow-header">
        <h2>Physical sides</h2>
      </header>
      <div class="hover-card-side-grid">
        <section class="hover-card-stage hover-card-stage-left">
          <span ng-hover-card open-delay="20" close-delay="100">
            <button type="button" variant="outline">Left</button>
            <aside side="left">
              <h3>Hover Card</h3>
              <p>This hover card appears on the left side of the trigger.</p>
            </aside>
          </span>
        </section>

        <section class="hover-card-stage hover-card-stage-top">
          <span ng-hover-card open-delay="20" close-delay="100">
            <button type="button" variant="outline">Top</button>
            <aside side="top">
              <h3>Hover Card</h3>
              <p>This hover card appears on the top side of the trigger.</p>
            </aside>
          </span>
        </section>

        <section class="hover-card-stage hover-card-stage-bottom">
          <span ng-hover-card open-delay="20" close-delay="100">
            <button type="button" variant="outline">Bottom</button>
            <aside side="bottom">
              <h3>Hover Card</h3>
              <p>This hover card appears on the bottom side of the trigger.</p>
            </aside>
          </span>
        </section>

        <section class="hover-card-stage hover-card-stage-right">
          <span ng-hover-card open-delay="20" close-delay="100">
            <button type="button" variant="outline">Right</button>
            <aside side="right">
              <h3>Hover Card</h3>
              <p>This hover card appears on the right side of the trigger.</p>
            </aside>
          </span>
        </section>
      </div>

      <footer class="hover-card-disabled-example">
        <span ng-hover-card open-delay="0" close-delay="0">
          <button type="button" variant="outline" disabled>Disabled</button>
          <aside>
            <p>Unavailable preview</p>
          </aside>
        </span>
      </footer>
    </main>
  </body>
</html>

Right-to-left

Physical left and right placement stays physical while text direction and content alignment follow the nearest authored dir attribute.

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 Hover Card 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 data-example="hover-card-rtl">
    <main class="hover-card-workflow" dir="rtl">
      <header class="hover-card-workflow-header">
        <h2>الجوانب الفعلية</h2>
      </header>
      <div class="hover-card-side-grid">
        <section class="hover-card-stage hover-card-stage-left">
          <span ng-hover-card open-delay="20" close-delay="100">
            <button type="button" variant="outline">يسار</button>
            <aside side="left">
              <h3>سماعات لاسلكية</h3>
              <p>٩٩.٩٩ $</p>
            </aside>
          </span>
        </section>

        <section class="hover-card-stage hover-card-stage-top">
          <span ng-hover-card open-delay="20" close-delay="100">
            <button type="button" variant="outline">أعلى</button>
            <aside side="top">
              <h3>سماعات لاسلكية</h3>
              <p>٩٩.٩٩ $</p>
            </aside>
          </span>
        </section>

        <section class="hover-card-stage hover-card-stage-bottom">
          <span ng-hover-card open-delay="20" close-delay="100">
            <button type="button" variant="outline">أسفل</button>
            <aside side="bottom">
              <h3>سماعات لاسلكية</h3>
              <p>٩٩.٩٩ $</p>
            </aside>
          </span>
        </section>

        <section class="hover-card-stage hover-card-stage-right">
          <span ng-hover-card open-delay="20" close-delay="100">
            <button type="button" variant="outline">يمين</button>
            <aside side="right">
              <h3>سماعات لاسلكية</h3>
              <p>٩٩.٩٩ $</p>
            </aside>
          </span>
        </section>
      </div>
    </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-hover-card]. Importing the package registers it with the AngularCSS angular.css module; there is no per-component JavaScript registration step.

Anatomy

Directive selectors

  • ng-hover-card

Semantic structure

A keyboard-focusable trigger and one preview content element are required. Title and description slots are optional semantic styling hooks inside the preview.

API

Attributes and state

AttributeAccessPurpose
aria-controlsOutputID of the element controlled by a trigger.
aria-expandedOutputOpen or expanded state exposed to assistive technology.
aria-hiddenOutputWhether generated or collapsed content is hidden from assistive technology.
close-delayInputPointer close delay in milliseconds.
openInputInitial or controlled open state.
open-delayInputPointer open delay in milliseconds.
sideInput/outputPhysical placement: left, top, bottom, or right.

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 owns delayed pointer and focus disclosure, physical side placement, Escape closure, and synchronized open state. It is non-modal and does not trap focus. Applications own preview content and may control the concise authored open attribute.

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

Accessibility

The trigger exposes aria-controls and aria-expanded; the preview exposes its hidden state without becoming modal. Keep the trigger keyboard focusable, preserve readable content order, and do not place essential information only inside a hover card.

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-hover-card], 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.

8 - menubar

Keyboard-first top-level navigation with open/close menu behavior.

Use ng-menubar around a strip of menu groups. Menu triggers support:

  • ArrowLeft/ArrowRight to move between menus
  • Enter/Space/ArrowDown to open a menu
  • Escape to close menus
  • Home/End shortcuts when a trigger is focused
<nav ng-menubar aria-label="Application menu">
  <section>
    <button>File</button>
    <menu>
      <button>New</button>
      <button>Open</button>
    </menu>
  </section>
  <section>
    <button>Edit</button>
  </section>
</nav>

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 Menubar</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="menubarDemo={bookmarks:false,fullUrls:true,profile:'benoit'}"
    data-example="menubar-demo"
  >
    <main class="visual-example">
      <nav ng-menubar aria-label="Browser application menu">
        <section>
          <button type="button">File</button>
          <menu>
            <section>
              <button type="button">New Tab <kbd>⌘T</kbd></button>
              <button type="button">New Window <kbd>⌘N</kbd></button>
              <button type="button" disabled>New Incognito Window</button>
            </section>
            <hr />
            <section>
              <details>
                <summary>Share</summary>
                <menu>
                  <section>
                    <button type="button">Email link</button>
                    <button type="button">Messages</button>
                    <button type="button">Notes</button>
                  </section>
                </menu>
              </details>
            </section>
            <hr />
            <section>
              <button type="button">Print... <kbd>⌘P</kbd></button>
            </section>
          </menu>
        </section>

        <section>
          <button type="button">Edit</button>
          <menu>
            <section>
              <button type="button">Undo <kbd>⌘Z</kbd></button>
              <button type="button">Redo <kbd>⇧⌘Z</kbd></button>
            </section>
            <hr />
            <section>
              <details>
                <summary>Find</summary>
                <menu>
                  <button type="button">Search the web</button>
                  <hr />
                  <button type="button">Find...</button>
                  <button type="button">Find Next</button>
                  <button type="button">Find Previous</button>
                </menu>
              </details>
            </section>
            <hr />
            <section>
              <button type="button">Cut</button>
              <button type="button">Copy</button>
              <button type="button">Paste</button>
            </section>
          </menu>
        </section>

        <section>
          <button type="button">View</button>
          <menu>
            <section>
              <button
                type="button"
                aria-checked="{{ menubarDemo.bookmarks }}"
                ng-click="menubarDemo.bookmarks=!menubarDemo.bookmarks"
              >
                Bookmarks Bar
              </button>
              <button
                type="button"
                aria-checked="{{ menubarDemo.fullUrls }}"
                ng-click="menubarDemo.fullUrls=!menubarDemo.fullUrls"
              >
                Full URLs
              </button>
            </section>
            <hr />
            <section>
              <button type="button" inset>Reload <kbd>⌘R</kbd></button>
              <button type="button" inset disabled>
                Force Reload <kbd>⇧⌘R</kbd>
              </button>
            </section>
            <hr />
            <button type="button" inset>Toggle Fullscreen</button>
            <hr />
            <button type="button" inset>Hide Sidebar</button>
          </menu>
        </section>

        <section>
          <button type="button">Profiles</button>
          <menu>
            <fieldset aria-label="Active profile">
              <button
                type="button"
                aria-checked="{{ menubarDemo.profile === 'andy' }}"
                ng-click="menubarDemo.profile='andy'"
              >
                Andy
              </button>
              <button
                type="button"
                aria-checked="{{ menubarDemo.profile === 'benoit' }}"
                ng-click="menubarDemo.profile='benoit'"
              >
                Benoit
              </button>
              <button
                type="button"
                aria-checked="{{ menubarDemo.profile === 'luis' }}"
                ng-click="menubarDemo.profile='luis'"
              >
                Luis
              </button>
            </fieldset>
            <hr />
            <button type="button" inset>Edit...</button>
            <hr />
            <button type="button" inset>Add Profile...</button>
          </menu>
        </section>
      </nav>
    </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-menubar]. Importing the package registers it with the AngularCSS angular.css module; there is no per-component JavaScript registration step.

Anatomy

Directive selectors

  • ng-menubar

Semantic structure

Each top-level section requires one native button 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
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-haspopupOutputType 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.
roleOutputExplicit semantic role when native HTML does not provide one.
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

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 owns top-level roving focus, menu and submenu disclosure, enabled-item navigation, Escape and outside-click closure, DOM-order synchronization for dynamically inserted menus, and direction-aware horizontal keys. AngularTS remains responsible for command execution, checkbox and radio values, and structural content such as ng-if.

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

Accessibility

The root exposes role="menubar" and keeps one enabled top-level trigger in the tab order. Triggers identify their menus with aria-controls; disabled triggers and items are skipped. Arrow keys follow visual direction, submenu keys remain local to the submenu, and Escape restores focus to the active trigger.

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

9 - navigation-menu

Site navigation with optional flyout content

Navigation menu is exposed as semantic nav markup with list, item, trigger, link, and content parts.

<nav ng-navigation-menu>
  <ul>
    <li>
      <button>Components</button>
      <section>Links</section>
    </li>
  </ul>
</nav>

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 Navigation 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 data-example="navigation-menu-demo">
    <main>
      <nav ng-navigation-menu aria-label="Primary navigation">
        <ul>
          <li>
            <button type="button">Getting started</button>
            <section>
              <ul>
                <li>
                  <a href="#introduction">
                    <strong>Introduction</strong>
                    <span>Reusable components built with semantic HTML.</span>
                  </a>
                </li>
                <li>
                  <a href="#installation">
                    <strong>Installation</strong>
                    <span
                      >How to install dependencies and structure your app.</span
                    >
                  </a>
                </li>
                <li>
                  <a href="#typography">
                    <strong>Typography</strong>
                    <span>Styles for headings, paragraphs, lists...etc</span>
                  </a>
                </li>
              </ul>
            </section>
          </li>

          <li>
            <button type="button">Components</button>
            <section>
              <ul>
                <li>
                  <a href="#alert-dialog">
                    <strong>Alert Dialog</strong>
                    <span
                      >A modal dialog with important content that expects a
                      response.</span
                    >
                  </a>
                </li>
                <li>
                  <a href="#hover-card">
                    <strong>Hover Card</strong>
                    <span>Preview content available behind a link.</span>
                  </a>
                </li>
                <li>
                  <a href="#progress">
                    <strong>Progress</strong>
                    <span>Displays the completion progress of a task.</span>
                  </a>
                </li>
                <li>
                  <a href="#scroll-area">
                    <strong>Scroll-area</strong>
                    <span>Visually or semantically separates content.</span>
                  </a>
                </li>
                <li>
                  <a href="#tabs">
                    <strong>Tabs</strong>
                    <span>Layered sections displayed one at a time.</span>
                  </a>
                </li>
                <li>
                  <a href="#tooltip">
                    <strong>Tooltip</strong>
                    <span>Information shown from keyboard focus or hover.</span>
                  </a>
                </li>
              </ul>
            </section>
          </li>

          <li>
            <button type="button">With Icon</button>
            <section>
              <ul>
                <li>
                  <a href="#backlog">
                    <svg
                      aria-hidden="true"
                      viewBox="0 0 24 24"
                      fill="none"
                      stroke="currentColor"
                      stroke-width="2"
                    >
                      <circle cx="12" cy="12" r="10"></circle>
                      <path d="M12 8v4m0 4h.01"></path>
                    </svg>
                    Backlog
                  </a>
                </li>
                <li>
                  <a href="#todo">
                    <svg
                      aria-hidden="true"
                      viewBox="0 0 24 24"
                      fill="none"
                      stroke="currentColor"
                      stroke-width="2"
                    >
                      <circle
                        cx="12"
                        cy="12"
                        r="10"
                        stroke-dasharray="4 3"
                      ></circle>
                    </svg>
                    To Do
                  </a>
                </li>
                <li>
                  <a href="#done">
                    <svg
                      aria-hidden="true"
                      viewBox="0 0 24 24"
                      fill="none"
                      stroke="currentColor"
                      stroke-width="2"
                    >
                      <circle cx="12" cy="12" r="10"></circle>
                      <path d="m8 12 2.5 2.5L16 9"></path>
                    </svg>
                    Done
                  </a>
                </li>
              </ul>
            </section>
          </li>

          <li>
            <a href="#docs">Docs</a>
          </li>
        </ul>
      </nav>
    </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-navigation-menu]. Importing the package registers it with the AngularCSS angular.css module; there is no per-component JavaScript registration step.

Anatomy

Directive selectors

  • ng-navigation-menu

Semantic structure

Use a native nav containing one direct list. Each list item may contain either a native link or a native button trigger followed by a semantic section. The root directive needs no child directives or anatomy classes.

API

Attributes and state

AttributeAccessPurpose
alignInputCross-axis alignment: start, center, or end.
aria-controlsOutputID of the element controlled by a trigger.
aria-expandedOutputOpen or expanded state exposed to assistive technology.
aria-hiddenInput/outputWhether 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.
disabledInputDisables native or component interaction.
openInputInitial or controlled open state.
roleInput/outputExplicit semantic role when native HTML does not provide one.

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
--navigation-menu-content-offsetComponent 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 site-navigation disclosure, focus restoration, direction-aware arrow movement, dynamic DOM-order synchronization, outside dismissal, and flyout collision handling. Native links continue to own navigation. URLs, routing, current-page state, authored controlled state, and application commands remain AngularTS or application concerns.

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

Accessibility

Use a native nav landmark containing a list. Keep destinations as native links and disclosure controls as native buttons; do not add menu or menuitem roles to site navigation. Triggers expose aria-expanded and aria-controls, direct links remain in horizontal keyboard order, and Escape restores focus to the active trigger.

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

10 - range-slider

Multiple native range inputs coordinated on one shared track.

Use ng-range-slider only when two or more native range inputs share one visual track. The parent inspects its direct inputs and computes composite geometry while every input retains native focus, keyboard, form, and AngularTS model behavior. Use the Range element for a single value.

<fieldset>
  <legend>Price range</legend>
  <div ng-range-slider min="0" max="100">
    <input
      aria-label="Minimum price"
      type="range"
      min="0"
      max="100"
      value="25"
    />
    <input
      aria-label="Maximum price"
      type="range"
      min="0"
      max="100"
      value="75"
    />
  </div>
</fieldset>

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 Range Slider</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="minimum = 25; maximum = 75"
    data-example="slider-demo"
  >
    <main class="visual-example">
      <header>
        <label id="price-range-label">Price range</label>
        <output>{{ minimum }}-{{ maximum }}</output>
      </header>
      <div
        ng-range-slider
        min="0"
        max="100"
        aria-labelledby="price-range-label"
      >
        <input
          aria-label="Minimum price"
          type="range"
          min="0"
          max="100"
          ng-model="minimum"
        />
        <input
          aria-label="Maximum price"
          type="range"
          min="0"
          max="100"
          ng-model="maximum"
        />
      </div>
    </main>
  </body>
</html>

Reference workflows

The workflow page covers controlled and multi-thumb ranges, disabled state, right-to-left direction, and vertical orientation. Every thumb uses the same absolute bounds so its native position remains accurate. Applications that require ordered or non-crossing values enforce that policy in AngularTS 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 Range Slider 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="slider = { controlledStart: 0.3, controlledEnd: 0.7, rangeStart: 25, rangeEnd: 50, multipleOne: 10, multipleTwo: 20, multipleThree: 70, rtl: 75, verticalOne: 50, verticalTwo: 25 }"
    data-example="slider-controlled slider-range slider-multiple slider-rtl slider-vertical"
  >
    <main class="slider-workflow-grid">
      <section
        class="slider-workflow-section"
        aria-labelledby="slider-controlled-title"
      >
        <header>
          <h2 id="slider-controlled-title">Temperature</h2>
          <output>
            {{ slider.controlledStart }}, {{ slider.controlledEnd }}
          </output>
        </header>
        <div ng-range-slider min="0" max="1" aria-label="Temperature range">
          <input
            type="range"
            min="0"
            max="1"
            step="0.1"
            ng-model="slider.controlledStart"
            aria-label="Minimum temperature"
          />
          <input
            type="range"
            min="0"
            max="1"
            step="0.1"
            ng-model="slider.controlledEnd"
            aria-label="Maximum temperature"
          />
        </div>
      </section>

      <section
        class="slider-workflow-section"
        aria-labelledby="slider-range-title"
      >
        <header>
          <h2 id="slider-range-title">Range</h2>
          <output>{{ slider.rangeStart }}–{{ slider.rangeEnd }}</output>
        </header>
        <div ng-range-slider min="0" max="100" aria-label="Price range">
          <input
            type="range"
            min="0"
            max="100"
            step="5"
            ng-model="slider.rangeStart"
            aria-label="Minimum price"
          />
          <input
            type="range"
            min="0"
            max="100"
            step="5"
            ng-model="slider.rangeEnd"
            aria-label="Maximum price"
          />
        </div>
      </section>

      <section
        class="slider-workflow-section"
        aria-labelledby="slider-multiple-title"
      >
        <header>
          <h2 id="slider-multiple-title">Multiple values</h2>
          <output>
            {{ slider.multipleOne }}, {{ slider.multipleTwo }}, {{
            slider.multipleThree }}
          </output>
        </header>
        <div
          ng-range-slider
          min="0"
          max="100"
          aria-label="Multiple slider values"
        >
          <input
            type="range"
            min="0"
            max="100"
            step="10"
            ng-model="slider.multipleOne"
            aria-label="First value"
          />
          <input
            type="range"
            min="0"
            max="100"
            step="10"
            ng-model="slider.multipleTwo"
            aria-label="Second value"
          />
          <input
            type="range"
            min="0"
            max="100"
            step="10"
            ng-model="slider.multipleThree"
            aria-label="Third value"
          />
        </div>
      </section>

      <section
        class="slider-workflow-section"
        dir="rtl"
        lang="ar"
        aria-labelledby="slider-rtl-title"
      >
        <header>
          <h2 id="slider-rtl-title">مستوى الصوت</h2>
          <output>{{ slider.rtl }}</output>
        </header>
        <input
          type="range"
          min="0"
          max="100"
          step="1"
          ng-model="slider.rtl"
          aria-label="مستوى الصوت"
        />
      </section>

      <section
        class="slider-workflow-section slider-workflow-wide"
        aria-labelledby="slider-vertical-title"
      >
        <header>
          <h2 id="slider-vertical-title">Vertical</h2>
          <output> {{ slider.verticalOne }}, {{ slider.verticalTwo }} </output>
        </header>
        <div class="slider-vertical-row">
          <input
            type="range"
            min="0"
            max="100"
            step="1"
            orientation="vertical"
            ng-model="slider.verticalOne"
            aria-label="First vertical value"
          />
          <input
            type="range"
            min="0"
            max="100"
            step="1"
            orientation="vertical"
            ng-model="slider.verticalTwo"
            aria-label="Second vertical value"
          />
        </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-range-slider]. Importing the package registers it with the AngularCSS angular.css module; there is no per-component JavaScript registration step.

Anatomy

Directive selectors

  • ng-range-slider

Semantic structure

Apply ng-range-slider to one container with two or more direct native input[type=range] children sharing the same minimum and maximum. Label every input independently. For one value, use a plain range input without an AngularCSS directive.

API

Attributes and state

AttributeAccessPurpose
aria-orientationOutputInteraction axis exposed to assistive technology.
dirInputText and interaction direction: ltr or rtl.
maxInputMaximum native or component value.
minInputMinimum native or component value.
orientationInput/outputLayout direction: horizontal or vertical.

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
--range-endComponent styling variable.
--range-startComponent styling variable.
--valueComponent 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 coordinates two or more native range inputs on one shared track. It computes only composite geometry and ARIA orientation; each native input and its AngularTS ng-model retain value, focus, keyboard, validation, and form ownership. Use a plain range element when only one value is required.

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

Accessibility

Associate every control with a visible label. Preserve native required, disabled, and invalid semantics, and connect help or error text with aria-describedby.

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-range-slider], 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.

11 - resizable

Pointer- and keyboard-resizable split panels powered by --panel-size CSS variables.

Use ng-resizable-panel-group with alternating native section and hr elements. Resize separators support pointer dragging and keyboard control.

<div ng-resizable-panel-group aria-label="Resizable layout">
  <section style="--panel-size: 1">Preview</section>
  <hr aria-label="Resize preview and details" />
  <section>Details</section>
</div>

Set orientation="vertical" on the group to stack panels. Handles receive separator roles, orientation, value bounds, current values, and aria-controls relationships. Dragging or pressing Arrow, Home, and End keys updates adjacent panel --panel-size values within data-min-size and data-max-size; RTL reverses horizontal changes. CSS renders a visible grip without additional markup.

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 Resizable</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="resizable-demo">
    <div
      ng-resizable-panel-group
      orientation="horizontal"
      aria-label="Resizable layout"
      class="visual-example"
    >
      <section style="--panel-size: 1">
        <div class="resizable-panel-content"><strong>One</strong></div>
      </section>
      <hr aria-label="Resize panel one and panels two and three" />
      <section style="--panel-size: 1">
        <div
          ng-resizable-panel-group
          orientation="vertical"
          aria-label="Resize panels two and three"
        >
          <section style="--panel-size: 1">
            <div class="resizable-panel-content"><strong>Two</strong></div>
          </section>
          <hr aria-label="Resize panel two and panel three" />
          <section style="--panel-size: 3">
            <div class="resizable-panel-content"><strong>Three</strong></div>
          </section>
        </div>
      </section>
    </div>
  </body>
</html>

Orientations and RTL

View source
<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>AngularCSS Resizable 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
    data-example="resizable-handle resizable-rtl resizable-vertical"
  >
    <main class="resizable-workflows visual-example">
      <section aria-label="Visible handle example">
        <div
          ng-resizable-panel-group
          id="handle-layout"
          orientation="horizontal"
          aria-label="Sidebar and content layout"
          class="resizable-reference-layout"
        >
          <section style="--panel-size: 1">
            <div class="resizable-panel-content"><strong>Sidebar</strong></div>
          </section>
          <hr aria-label="Resize sidebar and content" />
          <section style="--panel-size: 3">
            <div class="resizable-panel-content"><strong>Content</strong></div>
          </section>
        </div>
      </section>

      <section aria-label="Vertical layout example">
        <div
          ng-resizable-panel-group
          id="vertical-layout"
          orientation="vertical"
          data-step="0.5"
          aria-label="Header and content layout"
          class="resizable-reference-layout"
        >
          <section
            data-min-size="0.5"
            data-max-size="3.5"
            style="--panel-size: 1"
          >
            <div class="resizable-panel-content"><strong>Header</strong></div>
          </section>
          <hr aria-label="Resize header and content" />
          <section data-min-size="0.5" style="--panel-size: 3">
            <div class="resizable-panel-content"><strong>Content</strong></div>
          </section>
        </div>
      </section>

      <section
        class="resizable-workflow-wide"
        aria-label="Right to left nested layout example"
        lang="ar"
      >
        <div
          ng-resizable-panel-group
          id="rtl-layout"
          orientation="horizontal"
          aria-label="تخطيط قابل لتغيير الحجم"
          class="resizable-reference-layout"
          dir="rtl"
        >
          <section style="--panel-size: 1">
            <div class="resizable-panel-content"><strong>واحد</strong></div>
          </section>
          <hr aria-label="تغيير حجم اللوحات" />
          <section style="--panel-size: 1">
            <div
              ng-resizable-panel-group
              orientation="vertical"
              aria-label="تغيير حجم اللوحتين الثانية والثالثة"
              dir="rtl"
            >
              <section style="--panel-size: 1">
                <div class="resizable-panel-content">
                  <strong>اثنان</strong>
                </div>
              </section>
              <hr aria-label="تغيير حجم اللوحتين الثانية والثالثة" />
              <section style="--panel-size: 3">
                <div class="resizable-panel-content">
                  <strong>ثلاثة</strong>
                </div>
              </section>
            </div>
          </section>
        </div>
      </section>
    </main>
  </body>
</html>

Reactive Structure

View source
<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>AngularCSS Resizable 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="resizableState={orientation:'horizontal',firstSize:1,showThird:false}"
  >
    <main class="resizable-state-workflows">
      <section class="resizable-state-section" aria-labelledby="state-title">
        <h2 id="state-title">Reactive layout</h2>
        <div class="resizable-state-actions">
          <button
            size="sm"
            type="button"
            ng-click="resizableState.orientation=resizableState.orientation==='horizontal'?'vertical':'horizontal'"
          >
            Toggle orientation
          </button>
          <button size="sm" type="button" ng-click="resizableState.firstSize=2">
            Set first size to 2
          </button>
          <button
            size="sm"
            type="button"
            ng-click="resizableState.showThird=true"
          >
            Add third panel
          </button>
        </div>
        <div
          ng-resizable-panel-group
          id="state-layout"
          orientation="{{ resizableState.orientation }}"
          data-step="0.25"
          aria-label="Reactive resizable layout"
          class="resizable-reference-layout"
        >
          <section
            id="state-first-panel"
            style="--panel-size: {{ resizableState.firstSize }}"
          >
            <div class="resizable-panel-content"><strong>One</strong></div>
          </section>
          <hr
            id="state-first-handle"
            aria-label="Resize first and second panels"
          />
          <section style="--panel-size: 1">
            <div class="resizable-panel-content"><strong>Two</strong></div>
          </section>
          <hr
            ng-if="resizableState.showThird"
            id="state-inserted-handle"
            aria-label="Resize second and third panels"
          />
          <section
            ng-if="resizableState.showThird"
            id="state-inserted-panel"
            style="--panel-size: 1"
          >
            <div class="resizable-panel-content"><strong>Three</strong></div>
          </section>
        </div>
      </section>

      <section class="resizable-state-section" aria-labelledby="bounds-title">
        <h2 id="bounds-title">Bounded vertical layout</h2>
        <div
          ng-resizable-panel-group
          id="bounded-layout"
          orientation="vertical"
          data-step="0.5"
          aria-label="Bounded vertical layout"
          class="resizable-reference-layout"
        >
          <section
            data-min-size="0.5"
            data-max-size="2"
            style="--panel-size: 1"
          >
            <div class="resizable-panel-content"><strong>Top</strong></div>
          </section>
          <hr aria-label="Resize bounded panels" />
          <section data-min-size="0.5" style="--panel-size: 1">
            <div class="resizable-panel-content"><strong>Bottom</strong></div>
          </section>
        </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-resizable-panel-group]. Importing the package registers it with the AngularCSS angular.css module; there is no per-component JavaScript registration step.

Anatomy

Directive selectors

  • ng-resizable-panel-group

Semantic structure

Alternate direct .resizable-panel and .resizable-handle children inside each panel group. The root directive inspects those children; no child directives are required. Nested groups belong inside a panel.

API

Attributes and state

AttributeAccessPurpose
aria-controlsOutputID of the element controlled by a trigger.
aria-disabledInputSemantic disabled state.
aria-orientationInput/outputInteraction axis exposed to assistive technology.
aria-valuemaxOutputMaximum value exposed by an adjustable control.
aria-valueminOutputMinimum value exposed by an adjustable control.
aria-valuenowOutputCurrent value exposed by an adjustable control.
data-max-sizeInputLargest panel flex size allowed during resizing.
data-min-sizeInputSmallest panel flex size allowed during resizing.
data-resizingOutputPresent while a pointer resize operation is active.
data-stepInputPanel flex-size increment used by keyboard resizing.
dirInputText and interaction direction: ltr or rtl.
orientationInput/outputResize axis: horizontal or vertical.
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
--panel-sizeComponent 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 pairwise pointer and keyboard resizing, minimum and maximum bounds, direct-child panel/handle ownership, direction-aware deltas, and synchronized separator state. AngularTS or the application owns authored orientation, initial/external sizes, structural insertion, persistence, and business layout decisions.

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

Accessibility

Give each resize handle a concise accessible name. Handles expose separator semantics, the physical resize axis through aria-orientation, current and bounded values, and aria-controls relationships to both adjacent panels. Keyboard resizing follows text direction and preserves visible 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-resizable-panel-group], 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.

12 - sidebar

Application sidebar layout with collapsible state

Use ng-sidebar on an aside inside .sidebar-layout and connect native button triggers with aria-controls. Author physical side, visual variant, and collapse mode directly on the sidebar root.

<div>
  <aside id="app-sidebar" ng-sidebar side="left" collapsible="icon">
    <header>
      <a href="/">Acme Inc.</a>
    </header>
    <nav>
      <section>
        <h3>Workspace</h3>
        <div>
          <ul>
            <li>
              <a aria-current="page" href="/dashboard"> Dashboard </a>
            </li>
          </ul>
        </div>
      </section>
    </nav>
  </aside>
  <main>
    <button aria-controls="app-sidebar">Toggle sidebar</button>
  </main>
</div>

The directive reads root options and coordinates collapse, group relationships, and trigger accessibility state. AngularTS may control the boolean collapsed attribute; it continues to own filtering, shortcuts, routing, and actions. Compose nested disclosure with native details.disclosure and action menus with ng-dropdown-menu. Add responsive to an off-canvas sidebar to initialize it collapsed below 48rem and expanded at larger viewport widths.

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 Sidebar</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="sidebarOpen=true; sidebarQuery=''"
    ng-keydown="($event.ctrlKey || $event.metaKey) && $event.key === 'b' && (sidebarOpen = !sidebarOpen)"
    data-example="sidebar-demo sidebar-controlled"
  >
    <div class="visual-example">
      <aside
        id="app-sidebar"
        ng-sidebar
        collapsible="icon"
        ng-attr-collapsed="{{ sidebarOpen ? undefined : '' }}"
        class="sidebar-demo-panel"
        aria-label="Workspace navigation"
      >
        <header>
          <a href="#workspace" size="lg">
            <span class="sidebar-brand-mark" aria-hidden="true">A</span>
            <span class="sidebar-brand-copy">
              <strong>Acme Inc</strong>
              <small>Enterprise</small>
            </span>
          </a>
          <input
            ng-model="sidebarQuery"
            aria-label="Filter navigation"
            placeholder="Filter projects..."
          />
        </header>
        <hr />
        <nav>
          <section>
            <h3>Platform</h3>
            <ul>
              <li>
                <a href="#playground" aria-current="page">
                  <svg
                    viewBox="0 0 24 24"
                    fill="none"
                    stroke="currentColor"
                    stroke-width="2"
                    aria-hidden="true"
                  >
                    <rect x="4" y="4" width="16" height="16" rx="2"></rect>
                    <path d="m8 9 2 2-2 2M13 15h3"></path>
                  </svg>
                  <span>Playground</span>
                </a>
                <ul>
                  <li>
                    <a href="#history">History</a>
                  </li>
                  <li>
                    <a href="#starred">Starred</a>
                  </li>
                  <li>
                    <a href="#settings">Settings</a>
                  </li>
                </ul>
              </li>
              <li>
                <a href="#models">
                  <svg
                    viewBox="0 0 24 24"
                    fill="none"
                    stroke="currentColor"
                    stroke-width="2"
                    aria-hidden="true"
                  >
                    <circle cx="12" cy="8" r="3"></circle>
                    <path d="M5 20c1-4 3.5-6 7-6s6 2 7 6"></path>
                  </svg>
                  <span>Models</span>
                </a>
              </li>
              <li>
                <a href="#documentation">
                  <svg
                    viewBox="0 0 24 24"
                    fill="none"
                    stroke="currentColor"
                    stroke-width="2"
                    aria-hidden="true"
                  >
                    <path
                      d="M4 5.5A2.5 2.5 0 0 1 6.5 3H11v17H6.5A2.5 2.5 0 0 0 4 22zM20 5.5A2.5 2.5 0 0 0 17.5 3H13v17h4.5A2.5 2.5 0 0 1 20 22z"
                    ></path>
                  </svg>
                  <span>Documentation</span>
                </a>
              </li>
            </ul>
          </section>
          <section>
            <h3>Projects</h3>
            <ul>
              <li>
                <a href="#design">
                  <span class="sidebar-menu-dot" aria-hidden="true"></span>
                  <span>Design Engineering</span>
                </a>
                <output>24</output>
              </li>
              <li>
                <a href="#sales">
                  <span class="sidebar-menu-dot" aria-hidden="true"></span>
                  <span>Sales &amp; Marketing</span>
                </a>
                <output>12</output>
              </li>
              <li>
                <a href="#travel">
                  <span class="sidebar-menu-dot" aria-hidden="true"></span>
                  <span>Travel</span>
                </a>
                <output>3</output>
              </li>
            </ul>
          </section>
        </nav>
        <footer>
          <button size="lg">
            <span class="avatar"><span>JD</span></span>
            <span class="sidebar-brand-copy">
              <strong>Jane Doe</strong>
              <small>jane@example.com</small>
            </span>
          </button>
        </footer>
      </aside>
      <main class="sidebar-demo-inset">
        <header class="sidebar-demo-toolbar">
          <button
            variant="ghost"
            size="sm"
            aria-controls="app-sidebar"
            aria-keyshortcuts="Control+B Meta+B"
            ng-click="sidebarOpen=!sidebarOpen"
          >
            <svg
              viewBox="0 0 24 24"
              fill="none"
              stroke="currentColor"
              stroke-width="2"
              aria-hidden="true"
            >
              <rect x="3" y="4" width="18" height="16" rx="2"></rect>
              <path d="M9 4v16"></path>
            </svg>
            <span
              ng-bind="sidebarOpen ? 'Close Sidebar' : 'Open Sidebar'"
            ></span>
          </button>
        </header>
        <section class="sidebar-dashboard">
          <div>
            <span class="sidebar-dashboard-kicker">Overview</span>
            <h1>Dashboard</h1>
            <p>Monitor your team activity and active projects.</p>
          </div>
          <div class="sidebar-metric-grid">
            <article><span>Active projects</span><strong>24</strong></article>
            <article><span>Team members</span><strong>18</strong></article>
            <article><span>Completion</span><strong>86%</strong></article>
          </div>
          <output class="sidebar-query-output" aria-live="polite">
            Filter: <span ng-bind="sidebarQuery || 'All projects'"></span>
          </output>
        </section>
      </main>
    </div>
  </body>
</html>

Anatomy

Header and footer menus, labeled groups, actions, badges, submenus, and loading rows remain independently composable.

View source
<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>AngularCSS Sidebar Anatomy</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="sidebarAction='None'; projectsLoading=true"
    data-example="sidebar-footer sidebar-group sidebar-group-action sidebar-header sidebar-menu sidebar-menu-action sidebar-menu-badge sidebar-menu-sub sidebar-rsc"
  >
    <div class="sidebar-anatomy-shell visual-example">
      <aside
        id="anatomy-sidebar"
        ng-sidebar
        collapsible="none"
        class="sidebar-anatomy-panel"
      >
        <header>
          <div ng-dropdown-menu>
            <button size="lg">
              <span class="sidebar-brand-mark" aria-hidden="true">A</span>
              <span class="sidebar-brand-copy">
                <strong>Select Workspace</strong>
                <small>Acme Inc</small>
              </span>
            </button>
            <menu>
              <button ng-click="sidebarAction='Workspace: Acme Inc'">
                Acme Inc
              </button>
              <button ng-click="sidebarAction='Workspace: Acme Corp.'">
                Acme Corp.
              </button>
            </menu>
          </div>
        </header>
        <nav>
          <section>
            <h3>Projects</h3>
            <button
              aria-label="Add Project"
              ng-click="sidebarAction='Added project'"
            >
              <svg
                viewBox="0 0 24 24"
                fill="none"
                stroke="currentColor"
                stroke-width="2"
                aria-hidden="true"
              >
                <path d="M12 5v14M5 12h14"></path>
              </svg>
            </button>
            <ul>
              <li>
                <a href="#design">
                  <span class="sidebar-menu-dot" aria-hidden="true"></span>
                  <span>Design Engineering</span>
                </a>
                <div ng-dropdown-menu>
                  <button aria-label="More actions for Design Engineering">
                    <svg
                      viewBox="0 0 24 24"
                      fill="currentColor"
                      aria-hidden="true"
                    >
                      <circle cx="5" cy="12" r="1.5"></circle>
                      <circle cx="12" cy="12" r="1.5"></circle>
                      <circle cx="19" cy="12" r="1.5"></circle>
                    </svg>
                  </button>
                  <menu side="right" align="start">
                    <button
                      ng-click="sidebarAction='Edited Design Engineering'"
                    >
                      Edit Project
                    </button>
                    <button
                      ng-click="sidebarAction='Deleted Design Engineering'"
                    >
                      Delete Project
                    </button>
                  </menu>
                </div>
              </li>
              <li>
                <a href="#sales">
                  <span class="sidebar-menu-dot" aria-hidden="true"></span>
                  <span>Sales &amp; Marketing</span>
                </a>
                <output>12</output>
              </li>
              <li>
                <a href="#travel">
                  <span class="sidebar-menu-dot" aria-hidden="true"></span>
                  <span>Travel</span>
                </a>
                <output>3</output>
              </li>
            </ul>
          </section>
          <section>
            <h3>Documentation</h3>
            <ul>
              <li>
                <a href="#getting-started">Getting Started</a>
                <ul>
                  <li>
                    <a href="#installation">Installation</a>
                  </li>
                  <li>
                    <a aria-current="page" href="#structure"
                      >Project Structure</a
                    >
                  </li>
                </ul>
              </li>
            </ul>
          </section>
          <section>
            <h3>Remote projects</h3>
            <button
              aria-label="Toggle loading"
              ng-click="projectsLoading=!projectsLoading"
            >
              <svg
                viewBox="0 0 24 24"
                fill="none"
                stroke="currentColor"
                stroke-width="2"
                aria-hidden="true"
              >
                <path d="M20 12a8 8 0 1 1-2.3-5.7M20 4v6h-6"></path>
              </svg>
            </button>
            <ul>
              <li
                ng-if="projectsLoading"
                aria-label="Loading project"
                class="skeleton"
              ></li>
              <li
                ng-if="projectsLoading"
                aria-label="Loading project"
                class="skeleton"
              ></li>
              <li
                ng-if="projectsLoading"
                aria-label="Loading project"
                class="skeleton"
              ></li>
              <li ng-if="!projectsLoading">
                <a href="#support">Support</a>
              </li>
              <li ng-if="!projectsLoading">
                <a href="#feedback">Feedback</a>
              </li>
            </ul>
          </section>
        </nav>
        <footer>
          <div ng-dropdown-menu>
            <button size="lg">
              <span class="avatar"><span>UN</span></span>
              <span class="sidebar-brand-copy"
                ><strong>Username</strong><small>Account menu</small></span
              >
            </button>
            <menu side="top">
              <button ng-click="sidebarAction='Account'">Account</button>
              <button ng-click="sidebarAction='Billing'">Billing</button>
              <button ng-click="sidebarAction='Signed out'">Sign out</button>
            </menu>
          </div>
        </footer>
      </aside>
      <main class="sidebar-anatomy-inset">
        <div>
          <span class="sidebar-dashboard-kicker">Component anatomy</span>
          <h1>Sidebar primitives</h1>
          <p>
            Header, footer, groups, actions, badges, submenus, and loading
            states compose independently.
          </p>
        </div>
        <output
          class="sidebar-anatomy-output"
          aria-live="polite"
          ng-bind="sidebarAction"
        ></output>
      </main>
    </div>
  </body>
</html>

Disclosure Navigation

This example delegates both group and menu disclosure to the existing Disclosure pattern.

View source
<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>AngularCSS Sidebar Collapsible</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="sidebar-group-collapsible sidebar-menu-collapsible"
  >
    <div class="sidebar-collapsible-shell visual-example">
      <aside
        id="collapsible-sidebar"
        ng-sidebar
        collapsible="none"
        class="sidebar-collapsible-panel"
        aria-label="Documentation navigation"
      >
        <header>
          <a href="#docs" size="lg">
            <span class="sidebar-brand-mark" aria-hidden="true">D</span>
            <span class="sidebar-brand-copy">
              <strong>Documentation</strong>
              <small>Developer platform</small>
            </span>
          </a>
        </header>
        <nav>
          <details open class="disclosure">
            <summary>
              Help
              <svg
                viewBox="0 0 24 24"
                fill="none"
                stroke="currentColor"
                stroke-width="2"
                aria-hidden="true"
              >
                <path d="m6 9 6 6 6-6"></path>
              </svg>
            </summary>
            <div>
              <ul>
                <li>
                  <a href="#support">
                    <svg
                      viewBox="0 0 24 24"
                      fill="none"
                      stroke="currentColor"
                      stroke-width="2"
                      aria-hidden="true"
                    >
                      <circle cx="12" cy="12" r="9"></circle>
                      <path
                        d="M8.5 8.5 6 6m9.5 2.5L18 6m-9.5 9.5L6 18m9.5-2.5L18 18"
                      ></path>
                    </svg>
                    <span>Support</span>
                  </a>
                </li>
                <li>
                  <a href="#feedback">
                    <svg
                      viewBox="0 0 24 24"
                      fill="none"
                      stroke="currentColor"
                      stroke-width="2"
                      aria-hidden="true"
                    >
                      <path d="m22 2-7 20-4-9-9-4zM22 2 11 13"></path>
                    </svg>
                    <span>Feedback</span>
                  </a>
                </li>
              </ul>
            </div>
          </details>
          <section>
            <h3>Guide</h3>
            <ul>
              <li>
                <details open class="disclosure">
                  <summary>
                    <span>Getting Started</span>
                    <svg
                      viewBox="0 0 24 24"
                      fill="none"
                      stroke="currentColor"
                      stroke-width="2"
                      aria-hidden="true"
                    >
                      <path d="m9 18 6-6-6-6"></path>
                    </svg>
                  </summary>
                  <div>
                    <ul>
                      <li>
                        <a href="#installation">Installation</a>
                      </li>
                      <li>
                        <a aria-current="page" href="#structure"
                          >Project Structure</a
                        >
                      </li>
                    </ul>
                  </div>
                </details>
              </li>
              <li>
                <details class="disclosure">
                  <summary>
                    <span>Build Your Application</span>
                    <svg
                      viewBox="0 0 24 24"
                      fill="none"
                      stroke="currentColor"
                      stroke-width="2"
                      aria-hidden="true"
                    >
                      <path d="m9 18 6-6-6-6"></path>
                    </svg>
                  </summary>
                  <div>
                    <ul>
                      <li>
                        <a href="#routing">Routing</a>
                      </li>
                      <li>
                        <a href="#data">Data Fetching</a>
                      </li>
                      <li>
                        <a href="#rendering">Rendering</a>
                      </li>
                    </ul>
                  </div>
                </details>
              </li>
              <li>
                <details class="disclosure">
                  <summary>
                    <span>API Reference</span>
                    <svg
                      viewBox="0 0 24 24"
                      fill="none"
                      stroke="currentColor"
                      stroke-width="2"
                      aria-hidden="true"
                    >
                      <path d="m9 18 6-6-6-6"></path>
                    </svg>
                  </summary>
                  <div>
                    <ul>
                      <li>
                        <a href="#components">Components</a>
                      </li>
                      <li>
                        <a href="#functions">Functions</a>
                      </li>
                    </ul>
                  </div>
                </details>
              </li>
            </ul>
          </section>
        </nav>
      </aside>
      <main class="sidebar-collapsible-inset">
        <span class="sidebar-dashboard-kicker">Composition</span>
        <h1>Collapsible navigation</h1>
        <p>Disclosure behavior is supplied by native details elements.</p>
      </main>
    </div>
  </body>
</html>

RTL

Physical right placement and logical borders, actions, and submenu indentation follow the authored document direction.

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 Sidebar 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="sidebarOpen=true; sidebarAction='جاهز'"
    data-example="sidebar-rtl"
  >
    <div class="sidebar-rtl-shell visual-example">
      <main class="sidebar-rtl-inset">
        <header class="sidebar-demo-toolbar">
          <button
            variant="ghost"
            size="sm"
            aria-controls="rtl-sidebar"
            ng-click="sidebarOpen=!sidebarOpen"
          >
            <svg
              viewBox="0 0 24 24"
              fill="none"
              stroke="currentColor"
              stroke-width="2"
              aria-hidden="true"
            >
              <rect x="3" y="4" width="18" height="16" rx="2"></rect>
              <path d="M15 4v16"></path>
            </svg>
            <span ng-bind="sidebarOpen ? 'إغلاق الشريط' : 'فتح الشريط'"></span>
          </button>
        </header>
        <section class="sidebar-dashboard">
          <div>
            <span class="sidebar-dashboard-kicker">نظرة عامة</span>
            <h1>لوحة التحكم</h1>
            <p>تابع نشاط الفريق والمشاريع الحالية.</p>
          </div>
          <output
            class="sidebar-anatomy-output"
            aria-live="polite"
            ng-bind="sidebarAction"
          ></output>
        </section>
      </main>
      <aside
        id="rtl-sidebar"
        ng-sidebar
        side="right"
        variant="floating"
        collapsible="icon"
        ng-attr-collapsed="{{ sidebarOpen ? undefined : '' }}"
        class="sidebar-rtl-panel"
        aria-label="التنقل في مساحة العمل"
      >
        <header>
          <a href="#workspace" size="lg">
            <span class="sidebar-brand-mark" aria-hidden="true">أ</span>
            <span class="sidebar-brand-copy"
              ><strong>شركة أكمي</strong><small>المؤسسة</small></span
            >
          </a>
        </header>
        <nav>
          <section>
            <h3>المنصة</h3>
            <ul>
              <li>
                <a aria-current="page" href="#playground">
                  <svg
                    viewBox="0 0 24 24"
                    fill="none"
                    stroke="currentColor"
                    stroke-width="2"
                    aria-hidden="true"
                  >
                    <rect x="4" y="4" width="16" height="16" rx="2"></rect>
                    <path d="m8 9 2 2-2 2M13 15h3"></path>
                  </svg>
                  <span>ملعب</span>
                </a>
                <ul>
                  <li>
                    <a href="#history">السجل</a>
                  </li>
                  <li>
                    <a href="#starred">المميز</a>
                  </li>
                  <li>
                    <a href="#settings">الإعدادات</a>
                  </li>
                </ul>
              </li>
              <li>
                <a href="#models"
                  ><span class="sidebar-menu-dot" aria-hidden="true"></span
                  ><span>النماذج</span></a
                >
              </li>
            </ul>
          </section>
          <section>
            <h3>المشاريع</h3>
            <ul>
              <li>
                <a href="#design"
                  ><span class="sidebar-menu-dot" aria-hidden="true"></span
                  ><span>هندسة التصميم</span></a
                >
                <div ng-dropdown-menu>
                  <button aria-label="المزيد">
                    <svg
                      viewBox="0 0 24 24"
                      fill="currentColor"
                      aria-hidden="true"
                    >
                      <circle cx="5" cy="12" r="1.5"></circle>
                      <circle cx="12" cy="12" r="1.5"></circle>
                      <circle cx="19" cy="12" r="1.5"></circle>
                    </svg>
                  </button>
                  <menu side="left" align="start">
                    <button ng-click="sidebarAction='عرض المشروع'">
                      عرض المشروع
                    </button>
                    <button ng-click="sidebarAction='مشاركة المشروع'">
                      مشاركة المشروع
                    </button>
                  </menu>
                </div>
              </li>
              <li>
                <a href="#sales"
                  ><span class="sidebar-menu-dot" aria-hidden="true"></span
                  ><span>المبيعات والتسويق</span></a
                >
              </li>
              <li>
                <a href="#travel"
                  ><span class="sidebar-menu-dot" aria-hidden="true"></span
                  ><span>السفر</span></a
                >
              </li>
            </ul>
          </section>
        </nav>
        <footer>
          <button size="lg" ng-click="sidebarAction='الحساب'">
            <span class="avatar"><span>ش</span></span>
            <span class="sidebar-brand-copy"
              ><strong>أنغولار سي إس إس</strong
              ><small>team@angularcss.dev</small></span
            >
          </button>
        </footer>
      </aside>
    </div>
  </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-sidebar]. Importing the package registers it with the AngularCSS angular.css module; there is no per-component JavaScript registration step.

Anatomy

Directive selectors

  • ng-sidebar

Semantic structure

Place aside[ng-sidebar] beside main and connect native button triggers with aria-controls. The root directive inspects semantic header, nav, section, list, and footer descendants; no child sidebar directives or anatomy classes are required. Author side, variant, and collapse mode on the root. Compose nested disclosure with native details.disclosure and action menus with ng-dropdown-menu.

API

Attributes and state

AttributeAccessPurpose
aria-controlsOutputID of the element controlled by a trigger.
aria-currentInputCurrent item or date state.
aria-expandedOutputOpen or expanded state exposed to assistive technology.
aria-hiddenOutputWhether generated or collapsed content is hidden from assistive technology.
aria-labelledbyOutputID of the element that supplies the accessible name.
collapsedInput/outputCurrent collapsed state.
collapsibleInput/outputCollapse behavior: offcanvas, icon, or none.
inertOutputPrevents interaction while the component is hidden.
responsiveInputCollapses an off-canvas sidebar below 48rem and expands it above that breakpoint.
roleInput/outputExplicit semantic role when native HTML does not provide one.
sideInput/outputPhysical placement: left or right.
variantInput/outputSurface style: sidebar, floating, or inset.

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 reflects authored side, variant, collapsible, direction, responsive, active-item, group, and trigger state. It owns only sidebar collapse and accessibility synchronization; collapsible=none stays expanded, off-canvas collapse hides the landmark, and icon collapse keeps visible controls accessible. AngularTS remains responsible for controlled open state, shortcuts, filtering, routing, application actions, and structural rendering. Compose nested disclosure with the Disclosure pattern and action menus with Dropdown Menu.

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

Accessibility

Give the sidebar landmark and icon-only actions useful accessible names. Triggers expose aria-controls and expanded state, groups are associated with visible labels, and the current destination uses aria-current=page. Off-canvas collapse hides the landmark and restores trigger focus when necessary; icon collapse preserves access to its visible controls. Keep DOM order aligned with physical placement and use native links for destinations.

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

13 - tabs

Tabbed content sections

Add ng-tabs to a section, place native buttons in its list, and follow the list with direct semantic section or article panels. The directive assigns tab roles, relationships, selection state, and keyboard navigation.

<section ng-tabs>
  <menu>
    <button aria-selected="true">Overview</button>
  </menu>
  <section>Content</section>
</section>

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 Tabs</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="tabs-demo">
    <section ng-tabs>
      <menu aria-label="Project sections">
        <button aria-selected="true">Overview</button>
        <button>Analytics</button>
        <button>Reports</button>
        <button>Settings</button>
      </menu>
      <article>
        <header>
          <h2>Overview</h2>
          <p>
            View your key metrics and recent project activity. Track progress
            across all your active projects.
          </p>
        </header>
        <section class="muted">
          You have 12 active projects and 3 pending tasks.
        </section>
      </article>
      <article>
        <header>
          <h2>Analytics</h2>
          <p>
            Track performance and user engagement metrics. Monitor trends and
            identify growth opportunities.
          </p>
        </header>
        <section class="muted">
          Page views are up 25% compared to last month.
        </section>
      </article>
      <article>
        <header>
          <h2>Reports</h2>
          <p>
            Generate and download your detailed reports. Export data in multiple
            formats for analysis.
          </p>
        </header>
        <section class="muted">
          You have 5 reports ready and available to export.
        </section>
      </article>
      <article>
        <header>
          <h2>Settings</h2>
          <p>
            Manage your account preferences and options. Customize your
            experience to fit your needs.
          </p>
        </header>
        <section class="muted">
          Configure notifications, security, and themes.
        </section>
      </article>
    </section>
  </body>
</html>

Workflows

View source
<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>AngularCSS Tabs 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="advancedVisible = false; overviewDisabled = false"
    data-example="tabs-disabled tabs-icons tabs-line tabs-rtl tabs-vertical"
  >
    <main class="tabs-workflows visual-example">
      <section aria-labelledby="tabs-dynamic-heading">
        <header>
          <h2 id="tabs-dynamic-heading">Dynamic sections</h2>
          <button
            type="button"
            variant="outline"
            size="sm"
            ng-click="advancedVisible = !advancedVisible"
          >
            {{ advancedVisible ? 'Remove advanced' : 'Add advanced' }}
          </button>
        </header>
        <section ng-tabs id="dynamic-tabs">
          <menu aria-label="Dynamic project sections">
            <button aria-selected="true">Overview</button>
            <button>Activity</button>
            <button ng-if="advancedVisible">Advanced</button>
          </menu>
          <section>Project overview</section>
          <section>Recent project activity</section>
          <section ng-if="advancedVisible">Advanced project controls</section>
        </section>
      </section>

      <section aria-labelledby="tabs-disabled-heading">
        <header>
          <h2 id="tabs-disabled-heading">Disabled state</h2>
          <button
            type="button"
            variant="outline"
            size="sm"
            ng-click="overviewDisabled = !overviewDisabled"
          >
            {{ overviewDisabled ? 'Enable overview' : 'Disable overview' }}
          </button>
        </header>
        <section ng-tabs id="disabled-tabs">
          <menu aria-label="Account sections">
            <button aria-selected="true" ng-disabled="overviewDisabled">
              Overview
            </button>
            <button>Settings</button>
            <button disabled>Unavailable</button>
          </menu>
          <section>Account overview</section>
          <section>Account settings</section>
          <section>Unavailable settings</section>
        </section>
      </section>

      <section aria-labelledby="tabs-vertical-heading">
        <header><h2 id="tabs-vertical-heading">Vertical</h2></header>
        <section ng-tabs orientation="vertical" id="vertical-tabs">
          <menu aria-label="Preference sections">
            <button aria-selected="true">Account</button>
            <button>Password</button>
            <button>Notifications</button>
          </menu>
          <section>Account preferences</section>
          <section>Password preferences</section>
          <section>Notification preferences</section>
        </section>
      </section>

      <section aria-labelledby="tabs-variants-heading">
        <header><h2 id="tabs-variants-heading">Variants</h2></header>
        <div class="tabs-variant-stack">
          <section ng-tabs id="icon-tabs">
            <menu aria-label="Editor view">
              <button aria-selected="true">
                <svg
                  viewBox="0 0 24 24"
                  fill="none"
                  stroke="currentColor"
                  stroke-width="2"
                  aria-hidden="true"
                >
                  <rect x="3" y="4" width="18" height="16" rx="2" />
                  <path d="M3 9h18" />
                </svg>
                Preview
              </button>
              <button>
                <svg
                  viewBox="0 0 24 24"
                  fill="none"
                  stroke="currentColor"
                  stroke-width="2"
                  aria-hidden="true"
                >
                  <path d="m8 9-3 3 3 3M16 9l3 3-3 3" />
                </svg>
                Code
              </button>
            </menu>
          </section>
          <section ng-tabs id="line-tabs">
            <menu variant="line" aria-label="Report sections">
              <button aria-selected="true">Overview</button>
              <button>Analytics</button>
              <button>Reports</button>
            </menu>
          </section>
        </div>
      </section>

      <section aria-labelledby="tabs-rtl-heading" dir="rtl" lang="ar">
        <header><h2 id="tabs-rtl-heading">الاتجاه من اليمين</h2></header>
        <section ng-tabs id="rtl-tabs">
          <menu aria-label="أقسام المشروع">
            <button>نظرة عامة</button>
            <button aria-selected="true">التحليلات</button>
            <button>التقارير</button>
          </menu>
          <section>نظرة عامة على المشروع</section>
          <section>تحليلات المشروع</section>
          <section>تقارير المشروع</section>
        </section>
      </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-tabs]. Importing the package registers it with the AngularCSS angular.css module; there is no per-component JavaScript registration step.

Anatomy

Directive selectors

  • ng-tabs

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
aria-controlsOutputID of the element controlled by a trigger.
aria-hiddenOutputWhether generated or collapsed content is hidden from assistive technology.
aria-labelledbyOutputID of the element that supplies the accessible name.
aria-orientationInput/outputInteraction axis exposed to assistive technology.
aria-selectedInput/outputSelected item state.
dirInputText and interaction direction: ltr or rtl.
orientationInput/outputLayout direction: horizontal or vertical.
roleOutputExplicit semantic role when native HTML does not provide one.
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

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 supplies navigation semantics and keyboard state where required. URLs, routing, current-page state, and navigation side effects remain application-owned.

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

Accessibility

Use semantic navigation landmarks and links. Expose the current destination with aria-current and keep keyboard order consistent with visual order.

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

14 - toast

Notification toaster with close actions and toast variants.

Use ng-toast on the container; direct article children are toast items. Application code owns toast creation and queueing. Set type or variant to success, info, warning, error, or loading; the directive mirrors the result to data-type for styling.

<div ng-toast>
  <article type="success">
    <article>
      <article>Saved</article>
      <article>Update published.</article>
    </article>
  </article>
</div>

Toast titles and descriptions are connected through aria-labelledby and aria-describedby. Authored relationships are preserved. Toast action and close buttons default to type="button", so they do not submit an enclosing form.

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 Toast</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="canonical = { visible: false, status: 'Ready' }"
    data-example="toast-demo"
  >
    <main class="visual-example">
      <button
        type="button"
        variant="outline"
        ng-click="canonical.visible = true; canonical.status = 'Toast shown'"
      >
        Show Toast
      </button>
      <output class="output">{{ canonical.status }}</output>
    </main>

    <div ng-toast position="bottom-right" aria-label="Notifications">
      <article ng-if="canonical.visible" animate>
        <section>
          <h3>Event has been created</h3>
          <p>Sunday, December 03, 2023 at 9:00 AM</p>
        </section>
        <button
          variant="outline"
          size="sm"
          ng-click="canonical.visible = false; canonical.status = 'Undo selected'"
        >
          Undo
        </button>
      </article>
    </div>
  </body>
</html>

Reference workflows

The workflow page exercises description, all six positions, notification types, and the loading-to-success promise transition. Its controller is compiled from TypeScript; AngularCSS does not own the application queue or asynchronous 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 Toast 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/toast-demo.umd.js"></script>
  </head>
  <body
    ng-app="toastDemo"
    ng-controller="ToastDemoController as toast"
    ng-cloak
    data-example="toast-description toast-position toast-types"
  >
    <main class="toast-workflow-grid">
      <section
        class="toast-workflow-section"
        aria-labelledby="toast-description-title"
      >
        <h2 id="toast-description-title">With description</h2>
        <button
          type="button"
          variant="outline"
          ng-click="toast.showDescription()"
        >
          Show Toast
        </button>
        <div class="toast-inline-preview">
          <div ng-toast aria-label="Description notifications">
            <article ng-if="toast.descriptionVisible" animate>
              <section>
                <h3>Event has been created</h3>
                <p>Monday, January 3rd at 6:00pm</p>
              </section>
              <button
                variant="ghost"
                size="icon-sm"
                aria-label="Dismiss description toast"
                ng-click="toast.dismissDescription()"
              >
                <span></span>
              </button>
            </article>
          </div>
        </div>
        <output class="output">{{ toast.descriptionStatus }}</output>
      </section>

      <section
        class="toast-workflow-section toast-workflow-wide"
        aria-labelledby="toast-position-title"
      >
        <h2 id="toast-position-title">Position</h2>
        <div class="toast-button-grid" aria-label="Toast positions">
          <button
            type="button"
            variant="outline"
            ng-click="toast.showPosition('top-left')"
          >
            Top Left
          </button>
          <button
            type="button"
            variant="outline"
            ng-click="toast.showPosition('top-center')"
          >
            Top Center
          </button>
          <button
            type="button"
            variant="outline"
            ng-click="toast.showPosition('top-right')"
          >
            Top Right
          </button>
          <button
            type="button"
            variant="outline"
            ng-click="toast.showPosition('bottom-left')"
          >
            Bottom Left
          </button>
          <button
            type="button"
            variant="outline"
            ng-click="toast.showPosition('bottom-center')"
          >
            Bottom Center
          </button>
          <button
            type="button"
            variant="outline"
            ng-click="toast.showPosition('bottom-right')"
          >
            Bottom Right
          </button>
        </div>
        <div class="toast-position-preview">
          <div
            ng-toast
            position="{{ toast.position }}"
            aria-label="Position notifications"
          >
            <article ng-if="toast.positionVisible" animate>
              <section>
                <h3>Event has been created</h3>
              </section>
              <button
                variant="ghost"
                size="icon-sm"
                aria-label="Dismiss position toast"
                ng-click="toast.dismissPosition()"
              >
                <span></span>
              </button>
            </article>
          </div>
        </div>
        <output class="output">Position: {{ toast.position }}</output>
      </section>

      <section
        class="toast-workflow-section toast-workflow-wide"
        aria-labelledby="toast-types-title"
      >
        <h2 id="toast-types-title">Types</h2>
        <div class="toast-button-grid" aria-label="Toast types">
          <button
            type="button"
            variant="outline"
            ng-click="toast.showType('default')"
          >
            Default
          </button>
          <button
            type="button"
            variant="outline"
            ng-click="toast.showType('success')"
          >
            Success
          </button>
          <button
            type="button"
            variant="outline"
            ng-click="toast.showType('info')"
          >
            Info
          </button>
          <button
            type="button"
            variant="outline"
            ng-click="toast.showType('warning')"
          >
            Warning
          </button>
          <button
            type="button"
            variant="outline"
            ng-click="toast.showType('error')"
          >
            Error
          </button>
          <button
            type="button"
            variant="outline"
            ng-click="toast.showPromise()"
          >
            Promise
          </button>
        </div>
        <div class="toast-inline-preview toast-types-preview">
          <div ng-toast aria-label="Type notifications">
            <article type="{{ toast.type }}" ng-if="toast.typeVisible" animate>
              <figure ng-if="toast.type === 'success'"></figure>
              <figure ng-if="toast.type === 'info'"></figure>
              <figure ng-if="toast.type === 'warning'"></figure>
              <figure ng-if="toast.type === 'error'"></figure>
              <figure ng-if="toast.type === 'loading'"></figure>
              <section>
                <h3>{{ toast.typeMessage }}</h3>
              </section>
              <button
                variant="ghost"
                size="icon-sm"
                aria-label="Dismiss type toast"
                ng-click="toast.dismissType()"
              >
                <span></span>
              </button>
            </article>
          </div>
        </div>
        <output class="output">Type: {{ toast.type }}</output>
      </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-toast]. Importing the package registers it with the AngularCSS angular.css module; there is no per-component JavaScript registration step.

Anatomy

Directive selectors

  • ng-toast

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
aria-atomicInput/outputWhether an assistive technology announces the entire updated region.
aria-describedbyInput/outputID of the element that supplies the accessible description.
aria-labelInput/outputAccessible name when visible text is insufficient.
aria-labelledbyInput/outputID of the element that supplies the accessible name.
aria-liveInput/outputAnnouncement priority for updates to a live region.
positionInput/outputViewport placement: top-left, top-center, top-right, bottom-left, bottom-center, or bottom-right.
roleInput/outputExplicit semantic role when native HTML does not provide one.
typeInput/outputToast state: default, error, info, loading, success, or warning.

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 exposes presentation and announcement state. The application decides when feedback appears, changes, or is removed.

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

Accessibility

Use the appropriate live-region or status semantics for dynamic feedback. Decorative feedback must stay hidden from assistive technology.

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

15 - toolbar

Keyboard-navigable groups of actions

Add ng-toolbar to a semantic action container. AngularCSS provides one tab stop and arrow-key movement while native buttons, links, and AngularTS retain activation and command ownership.

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 Toolbar</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="toolbar-demo">
    <main class="visual-example">
      <menu ng-toolbar aria-label="Document actions">
        <button size="icon-sm" variant="ghost" aria-label="Undo"></button>
        <button size="icon-sm" variant="ghost" aria-label="Redo" disabled>
        </button>
        <hr />
        <button size="sm" variant="ghost">Copy</button>
        <button size="sm" variant="ghost">Archive</button>
        <button size="sm" variant="ghost">Export</button>
      </menu>
    </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-toolbar]. Importing the package registers it with the AngularCSS angular.css module; there is no per-component JavaScript registration step.

Anatomy

Directive selectors

  • ng-toolbar

Semantic structure

Apply ng-toolbar to a semantic menu or container with an accessible name. Use aria-orientation="vertical" for a vertical toolbar. Author direct native buttons or links and optional direct separators; no child directives or toolbar part classes are required.

API

Attributes and state

AttributeAccessPurpose
aria-disabledInput/outputSemantic disabled state.
aria-labelInputAccessible name when visible text is insufficient.
aria-orientationInput/outputInteraction axis exposed to assistive technology.
dirInputText and interaction direction: ltr or rtl.
disabledInputDisables native or component interaction.
hiddenInputNative visibility state observed when finding available items.
orientationInputLayout direction: horizontal or vertical.
roleOutputExplicit semantic role when native HTML does not provide one.
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

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

Toolbar owns one roving tab stop and direction-aware arrow, Home, and End navigation across direct native buttons and links. Native activation and AngularTS commands remain unchanged.

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

Accessibility

Give the toolbar an accessible name. Keep actions as native buttons or links, preserve visible focus, and avoid placing text inputs inside the roving-focus sequence.

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

16 - tooltip

Contextual hover and focus labels

Use a native trigger with short, non-interactive descriptive content. Content can be held visible with the wrapper’s concise open attribute for controlled examples.

<span ng-tooltip>
  <button>Hover</button>
  <span side="top">Add to library</span>
</span>

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 Tooltip</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="tooltip-demo">
    <main class="visual-example">
      <span ng-tooltip>
        <button type="button" variant="outline">Hover</button>
        <span side="top">Add to library</span>
      </span>
    </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-tooltip]. Importing the package registers it with the AngularCSS angular.css module; there is no per-component JavaScript registration step.

Anatomy

Directive selectors

  • ng-tooltip

Semantic structure

One trigger and one plain-text content element are required. Prefer a native button or link trigger. Tooltip content is descriptive and non-interactive; use Popover when the floating content needs controls or focus.

API

Attributes and state

AttributeAccessPurpose
aria-describedbyOutputID of the element that supplies the accessible description.
aria-hiddenOutputWhether generated or collapsed content is hidden from assistive technology.
openInputInitial or controlled open state.
roleOutputExplicit semantic role when native HTML does not provide one.
sideInput/outputPhysical placement: left, top, bottom, or right.

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 owns immediate hover and focus disclosure, Escape closure, synchronized controlled open state, text direction, and physical side placement. Tooltip content is descriptive, non-interactive, and never receives focus. AngularTS remains responsible for application state and the trigger action.

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

Accessibility

The trigger exposes aria-describedby while the content uses role="tooltip". Tooltips open from hover and keyboard focus, close on Escape, and must not contain interactive controls or essential information. Wrap a disabled button in a hoverable trigger only when its unavailable state needs explanation.

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

17 - tree

Hierarchical navigation and selection

Add ng-tree to a nested native list. The root directive supplies the hierarchical focus, expansion, selection, and typeahead behavior that HTML does not provide by itself.

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 Tree</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="tree-demo">
    <div class="visual-example">
      <ul
        ng-tree
        aria-label="Organization"
        ng-on-angularcss:tree-select="selectedNode=$event.detail.value"
      >
        <li data-value="operations" aria-expanded="true">
          <span>Operations <small>12</small></span>
          <ul>
            <li data-value="fulfillment">
              <span>Fulfillment <small>7</small></span>
            </li>
            <li data-value="support">
              <span>Support <small>5</small></span>
            </li>
          </ul>
        </li>
        <li data-value="finance" aria-expanded="false">
          <span>Finance <small>8</small></span>
          <ul>
            <li data-value="accounts"><span>Accounts</span></li>
            <li data-value="payroll"><span>Payroll</span></li>
          </ul>
        </li>
        <li data-value="legal" aria-disabled="true">
          <span>Legal <small>Restricted</small></span>
        </li>
      </ul>
      <output aria-live="polite">Selected: {{ selectedNode || 'none' }}</output>
    </div>
  </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-tree]. Importing the package registers it with the AngularCSS angular.css module; there is no per-component JavaScript registration step.

Anatomy

Directive selectors

  • ng-tree

Semantic structure

Apply ng-tree to a native ul or ol. Each direct or nested li contains one direct text span followed by an optional nested list; no child directives or tree part classes are required.

API

Attributes and state

AttributeAccessPurpose
aria-disabledInput/outputSemantic disabled state.
aria-expandedInput/outputOpen or expanded state exposed to assistive technology.
aria-labelInputAccessible name when visible text is insufficient.
aria-labelledbyOutputID of the element that supplies the accessible name.
aria-multiselectableInputSet to true to allow Ctrl or Command click selection of several items.
aria-selectedInput/outputSelected item state.
data-valueInputApplication value included in angularcss:tree-select.
disabledInputDisables native or component interaction.
hiddenInputNative visibility state observed when finding available items.
roleOutputExplicit semantic role when native HTML does not provide one.
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

This directive does not write component-specific CSS custom properties.

DOM events

  • angularcss:tree-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

Tree owns hierarchical roving focus, expansion, typeahead, selection state, and selection signaling. AngularTS owns node data, rendering, permissions, lazy loading, and the selected application record.

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

Accessibility

Use nested native lists with one direct text span per item. The directive supplies tree, group, and treeitem semantics, expanded and selected state, roving focus, arrow navigation, Home, End, and typeahead.

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