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:
- What the component does and where to use it.
- What semantic HTML and directive selectors it requires.
- Which parts, attributes, state hooks, CSS variables, and events it exposes.
- Which behavior belongs to AngularCSS, AngularTS, native HTML, or application
code.
- What accessibility and customization constraints apply.
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
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
| Attribute | Access | Purpose |
|---|
aria-current | Input/output | Current item or date state. |
aria-disabled | Output | Semantic disabled state. |
aria-label | Input/output | Accessible name when visible text is insufficient. |
aria-labelledby | Output | ID of the element that supplies the accessible name. |
aria-live | Output | Announcement priority for updates to a live region. |
aria-pressed | Input/output | Pressed state of a toggle control. |
data-booked | Output | Whether a generated calendar day is booked. |
data-booked-dates | Input | Comma-separated ISO dates styled as booked. |
data-calendar-generated | Input | Enables generated month markup inside the authored Calendar shell. |
data-calendar-preset | Input | Generated calendar preset: single, multiple, or range. |
data-caption-layout | Input | Caption controls: label or dropdown. |
data-columns | Input | Number of columns used for calendar grid keyboard movement; defaults to 7. |
data-disabled-after | Input | Last selectable date as an ISO date. |
data-disabled-before | Input | First selectable date as an ISO date. |
data-disabled-dates | Input | Comma-separated ISO dates that cannot be selected. |
data-end-year | Input | Final year offered by a dropdown caption. |
data-min-nights | Input | Minimum number of nights accepted by range selection. |
data-month | Input/output | Displayed month in YYYY-MM form. |
data-months | Output | Number of month grids currently rendered. |
data-number-of-months | Input | Number of consecutive months to render. |
data-outside | Input/output | Whether a day belongs to an adjacent month. |
data-range-end | Output | Marks the final day in the selected range. |
data-range-end-value | Input/output | Selected range end as an ISO date. |
data-range-invalid | Output | Whether the pending range violates the minimum-night constraint. |
data-range-middle | Output | Marks a day between the selected range boundaries. |
data-range-start | Output | Marks the first day in the selected range. |
data-range-start-value | Input/output | Selected range start as an ISO date. |
data-selection-mode | Input | Selection behavior: single, multiple, or range. |
data-show-outside-days | Input | Shows dates from adjacent months when true. |
data-show-week-numbers | Input/output | Shows ISO-style week numbers when true. |
data-start-year | Input | First year offered by a dropdown caption. |
data-value | Input/output | Selected ISO date for single selection. |
data-values | Input/output | Comma-separated selected ISO dates for multiple selection. |
data-week-start | Input | First weekday as an integer from 0 (Sunday) to 6 (Saturday). |
dir | Input | Text and interaction direction: ltr or rtl. |
lang | Input | Language used for generated labels and localized text. |
selected | Output | Selected value reflected by the component. |
tabindex | Input/output | Keyboard focus order for composite descendants. |
value | Output | Native 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
| Variable | Purpose |
|---|
--calendar-cell-size | Width and height of calendar controls and day cells. |
DOM events
angularcss:calendar-month-changeangularcss:calendar-range-invalidangularcss: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
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
| Attribute | Access | Purpose |
|---|
align | Input | Cross-axis alignment: start, center, or end. |
aria-current | Output | Current item or date state. |
aria-disabled | Output | Semantic disabled state. |
aria-hidden | Output | Whether generated or collapsed content is hidden from assistive technology. |
aria-label | Input/output | Accessible name when visible text is insufficient. |
aria-roledescription | Output | Human-readable description of the component role. |
autoplay | Input | Enables the locally bundled Embla autoplay plugin. |
autoplay-delay | Input | Autoplay delay in milliseconds. |
contain-scroll | Input | Embla scroll containment mode. |
dir | Input | Text and interaction direction: ltr or rtl. |
drag-free | Input | Allows free dragging between snap points. |
draggable | Input | Set to false to disable pointer dragging. |
loop | Input | Allows navigation to wrap from the final item to the first. |
orientation | Input/output | Layout direction: horizontal or vertical. |
role | Input/output | Explicit semantic role when native HTML does not provide one. |
skip-snaps | Input | Allows momentum to skip snap points. |
slides-to-scroll | Input | Number of slides advanced as one snap group. |
tabindex | Input/output | Keyboard focus order for composite descendants. |
Input attributes are read from authored HTML. Output attributes are maintained by AngularCSS for CSS and testing. Input/output attributes may be authored for a controlled initial state and are then synchronized by the directive.
CSS custom properties
| Variable | Purpose |
|---|
--carousel-gap | Gap between slides; defaults to four spacing units. |
--carousel-item-size | Slide basis; defaults to 100%. |
DOM events
angularcss:carousel-changeangularcss: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
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
| Attribute | Access | Purpose |
|---|
aria-activedescendant | Output | ID of the active option while focus remains on the composite control. |
aria-autocomplete | Input/output | How a text control presents completion suggestions. |
aria-controls | Output | ID of the element controlled by a trigger. |
aria-disabled | Input/output | Semantic disabled state. |
aria-expanded | Output | Open or expanded state exposed to assistive technology. |
aria-haspopup | Output | Type of popup controlled by the trigger. |
aria-hidden | Input/output | Whether generated or collapsed content is hidden from assistive technology. |
aria-invalid | Input | Validation state exposed to assistive technology and CSS. |
aria-label | Input/output | Accessible name when visible text is insufficient. |
aria-labelledby | Output | ID of the element that supplies the accessible name. |
aria-multiselectable | Output | Whether the composite allows multiple selected items. |
aria-orientation | Output | Interaction axis exposed to assistive technology. |
aria-selected | Input/output | Selected item state. |
auto-highlight | Input | Highlights the first enabled result when the popup opens or filters change. |
data-highlighted | Output | Current option highlighted for keyboard selection. |
data-value | Input | Application value reported when the corresponding option is selected. |
dir | Input | Text and interaction direction: ltr or rtl. |
disabled | Input | Disables native or component interaction. |
hidden | Input | Native visibility state observed when finding available items. |
multiple | Input | Keeps the popup open and reports selections for an application-owned collection. |
open | Input/output | Initial or externally synchronized disclosure state. |
required | Input | Marks a native form value as required. |
role | Output | Explicit semantic role when native HTML does not provide one. |
side | Output | Physical placement: left, top, bottom, or right. |
tabindex | Output | Keyboard focus order for composite descendants. |
Input attributes are read from authored HTML. Output attributes are maintained by AngularCSS for CSS and testing. Input/output attributes may be authored for a controlled initial state and are then synchronized by the directive.
CSS custom properties
| Variable | Purpose |
|---|
--combobox-anchor-width | Component styling variable. |
--combobox-content-top | Component styling variable. |
DOM events
angularcss:combobox-clearangularcss:combobox-open-changeangularcss:combobox-remove-lastangularcss: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>
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
Semantic structure
A command root requires one input and a result container. The root directive inspects semantic headers, sections, headings, buttons, separators, and keyboard hints; no child directives or anatomy classes are required. Compose modal palettes from Dialog.
API
Attributes and state
| Attribute | Access | Purpose |
|---|
aria-activedescendant | Output | ID of the active option while focus remains on the composite control. |
aria-autocomplete | Input/output | How a text control presents completion suggestions. |
aria-controls | Output | ID of the element controlled by a trigger. |
aria-disabled | Input/output | Semantic disabled state. |
aria-expanded | Output | Open or expanded state exposed to assistive technology. |
aria-hidden | Input/output | Whether generated or collapsed content is hidden from assistive technology. |
aria-labelledby | Output | ID of the element that supplies the accessible name. |
aria-orientation | Output | Interaction axis exposed to assistive technology. |
aria-selected | Input/output | Selected item state. |
dir | Input | Text and interaction direction: ltr or rtl. |
disabled | Input | Disables native or component interaction. |
hidden | Input | Native visibility state observed when finding available items. |
role | Output | Explicit semantic role when native HTML does not provide one. |
tabindex | Output | Keyboard focus order for composite descendants. |
Input attributes are read from authored HTML. Output attributes are maintained by AngularCSS for CSS and testing. Input/output attributes may be authored for a controlled initial state and are then synchronized by the directive.
CSS custom properties
This directive does not write component-specific CSS custom properties.
DOM events
This component does not emit a component-specific custom event.
Native DOM events continue to work normally. AngularTS event directives such as
ng-click and ng-keydown, plus the data-change model callback, remain application-owned.
Behavior
The directive follows the application-rendered result DOM and owns active-descendant navigation, enabled-option wrapping and boundaries, pointer synchronization, Enter activation through the authored click handler, semantic group and empty state, and scroll-to-active behavior. AngularTS remains responsible for query filtering, command execution, result data, structural bindings, and application keyboard shortcuts. Dialog remains responsible for modal disclosure, focus trapping, Escape, outside dismissal, and focus restoration.
AngularCSS does not replace AngularTS interpolation, bindings, structural
directives, form controllers, validation, or application state.
Accessibility
Give the search input and command surface useful accessible names. The input is connected to a listbox through aria-controls and aria-activedescendant; rendered options expose selected and disabled state, labeled groups retain group relationships, separators are decorative structure, and shortcut labels are hidden from repeated announcement. A modal composition must include an accessible Dialog title and description.
Authored accessible names and relationships are preserved. Test the final
composition with keyboard navigation and assistive technology because labels and
content come from the application.
Customization
Target [ng-command], semantic descendants, component classes, and generated state with ordinary CSS. Keep behavior and accessible state in the TypeScript directive; visual choices belong in the application stylesheet.
Read Customization for layer order, design tokens, state
variants, and iframe demo isolation.
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
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
| Attribute | Access | Purpose |
|---|
align | Input/output | Cross-axis alignment: start, center, or end. |
align-offset | Input | Additional alignment offset in CSS pixels. |
aria-checked | Input/output | ARIA relationship or state. |
aria-controls | Output | ID of the element controlled by a trigger. |
aria-disabled | Input/output | Semantic disabled state. |
aria-expanded | Output | Open or expanded state exposed to assistive technology. |
aria-haspopup | Output | Type of popup controlled by the trigger. |
aria-hidden | Output | Whether generated or collapsed content is hidden from assistive technology. |
dir | Input | Text and interaction direction: ltr or rtl. |
disabled | Input | Disables native or component interaction. |
open | Input | Initial or controlled open state. |
role | Output | Explicit semantic role when native HTML does not provide one. |
side | Input/output | Physical placement: left, top, bottom, or right. |
side-offset | Input | Distance from the invocation point in CSS pixels. |
tabindex | Input/output | Keyboard focus order for composite descendants. |
Input attributes are read from authored HTML. Output attributes are maintained by AngularCSS for CSS and testing. Input/output attributes may be authored for a controlled initial state and are then synchronized by the directive.
CSS custom properties
| Variable | Purpose |
|---|
--context-menu-available-height | Component styling variable. |
--context-menu-left | Component styling variable. |
--context-menu-top | Component 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
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
| Attribute | Access | Purpose |
|---|
align | Input | Cross-axis alignment: start, center, or end. |
align-offset | Input | Additional alignment offset in CSS pixels. |
aria-checked | Input/output | ARIA relationship or state. |
aria-controls | Output | ID of the element controlled by a trigger. |
aria-expanded | Output | Open or expanded state exposed to assistive technology. |
aria-haspopup | Input/output | Type of popup controlled by the trigger. |
aria-hidden | Output | Whether generated or collapsed content is hidden from assistive technology. |
aria-labelledby | Output | ID of the element that supplies the accessible name. |
dir | Input | Text and interaction direction: ltr or rtl. |
open | Input | Initial or controlled open state. |
role | Input/output | Explicit semantic role when native HTML does not provide one. |
side | Input | Physical placement: left, top, bottom, or right. |
side-offset | Input | Distance from the trigger in CSS pixels. |
size | Input | Visual size token supported by the component stylesheet. |
tabindex | Input/output | Keyboard focus order for composite descendants. |
Input attributes are read from authored HTML. Output attributes are maintained by AngularCSS for CSS and testing. Input/output attributes may be authored for a controlled initial state and are then synchronized by the directive.
CSS custom properties
| Variable | Purpose |
|---|
--dropdown-menu-available-height | Component 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
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
| Attribute | Access | Purpose |
|---|
aria-controls | Output | ID of the element controlled by a trigger. |
aria-expanded | Output | Open or expanded state exposed to assistive technology. |
aria-hidden | Output | Whether generated or collapsed content is hidden from assistive technology. |
close-delay | Input | Pointer close delay in milliseconds. |
open | Input | Initial or controlled open state. |
open-delay | Input | Pointer open delay in milliseconds. |
side | Input/output | Physical 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 menusEnter/Space/ArrowDown to open a menuEscape to close menusHome/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
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
| Attribute | Access | Purpose |
|---|
aria-checked | Input/output | ARIA relationship or state. |
aria-controls | Output | ID of the element controlled by a trigger. |
aria-expanded | Output | Open or expanded state exposed to assistive technology. |
aria-haspopup | Output | Type of popup controlled by the trigger. |
aria-hidden | Output | Whether generated or collapsed content is hidden from assistive technology. |
aria-labelledby | Output | ID of the element that supplies the accessible name. |
dir | Input | Text and interaction direction: ltr or rtl. |
open | Input | Initial or controlled open state. |
role | Output | Explicit semantic role when native HTML does not provide one. |
tabindex | Input/output | Keyboard focus order for composite descendants. |
Input attributes are read from authored HTML. Output attributes are maintained by AngularCSS for CSS and testing. Input/output attributes may be authored for a controlled initial state and are then synchronized by the directive.
CSS custom properties
This directive does not write component-specific CSS custom properties.
DOM events
This component does not emit a component-specific custom event.
Native DOM events continue to work normally. AngularTS event directives such as
ng-click and ng-keydown, plus the data-change model callback, remain application-owned.
Behavior
The directive 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
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
| Attribute | Access | Purpose |
|---|
align | Input | Cross-axis alignment: start, center, or end. |
aria-controls | Output | ID of the element controlled by a trigger. |
aria-expanded | Output | Open or expanded state exposed to assistive technology. |
aria-hidden | Input/output | Whether generated or collapsed content is hidden from assistive technology. |
aria-labelledby | Output | ID of the element that supplies the accessible name. |
dir | Input | Text and interaction direction: ltr or rtl. |
disabled | Input | Disables native or component interaction. |
open | Input | Initial or controlled open state. |
role | Input/output | Explicit 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
| Variable | Purpose |
|---|
--navigation-menu-content-offset | Component 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
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
| Attribute | Access | Purpose |
|---|
aria-orientation | Output | Interaction axis exposed to assistive technology. |
dir | Input | Text and interaction direction: ltr or rtl. |
max | Input | Maximum native or component value. |
min | Input | Minimum native or component value. |
orientation | Input/output | Layout 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
| Variable | Purpose |
|---|
--range-end | Component styling variable. |
--range-start | Component styling variable. |
--value | Component 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
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
| Attribute | Access | Purpose |
|---|
aria-controls | Output | ID of the element controlled by a trigger. |
aria-disabled | Input | Semantic disabled state. |
aria-orientation | Input/output | Interaction axis exposed to assistive technology. |
aria-valuemax | Output | Maximum value exposed by an adjustable control. |
aria-valuemin | Output | Minimum value exposed by an adjustable control. |
aria-valuenow | Output | Current value exposed by an adjustable control. |
data-max-size | Input | Largest panel flex size allowed during resizing. |
data-min-size | Input | Smallest panel flex size allowed during resizing. |
data-resizing | Output | Present while a pointer resize operation is active. |
data-step | Input | Panel flex-size increment used by keyboard resizing. |
dir | Input | Text and interaction direction: ltr or rtl. |
orientation | Input/output | Resize axis: horizontal or vertical. |
tabindex | Input/output | Keyboard focus order for composite descendants. |
Input attributes are read from authored HTML. Output attributes are maintained by AngularCSS for CSS and testing. Input/output attributes may be authored for a controlled initial state and are then synchronized by the directive.
CSS custom properties
| Variable | Purpose |
|---|
--panel-size | Component 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 & 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 & 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
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
| Attribute | Access | Purpose |
|---|
aria-controls | Output | ID of the element controlled by a trigger. |
aria-current | Input | Current item or date state. |
aria-expanded | Output | Open or expanded state exposed to assistive technology. |
aria-hidden | Output | Whether generated or collapsed content is hidden from assistive technology. |
aria-labelledby | Output | ID of the element that supplies the accessible name. |
collapsed | Input/output | Current collapsed state. |
collapsible | Input/output | Collapse behavior: offcanvas, icon, or none. |
inert | Output | Prevents interaction while the component is hidden. |
responsive | Input | Collapses an off-canvas sidebar below 48rem and expands it above that breakpoint. |
role | Input/output | Explicit semantic role when native HTML does not provide one. |
side | Input/output | Physical placement: left or right. |
variant | Input/output | Surface 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
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
| Attribute | Access | Purpose |
|---|
aria-controls | Output | ID of the element controlled by a trigger. |
aria-hidden | Output | Whether generated or collapsed content is hidden from assistive technology. |
aria-labelledby | Output | ID of the element that supplies the accessible name. |
aria-orientation | Input/output | Interaction axis exposed to assistive technology. |
aria-selected | Input/output | Selected item state. |
dir | Input | Text and interaction direction: ltr or rtl. |
orientation | Input/output | Layout direction: horizontal or vertical. |
role | Output | Explicit semantic role when native HTML does not provide one. |
tabindex | Output | Keyboard focus order for composite descendants. |
Input attributes are read from authored HTML. Output attributes are maintained by AngularCSS for CSS and testing. Input/output attributes may be authored for a controlled initial state and are then synchronized by the directive.
CSS custom properties
This directive does not write component-specific CSS custom properties.
DOM events
This component does not emit a component-specific custom event.
Native DOM events continue to work normally. AngularTS event directives such as
ng-click and ng-keydown, plus the data-change model callback, remain application-owned.
Behavior
The directive 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
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
| Attribute | Access | Purpose |
|---|
aria-atomic | Input/output | Whether an assistive technology announces the entire updated region. |
aria-describedby | Input/output | ID of the element that supplies the accessible description. |
aria-label | Input/output | Accessible name when visible text is insufficient. |
aria-labelledby | Input/output | ID of the element that supplies the accessible name. |
aria-live | Input/output | Announcement priority for updates to a live region. |
position | Input/output | Viewport placement: top-left, top-center, top-right, bottom-left, bottom-center, or bottom-right. |
role | Input/output | Explicit semantic role when native HTML does not provide one. |
type | Input/output | Toast 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
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
| Attribute | Access | Purpose |
|---|
aria-disabled | Input/output | Semantic disabled state. |
aria-label | Input | Accessible name when visible text is insufficient. |
aria-orientation | Input/output | Interaction axis exposed to assistive technology. |
dir | Input | Text and interaction direction: ltr or rtl. |
disabled | Input | Disables native or component interaction. |
hidden | Input | Native visibility state observed when finding available items. |
orientation | Input | Layout direction: horizontal or vertical. |
role | Output | Explicit semantic role when native HTML does not provide one. |
tabindex | Input/output | Keyboard focus order for composite descendants. |
Input attributes are read from authored HTML. Output attributes are maintained by AngularCSS for CSS and testing. Input/output attributes may be authored for a controlled initial state and are then synchronized by the directive.
CSS custom properties
This directive does not write component-specific CSS custom properties.
DOM events
This component does not emit a component-specific custom event.
Native DOM events continue to work normally. AngularTS event directives such as
ng-click and ng-keydown, plus the data-change model callback, remain application-owned.
Behavior
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
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
| Attribute | Access | Purpose |
|---|
aria-describedby | Output | ID of the element that supplies the accessible description. |
aria-hidden | Output | Whether generated or collapsed content is hidden from assistive technology. |
open | Input | Initial or controlled open state. |
role | Output | Explicit semantic role when native HTML does not provide one. |
side | Input/output | Physical 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
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
| Attribute | Access | Purpose |
|---|
aria-disabled | Input/output | Semantic disabled state. |
aria-expanded | Input/output | Open or expanded state exposed to assistive technology. |
aria-label | Input | Accessible name when visible text is insufficient. |
aria-labelledby | Output | ID of the element that supplies the accessible name. |
aria-multiselectable | Input | Set to true to allow Ctrl or Command click selection of several items. |
aria-selected | Input/output | Selected item state. |
data-value | Input | Application value included in angularcss:tree-select. |
disabled | Input | Disables native or component interaction. |
hidden | Input | Native visibility state observed when finding available items. |
role | Output | Explicit semantic role when native HTML does not provide one. |
tabindex | Input/output | Keyboard focus order for composite descendants. |
Input attributes are read from authored HTML. Output attributes are maintained by AngularCSS for CSS and testing. Input/output attributes may be authored for a controlled initial state and are then synchronized by the directive.
CSS custom properties
This directive does not write component-specific CSS custom properties.
DOM events
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.