docs: add draft aria component guides (pt 2)

(cherry picked from commit c61a195391)
This commit is contained in:
Ben Hong 2025-11-17 10:07:32 -05:00 committed by Jessica Janiuk
parent 7928ff1637
commit f62e2b599f
66 changed files with 2769 additions and 59 deletions

View file

@ -718,9 +718,9 @@ const DOCS_SUB_NAVIGATION_DATA: NavigationItem[] = [
contentPath: 'guide/aria/overview',
},
{
label: 'Autocomplete',
path: 'guide/aria/autocomplete',
contentPath: 'guide/aria/autocomplete',
label: 'Accordion',
path: 'guide/aria/accordion',
contentPath: 'guide/aria/accordion',
},
{
label: 'Autocomplete',
@ -732,11 +732,21 @@ const DOCS_SUB_NAVIGATION_DATA: NavigationItem[] = [
path: 'guide/aria/combobox',
contentPath: 'guide/aria/combobox',
},
{
label: 'Grid',
path: 'guide/aria/grid',
contentPath: 'guide/aria/grid',
},
{
label: 'Listbox',
path: 'guide/aria/listbox',
contentPath: 'guide/aria/listbox',
},
{
label: 'Menu',
path: 'guide/aria/menu',
contentPath: 'guide/aria/menu',
},
{
label: 'Multiselect',
path: 'guide/aria/multiselect',
@ -757,6 +767,11 @@ const DOCS_SUB_NAVIGATION_DATA: NavigationItem[] = [
path: 'guide/aria/toolbar',
contentPath: 'guide/aria/toolbar',
},
{
label: 'Tree',
path: 'guide/aria/tree',
contentPath: 'guide/aria/tree',
},
],
},
]

View file

@ -0,0 +1,9 @@
h1 {
background: #1e1e1e;
color: #e0e0e0;
padding: 2rem;
margin: 0;
font-family: system-ui, -apple-system, sans-serif;
font-size: 2rem;
text-align: center;
}

View file

@ -0,0 +1 @@
<h1>PLACEHOLDER</h1>

View file

@ -0,0 +1,8 @@
import {Component} from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.html',
styleUrl: './app.css',
})
export class App {}

View file

@ -0,0 +1,9 @@
h1 {
background: #1e1e1e;
color: #e0e0e0;
padding: 2rem;
margin: 0;
font-family: system-ui, -apple-system, sans-serif;
font-size: 2rem;
text-align: center;
}

View file

@ -0,0 +1 @@
<h1>PLACEHOLDER</h1>

View file

@ -0,0 +1,8 @@
import {Component} from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.html',
styleUrl: './app.css',
})
export class App {}

View file

@ -0,0 +1,9 @@
h1 {
background: #1e1e1e;
color: #e0e0e0;
padding: 2rem;
margin: 0;
font-family: system-ui, -apple-system, sans-serif;
font-size: 2rem;
text-align: center;
}

View file

@ -0,0 +1 @@
<h1>PLACEHOLDER</h1>

View file

@ -0,0 +1,8 @@
import {Component} from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.html',
styleUrl: './app.css',
})
export class App {}

View file

@ -0,0 +1,58 @@
:host {
display: flex;
justify-content: center;
}
[ngToolbar] {
gap: 1.5rem;
display: flex;
padding: 0.5rem 1rem;
border-radius: 0.5rem;
background-color: var(--septenary-contrast);
}
.group {
gap: 0.5rem;
display: flex;
}
.separator {
width: 1px;
align-self: center;
height: calc(100% - 1rem);
background-color: var(--quinary-contrast);
}
[ngToolbarWidget] {
border: none;
cursor: pointer;
padding: 0.5rem;
border-radius: 4px;
font-size: 1.25rem;
background-color: transparent;
color: var(--primary-contrast);
}
[ngToolbarWidget]:hover {
background-color: color-mix(in srgb, var(--primary-contrast) 10%, transparent);
}
[ngToolbarWidget]:active {
background-color: color-mix(in srgb, var(--primary-contrast) 15%, transparent);
}
[ngToolbarWidget]:focus {
outline-offset: -1px;
outline: 1px solid color-mix(in srgb, var(--hot-pink) 60%, transparent);
}
[ngToolbarWidget][aria-pressed="true"],
[ngToolbarWidget][aria-checked="true"] {
color: color-mix(in srgb, var(--hot-pink) 80%, var(--primary-contrast));
background-color: color-mix(in srgb, var(--hot-pink) 10%, transparent);
}
[ngToolbarWidget][aria-disabled="true"] {
cursor: default;
opacity: 0.45;
}

View file

@ -0,0 +1,109 @@
<link href="https://fonts.googleapis.com/icon?family=Material+Symbols+Outlined" rel="stylesheet" />
<div ngToolbar aria-label="Text Formatting Tools">
<div class="group">
<button
ngToolbarWidget
value="undo"
type="button"
aria-label="undo"
class="material-symbols-outlined"
>
undo
</button>
<button
disabled
ngToolbarWidget
value="redo"
type="button"
aria-label="redo"
class="material-symbols-outlined"
>
redo
</button>
</div>
<div class="separator" role="separator"></div>
<div class="group">
<button
ngToolbarWidget
value="bold"
type="button"
aria-label="bold"
#bold="ngToolbarWidget"
class="material-symbols-outlined"
[aria-pressed]="bold.selected()"
>
format_bold
</button>
<button
ngToolbarWidget
value="italic"
type="button"
aria-label="italic"
#italic="ngToolbarWidget"
class="material-symbols-outlined"
[aria-pressed]="italic.selected()"
>
format_italic
</button>
<button
ngToolbarWidget
value="underlined"
type="button"
aria-label="underlined"
#underlined="ngToolbarWidget"
class="material-symbols-outlined"
[aria-pressed]="underlined.selected()"
>
format_underlined
</button>
</div>
<div class="separator" role="separator"></div>
<div ngToolbarWidgetGroup role="radiogroup" class="group" aria-label="Text alignment options" disabled>
<button
ngToolbarWidget
role="radio"
type="button"
value="align left"
aria-label="align left"
#leftAlign="ngToolbarWidget"
class="material-symbols-outlined"
[aria-checked]="leftAlign.selected()"
>
format_align_left
</button>
<button
ngToolbarWidget
role="radio"
type="button"
value="align center"
aria-label="align center"
#centerAlign="ngToolbarWidget"
class="material-symbols-outlined"
[aria-checked]="centerAlign.selected()"
>
format_align_center
</button>
<button
ngToolbarWidget
role="radio"
type="button"
value="align right"
aria-label="align right"
#rightAlign="ngToolbarWidget"
class="material-symbols-outlined"
[aria-checked]="rightAlign.selected()"
>
format_align_right
</button>
</div>
</div>

View file

@ -0,0 +1,10 @@
import {Component} from '@angular/core';
import {Toolbar, ToolbarWidget, ToolbarWidgetGroup} from '@angular/aria/toolbar';
@Component({
selector: 'app-root',
templateUrl: 'app.html',
styleUrl: 'app.css',
imports: [Toolbar, ToolbarWidget, ToolbarWidgetGroup],
})
export class App {}

View file

@ -0,0 +1,53 @@
:host {
display: flex;
justify-content: center;
}
[ngToolbar] {
gap: 1.5rem;
display: flex;
padding: 0.5rem 1rem;
border-radius: 0.5rem;
background-color: var(--septenary-contrast);
}
.group {
gap: 0.5rem;
display: flex;
}
.separator {
width: 1px;
align-self: center;
height: calc(100% - 1rem);
background-color: var(--quinary-contrast);
}
[ngToolbarWidget] {
border: none;
cursor: pointer;
padding: 0.5rem;
border-radius: 4px;
font-size: 1.25rem;
background-color: transparent;
color: var(--primary-contrast);
}
[ngToolbarWidget]:hover {
background-color: color-mix(in srgb, var(--primary-contrast) 10%, transparent);
}
[ngToolbarWidget]:active {
background-color: color-mix(in srgb, var(--primary-contrast) 15%, transparent);
}
[ngToolbarWidget]:focus {
outline-offset: -1px;
outline: 1px solid color-mix(in srgb, var(--hot-pink) 60%, transparent);
}
[ngToolbarWidget][aria-pressed="true"],
[ngToolbarWidget][aria-checked="true"] {
color: color-mix(in srgb, var(--hot-pink) 80%, var(--primary-contrast));
background-color: color-mix(in srgb, var(--hot-pink) 10%, transparent);
}

View file

@ -0,0 +1,108 @@
<link href="https://fonts.googleapis.com/icon?family=Material+Symbols+Outlined" rel="stylesheet" />
<div ngToolbar aria-label="Text Formatting Tools">
<div class="group">
<button
ngToolbarWidget
value="undo"
type="button"
aria-label="undo"
class="material-symbols-outlined"
>
undo
</button>
<button
ngToolbarWidget
value="redo"
type="button"
aria-label="redo"
class="material-symbols-outlined"
>
redo
</button>
</div>
<div class="separator" role="separator"></div>
<div class="group">
<button
ngToolbarWidget
value="bold"
type="button"
aria-label="bold"
#bold="ngToolbarWidget"
class="material-symbols-outlined"
[aria-pressed]="bold.selected()"
>
format_bold
</button>
<button
ngToolbarWidget
value="italic"
type="button"
aria-label="italic"
#italic="ngToolbarWidget"
class="material-symbols-outlined"
[aria-pressed]="italic.selected()"
>
format_italic
</button>
<button
ngToolbarWidget
value="underlined"
type="button"
aria-label="underlined"
#underlined="ngToolbarWidget"
class="material-symbols-outlined"
[aria-pressed]="underlined.selected()"
>
format_underlined
</button>
</div>
<div class="separator" role="separator"></div>
<div ngToolbarWidgetGroup role="radiogroup" class="group" aria-label="Text alignment options">
<button
ngToolbarWidget
role="radio"
type="button"
value="align left"
aria-label="align left"
#leftAlign="ngToolbarWidget"
class="material-symbols-outlined"
[aria-checked]="leftAlign.selected()"
>
format_align_left
</button>
<button
ngToolbarWidget
role="radio"
type="button"
value="align center"
aria-label="align center"
#centerAlign="ngToolbarWidget"
class="material-symbols-outlined"
[aria-checked]="centerAlign.selected()"
>
format_align_center
</button>
<button
ngToolbarWidget
role="radio"
type="button"
value="align right"
aria-label="align right"
#rightAlign="ngToolbarWidget"
class="material-symbols-outlined"
[aria-checked]="rightAlign.selected()"
>
format_align_right
</button>
</div>
</div>

View file

@ -0,0 +1,10 @@
import {Component} from '@angular/core';
import {Toolbar, ToolbarWidget, ToolbarWidgetGroup} from '@angular/aria/toolbar';
@Component({
selector: 'app-root',
templateUrl: 'app.html',
styleUrl: 'app.css',
imports: [Toolbar, ToolbarWidget, ToolbarWidgetGroup],
})
export class App {}

View file

@ -0,0 +1,53 @@
:host {
display: flex;
justify-content: center;
}
[ngToolbar] {
gap: 1.5rem;
display: flex;
padding: 0.5rem 1rem;
border-radius: 0.5rem;
background-color: var(--septenary-contrast);
}
.group {
gap: 0.5rem;
display: flex;
}
.separator {
width: 1px;
align-self: center;
height: calc(100% - 1rem);
background-color: var(--quinary-contrast);
}
[ngToolbarWidget] {
border: none;
cursor: pointer;
padding: 0.5rem;
border-radius: 4px;
font-size: 1.25rem;
background-color: transparent;
color: var(--primary-contrast);
}
[ngToolbarWidget]:hover {
background-color: color-mix(in srgb, var(--primary-contrast) 10%, transparent);
}
[ngToolbarWidget]:active {
background-color: color-mix(in srgb, var(--primary-contrast) 15%, transparent);
}
[ngToolbarWidget]:focus {
outline-offset: -1px;
outline: 1px solid color-mix(in srgb, var(--hot-pink) 60%, transparent);
}
[ngToolbarWidget][aria-pressed="true"],
[ngToolbarWidget][aria-checked="true"] {
color: color-mix(in srgb, var(--hot-pink) 80%, var(--primary-contrast));
background-color: color-mix(in srgb, var(--hot-pink) 10%, transparent);
}

View file

@ -0,0 +1,108 @@
<link href="https://fonts.googleapis.com/icon?family=Material+Symbols+Outlined" rel="stylesheet" />
<div ngToolbar dir="rtl" aria-label="Text Formatting Tools">
<div class="group">
<button
ngToolbarWidget
value="undo"
type="button"
aria-label="undo"
class="material-symbols-outlined"
>
undo
</button>
<button
ngToolbarWidget
value="redo"
type="button"
aria-label="redo"
class="material-symbols-outlined"
>
redo
</button>
</div>
<div class="separator" role="separator"></div>
<div class="group">
<button
ngToolbarWidget
value="bold"
type="button"
aria-label="bold"
#bold="ngToolbarWidget"
class="material-symbols-outlined"
[aria-pressed]="bold.selected()"
>
format_bold
</button>
<button
ngToolbarWidget
value="italic"
type="button"
aria-label="italic"
#italic="ngToolbarWidget"
class="material-symbols-outlined"
[aria-pressed]="italic.selected()"
>
format_italic
</button>
<button
ngToolbarWidget
value="underlined"
type="button"
aria-label="underlined"
#underlined="ngToolbarWidget"
class="material-symbols-outlined"
[aria-pressed]="underlined.selected()"
>
format_underlined
</button>
</div>
<div class="separator" role="separator"></div>
<div ngToolbarWidgetGroup role="radiogroup" class="group" aria-label="Text alignment options">
<button
ngToolbarWidget
role="radio"
type="button"
value="align left"
aria-label="align left"
#leftAlign="ngToolbarWidget"
class="material-symbols-outlined"
[aria-checked]="leftAlign.selected()"
>
format_align_left
</button>
<button
ngToolbarWidget
role="radio"
type="button"
value="align center"
aria-label="align center"
#centerAlign="ngToolbarWidget"
class="material-symbols-outlined"
[aria-checked]="centerAlign.selected()"
>
format_align_center
</button>
<button
ngToolbarWidget
role="radio"
type="button"
value="align right"
aria-label="align right"
#rightAlign="ngToolbarWidget"
class="material-symbols-outlined"
[aria-checked]="rightAlign.selected()"
>
format_align_right
</button>
</div>
</div>

View file

@ -0,0 +1,11 @@
import {Dir} from '@angular/cdk/bidi';
import {Component} from '@angular/core';
import {Toolbar, ToolbarWidget, ToolbarWidgetGroup} from '@angular/aria/toolbar';
@Component({
selector: 'app-root',
templateUrl: 'app.html',
styleUrl: 'app.css',
imports: [Dir, Toolbar, ToolbarWidget, ToolbarWidgetGroup],
})
export class App {}

View file

@ -0,0 +1,53 @@
:host {
display: flex;
justify-content: center;
}
[ngToolbar] {
gap: 1.5rem;
display: flex;
padding: 0.5rem 1rem;
border-radius: 0.5rem;
background-color: var(--septenary-contrast);
}
.group {
gap: 0.5rem;
display: flex;
}
.separator {
width: 1px;
align-self: center;
height: calc(100% - 1rem);
background-color: var(--quinary-contrast);
}
[ngToolbarWidget] {
border: none;
cursor: pointer;
padding: 0.5rem;
border-radius: 4px;
font-size: 1.25rem;
background-color: transparent;
color: var(--primary-contrast);
}
[ngToolbarWidget]:hover {
background-color: color-mix(in srgb, var(--primary-contrast) 10%, transparent);
}
[ngToolbarWidget]:active {
background-color: color-mix(in srgb, var(--primary-contrast) 15%, transparent);
}
[ngToolbarWidget]:focus {
outline-offset: -1px;
outline: 1px solid color-mix(in srgb, var(--hot-pink) 60%, transparent);
}
[ngToolbarWidget][aria-pressed="true"],
[ngToolbarWidget][aria-checked="true"] {
color: color-mix(in srgb, var(--hot-pink) 80%, var(--primary-contrast));
background-color: color-mix(in srgb, var(--hot-pink) 10%, transparent);
}

View file

@ -0,0 +1,108 @@
<link href="https://fonts.googleapis.com/icon?family=Material+Symbols+Outlined" rel="stylesheet" />
<div ngToolbar dir="rtl" aria-label="Text Formatting Tools">
<div class="group">
<button
ngToolbarWidget
value="undo"
type="button"
aria-label="undo"
class="material-symbols-outlined"
>
undo
</button>
<button
ngToolbarWidget
value="redo"
type="button"
aria-label="redo"
class="material-symbols-outlined"
>
redo
</button>
</div>
<div class="separator" role="separator"></div>
<div class="group">
<button
ngToolbarWidget
value="bold"
type="button"
aria-label="bold"
#bold="ngToolbarWidget"
class="material-symbols-outlined"
[aria-pressed]="bold.selected()"
>
format_bold
</button>
<button
ngToolbarWidget
value="italic"
type="button"
aria-label="italic"
#italic="ngToolbarWidget"
class="material-symbols-outlined"
[aria-pressed]="italic.selected()"
>
format_italic
</button>
<button
ngToolbarWidget
value="underlined"
type="button"
aria-label="underlined"
#underlined="ngToolbarWidget"
class="material-symbols-outlined"
[aria-pressed]="underlined.selected()"
>
format_underlined
</button>
</div>
<div class="separator" role="separator"></div>
<div ngToolbarWidgetGroup role="radiogroup" class="group" aria-label="Text alignment options">
<button
ngToolbarWidget
role="radio"
type="button"
value="align left"
aria-label="align left"
#leftAlign="ngToolbarWidget"
class="material-symbols-outlined"
[aria-checked]="leftAlign.selected()"
>
format_align_left
</button>
<button
ngToolbarWidget
role="radio"
type="button"
value="align center"
aria-label="align center"
#centerAlign="ngToolbarWidget"
class="material-symbols-outlined"
[aria-checked]="centerAlign.selected()"
>
format_align_center
</button>
<button
ngToolbarWidget
role="radio"
type="button"
value="align right"
aria-label="align right"
#rightAlign="ngToolbarWidget"
class="material-symbols-outlined"
[aria-checked]="rightAlign.selected()"
>
format_align_right
</button>
</div>
</div>

View file

@ -0,0 +1,11 @@
import {Dir} from '@angular/cdk/bidi';
import {Component} from '@angular/core';
import {Toolbar, ToolbarWidget, ToolbarWidgetGroup} from '@angular/aria/toolbar';
@Component({
selector: 'app-root',
templateUrl: 'app.html',
styleUrl: 'app.css',
imports: [Dir, Toolbar, ToolbarWidget, ToolbarWidgetGroup],
})
export class App {}

View file

@ -0,0 +1,53 @@
:host {
display: flex;
justify-content: center;
}
[ngToolbar] {
gap: 1.5rem;
display: flex;
padding: 0.5rem 1rem;
border-radius: 0.5rem;
background-color: var(--septenary-contrast);
}
.group {
gap: 0.5rem;
display: flex;
}
.separator {
width: 1px;
align-self: center;
height: calc(100% - 1rem);
background-color: var(--quinary-contrast);
}
[ngToolbarWidget] {
border: none;
cursor: pointer;
padding: 0.5rem;
border-radius: 4px;
font-size: 1.25rem;
background-color: transparent;
color: var(--primary-contrast);
}
[ngToolbarWidget]:hover {
background-color: color-mix(in srgb, var(--primary-contrast) 10%, transparent);
}
[ngToolbarWidget]:active {
background-color: color-mix(in srgb, var(--primary-contrast) 15%, transparent);
}
[ngToolbarWidget]:focus {
outline-offset: -1px;
outline: 1px solid color-mix(in srgb, var(--hot-pink) 60%, transparent);
}
[ngToolbarWidget][aria-pressed="true"],
[ngToolbarWidget][aria-checked="true"] {
color: color-mix(in srgb, var(--hot-pink) 80%, var(--primary-contrast));
background-color: color-mix(in srgb, var(--hot-pink) 10%, transparent);
}

View file

@ -0,0 +1,108 @@
<link href="https://fonts.googleapis.com/icon?family=Material+Symbols+Outlined" rel="stylesheet" />
<div ngToolbar aria-label="Text Formatting Tools">
<div class="group">
<button
ngToolbarWidget
value="undo"
type="button"
aria-label="undo"
class="material-symbols-outlined"
>
undo
</button>
<button
ngToolbarWidget
value="redo"
type="button"
aria-label="redo"
class="material-symbols-outlined"
>
redo
</button>
</div>
<div class="separator" role="separator"></div>
<div class="group">
<button
ngToolbarWidget
value="bold"
type="button"
aria-label="bold"
#bold="ngToolbarWidget"
class="material-symbols-outlined"
[aria-pressed]="bold.selected()"
>
format_bold
</button>
<button
ngToolbarWidget
value="italic"
type="button"
aria-label="italic"
#italic="ngToolbarWidget"
class="material-symbols-outlined"
[aria-pressed]="italic.selected()"
>
format_italic
</button>
<button
ngToolbarWidget
value="underlined"
type="button"
aria-label="underlined"
#underlined="ngToolbarWidget"
class="material-symbols-outlined"
[aria-pressed]="underlined.selected()"
>
format_underlined
</button>
</div>
<div class="separator" role="separator"></div>
<div ngToolbarWidgetGroup role="radiogroup" class="group" aria-label="Text alignment options">
<button
ngToolbarWidget
role="radio"
type="button"
value="align left"
aria-label="align left"
#leftAlign="ngToolbarWidget"
class="material-symbols-outlined"
[aria-checked]="leftAlign.selected()"
>
format_align_left
</button>
<button
ngToolbarWidget
role="radio"
type="button"
value="align center"
aria-label="align center"
#centerAlign="ngToolbarWidget"
class="material-symbols-outlined"
[aria-checked]="centerAlign.selected()"
>
format_align_center
</button>
<button
ngToolbarWidget
role="radio"
type="button"
value="align right"
aria-label="align right"
#rightAlign="ngToolbarWidget"
class="material-symbols-outlined"
[aria-checked]="rightAlign.selected()"
>
format_align_right
</button>
</div>
</div>

View file

@ -0,0 +1,10 @@
import {Component} from '@angular/core';
import {Toolbar, ToolbarWidget, ToolbarWidgetGroup} from '@angular/aria/toolbar';
@Component({
selector: 'app-root',
templateUrl: 'app.html',
styleUrl: 'app.css',
imports: [Toolbar, ToolbarWidget, ToolbarWidgetGroup],
})
export class App {}

View file

@ -0,0 +1,53 @@
:host {
display: flex;
justify-content: center;
}
[ngToolbar] {
gap: 1.5rem;
display: flex;
padding: 0.5rem 1rem;
border-radius: 0.5rem;
background-color: var(--septenary-contrast);
}
.group {
gap: 0.5rem;
display: flex;
}
.separator {
width: 1px;
align-self: center;
height: calc(100% - 1rem);
background-color: var(--quinary-contrast);
}
[ngToolbarWidget] {
border: none;
cursor: pointer;
padding: 0.5rem;
border-radius: 4px;
font-size: 1.25rem;
background-color: transparent;
color: var(--primary-contrast);
}
[ngToolbarWidget]:hover {
background-color: color-mix(in srgb, var(--primary-contrast) 10%, transparent);
}
[ngToolbarWidget]:active {
background-color: color-mix(in srgb, var(--primary-contrast) 15%, transparent);
}
[ngToolbarWidget]:focus {
outline-offset: -1px;
outline: 1px solid color-mix(in srgb, var(--hot-pink) 60%, transparent);
}
[ngToolbarWidget][aria-pressed="true"],
[ngToolbarWidget][aria-checked="true"] {
color: color-mix(in srgb, var(--hot-pink) 80%, var(--primary-contrast));
background-color: color-mix(in srgb, var(--hot-pink) 10%, transparent);
}

View file

@ -0,0 +1,108 @@
<link href="https://fonts.googleapis.com/icon?family=Material+Symbols+Outlined" rel="stylesheet" />
<div ngToolbar aria-label="Text Formatting Tools">
<div class="group">
<button
ngToolbarWidget
value="undo"
type="button"
aria-label="undo"
class="material-symbols-outlined"
>
undo
</button>
<button
ngToolbarWidget
value="redo"
type="button"
aria-label="redo"
class="material-symbols-outlined"
>
redo
</button>
</div>
<div class="separator" role="separator"></div>
<div class="group">
<button
ngToolbarWidget
value="bold"
type="button"
aria-label="bold"
#bold="ngToolbarWidget"
class="material-symbols-outlined"
[aria-pressed]="bold.selected()"
>
format_bold
</button>
<button
ngToolbarWidget
value="italic"
type="button"
aria-label="italic"
#italic="ngToolbarWidget"
class="material-symbols-outlined"
[aria-pressed]="italic.selected()"
>
format_italic
</button>
<button
ngToolbarWidget
value="underlined"
type="button"
aria-label="underlined"
#underlined="ngToolbarWidget"
class="material-symbols-outlined"
[aria-pressed]="underlined.selected()"
>
format_underlined
</button>
</div>
<div class="separator" role="separator"></div>
<div ngToolbarWidgetGroup role="radiogroup" class="group" aria-label="Text alignment options">
<button
ngToolbarWidget
role="radio"
type="button"
value="align left"
aria-label="align left"
#leftAlign="ngToolbarWidget"
class="material-symbols-outlined"
[aria-checked]="leftAlign.selected()"
>
format_align_left
</button>
<button
ngToolbarWidget
role="radio"
type="button"
value="align center"
aria-label="align center"
#centerAlign="ngToolbarWidget"
class="material-symbols-outlined"
[aria-checked]="centerAlign.selected()"
>
format_align_center
</button>
<button
ngToolbarWidget
role="radio"
type="button"
value="align right"
aria-label="align right"
#rightAlign="ngToolbarWidget"
class="material-symbols-outlined"
[aria-checked]="rightAlign.selected()"
>
format_align_right
</button>
</div>
</div>

View file

@ -0,0 +1,10 @@
import {Component} from '@angular/core';
import {Toolbar, ToolbarWidget, ToolbarWidgetGroup} from '@angular/aria/toolbar';
@Component({
selector: 'app-root',
templateUrl: 'app.html',
styleUrl: 'app.css',
imports: [Toolbar, ToolbarWidget, ToolbarWidgetGroup],
})
export class App {}

View file

@ -0,0 +1,53 @@
:host {
display: flex;
justify-content: center;
}
[ngToolbar] {
gap: 1.5rem;
display: flex;
padding: 0.5rem 1rem;
border-radius: 0.5rem;
background-color: var(--septenary-contrast);
}
.group {
gap: 0.5rem;
display: flex;
}
.separator {
width: 1px;
align-self: center;
height: calc(100% - 1rem);
background-color: var(--quinary-contrast);
}
[ngToolbarWidget] {
border: none;
cursor: pointer;
padding: 0.5rem;
border-radius: 4px;
font-size: 1.25rem;
background-color: transparent;
color: var(--primary-contrast);
}
[ngToolbarWidget]:hover {
background-color: color-mix(in srgb, var(--primary-contrast) 10%, transparent);
}
[ngToolbarWidget]:active {
background-color: color-mix(in srgb, var(--primary-contrast) 15%, transparent);
}
[ngToolbarWidget]:focus {
outline-offset: -1px;
outline: 1px solid color-mix(in srgb, var(--hot-pink) 60%, transparent);
}
[ngToolbarWidget][aria-pressed="true"],
[ngToolbarWidget][aria-checked="true"] {
color: color-mix(in srgb, var(--hot-pink) 80%, var(--primary-contrast));
background-color: color-mix(in srgb, var(--hot-pink) 10%, transparent);
}

View file

@ -0,0 +1,108 @@
<link href="https://fonts.googleapis.com/icon?family=Material+Symbols+Outlined" rel="stylesheet" />
<div ngToolbar aria-label="Text Formatting Tools">
<div class="group">
<button
ngToolbarWidget
value="undo"
type="button"
aria-label="undo"
class="material-symbols-outlined"
>
undo
</button>
<button
ngToolbarWidget
value="redo"
type="button"
aria-label="redo"
class="material-symbols-outlined"
>
redo
</button>
</div>
<div class="separator" role="separator"></div>
<div class="group">
<button
ngToolbarWidget
value="bold"
type="button"
aria-label="bold"
#bold="ngToolbarWidget"
class="material-symbols-outlined"
[aria-pressed]="bold.selected()"
>
format_bold
</button>
<button
ngToolbarWidget
value="italic"
type="button"
aria-label="italic"
#italic="ngToolbarWidget"
class="material-symbols-outlined"
[aria-pressed]="italic.selected()"
>
format_italic
</button>
<button
ngToolbarWidget
value="underlined"
type="button"
aria-label="underlined"
#underlined="ngToolbarWidget"
class="material-symbols-outlined"
[aria-pressed]="underlined.selected()"
>
format_underlined
</button>
</div>
<div class="separator" role="separator"></div>
<div ngToolbarWidgetGroup role="radiogroup" class="group" aria-label="Text alignment options">
<button
ngToolbarWidget
role="radio"
type="button"
value="align left"
aria-label="align left"
#leftAlign="ngToolbarWidget"
class="material-symbols-outlined"
[aria-checked]="leftAlign.selected()"
>
format_align_left
</button>
<button
ngToolbarWidget
role="radio"
type="button"
value="align center"
aria-label="align center"
#centerAlign="ngToolbarWidget"
class="material-symbols-outlined"
[aria-checked]="centerAlign.selected()"
>
format_align_center
</button>
<button
ngToolbarWidget
role="radio"
type="button"
value="align right"
aria-label="align right"
#rightAlign="ngToolbarWidget"
class="material-symbols-outlined"
[aria-checked]="rightAlign.selected()"
>
format_align_right
</button>
</div>
</div>

View file

@ -0,0 +1,10 @@
import {Component} from '@angular/core';
import {Toolbar, ToolbarWidget, ToolbarWidgetGroup} from '@angular/aria/toolbar';
@Component({
selector: 'app-root',
templateUrl: 'app.html',
styleUrl: 'app.css',
imports: [Toolbar, ToolbarWidget, ToolbarWidgetGroup],
})
export class App {}

View file

@ -0,0 +1,58 @@
:host {
display: flex;
justify-content: center;
}
[ngToolbar] {
gap: 1.5rem;
display: flex;
padding: 0.5rem 1rem;
border-radius: 0.5rem;
background-color: var(--septenary-contrast);
}
.group {
gap: 0.5rem;
display: flex;
}
.separator {
width: 1px;
align-self: center;
height: calc(100% - 1rem);
background-color: var(--quinary-contrast);
}
[ngToolbarWidget] {
border: none;
cursor: pointer;
padding: 0.5rem;
border-radius: 4px;
font-size: 1.25rem;
background-color: transparent;
color: var(--primary-contrast);
}
[ngToolbarWidget]:hover {
background-color: color-mix(in srgb, var(--primary-contrast) 10%, transparent);
}
[ngToolbarWidget]:active {
background-color: color-mix(in srgb, var(--primary-contrast) 15%, transparent);
}
[ngToolbarWidget]:focus {
outline-offset: -1px;
outline: 1px solid color-mix(in srgb, var(--hot-pink) 60%, transparent);
}
[ngToolbarWidget][aria-pressed="true"],
[ngToolbarWidget][aria-checked="true"] {
color: color-mix(in srgb, var(--hot-pink) 80%, var(--primary-contrast));
background-color: color-mix(in srgb, var(--hot-pink) 10%, transparent);
}
[ngToolbarWidget][aria-disabled="true"] {
cursor: default;
opacity: 0.45;
}

View file

@ -0,0 +1,109 @@
<link href="https://fonts.googleapis.com/icon?family=Material+Symbols+Outlined" rel="stylesheet" />
<div ngToolbar aria-label="Text Formatting Tools">
<div class="group">
<button
ngToolbarWidget
value="undo"
type="button"
aria-label="undo"
class="material-symbols-outlined"
>
undo
</button>
<button
disabled
ngToolbarWidget
value="redo"
type="button"
aria-label="redo"
class="material-symbols-outlined"
>
redo
</button>
</div>
<div class="separator" role="separator"></div>
<div class="group">
<button
ngToolbarWidget
value="bold"
type="button"
aria-label="bold"
#bold="ngToolbarWidget"
class="material-symbols-outlined"
[aria-pressed]="bold.selected()"
>
format_bold
</button>
<button
ngToolbarWidget
value="italic"
type="button"
aria-label="italic"
#italic="ngToolbarWidget"
class="material-symbols-outlined"
[aria-pressed]="italic.selected()"
>
format_italic
</button>
<button
ngToolbarWidget
value="underlined"
type="button"
aria-label="underlined"
#underlined="ngToolbarWidget"
class="material-symbols-outlined"
[aria-pressed]="underlined.selected()"
>
format_underlined
</button>
</div>
<div class="separator" role="separator"></div>
<div ngToolbarWidgetGroup role="radiogroup" class="group" aria-label="Text alignment options" disabled>
<button
ngToolbarWidget
role="radio"
type="button"
value="align left"
aria-label="align left"
#leftAlign="ngToolbarWidget"
class="material-symbols-outlined"
[aria-checked]="leftAlign.selected()"
>
format_align_left
</button>
<button
ngToolbarWidget
role="radio"
type="button"
value="align center"
aria-label="align center"
#centerAlign="ngToolbarWidget"
class="material-symbols-outlined"
[aria-checked]="centerAlign.selected()"
>
format_align_center
</button>
<button
ngToolbarWidget
role="radio"
type="button"
value="align right"
aria-label="align right"
#rightAlign="ngToolbarWidget"
class="material-symbols-outlined"
[aria-checked]="rightAlign.selected()"
>
format_align_right
</button>
</div>
</div>

View file

@ -0,0 +1,10 @@
import {Component} from '@angular/core';
import {Toolbar, ToolbarWidget, ToolbarWidgetGroup} from '@angular/aria/toolbar';
@Component({
selector: 'app-root',
templateUrl: 'app.html',
styleUrl: 'app.css',
imports: [Toolbar, ToolbarWidget, ToolbarWidgetGroup],
})
export class App {}

View file

@ -0,0 +1,9 @@
h1 {
background: #1e1e1e;
color: #e0e0e0;
padding: 2rem;
margin: 0;
font-family: system-ui, -apple-system, sans-serif;
font-size: 2rem;
text-align: center;
}

View file

@ -0,0 +1 @@
<h1>PLACEHOLDER</h1>

View file

@ -0,0 +1,8 @@
import {Component} from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.html',
styleUrl: './app.css',
})
export class App {}

View file

@ -0,0 +1,9 @@
h1 {
background: #1e1e1e;
color: #e0e0e0;
padding: 2rem;
margin: 0;
font-family: system-ui, -apple-system, sans-serif;
font-size: 2rem;
text-align: center;
}

View file

@ -0,0 +1 @@
<h1>PLACEHOLDER</h1>

View file

@ -0,0 +1,8 @@
import {Component} from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.html',
styleUrl: './app.css',
})
export class App {}

View file

@ -0,0 +1,9 @@
h1 {
background: #1e1e1e;
color: #e0e0e0;
padding: 2rem;
margin: 0;
font-family: system-ui, -apple-system, sans-serif;
font-size: 2rem;
text-align: center;
}

View file

@ -0,0 +1 @@
<h1>PLACEHOLDER</h1>

View file

@ -0,0 +1,8 @@
import {Component} from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.html',
styleUrl: './app.css',
})
export class App {}

View file

@ -0,0 +1,9 @@
h1 {
background: #1e1e1e;
color: #e0e0e0;
padding: 2rem;
margin: 0;
font-family: system-ui, -apple-system, sans-serif;
font-size: 2rem;
text-align: center;
}

View file

@ -0,0 +1 @@
<h1>PLACEHOLDER</h1>

View file

@ -0,0 +1,8 @@
import {Component} from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.html',
styleUrl: './app.css',
})
export class App {}

View file

@ -0,0 +1,9 @@
h1 {
background: #1e1e1e;
color: #e0e0e0;
padding: 2rem;
margin: 0;
font-family: system-ui, -apple-system, sans-serif;
font-size: 2rem;
text-align: center;
}

View file

@ -0,0 +1 @@
<h1>PLACEHOLDER</h1>

View file

@ -0,0 +1,8 @@
import {Component} from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.html',
styleUrl: './app.css',
})
export class App {}

View file

@ -0,0 +1,9 @@
h1 {
background: #1e1e1e;
color: #e0e0e0;
padding: 2rem;
margin: 0;
font-family: system-ui, -apple-system, sans-serif;
font-size: 2rem;
text-align: center;
}

View file

@ -0,0 +1 @@
<h1>PLACEHOLDER</h1>

View file

@ -0,0 +1,8 @@
import {Component} from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.html',
styleUrl: './app.css',
})
export class App {}

View file

@ -0,0 +1,9 @@
h1 {
background: #1e1e1e;
color: #e0e0e0;
padding: 2rem;
margin: 0;
font-family: system-ui, -apple-system, sans-serif;
font-size: 2rem;
text-align: center;
}

View file

@ -0,0 +1 @@
<h1>PLACEHOLDER</h1>

View file

@ -0,0 +1,8 @@
import {Component} from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.html',
styleUrl: './app.css',
})
export class App {}

View file

@ -0,0 +1,9 @@
h1 {
background: #1e1e1e;
color: #e0e0e0;
padding: 2rem;
margin: 0;
font-family: system-ui, -apple-system, sans-serif;
font-size: 2rem;
text-align: center;
}

View file

@ -0,0 +1 @@
<h1>PLACEHOLDER</h1>

View file

@ -0,0 +1,8 @@
import {Component} from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.html',
styleUrl: './app.css',
})
export class App {}

View file

@ -0,0 +1,9 @@
h1 {
background: #1e1e1e;
color: #e0e0e0;
padding: 2rem;
margin: 0;
font-family: system-ui, -apple-system, sans-serif;
font-size: 2rem;
text-align: center;
}

View file

@ -0,0 +1 @@
<h1>PLACEHOLDER</h1>

View file

@ -0,0 +1,8 @@
import {Component} from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.html',
styleUrl: './app.css',
})
export class App {}

View file

@ -1,22 +1,246 @@
<docs-decorative-header title="Accordion">
<!-- TODO: Add a short description about Accordion. -->
</docs-decorative-header>
## Overview
An accordion organizes related content into expandable and collapsible sections, reducing page scrolling and helping users focus on relevant information. Each section has a trigger button and a content panel. Clicking a trigger toggles the visibility of its associated panel.
<docs-code-multifile preview hideCode path="adev/src/content/examples/aria/accordion/src/single-expansion/app/app.ts">
<docs-code header="app.ts" path="adev/src/content/examples/aria/accordion/src/single-expansion/app/app.ts"/>
<docs-code header="app.html" path="adev/src/content/examples/aria/accordion/src/single-expansion/app/app.html"/>
<docs-code header="app.css" path="adev/src/content/examples/aria/accordion/src/single-expansion/app/app.css"/>
</docs-code-multifile>
<docs-pill-row>
<docs-pill href="https://www.w3.org/WAI/ARIA/apg/patterns/accordion/" title="ARIA pattern"/>
<!-- TODO: Add a link to the Accordion API reference. -->
<docs-pill href="api/aria/accordion" title="API Reference"/>
</docs-pill-row>
<!-- TODO: Add a top level component preview with code example hidden.
## Usage
<docs-code-multifile preview themed hideCode path="adev/src/content/examples/aria/src/accordion/app/app.component.ts">
<docs-code header="app/app.component.html" path="adev/src/content/examples/aria/src/accordion/app/app.component.html"/>
<docs-code header="app/app.component.ts" path="adev/src/content/examples/aria/src/accordion/app/app.component.ts"/>
<docs-code header="app/app.component.css" path="adev/src/content/examples/aria/src/accordion/app/app.component.css"/>
Accordions work well for organizing content into logical groups where users typically need to view one section at a time.
**Use accordions when:**
- Displaying FAQs with multiple questions and answers
- Organizing long forms into manageable sections
- Reducing scrolling on content-heavy pages
- Progressively disclosing related information
**Avoid accordions when:**
- Building navigation menus (use the [Menu](guide/aria/menu) component instead)
- Creating tabbed interfaces (use the [Tabs](guide/aria/tabs) component instead)
- Showing a single collapsible section (use a disclosure pattern instead)
- Users need to see multiple sections simultaneously (consider a different layout)
## Features
- **Expansion modes** - Control whether one or multiple panels can be open at the same time
- **Keyboard navigation** - Navigate between triggers using arrow keys, Home, and End
- **Lazy rendering** - Content is only created when a panel first expands, improving initial load performance
- **Disabled states** - Disable the entire group or individual triggers
- **Focus management** - Control whether disabled items can receive keyboard focus
- **Programmatic control** - Expand, collapse, or toggle panels from your component code
- **RTL support** - Automatic support for right-to-left languages
## Examples
### Single expansion mode
Set `[multiExpandable]="false"` to allow only one panel to be open at a time. Opening a new panel automatically closes any previously open panel.
<docs-code-multifile preview hideCode path="adev/src/content/examples/aria/accordion/src/single-expansion/app/app.ts">
<docs-code header="app.ts" path="adev/src/content/examples/aria/accordion/src/single-expansion/app/app.ts"/>
<docs-code header="app.html" path="adev/src/content/examples/aria/accordion/src/single-expansion/app/app.html"/>
</docs-code-multifile>
-->
This mode works well for FAQs or situations where you want users to focus on one answer at a time.
### Example with TailwindCSS
### Multiple expansion mode
<!-- TODO: Add more code examples with different styles. -->
Set `[multiExpandable]="true"` to allow multiple panels to be open simultaneously. Users can expand as many panels as needed without closing others.
<docs-code-multifile preview hideCode path="adev/src/content/examples/aria/accordion/src/multi-expansion/app/app.ts">
<docs-code header="app.ts" path="adev/src/content/examples/aria/accordion/src/multi-expansion/app/app.ts"/>
<docs-code header="app.html" path="adev/src/content/examples/aria/accordion/src/multi-expansion/app/app.html"/>
</docs-code-multifile>
This mode is useful for form sections or when users need to compare content across multiple panels.
NOTE: The `multiExpandable` input defaults to `true`. Set it to `false` explicitly if you want single expansion behavior.
### Disabled accordion items
Disable specific triggers using the `disabled` input. Control how disabled items behave during keyboard navigation using the `softDisabled` input on the accordion group.
<docs-code-multifile preview hideCode path="adev/src/content/examples/aria/accordion/src/disabled-focusable/app/app.ts">
<docs-code header="app.ts" path="adev/src/content/examples/aria/accordion/src/disabled-focusable/app/app.ts"/>
<docs-code header="app.html" path="adev/src/content/examples/aria/accordion/src/disabled-focusable/app/app.html"/>
</docs-code-multifile>
When `[softDisabled]="true"` (the default), disabled items can receive focus but cannot be activated. When `[softDisabled]="false"`, disabled items are skipped entirely during keyboard navigation.
### Lazy content rendering
Use the `ngAccordionContent` directive on an `ng-template` to defer rendering content until the panel first expands. This improves performance for accordions with heavy content like images, charts, or complex components.
```angular-html
<div ngAccordionGroup>
<div>
<button ngAccordionTrigger panelId="item-1">
Trigger Text
</button>
<div ngAccordionPanel panelId="item-1">
<ng-template ngAccordionContent>
<!-- This content only renders when the panel first opens -->
<img src="large-image.jpg" alt="Description">
<app-expensive-component />
</ng-template>
</div>
</div>
</div>
```
By default, content remains in the DOM after the panel collapses. Set `[preserveContent]="false"` to remove the content from the DOM when the panel closes.
## Showcase
TBD
## APIs
### AccordionGroup
The container directive that manages keyboard navigation and expansion behavior for a group of accordion items.
#### Inputs
| Property | Type | Default | Description |
| ----------------- | --------- | ------- | ------------------------------------------------------------------------- |
| `disabled` | `boolean` | `false` | Disables all triggers in the group |
| `multiExpandable` | `boolean` | `true` | Whether multiple panels can be expanded simultaneously |
| `softDisabled` | `boolean` | `true` | When `true`, disabled items are focusable. When `false`, they are skipped |
| `wrap` | `boolean` | `false` | Whether keyboard navigation wraps from last to first item and vice versa |
#### Methods
| Method | Parameters | Description |
| ------------- | ---------- | ---------------------------------------------------------------- |
| `expandAll` | none | Expands all panels (only works when `multiExpandable` is `true`) |
| `collapseAll` | none | Collapses all panels |
### AccordionTrigger
The directive applied to the button element that toggles panel visibility.
#### Inputs
| Property | Type | Default | Description |
| ---------- | --------- | ------- | -------------------------------------------------------------- |
| `id` | `string` | auto | Unique identifier for the trigger |
| `panelId` | `string` | — | **Required.** Must match the `panelId` of the associated panel |
| `disabled` | `boolean` | `false` | Disables this trigger |
| `expanded` | `boolean` | `false` | Whether the panel is expanded (supports two-way binding) |
#### Signals
| Property | Type | Description |
| -------- | ----------------- | --------------------------------------- |
| `active` | `Signal<boolean>` | Whether the trigger currently has focus |
#### Methods
| Method | Parameters | Description |
| ---------- | ---------- | --------------------------------- |
| `expand` | none | Expands the associated panel |
| `collapse` | none | Collapses the associated panel |
| `toggle` | none | Toggles the panel expansion state |
### AccordionPanel
The directive applied to the element containing the collapsible content.
#### Inputs
| Property | Type | Default | Description |
| ----------------- | --------- | ------- | ---------------------------------------------------------------- |
| `id` | `string` | auto | Unique identifier for the panel |
| `panelId` | `string` | — | **Required.** Must match the `panelId` of the associated trigger |
| `preserveContent` | `boolean` | `true` | Whether to keep content in DOM after panel collapses |
#### Signals
| Property | Type | Description |
| --------- | ----------------- | --------------------------------------- |
| `visible` | `Signal<boolean>` | Whether the panel is currently expanded |
#### Methods
| Method | Parameters | Description |
| ---------- | ---------- | --------------------------- |
| `expand` | none | Expands this panel |
| `collapse` | none | Collapses this panel |
| `toggle` | none | Toggles the expansion state |
### AccordionContent
The structural directive applied to an `ng-template` inside an accordion panel to enable lazy rendering.
This directive has no inputs, outputs, or methods. Apply it to an `ng-template` element:
```angular-html
<div ngAccordionPanel panelId="item-1">
<ng-template ngAccordionContent>
<!-- Content here is lazily rendered -->
</ng-template>
</div>
```
## Styling
Angular automatically applies attributes to accordion elements that you can use in your CSS selectors.
The accordion group receives the `ng-accordion-group` attribute:
```css
[ng-accordion-group] {
display: flex;
flex-direction: column;
gap: 0.5rem;
}
```
Triggers receive the `ng-accordion-trigger` attribute and a `data-active` attribute when focused:
```css
[ng-accordion-trigger] {
cursor: pointer;
background: transparent;
border: none;
padding: 1rem;
width: 100%;
text-align: left;
font-weight: 500;
}
[ng-accordion-trigger][data-active] {
background: var(--focus-background);
outline: 2px solid var(--focus-color);
}
[ng-accordion-trigger][aria-expanded="true"] {
font-weight: 600;
}
```
Panels receive the `ng-accordion-panel` attribute and an `inert` attribute when collapsed:
```css
[ng-accordion-panel]:not([inert]) {
padding: 1rem;
border-top: 1px solid var(--border-color);
}
```
TIP: Use the `:not([inert])` selector to style expanded panels, as Angular automatically adds the `inert` attribute to collapsed panels to hide them from assistive technologies.

View file

@ -1,22 +1,219 @@
<docs-decorative-header title="Grid">
<!-- TODO: Add a short description about Grid. -->
</docs-decorative-header>
## Overview
A grid enables users to navigate two-dimensional data or interactive elements using directional arrow keys, Home, End, and Page Up/Down. Grids work for data tables, calendars, spreadsheets, and layout patterns that group related interactive elements.
<docs-code-multifile preview hideCode path="adev/src/content/examples/aria/grid/src/grid-table/app/app.ts">
<docs-code header="app.ts" path="adev/src/content/examples/aria/grid/src/grid-table/app/app.ts"/>
<docs-code header="app.html" path="adev/src/content/examples/aria/grid/src/grid-table/app/app.html"/>
<docs-code header="app.css" path="adev/src/content/examples/aria/grid/src/grid-table/app/app.css"/>
</docs-code-multifile>
<docs-pill-row>
<docs-pill href="https://www.w3.org/WAI/ARIA/apg/patterns/grid/" title="ARIA pattern"/>
<!-- TODO: Add a link to the Grid API reference. -->
<docs-pill href="api/aria/grid" title="API Reference"/>
</docs-pill-row>
<!-- TODO: Add a top level component preview with code example hidden.
## Usage
<docs-code-multifile preview themed hideCode path="adev/src/content/examples/aria/src/grid/app/app.component.ts">
<docs-code header="app/app.component.html" path="adev/src/content/examples/aria/src/grid/app/app.component.html"/>
<docs-code header="app/app.component.ts" path="adev/src/content/examples/aria/src/grid/app/app.component.ts"/>
<docs-code header="app/app.component.css" path="adev/src/content/examples/aria/src/grid/app/app.component.css"/>
Grids work well for data or interactive elements organized in rows and columns where users need keyboard navigation in multiple directions.
**Use grids when:**
- Building interactive data tables with editable or selectable cells
- Creating calendars or date pickers
- Implementing spreadsheet-like interfaces
- Grouping interactive elements (buttons, checkboxes) to reduce tab stops on a page
- Building interfaces requiring two-dimensional keyboard navigation
**Avoid grids when:**
- Displaying simple read-only tables (use semantic HTML `<table>` instead)
- Showing single-column lists (use [Listbox](guide/aria/listbox) instead)
- Displaying hierarchical data (use [Tree](guide/aria/tree) instead)
- Building forms without tabular layout (use standard form controls)
## Features
- **Two-dimensional navigation** - Arrow keys move between cells in all directions
- **Focus modes** - Choose between roving tabindex or activedescendant focus strategies
- **Selection support** - Optional cell selection with single or multi-select modes
- **Wrapping behavior** - Configure how navigation wraps at grid edges (continuous, loop, or nowrap)
- **Range selection** - Select multiple cells with modifier keys or dragging
- **Disabled states** - Disable the entire grid or individual cells
- **RTL support** - Automatic right-to-left language navigation
## Examples
### Data table grid
Use a grid for interactive tables where users need to navigate between cells using arrow keys. This example shows a basic data table with keyboard navigation.
<docs-code-multifile preview hideCode path="adev/src/content/examples/aria/grid/src/grid-table/app/app.ts">
<docs-code header="app.ts" path="adev/src/content/examples/aria/grid/src/grid-table/app/app.ts"/>
<docs-code header="app.html" path="adev/src/content/examples/aria/grid/src/grid-table/app/app.html"/>
</docs-code-multifile>
-->
Apply the `ngGrid` directive to the table element, `ngGridRow` to each row, and `ngGridCell` to each cell.
### Example with TailwindCSS
### Calendar grid
<!-- TODO: Add more code examples with different styles. -->
Calendars are a common use case for grids. This example shows a month view where users navigate dates using arrow keys.
<docs-code-multifile preview hideCode path="adev/src/content/examples/aria/grid/src/grid-calendar/app/app.ts">
<docs-code header="app.ts" path="adev/src/content/examples/aria/grid/src/grid-calendar/app/app.ts"/>
<docs-code header="app.html" path="adev/src/content/examples/aria/grid/src/grid-calendar/app/app.html"/>
</docs-code-multifile>
Users can activate a date by pressing Enter or Space when focused on a cell.
### Layout grid
Use a layout grid to group interactive elements and reduce tab stops. This example shows a grid of pill buttons.
<docs-code-multifile preview hideCode path="adev/src/content/examples/aria/grid/src/grid-pill-list/app/app.ts">
<docs-code header="app.ts" path="adev/src/content/examples/aria/grid/src/grid-pill-list/app/app.ts"/>
<docs-code header="app.html" path="adev/src/content/examples/aria/grid/src/grid-pill-list/app/app.html"/>
</docs-code-multifile>
Instead of tabbing through each button, users navigate with arrow keys and only one button receives tab focus.
### Selection and focus modes
Enable selection with `[enableSelection]="true"` and configure how focus and selection interact.
```angular-html
<table ngGrid
[enableSelection]="true"
[selectionMode]="'explicit'"
[multi]="true"
[focusMode]="'roving'">
<tr ngGridRow>
<td ngGridCell>Cell 1</td>
<td ngGridCell>Cell 2</td>
</tr>
</table>
```
**Selection modes:**
- `follow`: Focused cell is automatically selected
- `explicit`: Users select cells with Space or click
**Focus modes:**
- `roving`: Focus moves to cells using `tabindex` (better for simple grids)
- `activedescendant`: Focus stays on grid container, `aria-activedescendant` indicates active cell (better for virtual scrolling)
## Showcase
TBD
## APIs
### Grid
The container directive that provides keyboard navigation and focus management for rows and cells.
#### Inputs
| Property | Type | Default | Description |
| ---------------------- | ------------------------------------ | ---------- | ------------------------------------------------------------- |
| `enableSelection` | `boolean` | `false` | Whether selection is enabled for the grid |
| `disabled` | `boolean` | `false` | Disables the entire grid |
| `softDisabled` | `boolean` | `true` | When `true`, disabled cells are focusable but not interactive |
| `focusMode` | `'roving' \| 'activedescendant'` | `'roving'` | Focus strategy used by the grid |
| `rowWrap` | `'continuous' \| 'loop' \| 'nowrap'` | `'loop'` | Navigation wrapping behavior along rows |
| `colWrap` | `'continuous' \| 'loop' \| 'nowrap'` | `'loop'` | Navigation wrapping behavior along columns |
| `multi` | `boolean` | `false` | Whether multiple cells can be selected |
| `selectionMode` | `'follow' \| 'explicit'` | `'follow'` | Whether selection follows focus or requires explicit action |
| `enableRangeSelection` | `boolean` | `false` | Enable range selections with modifier keys or dragging |
### GridRow
Represents a row within a grid and serves as a container for grid cells.
#### Inputs
| Property | Type | Default | Description |
| ---------- | -------- | ------- | ------------------------------------- |
| `rowIndex` | `number` | auto | The index of this row within the grid |
### GridCell
Represents an individual cell within a grid row.
#### Inputs
| Property | Type | Default | Description |
| ------------- | ---------------------------- | -------------- | ------------------------------------------------------- |
| `id` | `string` | auto | Unique identifier for the cell |
| `role` | `string` | `'gridcell'` | Cell role: `gridcell`, `columnheader`, or `rowheader` |
| `disabled` | `boolean` | `false` | Disables this cell |
| `selected` | `boolean` | `false` | Whether the cell is selected (supports two-way binding) |
| `selectable` | `boolean` | `true` | Whether the cell can be selected |
| `rowSpan` | `number` | — | Number of rows the cell spans |
| `colSpan` | `number` | — | Number of columns the cell spans |
| `rowIndex` | `number` | — | Row index of the cell |
| `colIndex` | `number` | — | Column index of the cell |
| `orientation` | `'vertical' \| 'horizontal'` | `'horizontal'` | Orientation for widgets within the cell |
| `wrap` | `boolean` | `true` | Whether widget navigation wraps within the cell |
#### Signals
| Property | Type | Description |
| -------- | ----------------- | ------------------------------------ |
| `active` | `Signal<boolean>` | Whether the cell currently has focus |
## Styling
Angular automatically applies attributes to grid elements that you can use in your CSS selectors.
The grid receives the `ng-grid` attribute:
```css
[ng-grid] {
border-collapse: collapse;
width: 100%;
}
```
Rows receive the `ng-grid-row` attribute:
```css
[ng-grid-row] {
border-bottom: 1px solid #ddd;
}
```
Cells receive the `ng-grid-cell` attribute and a `data-active` attribute when focused:
```css
[ng-grid-cell] {
padding: 0.75rem;
border: 1px solid #ddd;
}
[ng-grid-cell][data-active] {
outline: 2px solid var(--focus-color);
outline-offset: -2px;
}
[ng-grid-cell][aria-selected="true"] {
background: var(--selected-background);
}
```
Style column and row headers based on their role:
```css
[ng-grid-cell][role="columnheader"],
[ng-grid-cell][role="rowheader"] {
font-weight: 600;
background: var(--header-background);
}
```
TIP: Use the `[data-active]` selector to style focused cells, as Angular automatically adds this attribute when a cell has focus.

View file

@ -1,22 +1,249 @@
<docs-decorative-header title="Menu">
<!-- TODO: Add a short description about Menu. -->
</docs-decorative-header>
<docs-pill-row>
<docs-pill href="https://www.w3.org/WAI/ARIA/apg/patterns/menu/" title="ARIA pattern"/>
<!-- TODO: Add a link to the Menu API reference. -->
</docs-pill-row>
## Overview
<!-- TODO: Add a top level component preview with code example hidden.
A menu offers a list of actions or options to users, typically appearing in response to a button click or right-click. Menus support keyboard navigation with arrow keys, submenus, checkboxes, radio buttons, and disabled items.
<docs-code-multifile preview themed hideCode path="adev/src/content/examples/aria/src/menu/app/app.component.ts">
<docs-code header="app/app.component.html" path="adev/src/content/examples/aria/src/menu/app/app.component.html"/>
<docs-code header="app/app.component.ts" path="adev/src/content/examples/aria/src/menu/app/app.component.ts"/>
<docs-code header="app/app.component.css" path="adev/src/content/examples/aria/src/menu/app/app.component.css"/>
<docs-code-multifile preview hideCode path="adev/src/content/examples/aria/menu/src/menu-trigger/app/app.ts">
<docs-code header="app.ts" path="adev/src/content/examples/aria/menu/src/menu-trigger/app/app.ts"/>
<docs-code header="app.html" path="adev/src/content/examples/aria/menu/src/menu-trigger/app/app.html"/>
<docs-code header="app.css" path="adev/src/content/examples/aria/menu/src/menu-trigger/app/app.css"/>
</docs-code-multifile>
-->
<docs-pill-row>
<docs-pill href="https://www.w3.org/WAI/ARIA/apg/patterns/menubar/" title="ARIA pattern"/>
<docs-pill href="api/aria/menu" title="API Reference"/>
</docs-pill-row>
### Example with TailwindCSS
## Usage
<!-- TODO: Add more code examples with different styles. -->
Menus work well for presenting lists of actions or commands that users can choose from.
**Use menus when:**
- Building application command menus (File, Edit, View)
- Creating context menus (right-click actions)
- Showing dropdown action lists
- Implementing toolbar dropdowns
- Organizing settings or options
**Avoid menus when:**
- Building site navigation (use navigation landmarks instead)
- Creating form selects (use the [Select](guide/aria/select) component)
- Switching between content panels (use [Tabs](guide/aria/tabs))
- Showing collapsible content (use [Accordion](guide/aria/accordion))
## Features
- **Keyboard navigation** - Arrow keys, Home/End, and character search for efficient navigation
- **Submenus** - Nested menu support with automatic positioning
- **Menu types** - Standalone menus, triggered menus, and menubars
- **Checkboxes and radios** - Toggle and selection menu items
- **Disabled items** - Soft or hard disabled states with focus management
- **Auto-close behavior** - Configurable close on selection
- **RTL support** - Right-to-left language navigation
## Examples
### Menu with trigger
Create a dropdown menu by pairing a trigger button with a menu. The trigger opens and closes the menu.
<docs-code-multifile preview hideCode path="adev/src/content/examples/aria/menu/src/menu-trigger/app/app.ts">
<docs-code header="app.ts" path="adev/src/content/examples/aria/menu/src/menu-trigger/app/app.ts"/>
<docs-code header="app.html" path="adev/src/content/examples/aria/menu/src/menu-trigger/app/app.html"/>
</docs-code-multifile>
The menu automatically closes when a user selects an item or presses Escape.
### Menubar
A menubar provides persistent access to multiple menus, commonly used in application toolbars.
<docs-code-multifile preview hideCode path="adev/src/content/examples/aria/menu/src/menu-bar/app/app.ts">
<docs-code header="app.ts" path="adev/src/content/examples/aria/menu/src/menu-bar/app/app.ts"/>
<docs-code header="app.html" path="adev/src/content/examples/aria/menu/src/menu-bar/app/app.html"/>
</docs-code-multifile>
Users navigate between top-level menus with arrow keys and open menus with Enter or by hovering.
### Context menu
Context menus appear at the cursor position when users right-click an element.
<docs-code-multifile preview hideCode path="adev/src/content/examples/aria/menu/src/menu-context/app/app.ts">
<docs-code header="app.ts" path="adev/src/content/examples/aria/menu/src/menu-context/app/app.ts"/>
<docs-code header="app.html" path="adev/src/content/examples/aria/menu/src/menu-context/app/app.html"/>
</docs-code-multifile>
Position the menu using the `contextmenu` event coordinates.
### Standalone menu
A standalone menu doesn't require a trigger and remains visible in the interface.
<docs-code-multifile preview hideCode path="adev/src/content/examples/aria/menu/src/menu-standalone/app/app.ts">
<docs-code header="app.ts" path="adev/src/content/examples/aria/menu/src/menu-standalone/app/app.ts"/>
<docs-code header="app.html" path="adev/src/content/examples/aria/menu/src/menu-standalone/app/app.html"/>
</docs-code-multifile>
Standalone menus work well for always-visible action lists or navigation.
### Disabled menu items
Disable specific menu items using the `disabled` input. Control focus behavior with `softDisabled`.
<docs-code-multifile preview hideCode path="adev/src/content/examples/aria/menu/src/menu-trigger-disabled/app/app.ts">
<docs-code header="app.ts" path="adev/src/content/examples/aria/menu/src/menu-trigger-disabled/app/app.ts"/>
<docs-code header="app.html" path="adev/src/content/examples/aria/menu/src/menu-trigger-disabled/app/app.html"/>
</docs-code-multifile>
When `[softDisabled]="true"`, disabled items can receive focus but cannot be activated. When `[softDisabled]="false"`, disabled items are skipped during keyboard navigation.
## Showcase
TBD
## APIs
### Menu
The container directive for menu items.
#### Inputs
| Property | Type | Default | Description |
| -------------- | --------- | ------- | ------------------------------------------------------------- |
| `disabled` | `boolean` | `false` | Disables all items in the menu |
| `wrap` | `boolean` | `true` | Whether keyboard navigation wraps at edges |
| `softDisabled` | `boolean` | `true` | When `true`, disabled items are focusable but not interactive |
#### Methods
| Method | Parameters | Description |
| ---------------- | ---------- | ---------------------------------- |
| `close` | none | Closes the menu |
| `focusFirstItem` | none | Moves focus to the first menu item |
### MenuBar
A horizontal container for multiple menus.
#### Inputs
| Property | Type | Default | Description |
| -------------- | --------- | ------- | ------------------------------------------------------------- |
| `disabled` | `boolean` | `false` | Disables the entire menubar |
| `wrap` | `boolean` | `true` | Whether keyboard navigation wraps at edges |
| `softDisabled` | `boolean` | `true` | When `true`, disabled items are focusable but not interactive |
### MenuItem
An individual item within a menu.
#### Inputs
| Property | Type | Default | Description |
| ------------ | --------- | ------- | ---------------------------------------------------- |
| `value` | `any` | — | **Required.** Value for this item |
| `disabled` | `boolean` | `false` | Disables this menu item |
| `submenu` | `Menu` | — | Reference to a submenu |
| `searchTerm` | `string` | `''` | Search term for typeahead (supports two-way binding) |
#### Signals
| Property | Type | Description |
| ---------- | ----------------- | ------------------------------------------ |
| `active` | `Signal<boolean>` | Whether the item currently has focus |
| `expanded` | `Signal<boolean>` | Whether the submenu is expanded |
| `hasPopup` | `Signal<boolean>` | Whether the item has an associated submenu |
NOTE: MenuItem does not expose public methods. Use the `submenu` input to associate submenus with menu items.
### MenuTrigger
A button or element that opens a menu.
#### Inputs
| Property | Type | Default | Description |
| -------------- | --------- | ------- | ------------------------------------------ |
| `menu` | `Menu` | — | **Required.** The menu to trigger |
| `disabled` | `boolean` | `false` | Disables the trigger |
| `softDisabled` | `boolean` | `true` | When `true`, disabled trigger is focusable |
#### Signals
| Property | Type | Description |
| ---------- | ----------------- | ------------------------------------------ |
| `expanded` | `Signal<boolean>` | Whether the menu is currently open |
| `hasPopup` | `Signal<boolean>` | Whether the trigger has an associated menu |
#### Methods
| Method | Parameters | Description |
| -------- | ---------- | ---------------------------- |
| `open` | none | Opens the menu |
| `close` | none | Closes the menu |
| `toggle` | none | Toggles the menu open/closed |
## Styling
Angular automatically applies attributes to menu elements that you can use in your CSS selectors.
The menu container receives the `ng-menu` attribute:
```css
[ng-menu] {
background: white;
border: 1px solid #ccc;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
padding: 0.5rem 0;
min-width: 200px;
}
```
Menu items receive the `ng-menu-item` attribute and a `data-active` attribute when focused:
```css
[ng-menu-item] {
padding: 0.5rem 1rem;
cursor: pointer;
display: flex;
align-items: center;
gap: 0.5rem;
}
[ng-menu-item][data-active] {
background: var(--hover-background);
}
[ng-menu-item][aria-disabled="true"] {
opacity: 0.5;
cursor: not-allowed;
}
```
Style menu items with submenus using the `aria-haspopup` attribute:
```css
[ng-menu-item][aria-haspopup="menu"]::after {
content: '▶';
margin-left: auto;
}
```
Menubars receive the `ng-menu-bar` attribute:
```css
[ng-menu-bar] {
display: flex;
gap: 0.5rem;
background: var(--toolbar-background);
padding: 0.5rem;
}
```
TIP: Use `[data-active]` to style focused menu items and `[aria-expanded="true"]` to style open menu triggers.

View file

@ -1,22 +1,252 @@
<docs-decorative-header title="Tabs">
<!-- TODO: Add a short description about Tabs. -->
</docs-decorative-header>
## Overview
Tabs display layered content sections where only one panel is visible at a time. Users switch between panels by clicking tab buttons or using arrow keys to navigate the tab list.
<docs-code-multifile preview hideCode path="adev/src/content/examples/aria/tabs/src/selection-follows-focus/app/app.ts">
<docs-code header="app.ts" path="adev/src/content/examples/aria/tabs/src/selection-follows-focus/app/app.ts"/>
<docs-code header="app.html" path="adev/src/content/examples/aria/tabs/src/selection-follows-focus/app/app.html"/>
<docs-code header="app.css" path="adev/src/content/examples/aria/tabs/src/selection-follows-focus/app/app.css"/>
</docs-code-multifile>
<docs-pill-row>
<docs-pill href="https://www.w3.org/WAI/ARIA/apg/patterns/tabs/" title="ARIA pattern"/>
<!-- TODO: Add a link to the Tabs API reference. -->
<docs-pill href="api/aria/tabs" title="API Reference"/>
</docs-pill-row>
<!-- TODO: Add a top level component preview with code example hidden.
## Usage
<docs-code-multifile preview themed hideCode path="adev/src/content/examples/aria/src/tabs/app/app.component.ts">
<docs-code header="app/app.component.html" path="adev/src/content/examples/aria/src/tabs/app/app.component.html"/>
<docs-code header="app/app.component.ts" path="adev/src/content/examples/aria/src/tabs/app/app.component.ts"/>
<docs-code header="app/app.component.css" path="adev/src/content/examples/aria/src/tabs/app/app.component.css"/>
Tabs work well for organizing related content into distinct sections where users switch between different views or categories.
**Use tabs when:**
- Organizing related content into distinct sections
- Creating settings panels with multiple categories
- Building documentation with multiple topics
- Implementing dashboards with different views
- Showing content where users need to switch contexts
**Avoid tabs when:**
- Building sequential forms or wizards (use a stepper pattern)
- Navigating between pages (use router navigation)
- Showing single content sections (no need for tabs)
- Having more than 7-8 tabs (consider a different layout)
## Features
- **Selection modes** - Tabs activate automatically on focus or require manual activation
- **Keyboard navigation** - Arrow keys, Home, and End for efficient tab navigation
- **Orientation** - Horizontal or vertical tab list layouts
- **Lazy content** - Tab panels render only when first activated
- **Disabled tabs** - Disable individual tabs with focus management
- **Focus modes** - Roving tabindex or activedescendant focus strategies
- **RTL support** - Right-to-left language navigation
## Examples
### Selection follows focus
When selection follows focus, tabs activate immediately as you navigate with arrow keys. This provides instant feedback and works well for lightweight content.
<docs-code-multifile preview hideCode path="adev/src/content/examples/aria/tabs/src/selection-follows-focus/app/app.ts">
<docs-code header="app.ts" path="adev/src/content/examples/aria/tabs/src/selection-follows-focus/app/app.ts"/>
<docs-code header="app.html" path="adev/src/content/examples/aria/tabs/src/selection-follows-focus/app/app.html"/>
</docs-code-multifile>
-->
Set `[selectionMode]="'follow'"` on the tab list to enable this behavior.
### Example with TailwindCSS
### Manual activation
<!-- TODO: Add more code examples with different styles. -->
With manual activation, arrow keys move focus between tabs without changing the selected tab. Users press Space or Enter to activate the focused tab.
<docs-code-multifile preview hideCode path="adev/src/content/examples/aria/tabs/src/explicit-selection/app/app.ts">
<docs-code header="app.ts" path="adev/src/content/examples/aria/tabs/src/explicit-selection/app/app.ts"/>
<docs-code header="app.html" path="adev/src/content/examples/aria/tabs/src/explicit-selection/app/app.html"/>
</docs-code-multifile>
Use `[selectionMode]="'explicit'"` for heavy content panels to avoid unnecessary rendering.
### Vertical tabs
Arrange tabs vertically for interfaces like settings panels or navigation sidebars.
<docs-code-multifile preview hideCode path="adev/src/content/examples/aria/tabs/src/vertical-orientation/app/app.ts">
<docs-code header="app.ts" path="adev/src/content/examples/aria/tabs/src/vertical-orientation/app/app.ts"/>
<docs-code header="app.html" path="adev/src/content/examples/aria/tabs/src/vertical-orientation/app/app.html"/>
</docs-code-multifile>
Set `[orientation]="'vertical'"` on the tab list. Navigation changes to Up/Down arrow keys.
### Lazy content rendering
Use the `ngTabContent` directive on an `ng-template` to defer rendering tab panels until they're first shown.
```angular-html
<div ngTabs>
<ul ngTabList [(selectedTab)]="selectedTab">
<li ngTab value="tab1">Tab 1</li>
<li ngTab value="tab2">Tab 2</li>
</ul>
<div ngTabPanel value="tab1">
<ng-template ngTabContent>
<!-- This content only renders when Tab 1 is first shown -->
<app-heavy-component />
</ng-template>
</div>
<div ngTabPanel value="tab2">
<ng-template ngTabContent>
<!-- This content only renders when Tab 2 is first shown -->
<app-another-component />
</ng-template>
</div>
</div>
```
By default, content remains in the DOM after the panel is hidden. Set `[preserveContent]="false"` to remove content when the panel is deactivated.
### Disabled tabs
Disable specific tabs to prevent user interaction. Control whether disabled tabs can receive keyboard focus.
<docs-code-multifile preview hideCode path="adev/src/content/examples/aria/tabs/src/disabled-focusable/app/app.ts">
<docs-code header="app.ts" path="adev/src/content/examples/aria/tabs/src/disabled-focusable/app/app.ts"/>
<docs-code header="app.html" path="adev/src/content/examples/aria/tabs/src/disabled-focusable/app/app.html"/>
</docs-code-multifile>
When `[softDisabled]="true"` on the tab list, disabled tabs can receive focus but cannot be activated. When `[softDisabled]="false"`, disabled tabs are skipped during keyboard navigation.
## Showcase
TBD
## APIs
### Tabs
The container directive that coordinates tab lists and panels.
This directive has no inputs or outputs. It serves as the root container for `ngTabList`, `ngTab`, and `ngTabPanel` directives.
### TabList
The container for tab buttons that manages selection and keyboard navigation.
#### Inputs
| Property | Type | Default | Description |
| --------------- | ---------------------------- | -------------- | ------------------------------------------------------------------ |
| `orientation` | `'horizontal' \| 'vertical'` | `'horizontal'` | Tab list layout direction |
| `wrap` | `boolean` | `false` | Whether keyboard navigation wraps from last to first tab |
| `softDisabled` | `boolean` | `true` | When `true`, disabled tabs are focusable but not activatable |
| `selectionMode` | `'follow' \| 'explicit'` | `'follow'` | Whether tabs activate on focus or require explicit activation |
| `selectedTab` | `any` | — | The value of the currently selected tab (supports two-way binding) |
### Tab
An individual tab button.
#### Inputs
| Property | Type | Default | Description |
| ---------- | --------- | ------- | --------------------------------------- |
| `value` | `any` | — | **Required.** Unique value for this tab |
| `disabled` | `boolean` | `false` | Disables this tab |
#### Signals
| Property | Type | Description |
| ---------- | ----------------- | ------------------------------------- |
| `selected` | `Signal<boolean>` | Whether the tab is currently selected |
| `active` | `Signal<boolean>` | Whether the tab currently has focus |
### TabPanel
The content panel associated with a tab.
#### Inputs
| Property | Type | Default | Description |
| ----------------- | --------- | ------- | ---------------------------------------------------------- |
| `value` | `any` | — | **Required.** Must match the `value` of the associated tab |
| `preserveContent` | `boolean` | `true` | Whether to keep panel content in DOM after deactivation |
#### Signals
| Property | Type | Description |
| --------- | ----------------- | -------------------------------------- |
| `visible` | `Signal<boolean>` | Whether the panel is currently visible |
### TabContent
A structural directive for lazy rendering tab panel content.
This directive has no inputs, outputs, or methods. Apply it to an `ng-template` element inside a tab panel:
```angular-html
<div ngTabPanel value="tab1">
<ng-template ngTabContent>
<!-- Content here is lazily rendered -->
</ng-template>
</div>
```
## Styling
Angular automatically applies attributes to tab elements that you can use in your CSS selectors.
The tab list receives the `ng-tab-list` attribute:
```css
[ng-tab-list] {
display: flex;
gap: 0.5rem;
border-bottom: 2px solid #ddd;
}
[ng-tab-list][aria-orientation="vertical"] {
flex-direction: column;
border-bottom: none;
border-right: 2px solid #ddd;
}
```
Tabs receive the `ng-tab` attribute with `data-active` when focused and `aria-selected` when selected:
```css
[ng-tab] {
padding: 0.75rem 1.5rem;
cursor: pointer;
border: 1px solid transparent;
background: transparent;
}
[ng-tab][data-active] {
outline: 2px solid var(--focus-color);
}
[ng-tab][aria-selected="true"] {
border-color: var(--primary-color);
border-bottom-color: white;
background: white;
font-weight: 600;
}
```
Tab panels receive the `ng-tab-panel` attribute:
```css
[ng-tab-panel] {
padding: 1.5rem;
}
[ng-tab-panel][hidden] {
display: none;
}
```
TIP: Use `[data-active]` to style the focused tab and `[aria-selected="true"]` to style the selected tab. These are often the same tab but can differ in manual activation mode.

View file

@ -1,22 +1,241 @@
<docs-decorative-header title="Tree">
<!-- TODO: Add a short description about Tree. -->
</docs-decorative-header>
<docs-pill-row>
<docs-pill href="https://www.w3.org/WAI/ARIA/apg/patterns/tree/" title="ARIA pattern"/>
<!-- TODO: Add a link to the Tree API reference. -->
</docs-pill-row>
## Overview
<!-- TODO: Add a top level component preview with code example hidden.
A tree displays hierarchical data where items can expand to reveal children or collapse to hide them. Users navigate with arrow keys, expand and collapse nodes, and optionally select items for navigation or data selection scenarios.
<docs-code-multifile preview themed hideCode path="adev/src/content/examples/aria/src/tree/app/app.component.ts">
<docs-code header="app/app.component.html" path="adev/src/content/examples/aria/src/tree/app/app.component.html"/>
<docs-code header="app/app.component.ts" path="adev/src/content/examples/aria/src/tree/app/app.component.ts"/>
<docs-code header="app/app.component.css" path="adev/src/content/examples/aria/src/tree/app/app.component.css"/>
<docs-code-multifile preview hideCode path="adev/src/content/examples/aria/tree/src/tree-nav/app/app.ts">
<docs-code header="app.ts" path="adev/src/content/examples/aria/tree/src/tree-nav/app/app.ts"/>
<docs-code header="app.html" path="adev/src/content/examples/aria/tree/src/tree-nav/app/app.html"/>
<docs-code header="app.css" path="adev/src/content/examples/aria/tree/src/tree-nav/app/app.css"/>
</docs-code-multifile>
-->
<docs-pill-row>
<docs-pill href="https://www.w3.org/WAI/ARIA/apg/patterns/treeview/" title="ARIA pattern"/>
<docs-pill href="api/aria/tree" title="API Reference"/>
</docs-pill-row>
### Example with TailwindCSS
## Usage
<!-- TODO: Add more code examples with different styles. -->
Trees work well for displaying hierarchical data where users need to navigate through nested structures.
**Use trees when:**
- Building file system navigation
- Showing folder and document hierarchies
- Creating nested menu structures
- Displaying organization charts
- Browsing hierarchical data
- Implementing site navigation with nested sections
**Avoid trees when:**
- Displaying flat lists (use [Listbox](guide/aria/listbox) instead)
- Showing data tables (use [Grid](guide/aria/grid) instead)
- Creating simple dropdowns (use [Select](guide/aria/select) instead)
- Building breadcrumb navigation (use breadcrumb patterns)
## Features
- **Hierarchical navigation** - Nested tree structure with expand and collapse functionality
- **Selection modes** - Single or multi-selection with explicit or follow-focus behavior
- **Selection follows focus** - Optional automatic selection when focus changes
- **Keyboard navigation** - Arrow keys, Home, End, and type-ahead search
- **Expand/collapse** - Right/Left arrows or Enter to toggle parent nodes
- **Disabled items** - Disable specific nodes with focus management
- **Focus modes** - Roving tabindex or activedescendant focus strategies
- **RTL support** - Right-to-left language navigation
## Examples
### Navigation tree
Use a tree for navigation where clicking items triggers actions rather than selecting them.
<docs-code-multifile preview hideCode path="adev/src/content/examples/aria/tree/src/tree-nav/app/app.ts">
<docs-code header="app.ts" path="adev/src/content/examples/aria/tree/src/tree-nav/app/app.ts"/>
<docs-code header="app.html" path="adev/src/content/examples/aria/tree/src/tree-nav/app/app.html"/>
</docs-code-multifile>
Set `[nav]="true"` to enable navigation mode. This uses `aria-current` to indicate the current page instead of selection.
### Single selection
Enable single selection for scenarios where users choose one item from the tree.
<docs-code-multifile preview hideCode path="adev/src/content/examples/aria/tree/src/tree-single-select/app/app.ts">
<docs-code header="app.ts" path="adev/src/content/examples/aria/tree/src/tree-single-select/app/app.ts"/>
<docs-code header="app.html" path="adev/src/content/examples/aria/tree/src/tree-single-select/app/app.html"/>
</docs-code-multifile>
Leave `[multi]="false"` (the default) for single selection. Users press Space to select the focused item.
### Multi-selection
Allow users to select multiple items from the tree.
<docs-code-multifile preview hideCode path="adev/src/content/examples/aria/tree/src/tree-multi-select/app/app.ts">
<docs-code header="app.ts" path="adev/src/content/examples/aria/tree/src/tree-multi-select/app/app.ts"/>
<docs-code header="app.html" path="adev/src/content/examples/aria/tree/src/tree-multi-select/app/app.html"/>
</docs-code-multifile>
Set `[multi]="true"` on the tree. Users select items individually with Space or select ranges with Shift+Arrow keys.
### Selection follows focus
When selection follows focus, the focused item is automatically selected. This simplifies interaction for navigation scenarios.
<docs-code-multifile preview hideCode path="adev/src/content/examples/aria/tree/src/tree-single-select-follow-focus/app/app.ts">
<docs-code header="app.ts" path="adev/src/content/examples/aria/tree/src/tree-single-select-follow-focus/app/app.ts"/>
<docs-code header="app.html" path="adev/src/content/examples/aria/tree/src/tree-single-select-follow-focus/app/app.html"/>
</docs-code-multifile>
Set `[selectionMode]="'follow'"` on the tree. Selection automatically updates as users navigate with arrow keys.
### Disabled tree items
Disable specific tree nodes to prevent interaction. Control whether disabled items can receive focus.
<docs-code-multifile preview hideCode path="adev/src/content/examples/aria/tree/src/tree-disabled-focusable/app/app.ts">
<docs-code header="app.ts" path="adev/src/content/examples/aria/tree/src/tree-disabled-focusable/app/app.ts"/>
<docs-code header="app.html" path="adev/src/content/examples/aria/tree/src/tree-disabled-focusable/app/app.html"/>
</docs-code-multifile>
When `[softDisabled]="true"` on the tree, disabled items can receive focus but cannot be activated or selected. When `[softDisabled]="false"`, disabled items are skipped during keyboard navigation.
## Showcase
TBD
## APIs
### Tree
The container directive that manages hierarchical navigation and selection.
#### Inputs
| Property | Type | Default | Description |
| --------------- | -------------------------------- | ------------ | ------------------------------------------------------------- |
| `disabled` | `boolean` | `false` | Disables the entire tree |
| `softDisabled` | `boolean` | `true` | When `true`, disabled items are focusable but not interactive |
| `multi` | `boolean` | `false` | Whether multiple items can be selected |
| `selectionMode` | `'explicit' \| 'follow'` | `'explicit'` | Whether selection requires explicit action or follows focus |
| `nav` | `boolean` | `false` | Whether the tree is in navigation mode (uses `aria-current`) |
| `wrap` | `boolean` | `true` | Whether keyboard navigation wraps from last to first item |
| `focusMode` | `'roving' \| 'activedescendant'` | `'roving'` | Focus strategy used by the tree |
| `values` | `any[]` | `[]` | Selected item values (supports two-way binding) |
#### Methods
| Method | Parameters | Description |
| ---------------- | ---------- | --------------------------------------------- |
| `expandAll` | none | Expands all tree nodes |
| `collapseAll` | none | Collapses all tree nodes |
| `selectAll` | none | Selects all items (only in multi-select mode) |
| `clearSelection` | none | Clears all selection |
### TreeItem
An individual node in the tree that can contain child nodes.
#### Inputs
| Property | Type | Default | Description |
| ---------- | --------- | ------- | ------------------------------------------------------- |
| `value` | `any` | — | **Required.** Unique value for this tree item |
| `disabled` | `boolean` | `false` | Disables this item |
| `expanded` | `boolean` | `false` | Whether the node is expanded (supports two-way binding) |
#### Signals
| Property | Type | Description |
| ------------- | ----------------- | ------------------------------------ |
| `selected` | `Signal<boolean>` | Whether the item is selected |
| `active` | `Signal<boolean>` | Whether the item currently has focus |
| `hasChildren` | `Signal<boolean>` | Whether the item has child nodes |
#### Methods
| Method | Parameters | Description |
| ---------- | ---------- | --------------------------- |
| `expand` | none | Expands this node |
| `collapse` | none | Collapses this node |
| `toggle` | none | Toggles the expansion state |
### TreeGroup
A container for child tree items.
This directive has no inputs, outputs, or methods. It serves as a container to organize child `ngTreeItem` elements:
```angular-html
<li ngTreeItem value="parent">
Parent Item
<ul ngTreeGroup>
<li ngTreeItem value="child1">Child 1</li>
<li ngTreeItem value="child2">Child 2</li>
</ul>
</li>
```
## Styling
Angular automatically applies attributes to tree elements that you can use in your CSS selectors.
The tree receives the `ng-tree` attribute:
```css
[ng-tree] {
list-style: none;
padding: 0;
margin: 0;
}
```
Tree items receive the `ng-tree-item` attribute with `data-active` when focused and `aria-selected` when selected:
```css
[ng-tree-item] {
padding: 0.5rem;
cursor: pointer;
display: flex;
align-items: center;
gap: 0.5rem;
}
[ng-tree-item][data-active] {
outline: 2px solid var(--focus-color);
}
[ng-tree-item][aria-selected="true"] {
background: var(--selected-background);
font-weight: 600;
}
```
Tree groups receive the `ng-tree-group` attribute for indenting child items:
```css
[ng-tree-group] {
padding-left: 1.5rem;
list-style: none;
}
```
Style expandable items using the `aria-expanded` attribute:
```css
/* Expand/collapse indicator */
[ng-tree-item][aria-expanded]::before {
content: '▶';
transition: transform 0.2s;
}
[ng-tree-item][aria-expanded="true"]::before {
transform: rotate(90deg);
}
```
TIP: Use `[aria-expanded]` to determine if an item is expandable and style its expand/collapse indicator accordingly.