Vue d'ensemble
Un conteneur qui regroupe des contrôles et des actions liés avec navigation au clavier, couramment utilisé pour la mise en forme de texte, les barres d'outils et les panneaux de commandes.
app.ts
import {Component, signal} 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 {
readonly bold = signal(false);
readonly italic = signal(false);
readonly underlined = signal(false);
readonly alignment = signal<'left' | 'center' | 'right'>('left');
}
app.html
<div ngToolbar aria-label="Text Formatting Tools">
<div class="group">
<button
ngToolbarWidget
value="undo"
type="button"
aria-label="undo"
class="material-symbols-outlined"
translate="no"
>
undo
</button>
<button
ngToolbarWidget
value="redo"
type="button"
aria-label="redo"
class="material-symbols-outlined"
translate="no"
>
redo
</button>
</div>
<div class="separator" role="separator"></div>
<div class="group">
<button
ngToolbarWidget
value="bold"
type="button"
aria-label="bold"
class="material-symbols-outlined"
[attr.aria-pressed]="bold()"
(click)="bold.set(!bold())"
translate="no"
>
format_bold
</button>
<button
ngToolbarWidget
value="italic"
type="button"
aria-label="italic"
class="material-symbols-outlined"
[attr.aria-pressed]="italic()"
(click)="italic.set(!italic())"
translate="no"
>
format_italic
</button>
<button
ngToolbarWidget
value="underlined"
type="button"
aria-label="underlined"
class="material-symbols-outlined"
[attr.aria-pressed]="underlined()"
(click)="underlined.set(!underlined())"
translate="no"
>
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"
class="material-symbols-outlined"
[attr.aria-checked]="alignment() === 'left'"
(click)="alignment.set('left')"
translate="no"
>
format_align_left
</button>
<button
ngToolbarWidget
role="radio"
type="button"
value="align center"
aria-label="align center"
class="material-symbols-outlined"
[attr.aria-checked]="alignment() === 'center'"
(click)="alignment.set('center')"
translate="no"
>
format_align_center
</button>
<button
ngToolbarWidget
role="radio"
type="button"
value="align right"
aria-label="align right"
class="material-symbols-outlined"
[attr.aria-checked]="alignment() === 'right'"
(click)="alignment.set('right')"
translate="no"
>
format_align_right
</button>
</div>
</div>
app.css
@import url('https://fonts.googleapis.com/icon?family=Material+Symbols+Outlined');
: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);
}
app.ts
import {Component, signal} 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 {
readonly bold = signal(false);
readonly italic = signal(false);
readonly underlined = signal(false);
readonly alignment = signal<'left' | 'center' | 'right'>('left');
}
app.html
<div ngToolbar class="material-toolbar" aria-label="Text Formatting Tools">
<div class="group">
<button
ngToolbarWidget
value="undo"
type="button"
aria-label="undo"
class="material-symbols-outlined"
translate="no"
>
undo
</button>
<button
ngToolbarWidget
value="redo"
type="button"
aria-label="redo"
class="material-symbols-outlined"
translate="no"
>
redo
</button>
</div>
<div class="separator" role="separator"></div>
<div class="group">
<button
ngToolbarWidget
value="bold"
type="button"
aria-label="bold"
class="material-symbols-outlined"
[attr.aria-pressed]="bold()"
(click)="bold.set(!bold())"
translate="no"
>
format_bold
</button>
<button
ngToolbarWidget
value="italic"
type="button"
aria-label="italic"
class="material-symbols-outlined"
[attr.aria-pressed]="italic()"
(click)="italic.set(!italic())"
translate="no"
>
format_italic
</button>
<button
ngToolbarWidget
value="underlined"
type="button"
aria-label="underlined"
class="material-symbols-outlined"
[attr.aria-pressed]="underlined()"
(click)="underlined.set(!underlined())"
translate="no"
>
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"
class="material-symbols-outlined"
[attr.aria-checked]="alignment() === 'left'"
(click)="alignment.set('left')"
translate="no"
>
format_align_left
</button>
<button
ngToolbarWidget
role="radio"
type="button"
value="align center"
aria-label="align center"
class="material-symbols-outlined"
[attr.aria-checked]="alignment() === 'center'"
(click)="alignment.set('center')"
translate="no"
>
format_align_center
</button>
<button
ngToolbarWidget
role="radio"
type="button"
value="align right"
aria-label="align right"
class="material-symbols-outlined"
[attr.aria-checked]="alignment() === 'right'"
(click)="alignment.set('right')"
translate="no"
>
format_align_right
</button>
</div>
</div>
app.css
@import url('https://fonts.googleapis.com/icon?family=Material+Symbols+Outlined');
:host {
display: flex;
justify-content: center;
}
[ngToolbar] {
display: flex;
}
[ngToolbarWidget] {
border: none;
outline: none;
cursor: pointer;
width: 3rem;
height: 3rem;
font-size: 1.25rem;
border-radius: 2rem;
color: color-mix(in srgb, var(--vivid-pink) 70%, var(--full-contrast));
background-color: color-mix(in srgb, var(--vivid-pink) 20%, transparent);
transition:
width 0.2s ease-in-out,
background-color 0.15s ease-in-out,
color 0.15s ease-in-out;
}
[ngToolbarWidget]:focus {
outline-offset: 2px;
outline: 2px solid var(--vivid-pink);
}
[ngToolbarWidget]:active,
[ngToolbarWidget][aria-pressed='true'],
[ngToolbarWidget][aria-checked='true'] {
background-color: color-mix(in srgb, var(--vivid-pink) 80%, var(--full-contrast));
color: var(--page-background);
}
[ngToolbarWidget][aria-checked='true'] {
width: 4.5rem;
}
.group {
display: flex;
gap: 0.5rem;
}
.separator {
width: 1px;
margin: 0 1rem;
height: calc(100% - 2rem);
background-color: var(--quinary-contrast);
}
app.ts
import {Component, signal} 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 {
readonly bold = signal(false);
readonly italic = signal(false);
readonly underlined = signal(false);
readonly alignment = signal<'left' | 'center' | 'right'>('left');
}
app.html
<div ngToolbar class="retro-toolbar" aria-label="Text Formatting Tools">
<div class="group">
<button
ngToolbarWidget
value="undo"
type="button"
aria-label="undo"
class="material-symbols-outlined"
translate="no"
>
undo
</button>
<button
ngToolbarWidget
value="redo"
type="button"
aria-label="redo"
class="material-symbols-outlined"
translate="no"
>
redo
</button>
</div>
<div class="separator" role="separator"></div>
<div class="group">
<button
ngToolbarWidget
value="bold"
type="button"
aria-label="bold"
class="material-symbols-outlined"
[attr.aria-pressed]="bold()"
(click)="bold.set(!bold())"
translate="no"
>
format_bold
</button>
<button
ngToolbarWidget
value="italic"
type="button"
aria-label="italic"
class="material-symbols-outlined"
[attr.aria-pressed]="italic()"
(click)="italic.set(!italic())"
translate="no"
>
format_italic
</button>
<button
ngToolbarWidget
value="underlined"
type="button"
aria-label="underlined"
class="material-symbols-outlined"
[attr.aria-pressed]="underlined()"
(click)="underlined.set(!underlined())"
translate="no"
>
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"
class="material-symbols-outlined"
[attr.aria-checked]="alignment() === 'left'"
(click)="alignment.set('left')"
translate="no"
>
format_align_left
</button>
<button
ngToolbarWidget
role="radio"
type="button"
value="align center"
aria-label="align center"
class="material-symbols-outlined"
[attr.aria-checked]="alignment() === 'center'"
(click)="alignment.set('center')"
translate="no"
>
format_align_center
</button>
<button
ngToolbarWidget
role="radio"
type="button"
value="align right"
aria-label="align right"
class="material-symbols-outlined"
[attr.aria-checked]="alignment() === 'right'"
(click)="alignment.set('right')"
translate="no"
>
format_align_right
</button>
</div>
</div>
app.css
@import url('https://fonts.googleapis.com/icon?family=Material+Symbols+Outlined');
:host {
display: flex;
justify-content: center;
font-family: 'Press Start 2P';
--retro-button-color: color-mix(in srgb, var(--vivid-pink) 80%, var(--page-background));
--retro-pressed-tint: #fbfbfb;
--retro-shadow-light: color-mix(in srgb, var(--retro-button-color) 90%, #fff);
--retro-shadow-dark: color-mix(in srgb, var(--retro-button-color) 90%, #000);
--retro-elevated-shadow:
inset 4px 4px 0px 0px var(--retro-shadow-light),
inset -4px -4px 0px 0px var(--retro-shadow-dark), 4px 0px 0px 0px var(--tertiary-contrast),
0px 4px 0px 0px var(--tertiary-contrast), -4px 0px 0px 0px var(--tertiary-contrast),
0px -4px 0px 0px var(--tertiary-contrast);
--retro-flat-shadow:
4px 0px 0px 0px var(--tertiary-contrast), 0px 4px 0px 0px var(--tertiary-contrast),
-4px 0px 0px 0px var(--tertiary-contrast), 0px -4px 0px 0px var(--tertiary-contrast);
--retro-clickable-shadow:
inset 4px 4px 0px 0px var(--retro-shadow-light),
inset -4px -4px 0px 0px var(--retro-shadow-dark), 4px 0px 0px 0px var(--tertiary-contrast),
0px 4px 0px 0px var(--tertiary-contrast), -4px 0px 0px 0px var(--tertiary-contrast),
0px -4px 0px 0px var(--tertiary-contrast), 8px 8px 0px 0px var(--tertiary-contrast);
--retro-pressed-shadow:
inset 4px 4px 0px 0px var(--retro-shadow-dark),
inset -4px -4px 0px 0px var(--retro-shadow-light), 4px 0px 0px 0px var(--tertiary-contrast),
0px 4px 0px 0px var(--tertiary-contrast), -4px 0px 0px 0px var(--tertiary-contrast),
0px -4px 0px 0px var(--tertiary-contrast), 0px 0px 0px 0px var(--tertiary-contrast);
}
:host-context(.docs-dark-mode) {
--retro-pressed-tint: #151417;
}
[ngToolbar] {
gap: 1.5rem;
display: flex;
padding: 1rem;
}
.group {
gap: 1rem;
display: flex;
}
.separator {
width: 1px;
align-self: center;
height: calc(100% - 1rem);
background-color: var(--quinary-contrast);
}
[ngToolbarWidget] {
border: none;
outline: none;
cursor: pointer;
padding: 0.5rem;
font-size: 1.25rem;
color: var(--page-background);
background-color: var(--retro-button-color);
box-shadow: var(--retro-clickable-shadow);
transition:
transform 0.1s,
box-shadow 0.1s;
}
[ngToolbarWidget]:focus,
[ngToolbarWidget]:hover {
transform: translate(1px, 1px);
}
[ngToolbarWidget]:active,
[ngToolbarWidget][aria-pressed='true'],
[ngToolbarWidget][aria-checked='true'] {
transform: translate(4px, 4px);
box-shadow: var(--retro-pressed-shadow);
background-color: color-mix(in srgb, var(--retro-button-color) 60%, var(--retro-pressed-tint));
}
[ngToolbarWidget]:focus {
outline-offset: 4px;
outline: 4px dashed var(--retro-button-color);
}
Utilisation
Une toolbar convient le mieux pour regrouper des contrôles liés auxquels les utilisateurs accèdent fréquemment. Envisagez une toolbar lorsque :
- Plusieurs actions liées - Vous avez plusieurs contrôles qui remplissent des fonctions liées (comme des boutons de mise en forme de texte)
- L'efficacité au clavier compte - Les utilisateurs profitent d'une navigation rapide au clavier avec les touches fléchées
- Contrôles groupés - Vous devez organiser les contrôles en sections logiques avec des séparateurs
- Accès fréquent - Les contrôles sont utilisés de façon répétée au sein d'un flux de travail
Évitez une toolbar lorsque :
- Un simple groupe de boutons suffit - Pour seulement 2 ou 3 actions sans lien entre elles, des boutons individuels fonctionnent mieux
- Les contrôles ne sont pas liés - Une toolbar implique un regroupement logique ; des contrôles sans lien entre eux déroutent les utilisateurs
- Navigation imbriquée complexe - Les hiérarchies profondes sont mieux servies par des menus ou des composants de navigation
Fonctionnalités
La toolbar d'Angular fournit une implémentation de toolbar entièrement accessible avec :
- Navigation au clavier - Parcourez les widgets avec les touches fléchées, Début et Fin grâce au roving tabindex, tandis que les contrôles conservent leur activation native
- Prise en charge des lecteurs d'écran - Attributs ARIA intégrés pour les technologies d'assistance
- Groupes de widgets - Organisez des widgets liés, comme des groupes de boutons radio ou de boutons bascule
- Orientation flexible - Dispositions horizontales ou verticales avec navigation au clavier automatique
- Réactivité à base de signals - Gestion d'état réactive avec les signals Angular
- Prise en charge du texte bidirectionnel - Gère automatiquement les langues de droite à gauche (RTL)
- Focus configurable - Choisissez entre une navigation qui boucle ou des arrêts nets aux extrémités
Exemples
Toolbar horizontale de base
Les toolbars horizontales organisent les contrôles de gauche à droite, comme c'est courant dans les éditeurs de texte et les outils de design. Les touches fléchées permettent de passer d'un widget à l'autre, en gardant le focus dans la toolbar jusqu'à ce que l'utilisateur appuie sur Tab pour passer à l'élément suivant de la page.
app.ts
import {Component, signal} 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 {
readonly bold = signal(false);
readonly italic = signal(false);
readonly underlined = signal(false);
readonly alignment = signal<'left' | 'center' | 'right'>('left');
}
app.html
<div ngToolbar aria-label="Text Formatting Tools">
<div class="group">
<button
ngToolbarWidget
value="undo"
type="button"
aria-label="undo"
class="material-symbols-outlined"
translate="no"
>
undo
</button>
<button
ngToolbarWidget
value="redo"
type="button"
aria-label="redo"
class="material-symbols-outlined"
translate="no"
>
redo
</button>
</div>
<div class="separator" role="separator"></div>
<div class="group">
<button
ngToolbarWidget
value="bold"
type="button"
aria-label="bold"
class="material-symbols-outlined"
[attr.aria-pressed]="bold()"
(click)="bold.set(!bold())"
translate="no"
>
format_bold
</button>
<button
ngToolbarWidget
value="italic"
type="button"
aria-label="italic"
class="material-symbols-outlined"
[attr.aria-pressed]="italic()"
(click)="italic.set(!italic())"
translate="no"
>
format_italic
</button>
<button
ngToolbarWidget
value="underlined"
type="button"
aria-label="underlined"
class="material-symbols-outlined"
[attr.aria-pressed]="underlined()"
(click)="underlined.set(!underlined())"
translate="no"
>
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"
class="material-symbols-outlined"
[attr.aria-checked]="alignment() === 'left'"
(click)="alignment.set('left')"
translate="no"
>
format_align_left
</button>
<button
ngToolbarWidget
role="radio"
type="button"
value="align center"
aria-label="align center"
class="material-symbols-outlined"
[attr.aria-checked]="alignment() === 'center'"
(click)="alignment.set('center')"
translate="no"
>
format_align_center
</button>
<button
ngToolbarWidget
role="radio"
type="button"
value="align right"
aria-label="align right"
class="material-symbols-outlined"
[attr.aria-checked]="alignment() === 'right'"
(click)="alignment.set('right')"
translate="no"
>
format_align_right
</button>
</div>
</div>
app.css
@import url('https://fonts.googleapis.com/icon?family=Material+Symbols+Outlined');
: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);
}
app.ts
import {Component, signal} 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 {
readonly bold = signal(false);
readonly italic = signal(false);
readonly underlined = signal(false);
readonly alignment = signal<'left' | 'center' | 'right'>('left');
}
app.html
<div ngToolbar class="material-toolbar" aria-label="Text Formatting Tools">
<div class="group">
<button
ngToolbarWidget
value="undo"
type="button"
aria-label="undo"
class="material-symbols-outlined"
translate="no"
>
undo
</button>
<button
ngToolbarWidget
value="redo"
type="button"
aria-label="redo"
class="material-symbols-outlined"
translate="no"
>
redo
</button>
</div>
<div class="separator" role="separator"></div>
<div class="group">
<button
ngToolbarWidget
value="bold"
type="button"
aria-label="bold"
class="material-symbols-outlined"
[attr.aria-pressed]="bold()"
(click)="bold.set(!bold())"
translate="no"
>
format_bold
</button>
<button
ngToolbarWidget
value="italic"
type="button"
aria-label="italic"
class="material-symbols-outlined"
[attr.aria-pressed]="italic()"
(click)="italic.set(!italic())"
translate="no"
>
format_italic
</button>
<button
ngToolbarWidget
value="underlined"
type="button"
aria-label="underlined"
class="material-symbols-outlined"
[attr.aria-pressed]="underlined()"
(click)="underlined.set(!underlined())"
translate="no"
>
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"
class="material-symbols-outlined"
[attr.aria-checked]="alignment() === 'left'"
(click)="alignment.set('left')"
translate="no"
>
format_align_left
</button>
<button
ngToolbarWidget
role="radio"
type="button"
value="align center"
aria-label="align center"
class="material-symbols-outlined"
[attr.aria-checked]="alignment() === 'center'"
(click)="alignment.set('center')"
translate="no"
>
format_align_center
</button>
<button
ngToolbarWidget
role="radio"
type="button"
value="align right"
aria-label="align right"
class="material-symbols-outlined"
[attr.aria-checked]="alignment() === 'right'"
(click)="alignment.set('right')"
translate="no"
>
format_align_right
</button>
</div>
</div>
app.css
@import url('https://fonts.googleapis.com/icon?family=Material+Symbols+Outlined');
:host {
display: flex;
justify-content: center;
}
[ngToolbar] {
display: flex;
}
[ngToolbarWidget] {
border: none;
outline: none;
cursor: pointer;
width: 3rem;
height: 3rem;
font-size: 1.25rem;
border-radius: 2rem;
color: color-mix(in srgb, var(--vivid-pink) 70%, var(--full-contrast));
background-color: color-mix(in srgb, var(--vivid-pink) 20%, transparent);
transition:
width 0.2s ease-in-out,
background-color 0.15s ease-in-out,
color 0.15s ease-in-out;
}
[ngToolbarWidget]:focus {
outline-offset: 2px;
outline: 2px solid var(--vivid-pink);
}
[ngToolbarWidget]:active,
[ngToolbarWidget][aria-pressed='true'],
[ngToolbarWidget][aria-checked='true'] {
background-color: color-mix(in srgb, var(--vivid-pink) 80%, var(--full-contrast));
color: var(--page-background);
}
[ngToolbarWidget][aria-checked='true'] {
width: 4.5rem;
}
.group {
display: flex;
gap: 0.5rem;
}
.separator {
width: 1px;
margin: 0 1rem;
height: calc(100% - 2rem);
background-color: var(--quinary-contrast);
}
app.ts
import {Component, signal} 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 {
readonly bold = signal(false);
readonly italic = signal(false);
readonly underlined = signal(false);
readonly alignment = signal<'left' | 'center' | 'right'>('left');
}
app.html
<div ngToolbar class="retro-toolbar" aria-label="Text Formatting Tools">
<div class="group">
<button
ngToolbarWidget
value="undo"
type="button"
aria-label="undo"
class="material-symbols-outlined"
translate="no"
>
undo
</button>
<button
ngToolbarWidget
value="redo"
type="button"
aria-label="redo"
class="material-symbols-outlined"
translate="no"
>
redo
</button>
</div>
<div class="separator" role="separator"></div>
<div class="group">
<button
ngToolbarWidget
value="bold"
type="button"
aria-label="bold"
class="material-symbols-outlined"
[attr.aria-pressed]="bold()"
(click)="bold.set(!bold())"
translate="no"
>
format_bold
</button>
<button
ngToolbarWidget
value="italic"
type="button"
aria-label="italic"
class="material-symbols-outlined"
[attr.aria-pressed]="italic()"
(click)="italic.set(!italic())"
translate="no"
>
format_italic
</button>
<button
ngToolbarWidget
value="underlined"
type="button"
aria-label="underlined"
class="material-symbols-outlined"
[attr.aria-pressed]="underlined()"
(click)="underlined.set(!underlined())"
translate="no"
>
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"
class="material-symbols-outlined"
[attr.aria-checked]="alignment() === 'left'"
(click)="alignment.set('left')"
translate="no"
>
format_align_left
</button>
<button
ngToolbarWidget
role="radio"
type="button"
value="align center"
aria-label="align center"
class="material-symbols-outlined"
[attr.aria-checked]="alignment() === 'center'"
(click)="alignment.set('center')"
translate="no"
>
format_align_center
</button>
<button
ngToolbarWidget
role="radio"
type="button"
value="align right"
aria-label="align right"
class="material-symbols-outlined"
[attr.aria-checked]="alignment() === 'right'"
(click)="alignment.set('right')"
translate="no"
>
format_align_right
</button>
</div>
</div>
app.css
@import url('https://fonts.googleapis.com/icon?family=Material+Symbols+Outlined');
:host {
display: flex;
justify-content: center;
font-family: 'Press Start 2P';
--retro-button-color: color-mix(in srgb, var(--vivid-pink) 80%, var(--page-background));
--retro-pressed-tint: #fbfbfb;
--retro-shadow-light: color-mix(in srgb, var(--retro-button-color) 90%, #fff);
--retro-shadow-dark: color-mix(in srgb, var(--retro-button-color) 90%, #000);
--retro-elevated-shadow:
inset 4px 4px 0px 0px var(--retro-shadow-light),
inset -4px -4px 0px 0px var(--retro-shadow-dark), 4px 0px 0px 0px var(--tertiary-contrast),
0px 4px 0px 0px var(--tertiary-contrast), -4px 0px 0px 0px var(--tertiary-contrast),
0px -4px 0px 0px var(--tertiary-contrast);
--retro-flat-shadow:
4px 0px 0px 0px var(--tertiary-contrast), 0px 4px 0px 0px var(--tertiary-contrast),
-4px 0px 0px 0px var(--tertiary-contrast), 0px -4px 0px 0px var(--tertiary-contrast);
--retro-clickable-shadow:
inset 4px 4px 0px 0px var(--retro-shadow-light),
inset -4px -4px 0px 0px var(--retro-shadow-dark), 4px 0px 0px 0px var(--tertiary-contrast),
0px 4px 0px 0px var(--tertiary-contrast), -4px 0px 0px 0px var(--tertiary-contrast),
0px -4px 0px 0px var(--tertiary-contrast), 8px 8px 0px 0px var(--tertiary-contrast);
--retro-pressed-shadow:
inset 4px 4px 0px 0px var(--retro-shadow-dark),
inset -4px -4px 0px 0px var(--retro-shadow-light), 4px 0px 0px 0px var(--tertiary-contrast),
0px 4px 0px 0px var(--tertiary-contrast), -4px 0px 0px 0px var(--tertiary-contrast),
0px -4px 0px 0px var(--tertiary-contrast), 0px 0px 0px 0px var(--tertiary-contrast);
}
:host-context(.docs-dark-mode) {
--retro-pressed-tint: #151417;
}
[ngToolbar] {
gap: 1.5rem;
display: flex;
padding: 1rem;
}
.group {
gap: 1rem;
display: flex;
}
.separator {
width: 1px;
align-self: center;
height: calc(100% - 1rem);
background-color: var(--quinary-contrast);
}
[ngToolbarWidget] {
border: none;
outline: none;
cursor: pointer;
padding: 0.5rem;
font-size: 1.25rem;
color: var(--page-background);
background-color: var(--retro-button-color);
box-shadow: var(--retro-clickable-shadow);
transition:
transform 0.1s,
box-shadow 0.1s;
}
[ngToolbarWidget]:focus,
[ngToolbarWidget]:hover {
transform: translate(1px, 1px);
}
[ngToolbarWidget]:active,
[ngToolbarWidget][aria-pressed='true'],
[ngToolbarWidget][aria-checked='true'] {
transform: translate(4px, 4px);
box-shadow: var(--retro-pressed-shadow);
background-color: color-mix(in srgb, var(--retro-button-color) 60%, var(--retro-pressed-tint));
}
[ngToolbarWidget]:focus {
outline-offset: 4px;
outline: 4px dashed var(--retro-button-color);
}
Toolbar verticale
Les toolbars verticales empilent les contrôles de haut en bas, ce qui est utile pour les panneaux latéraux ou les palettes de commandes verticales. Les touches fléchées haut et bas permettent de passer d'un widget à l'autre.
app.ts
import {Component, signal} from '@angular/core';
import {Toolbar, ToolbarWidget} from '@angular/aria/toolbar';
@Component({
selector: 'app-root',
templateUrl: 'app.html',
styleUrl: 'app.css',
imports: [Toolbar, ToolbarWidget],
})
export class App {
readonly bold = signal(false);
readonly italic = signal(false);
readonly underlined = signal(false);
}
app.html
<div ngToolbar orientation="vertical" aria-label="Text Formatting Tools">
<div class="group">
<button
ngToolbarWidget
value="undo"
type="button"
aria-label="undo"
class="material-symbols-outlined"
translate="no"
>
undo
</button>
<button
ngToolbarWidget
value="redo"
type="button"
aria-label="redo"
class="material-symbols-outlined"
translate="no"
>
redo
</button>
</div>
<div class="separator" role="separator"></div>
<div class="group">
<button
ngToolbarWidget
value="bold"
type="button"
aria-label="bold"
class="material-symbols-outlined"
[attr.aria-pressed]="bold()"
(click)="bold.set(!bold())"
translate="no"
>
format_bold
</button>
<button
ngToolbarWidget
value="italic"
type="button"
aria-label="italic"
class="material-symbols-outlined"
[attr.aria-pressed]="italic()"
(click)="italic.set(!italic())"
translate="no"
>
format_italic
</button>
<button
ngToolbarWidget
value="underlined"
type="button"
aria-label="underlined"
class="material-symbols-outlined"
[attr.aria-pressed]="underlined()"
(click)="underlined.set(!underlined())"
translate="no"
>
format_underlined
</button>
</div>
</div>
app.css
@import url('https://fonts.googleapis.com/icon?family=Material+Symbols+Outlined');
:host {
display: flex;
justify-content: flex-start;
}
[ngToolbar] {
gap: 1.5rem;
display: flex;
flex-direction: column;
padding: 1rem 0.5rem;
border-radius: 0.5rem;
background-color: var(--septenary-contrast);
}
.group {
gap: 0.5rem;
display: flex;
flex-direction: column;
}
.separator {
height: 1px;
align-self: center;
width: 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);
}
app.ts
import {Component, signal} from '@angular/core';
import {Toolbar, ToolbarWidget} from '@angular/aria/toolbar';
@Component({
selector: 'app-root',
templateUrl: 'app.html',
styleUrl: 'app.css',
imports: [Toolbar, ToolbarWidget],
})
export class App {
readonly bold = signal(false);
readonly italic = signal(false);
readonly underlined = signal(false);
}
app.html
<div
ngToolbar
orientation="vertical"
class="vertical-material-toolbar"
aria-label="Text Formatting Tools"
>
<div class="group">
<button
ngToolbarWidget
value="undo"
type="button"
aria-label="undo"
class="material-symbols-outlined"
translate="no"
>
undo
</button>
<button
ngToolbarWidget
value="redo"
type="button"
aria-label="redo"
class="material-symbols-outlined"
translate="no"
>
redo
</button>
</div>
<div class="separator" role="separator"></div>
<div class="group">
<button
ngToolbarWidget
value="bold"
type="button"
aria-label="bold"
class="material-symbols-outlined"
[attr.aria-pressed]="bold()"
(click)="bold.set(!bold())"
translate="no"
>
format_bold
</button>
<button
ngToolbarWidget
value="italic"
type="button"
aria-label="italic"
class="material-symbols-outlined"
[attr.aria-pressed]="italic()"
(click)="italic.set(!italic())"
translate="no"
>
format_italic
</button>
<button
ngToolbarWidget
value="underlined"
type="button"
aria-label="underlined"
class="material-symbols-outlined"
[attr.aria-pressed]="underlined()"
(click)="underlined.set(!underlined())"
translate="no"
>
format_underlined
</button>
</div>
</div>
app.css
@import url('https://fonts.googleapis.com/icon?family=Material+Symbols+Outlined');
:host {
display: flex;
justify-content: flex-start;
}
[ngToolbar] {
display: flex;
flex-direction: column;
}
[ngToolbarWidget] {
border: none;
outline: none;
cursor: pointer;
width: 3rem;
height: 3rem;
font-size: 1.25rem;
border-radius: 2rem;
color: color-mix(in srgb, var(--vivid-pink) 70%, var(--full-contrast));
background-color: color-mix(in srgb, var(--vivid-pink) 20%, transparent);
transition:
height 0.2s ease-in-out,
background-color 0.15s ease-in-out,
color 0.15s ease-in-out;
}
[ngToolbarWidget]:focus {
outline-offset: 2px;
outline: 2px solid var(--vivid-pink);
}
[ngToolbarWidget]:active,
[ngToolbarWidget][aria-pressed='true'],
[ngToolbarWidget][aria-checked='true'] {
background-color: var(--vivid-pink);
color: var(--page-background);
}
[ngToolbarWidget][aria-checked='true'] {
height: 4.5rem;
}
.group {
gap: 0.5rem;
display: flex;
flex-direction: column;
}
.separator {
width: 1px;
margin: 1rem 0;
height: calc(100% - 2rem);
background-color: var(--quinary-contrast);
}
app.ts
import {Component, signal} from '@angular/core';
import {Toolbar, ToolbarWidget} from '@angular/aria/toolbar';
@Component({
selector: 'app-root',
templateUrl: 'app.html',
styleUrl: 'app.css',
imports: [Toolbar, ToolbarWidget],
})
export class App {
readonly bold = signal(false);
readonly italic = signal(false);
readonly underlined = signal(false);
}
app.html
<div ngToolbar orientation="vertical" class="retro-toolbar" aria-label="Text Formatting Tools">
<div class="group">
<button
ngToolbarWidget
value="undo"
type="button"
aria-label="undo"
class="material-symbols-outlined"
translate="no"
>
undo
</button>
<button
ngToolbarWidget
value="redo"
type="button"
aria-label="redo"
class="material-symbols-outlined"
translate="no"
>
redo
</button>
</div>
<div class="separator" role="separator"></div>
<div class="group">
<button
ngToolbarWidget
value="bold"
type="button"
aria-label="bold"
class="material-symbols-outlined"
[attr.aria-pressed]="bold()"
(click)="bold.set(!bold())"
translate="no"
>
format_bold
</button>
<button
ngToolbarWidget
value="italic"
type="button"
aria-label="italic"
class="material-symbols-outlined"
[attr.aria-pressed]="italic()"
(click)="italic.set(!italic())"
translate="no"
>
format_italic
</button>
<button
ngToolbarWidget
value="underlined"
type="button"
aria-label="underlined"
class="material-symbols-outlined"
[attr.aria-pressed]="underlined()"
(click)="underlined.set(!underlined())"
translate="no"
>
format_underlined
</button>
</div>
</div>
app.css
@import url('https://fonts.googleapis.com/icon?family=Material+Symbols+Outlined');
:host {
display: flex;
justify-content: flex-start;
font-family: 'Press Start 2P';
--retro-button-color: color-mix(in srgb, var(--vivid-pink) 80%, var(--page-background));
--retro-pressed-tint: #fbfbfb;
--retro-shadow-light: color-mix(in srgb, var(--retro-button-color) 90%, #fff);
--retro-shadow-dark: color-mix(in srgb, var(--retro-button-color) 90%, #000);
--retro-elevated-shadow:
inset 4px 4px 0px 0px var(--retro-shadow-light),
inset -4px -4px 0px 0px var(--retro-shadow-dark), 4px 0px 0px 0px var(--tertiary-contrast),
0px 4px 0px 0px var(--tertiary-contrast), -4px 0px 0px 0px var(--tertiary-contrast),
0px -4px 0px 0px var(--tertiary-contrast);
--retro-flat-shadow:
4px 0px 0px 0px var(--tertiary-contrast), 0px 4px 0px 0px var(--tertiary-contrast),
-4px 0px 0px 0px var(--tertiary-contrast), 0px -4px 0px 0px var(--tertiary-contrast);
--retro-clickable-shadow:
inset 4px 4px 0px 0px var(--retro-shadow-light),
inset -4px -4px 0px 0px var(--retro-shadow-dark), 4px 0px 0px 0px var(--tertiary-contrast),
0px 4px 0px 0px var(--tertiary-contrast), -4px 0px 0px 0px var(--tertiary-contrast),
0px -4px 0px 0px var(--tertiary-contrast), 8px 8px 0px 0px var(--tertiary-contrast);
--retro-pressed-shadow:
inset 4px 4px 0px 0px var(--retro-shadow-dark),
inset -4px -4px 0px 0px var(--retro-shadow-light), 4px 0px 0px 0px var(--tertiary-contrast),
0px 4px 0px 0px var(--tertiary-contrast), -4px 0px 0px 0px var(--tertiary-contrast),
0px -4px 0px 0px var(--tertiary-contrast), 0px 0px 0px 0px var(--tertiary-contrast);
}
:host-context(.docs-dark-mode) {
--retro-pressed-tint: #151417;
}
[ngToolbar] {
gap: 1.5rem;
display: flex;
padding: 1rem;
flex-direction: column;
}
.group {
gap: 1rem;
display: flex;
flex-direction: column;
}
.separator {
width: 1px;
align-self: center;
height: calc(100% - 1rem);
background-color: var(--quinary-contrast);
}
[ngToolbarWidget] {
border: none;
outline: none;
cursor: pointer;
padding: 0.5rem;
font-size: 1.25rem;
color: var(--page-background);
background-color: var(--retro-button-color);
box-shadow: var(--retro-clickable-shadow);
transition:
transform 0.1s,
box-shadow 0.1s;
}
[ngToolbarWidget]:focus,
[ngToolbarWidget]:hover {
transform: translate(1px, 1px);
}
[ngToolbarWidget]:active,
[ngToolbarWidget][aria-pressed='true'],
[ngToolbarWidget][aria-checked='true'] {
transform: translate(4px, 4px);
box-shadow: var(--retro-pressed-shadow);
background-color: color-mix(in srgb, var(--retro-button-color) 60%, var(--retro-pressed-tint));
}
[ngToolbarWidget]:focus {
outline-offset: 4px;
outline: 4px dashed var(--retro-button-color);
}
Groupes de widgets
Les groupes de widgets organisent des contrôles liés qui fonctionnent ensemble, comme des options d'alignement de texte ou des bascules de mise en forme. Les groupes conservent la navigation par roving tabindex tout en présentant la structure sémantique appropriée aux technologies d'assistance.
Dans les exemples ci-dessus, les boutons d'alignement sont enveloppés dans ngToolbarWidgetGroup avec role="radiogroup". La sélection est découplée du conteneur de la toolbar, ce qui vous permet de gérer l'état avec des signals Angular ou des directives personnalisées :
<!-- Mutually exclusive radio group -->
<div ngToolbarWidgetGroup role="radiogroup" aria-label="Alignment">
<button
ngToolbarWidget
role="radio"
type="button"
[attr.aria-checked]="alignment() === 'left'"
(click)="alignment.set('left')"
>
Left
</button>
<button
ngToolbarWidget
role="radio"
type="button"
[attr.aria-checked]="alignment() === 'center'"
(click)="alignment.set('center')"
>
Center
</button>
<button
ngToolbarWidget
role="radio"
type="button"
[attr.aria-checked]="alignment() === 'right'"
(click)="alignment.set('right')"
>
Right
</button>
</div>
<!-- Toggle button group -->
<div class="group" role="group" aria-label="Text styling">
<button ngToolbarWidget type="button" [attr.aria-pressed]="bold()" (click)="bold.set(!bold())">
Bold
</button>
<button
ngToolbarWidget
type="button"
[attr.aria-pressed]="italic()"
(click)="italic.set(!italic())"
>
Italic
</button>
</div>
Widgets désactivés
Les toolbars prennent en charge deux modes de désactivation :
- Les widgets soft-disabled restent focusables mais indiquent visuellement qu'ils ne sont pas disponibles
- Les widgets hard-disabled sont entièrement retirés de la navigation au clavier.
Par défaut, softDisabled vaut true, ce qui permet aux widgets désactivés de recevoir encore le focus. Pour activer le mode hard-disabled, définissez [softDisabled]="false" sur la toolbar.
app.ts
import {Component, signal} 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 {
readonly bold = signal(false);
readonly italic = signal(false);
readonly underlined = signal(false);
readonly alignment = signal<'left' | 'center' | 'right'>('left');
}
app.html
<div ngToolbar aria-label="Text Formatting Tools">
<div class="group">
<button
ngToolbarWidget
value="undo"
type="button"
aria-label="undo"
class="material-symbols-outlined"
translate="no"
>
undo
</button>
<button
disabled
ngToolbarWidget
value="redo"
type="button"
aria-label="redo"
class="material-symbols-outlined"
translate="no"
>
redo
</button>
</div>
<div class="separator" role="separator"></div>
<div class="group">
<button
ngToolbarWidget
value="bold"
type="button"
aria-label="bold"
class="material-symbols-outlined"
[attr.aria-pressed]="bold()"
(click)="bold.set(!bold())"
translate="no"
>
format_bold
</button>
<button
ngToolbarWidget
value="italic"
type="button"
aria-label="italic"
class="material-symbols-outlined"
[attr.aria-pressed]="italic()"
(click)="italic.set(!italic())"
translate="no"
>
format_italic
</button>
<button
ngToolbarWidget
value="underlined"
type="button"
aria-label="underlined"
class="material-symbols-outlined"
[attr.aria-pressed]="underlined()"
(click)="underlined.set(!underlined())"
translate="no"
>
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"
class="material-symbols-outlined"
[attr.aria-checked]="alignment() === 'left'"
translate="no"
>
format_align_left
</button>
<button
ngToolbarWidget
role="radio"
type="button"
value="align center"
aria-label="align center"
class="material-symbols-outlined"
[attr.aria-checked]="alignment() === 'center'"
translate="no"
>
format_align_center
</button>
<button
ngToolbarWidget
role="radio"
type="button"
value="align right"
aria-label="align right"
class="material-symbols-outlined"
[attr.aria-checked]="alignment() === 'right'"
translate="no"
>
format_align_right
</button>
</div>
</div>
app.css
@import url('https://fonts.googleapis.com/icon?family=Material+Symbols+Outlined');
: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;
}
app.ts
import {Component, signal} 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 {
readonly bold = signal(false);
readonly italic = signal(false);
readonly underlined = signal(false);
readonly alignment = signal<'left' | 'center' | 'right'>('left');
}
app.html
<div ngToolbar class="disabled-material-toolbar" aria-label="Text Formatting Tools">
<div class="group">
<button
ngToolbarWidget
value="undo"
type="button"
aria-label="undo"
class="material-symbols-outlined"
translate="no"
>
undo
</button>
<button
disabled
ngToolbarWidget
value="redo"
type="button"
aria-label="redo"
class="material-symbols-outlined"
translate="no"
>
redo
</button>
</div>
<div class="separator" role="separator"></div>
<div class="group">
<button
ngToolbarWidget
value="bold"
type="button"
aria-label="bold"
class="material-symbols-outlined"
[attr.aria-pressed]="bold()"
(click)="bold.set(!bold())"
translate="no"
>
format_bold
</button>
<button
ngToolbarWidget
value="italic"
type="button"
aria-label="italic"
class="material-symbols-outlined"
[attr.aria-pressed]="italic()"
(click)="italic.set(!italic())"
translate="no"
>
format_italic
</button>
<button
ngToolbarWidget
value="underlined"
type="button"
aria-label="underlined"
class="material-symbols-outlined"
[attr.aria-pressed]="underlined()"
(click)="underlined.set(!underlined())"
translate="no"
>
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"
class="material-symbols-outlined"
[attr.aria-checked]="alignment() === 'left'"
translate="no"
>
format_align_left
</button>
<button
ngToolbarWidget
role="radio"
type="button"
value="align center"
aria-label="align center"
class="material-symbols-outlined"
[attr.aria-checked]="alignment() === 'center'"
translate="no"
>
format_align_center
</button>
<button
ngToolbarWidget
role="radio"
type="button"
value="align right"
aria-label="align right"
class="material-symbols-outlined"
[attr.aria-checked]="alignment() === 'right'"
translate="no"
>
format_align_right
</button>
</div>
</div>
app.css
@import url('https://fonts.googleapis.com/icon?family=Material+Symbols+Outlined');
:host {
display: flex;
justify-content: center;
}
[ngToolbar] {
display: flex;
}
[ngToolbarWidget] {
border: none;
outline: none;
cursor: pointer;
width: 3rem;
height: 3rem;
font-size: 1.25rem;
border-radius: 2rem;
color: color-mix(in srgb, var(--vivid-pink) 70%, var(--full-contrast));
background-color: color-mix(in srgb, var(--vivid-pink) 20%, transparent);
transition:
width 0.2s ease-in-out,
background-color 0.15s ease-in-out,
color 0.15s ease-in-out;
}
[ngToolbarWidget]:focus {
outline-offset: 2px;
outline: 2px solid var(--vivid-pink);
}
[ngToolbarWidget]:active,
[ngToolbarWidget][aria-pressed='true'],
[ngToolbarWidget][aria-checked='true'] {
background-color: color-mix(in srgb, var(--vivid-pink) 80%, var(--full-contrast));
color: var(--page-background);
}
[ngToolbarWidget][aria-checked='true'] {
width: 4.5rem;
}
.group {
display: flex;
gap: 0.5rem;
}
.separator {
width: 1px;
margin: 0 1rem;
height: calc(100% - 2rem);
background-color: var(--quinary-contrast);
}
[ngToolbarWidget][aria-disabled="true"] {
cursor: default;
opacity: 0.45;
}
app.ts
import {Component, signal} 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 {
readonly bold = signal(false);
readonly italic = signal(false);
readonly underlined = signal(false);
readonly alignment = signal<'left' | 'center' | 'right'>('left');
}
app.html
<div ngToolbar class="retro-toolbar" aria-label="Text Formatting Tools">
<div class="group">
<button
ngToolbarWidget
value="undo"
type="button"
aria-label="undo"
class="material-symbols-outlined"
translate="no"
>
undo
</button>
<button
disabled
ngToolbarWidget
value="redo"
type="button"
aria-label="redo"
class="material-symbols-outlined"
translate="no"
>
redo
</button>
</div>
<div class="separator" role="separator"></div>
<div class="group">
<button
ngToolbarWidget
value="bold"
type="button"
aria-label="bold"
class="material-symbols-outlined"
[attr.aria-pressed]="bold()"
(click)="bold.set(!bold())"
translate="no"
>
format_bold
</button>
<button
ngToolbarWidget
value="italic"
type="button"
aria-label="italic"
class="material-symbols-outlined"
[attr.aria-pressed]="italic()"
(click)="italic.set(!italic())"
translate="no"
>
format_italic
</button>
<button
ngToolbarWidget
value="underlined"
type="button"
aria-label="underlined"
class="material-symbols-outlined"
[attr.aria-pressed]="underlined()"
(click)="underlined.set(!underlined())"
translate="no"
>
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"
class="material-symbols-outlined"
[attr.aria-checked]="alignment() === 'left'"
translate="no"
>
format_align_left
</button>
<button
ngToolbarWidget
role="radio"
type="button"
value="align center"
aria-label="align center"
class="material-symbols-outlined"
[attr.aria-checked]="alignment() === 'center'"
translate="no"
>
format_align_center
</button>
<button
ngToolbarWidget
role="radio"
type="button"
value="align right"
aria-label="align right"
class="material-symbols-outlined"
[attr.aria-checked]="alignment() === 'right'"
translate="no"
>
format_align_right
</button>
</div>
</div>
app.css
@import url('https://fonts.googleapis.com/icon?family=Material+Symbols+Outlined');
:host {
display: flex;
justify-content: center;
font-family: 'Press Start 2P';
--retro-button-color: color-mix(in srgb, var(--vivid-pink) 80%, var(--page-background));
--retro-pressed-tint: #fbfbfb;
--retro-shadow-light: color-mix(in srgb, var(--retro-button-color) 90%, #fff);
--retro-shadow-dark: color-mix(in srgb, var(--retro-button-color) 90%, #000);
--retro-elevated-shadow:
inset 4px 4px 0px 0px var(--retro-shadow-light),
inset -4px -4px 0px 0px var(--retro-shadow-dark), 4px 0px 0px 0px var(--tertiary-contrast),
0px 4px 0px 0px var(--tertiary-contrast), -4px 0px 0px 0px var(--tertiary-contrast),
0px -4px 0px 0px var(--tertiary-contrast);
--retro-flat-shadow:
4px 0px 0px 0px var(--tertiary-contrast), 0px 4px 0px 0px var(--tertiary-contrast),
-4px 0px 0px 0px var(--tertiary-contrast), 0px -4px 0px 0px var(--tertiary-contrast);
--retro-clickable-shadow:
inset 4px 4px 0px 0px var(--retro-shadow-light),
inset -4px -4px 0px 0px var(--retro-shadow-dark), 4px 0px 0px 0px var(--tertiary-contrast),
0px 4px 0px 0px var(--tertiary-contrast), -4px 0px 0px 0px var(--tertiary-contrast),
0px -4px 0px 0px var(--tertiary-contrast), 8px 8px 0px 0px var(--tertiary-contrast);
--retro-pressed-shadow:
inset 4px 4px 0px 0px var(--retro-shadow-dark),
inset -4px -4px 0px 0px var(--retro-shadow-light), 4px 0px 0px 0px var(--tertiary-contrast),
0px 4px 0px 0px var(--tertiary-contrast), -4px 0px 0px 0px var(--tertiary-contrast),
0px -4px 0px 0px var(--tertiary-contrast), 0px 0px 0px 0px var(--tertiary-contrast);
}
:host-context(.docs-dark-mode) {
--retro-pressed-tint: #151417;
}
[ngToolbar] {
gap: 1.5rem;
display: flex;
padding: 1rem;
}
.group {
gap: 1rem;
display: flex;
}
.separator {
width: 1px;
align-self: center;
height: calc(100% - 1rem);
background-color: var(--quinary-contrast);
}
[ngToolbarWidget] {
border: none;
outline: none;
cursor: pointer;
padding: 0.5rem;
font-size: 1.25rem;
color: var(--page-background);
background-color: var(--retro-button-color);
box-shadow: var(--retro-clickable-shadow);
transition:
transform 0.1s,
box-shadow 0.1s;
}
[ngToolbarWidget]:focus,
[ngToolbarWidget]:hover {
transform: translate(1px, 1px);
}
[ngToolbarWidget]:active,
[ngToolbarWidget][aria-pressed='true'],
[ngToolbarWidget][aria-checked='true'] {
transform: translate(4px, 4px);
box-shadow: var(--retro-pressed-shadow);
background-color: color-mix(in srgb, var(--retro-button-color) 60%, var(--retro-pressed-tint));
}
[ngToolbarWidget]:focus {
outline-offset: 4px;
outline: 4px dashed var(--retro-button-color);
}
[ngToolbarWidget][aria-disabled='true'] {
cursor: default;
opacity: 0.45;
}
Prise en charge de la lecture de droite à gauche (RTL)
Les toolbars prennent automatiquement en charge les langues de droite à gauche. Enveloppez la toolbar dans un conteneur avec dir="rtl" pour inverser la disposition et le sens de la navigation au clavier. La navigation avec les touches fléchées s'ajuste automatiquement : la flèche gauche passe au widget suivant, la flèche droite au précédent.
app.ts
import {Dir} from '@angular/cdk/bidi';
import {Component, signal} 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 {
readonly bold = signal(false);
readonly italic = signal(false);
readonly underlined = signal(false);
readonly alignment = signal<'left' | 'center' | 'right'>('left');
}
app.html
<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"
translate="no"
>
undo
</button>
<button
ngToolbarWidget
value="redo"
type="button"
aria-label="redo"
class="material-symbols-outlined"
translate="no"
>
redo
</button>
</div>
<div class="separator" role="separator"></div>
<div class="group">
<button
ngToolbarWidget
value="bold"
type="button"
aria-label="bold"
class="material-symbols-outlined"
[attr.aria-pressed]="bold()"
(click)="bold.set(!bold())"
translate="no"
>
format_bold
</button>
<button
ngToolbarWidget
value="italic"
type="button"
aria-label="italic"
class="material-symbols-outlined"
[attr.aria-pressed]="italic()"
(click)="italic.set(!italic())"
translate="no"
>
format_italic
</button>
<button
ngToolbarWidget
value="underlined"
type="button"
aria-label="underlined"
class="material-symbols-outlined"
[attr.aria-pressed]="underlined()"
(click)="underlined.set(!underlined())"
translate="no"
>
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"
class="material-symbols-outlined"
[attr.aria-checked]="alignment() === 'left'"
(click)="alignment.set('left')"
translate="no"
>
format_align_left
</button>
<button
ngToolbarWidget
role="radio"
type="button"
value="align center"
aria-label="align center"
class="material-symbols-outlined"
[attr.aria-checked]="alignment() === 'center'"
(click)="alignment.set('center')"
translate="no"
>
format_align_center
</button>
<button
ngToolbarWidget
role="radio"
type="button"
value="align right"
aria-label="align right"
class="material-symbols-outlined"
[attr.aria-checked]="alignment() === 'right'"
(click)="alignment.set('right')"
translate="no"
>
format_align_right
</button>
</div>
</div>
app.css
@import url('https://fonts.googleapis.com/icon?family=Material+Symbols+Outlined');
: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);
}
app.ts
import {Dir} from '@angular/cdk/bidi';
import {Component, signal} 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 {
readonly bold = signal(false);
readonly italic = signal(false);
readonly underlined = signal(false);
readonly alignment = signal<'left' | 'center' | 'right'>('left');
}
app.html
<div ngToolbar dir="rtl" class="rtl-material-toolbar" aria-label="Text Formatting Tools">
<div class="group">
<button
ngToolbarWidget
value="undo"
type="button"
aria-label="undo"
class="material-symbols-outlined"
translate="no"
>
undo
</button>
<button
ngToolbarWidget
value="redo"
type="button"
aria-label="redo"
class="material-symbols-outlined"
translate="no"
>
redo
</button>
</div>
<div class="separator" role="separator"></div>
<div class="group">
<button
ngToolbarWidget
value="bold"
type="button"
aria-label="bold"
class="material-symbols-outlined"
[attr.aria-pressed]="bold()"
(click)="bold.set(!bold())"
translate="no"
>
format_bold
</button>
<button
ngToolbarWidget
value="italic"
type="button"
aria-label="italic"
class="material-symbols-outlined"
[attr.aria-pressed]="italic()"
(click)="italic.set(!italic())"
translate="no"
>
format_italic
</button>
<button
ngToolbarWidget
value="underlined"
type="button"
aria-label="underlined"
class="material-symbols-outlined"
[attr.aria-pressed]="underlined()"
(click)="underlined.set(!underlined())"
translate="no"
>
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"
class="material-symbols-outlined"
[attr.aria-checked]="alignment() === 'left'"
(click)="alignment.set('left')"
translate="no"
>
format_align_left
</button>
<button
ngToolbarWidget
role="radio"
type="button"
value="align center"
aria-label="align center"
class="material-symbols-outlined"
[attr.aria-checked]="alignment() === 'center'"
(click)="alignment.set('center')"
translate="no"
>
format_align_center
</button>
<button
ngToolbarWidget
role="radio"
type="button"
value="align right"
aria-label="align right"
class="material-symbols-outlined"
[attr.aria-checked]="alignment() === 'right'"
(click)="alignment.set('right')"
translate="no"
>
format_align_right
</button>
</div>
</div>
app.css
@import url('https://fonts.googleapis.com/icon?family=Material+Symbols+Outlined');
:host {
display: flex;
justify-content: center;
}
[ngToolbar] {
display: flex;
}
[ngToolbarWidget] {
border: none;
outline: none;
cursor: pointer;
width: 3rem;
height: 3rem;
font-size: 1.25rem;
border-radius: 2rem;
color: color-mix(in srgb, var(--vivid-pink) 70%, var(--full-contrast));
background-color: color-mix(in srgb, var(--vivid-pink) 20%, transparent);
transition:
width 0.2s ease-in-out,
background-color 0.15s ease-in-out,
color 0.15s ease-in-out;
}
[ngToolbarWidget]:focus {
outline-offset: 2px;
outline: 2px solid var(--vivid-pink);
}
[ngToolbarWidget]:active,
[ngToolbarWidget][aria-pressed='true'],
[ngToolbarWidget][aria-checked='true'] {
background-color: var(--vivid-pink);
color: var(--page-background);
}
[ngToolbarWidget][aria-checked='true'] {
width: 4.5rem;
}
.group {
display: flex;
gap: 0.5rem;
}
.separator {
width: 1px;
margin: 0 1rem;
height: calc(100% - 2rem);
background-color: var(--quinary-contrast);
}
app.ts
import {Dir} from '@angular/cdk/bidi';
import {Component, signal} 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 {
readonly bold = signal(false);
readonly italic = signal(false);
readonly underlined = signal(false);
readonly alignment = signal<'left' | 'center' | 'right'>('left');
}
app.html
<div ngToolbar dir="rtl" class="rtl-retro-toolbar" aria-label="Text Formatting Tools">
<div class="group">
<button
ngToolbarWidget
value="undo"
type="button"
aria-label="undo"
class="material-symbols-outlined"
translate="no"
>
undo
</button>
<button
ngToolbarWidget
value="redo"
type="button"
aria-label="redo"
class="material-symbols-outlined"
translate="no"
>
redo
</button>
</div>
<div class="separator" role="separator"></div>
<div class="group">
<button
ngToolbarWidget
value="bold"
type="button"
aria-label="bold"
class="material-symbols-outlined"
[attr.aria-pressed]="bold()"
(click)="bold.set(!bold())"
translate="no"
>
format_bold
</button>
<button
ngToolbarWidget
value="italic"
type="button"
aria-label="italic"
class="material-symbols-outlined"
[attr.aria-pressed]="italic()"
(click)="italic.set(!italic())"
translate="no"
>
format_italic
</button>
<button
ngToolbarWidget
value="underlined"
type="button"
aria-label="underlined"
class="material-symbols-outlined"
[attr.aria-pressed]="underlined()"
(click)="underlined.set(!underlined())"
translate="no"
>
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"
class="material-symbols-outlined"
[attr.aria-checked]="alignment() === 'left'"
(click)="alignment.set('left')"
translate="no"
>
format_align_left
</button>
<button
ngToolbarWidget
role="radio"
type="button"
value="align center"
aria-label="align center"
class="material-symbols-outlined"
[attr.aria-checked]="alignment() === 'center'"
(click)="alignment.set('center')"
translate="no"
>
format_align_center
</button>
<button
ngToolbarWidget
role="radio"
type="button"
value="align right"
aria-label="align right"
class="material-symbols-outlined"
[attr.aria-checked]="alignment() === 'right'"
(click)="alignment.set('right')"
translate="no"
>
format_align_right
</button>
</div>
</div>
app.css
@import url('https://fonts.googleapis.com/icon?family=Material+Symbols+Outlined');
:host {
display: flex;
justify-content: center;
font-family: 'Press Start 2P';
--retro-button-color: color-mix(in srgb, var(--vivid-pink) 80%, var(--page-background));
--retro-pressed-tint: #fbfbfb;
--retro-shadow-light: color-mix(in srgb, var(--retro-button-color) 90%, #fff);
--retro-shadow-dark: color-mix(in srgb, var(--retro-button-color) 90%, #000);
--retro-elevated-shadow:
inset 4px 4px 0px 0px var(--retro-shadow-light),
inset -4px -4px 0px 0px var(--retro-shadow-dark), 4px 0px 0px 0px var(--tertiary-contrast),
0px 4px 0px 0px var(--tertiary-contrast), -4px 0px 0px 0px var(--tertiary-contrast),
0px -4px 0px 0px var(--tertiary-contrast);
--retro-flat-shadow:
4px 0px 0px 0px var(--tertiary-contrast), 0px 4px 0px 0px var(--tertiary-contrast),
-4px 0px 0px 0px var(--tertiary-contrast), 0px -4px 0px 0px var(--tertiary-contrast);
--retro-clickable-shadow:
inset 4px 4px 0px 0px var(--retro-shadow-light),
inset -4px -4px 0px 0px var(--retro-shadow-dark), 4px 0px 0px 0px var(--tertiary-contrast),
0px 4px 0px 0px var(--tertiary-contrast), -4px 0px 0px 0px var(--tertiary-contrast),
0px -4px 0px 0px var(--tertiary-contrast), 8px 8px 0px 0px var(--tertiary-contrast);
--retro-pressed-shadow:
inset 4px 4px 0px 0px var(--retro-shadow-dark),
inset -4px -4px 0px 0px var(--retro-shadow-light), 4px 0px 0px 0px var(--tertiary-contrast),
0px 4px 0px 0px var(--tertiary-contrast), -4px 0px 0px 0px var(--tertiary-contrast),
0px -4px 0px 0px var(--tertiary-contrast), 0px 0px 0px 0px var(--tertiary-contrast);
}
:host-context(.docs-dark-mode) {
--retro-pressed-tint: #151417;
}
[ngToolbar] {
gap: 1.5rem;
display: flex;
padding: 1rem;
}
.group {
gap: 1rem;
display: flex;
}
.separator {
width: 1px;
align-self: center;
height: calc(100% - 1rem);
background-color: var(--quinary-contrast);
}
[ngToolbarWidget] {
border: none;
outline: none;
cursor: pointer;
padding: 0.5rem;
font-size: 1.25rem;
color: var(--page-background);
background-color: var(--retro-button-color);
box-shadow: var(--retro-clickable-shadow);
transition:
transform 0.1s,
box-shadow 0.1s;
}
[ngToolbarWidget]:focus,
[ngToolbarWidget]:hover {
transform: translate(1px, 1px);
}
[ngToolbarWidget]:active,
[ngToolbarWidget][aria-pressed='true'],
[ngToolbarWidget][aria-checked='true'] {
transform: translate(4px, 4px);
box-shadow: var(--retro-pressed-shadow);
background-color: color-mix(in srgb, var(--retro-button-color) 60%, var(--retro-pressed-tint));
}
[ngToolbarWidget]:focus {
outline-offset: 4px;
outline: 4px dashed var(--retro-button-color);
}
Tests
Angular Aria fournit des harnais de composant pour tester les composants de toolbar. Voici un exemple d'utilisation des harnais dans un test de composant :
import {ComponentFixture, TestBed} from '@angular/core/testing';
import {HarnessLoader} from '@angular/cdk/testing';
import {TestbedHarnessEnvironment} from '@angular/cdk/testing/testbed';
import {ToolbarHarness} from '@angular/aria/toolbar/testing';
import {MyToolbarComponent} from './my-toolbar'; // Your component
describe('MyToolbarComponent', () => {
let fixture: ComponentFixture<MyToolbarComponent>;
let loader: HarnessLoader;
beforeEach(async () => {
TestBed.configureTestingModule({
imports: [MyToolbarComponent],
});
fixture = TestBed.createComponent(MyToolbarComponent);
await fixture.whenStable();
loader = TestbedHarnessEnvironment.loader(fixture);
});
it('should have widgets and update toggle state on click', async () => {
// Load the toolbar harness
const toolbar = await loader.getHarness(ToolbarHarness);
// Get all widgets
const widgets = await toolbar.getWidgets();
expect(widgets.length).toBe(3);
// Click the first widget
await widgets[0].click();
// Verify pressed state updated via click handler
expect(await widgets[0].isSelected()).toBe(true);
});
});
Référence de l'API
Pour une documentation détaillée de l'API, consultez les références d'API suivantes :