Angular 89 🅰️ Angular Material — Components
Angular Material is the official UI component library for Angular, built and maintained by the Angular team. It implements Google’s Material Design specification, providing a set of high-quality, accessible, and customizable components that integrate deeply with the framework. Unlike third-party libraries that layer on top of Angular, Angular Material is designed alongside it — the form controls respond to Reactive Forms, the components support OnPush change detection, and the CDK (Component Dev Kit) provides the low-level primitives for building custom components with the same interaction patterns .
This chapter covers the component catalog, the architectural relationship between Angular Material and the CDK, the patterns that make the components testable and accessible, and the practical decisions that determine when to use a Material component versus building a custom one.
Key point: Angular Material is not just a collection of pre-styled components. It is a design system implementation built on top of the CDK. The CDK provides the behavior — overlay positioning, focus management, keyboard interaction, drag-and-drop — and Angular Material provides the visual design on top of it. When you use a mat-select, you are using a CDK overlay with Material styling. When you build a custom dropdown, you use the same CDK overlay directly .
Why Angular Material exists
Building a consistent UI from scratch requires solving the same problems repeatedly: positioning an overlay relative to its trigger, trapping focus in a dialog, managing keyboard navigation in a list, announcing changes to screen readers. These problems are not specific to any application. They are the shared infrastructure of interactive UI.
The consistency problem. An application with three developers and no component library has three different button implementations, three different dialog patterns, and three different ways of handling form validation. Angular Material provides one implementation of each pattern, styled according to Material Design, and updated by the Angular team. The consistency is not just visual — it is behavioral. Every mat-button responds to keyboard focus the same way. Every mat-dialog manages focus the same way .
The accessibility problem. Implementing accessible dialogs, menus, and form controls is harder than it looks. A mat-dialog automatically moves focus into the dialog when it opens, traps Tab focus within it, and restores focus to the trigger element when it closes . The mat-menu supports arrow key navigation, Home and End keys, and type-ahead selection. These behaviors are built into the components, not left to the application developer.
The theming problem. Angular Material 19 uses Material Design 3 (M3), which is based on design tokens — CSS variables that define every color, typography size, and shape in the system . Changing a theme is a matter of overriding tokens, not fighting specificity wars with component styles. The light-dark() CSS function enables native light/dark mode switching without JavaScript .
The integration problem. Angular Material components work with Angular’s Reactive Forms. A mat-form-field wraps an input and displays validation errors based on the form control’s state. A mat-datepicker integrates with FormControl. A mat-select supports formControl and ngModel . This integration is what distinguishes Angular Material from a generic CSS framework — the components understand Angular’s forms, change detection, and dependency injection.
The trade-off. Angular Material is opinionated. The components look like Material Design and behave like Material Design. An application that wants a different visual language must override the tokens extensively, which is possible but time-consuming. The library is also not exhaustive — it covers the common components but not every edge case. The CDK is the escape hatch: when Material does not have the component you need, the CDK has the primitives to build it .
a. The Component Catalog
Angular Material organizes its components into categories. The catalog is not exhaustive, but it covers the components that most applications need.
Form controls capture user input. MatFormField wraps inputs with labels, hints, and error messages. MatInput is the text input. MatSelect is the dropdown. MatCheckbox, MatRadioButton, and MatSlideToggle capture boolean and choice input. MatDatepicker and MatTimepicker capture dates and times . The timepicker is new in Angular Material 19 and replaces the need for third-party time selection libraries .
Navigation components help users move through the application. MatToolbar is the top-level header. MatSidenav and MatDrawer provide collapsible side content. MatTabs organizes content into switchable views. MatMenu provides floating option panels. MatStepper presents content as a sequence of steps .
Layout components organize content on the page. MatCard is a styled container for itemized content. MatList presents conventional lists. MatGridList provides a flexible grid. MatExpansionPanel creates collapsible sections. MatDivider separates content .
Data display components present information. MatTable displays tabular data with sorting, pagination, and filtering. MatPaginator controls paged data. MatSort configures how tabular data is sorted. MatChips presents items as small tactile entities. MatTree presents hierarchical content .
Feedback components communicate status. MatDialog is a configurable modal for dynamic content. MatSnackBar displays short actionable messages. MatProgressBar and MatProgressSpinner indicate loading. MatTooltip displays floating content on hover .
Buttons and indicators provide interaction. MatButton is the interactive button. MatButtonToggle groups toggleable options. MatBadge overlays a small value indicator. MatIcon renders a specified icon .
The components are imported individually. An application that uses only buttons and dialogs imports MatButtonModule and MatDialogModule, not the entire library. This keeps the bundle small and the dependency graph clear .
b. The CDK Relationship
The Component Dev Kit (CDK) is the foundation that Angular Material is built on. It is a separate package (@angular/cdk) that provides the behavior without the visual design. The components in Angular Material are thin layers of Material styling on top of CDK primitives .
The CDK provides several categories of primitives:
Overlay positions floating content relative to a trigger. MatSelect, MatMenu, MatDialog, and MatTooltip all use the CDK overlay. A custom dropdown or popover uses the same Overlay service directly .
A11y provides focus management, live announcers, and keyboard navigation utilities. FocusTrap constrains Tab focus in a dialog. FocusMonitor detects how an element received focus. LiveAnnouncer announces messages to screen readers. ListKeyManager handles keyboard navigation in lists .
Drag and Drop enables draggable lists and grids. Angular Material 19 adds 2D drag-and-drop support for mixed orientation layouts like kanban boards .
Portal renders content into a different part of the DOM. Dialogs, menus, and tooltips use portals to render their content outside the component’s DOM hierarchy.
Scrolling provides virtual scrolling for large lists. CdkVirtualScrollViewport renders only the visible items, which allows the list to contain thousands of items without performance degradation.
The relationship is intentional. When Angular Material does not have a component you need, the CDK has the primitives to build it. A custom date range picker, a tree with drag-and-drop reordering, or a multi-select with search are all built on CDK primitives that Angular Material also uses .
c. Theming and Material 3
Angular Material 19 uses Material Design 3 (M3) as the default design system. The M3 theming system is based on design tokens — CSS variables that define every visual property. The tokens are prefixed with --mat-sys- and include colors (--mat-sys-primary, --mat-sys-surface), typography sizes, and shape radii .
The mat.theme API generates the token set from a theme configuration. A theme specifies the primary, tertiary, and error colors, the typography scale, and the density. The generated tokens are applied globally, and components read from them .
@use '@angular/material' as mat;
html {
@include mat.theme((
color: (
primary: mat.$azure-palette,
tertiary: mat.$blue-palette,
),
typography: Roboto,
density: 0,
));
}
Dark mode is handled through the light-dark() CSS function. The theme defines both light and dark token values, and the browser selects the appropriate set based on the color-scheme property. Setting color-scheme: light dark on the html element enables automatic switching based on the operating system preference .
The component override mixins allow per-component customization. The mat.button-overrides() mixin accepts a map of token overrides for buttons. The mat.form-field-overrides() mixin does the same for form fields. This is granular customization without global side effects .
Complete Example Session
This session builds a task list page using Angular Material components: a toolbar, a sidenav, a form field, a table with sorting and pagination, a dialog, and a snackbar.
// ============================================
// PART 1: THE MODULE IMPORTS
// ============================================
import { Component, signal, computed, viewChild, inject } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { MatToolbarModule } from '@angular/material/toolbar';
import { MatSidenavModule } from '@angular/material/sidenav';
import { MatListModule } from '@angular/material/list';
import { MatIconModule } from '@angular/material/icon';
import { MatButtonModule } from '@angular/material/button';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatInputModule } from '@angular/material/input';
import { MatTableModule } from '@angular/material/table';
import { MatSortModule, MatSort } from '@angular/material/sort';
import { MatPaginatorModule, MatPaginator, PageEvent } from '@angular/material/paginator';
import { MatDialogModule, MatDialog } from '@angular/material/dialog';
import { MatSnackBarModule, MatSnackBar } from '@angular/material/snack-bar';
// ============================================
// PART 2: THE TASK MODEL AND SERVICE
// ============================================
interface Task {
id: string;
title: string;
status: 'todo' | 'in_progress' | 'done';
priority: 'low' | 'medium' | 'high';
createdAt: Date;
}
@Injectable({ providedIn: 'root' })
export class TaskService {
private tasks = signal<Task[]>([]);
readonly allTasks = this.tasks.asReadonly();
addTask(title: string, priority: Task['priority']): void {
this.tasks.update((tasks) => [
...tasks,
{
id: crypto.randomUUID(),
title,
status: 'todo',
priority,
createdAt: new Date(),
},
]);
}
removeTask(id: string): void {
this.tasks.update((tasks) => tasks.filter((t) => t.id !== id));
}
}
// ============================================
// PART 3: THE TASK DIALOG
// ============================================
@Component({
selector: 'app-task-dialog',
imports: [MatDialogModule, MatFormFieldModule, MatInputModule, MatButtonModule, FormsModule],
template: `
<h2 mat-dialog-title>Add Task</h2>
<mat-dialog-content>
<mat-form-field appearance="outline">
<mat-label>Title</mat-label>
<input matInput [(ngModel)]="title" placeholder="Enter task title" />
</mat-form-field>
</mat-dialog-content>
<mat-dialog-actions align="end">
<button mat-button mat-dialog-close>Cancel</button>
<button mat-raised-button color="primary" [mat-dialog-close]="title">Add</button>
</mat-dialog-actions>
`,
})
export class TaskDialogComponent {
title = '';
}
// ============================================
// PART 4: THE MAIN COMPONENT
// ============================================
@Component({
selector: 'app-task-list',
imports: [
MatToolbarModule, MatSidenavModule, MatListModule, MatIconModule,
MatButtonModule, MatTableModule, MatSortModule, MatPaginatorModule,
MatDialogModule, MatSnackBarModule,
],
template: `
<mat-toolbar color="primary">
<button mat-icon-button (click)="sidenav.toggle()">
<mat-icon>menu</mat-icon>
</button>
<span>Task Manager</span>
<span class="spacer"></span>
<button mat-raised-button (click)="openDialog()">Add Task</button>
</mat-toolbar>
<mat-sidenav-container>
<mat-sidenav #sidenav mode="side" opened>
<mat-nav-list>
<a mat-list-item>All Tasks</a>
<a mat-list-item>In Progress</a>
<a mat-list-item>Completed</a>
</mat-nav-list>
</mat-sidenav>
<mat-sidenav-content>
<table mat-table [dataSource]="dataSource()" matSort>
<ng-container matColumnDef="title">
<th mat-header-cell *matHeaderCellDef mat-sort-header>Title</th>
<td mat-cell *matCellDef="let task">{{ task.title }}</td>
</ng-container>
<ng-container matColumnDef="status">
<th mat-header-cell *matHeaderCellDef mat-sort-header>Status</th>
<td mat-cell *matCellDef="let task">{{ task.status }}</td>
</ng-container>
<ng-container matColumnDef="priority">
<th mat-header-cell *matHeaderCellDef mat-sort-header>Priority</th>
<td mat-cell *matCellDef="let task">{{ task.priority }}</td>
</ng-container>
<ng-container matColumnDef="actions">
<th mat-header-cell *matHeaderCellDef>Actions</th>
<td mat-cell *matCellDef="let task">
<button mat-icon-button (click)="deleteTask(task.id)">
<mat-icon>delete</mat-icon>
</button>
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns"></tr>
</table>
<mat-paginator [pageSizeOptions]="[5, 10, 20]" showFirstLastButtons />
</mat-sidenav-content>
</mat-sidenav-container>
`,
styles: [`
.spacer { flex: 1 1 auto; }
mat-sidenav-container { height: calc(100vh - 64px); }
table { width: 100%; }
`],
})
export class TaskListComponent implements AfterViewInit {
private taskService = inject(TaskService);
private dialog = inject(MatDialog);
private snackBar = inject(MatSnackBar);
displayedColumns = ['title', 'status', 'priority', 'actions'];
dataSource = this.taskService.allTasks;
sort = viewChild(MatSort);
paginator = viewChild(MatPaginator);
ngAfterViewInit() {
// In a real application, connect the sort and paginator to a MatTableDataSource
}
openDialog(): void {
const dialogRef = this.dialog.open(TaskDialogComponent, {
width: '400px',
});
dialogRef.afterClosed().subscribe((title: string | undefined) => {
if (title) {
this.taskService.addTask(title, 'medium');
this.snackBar.open('Task added', 'Dismiss', { duration: 3000 });
}
});
}
deleteTask(id: string): void {
this.taskService.removeTask(id);
this.snackBar.open('Task deleted', 'Dismiss', { duration: 3000 });
}
}
The toolbar provides the top-level header. The sidenav provides navigation. The table displays tasks with sortable columns. The paginator controls the page size. The dialog captures a new task title. The snackbar confirms the action .
// ============================================
// PART 5: THE THEME CONFIGURATION
// ============================================
// styles.scss
@use '@angular/material' as mat;
html {
color-scheme: light dark;
@include mat.theme((
color: (
primary: mat.$azure-palette,
tertiary: mat.$blue-palette,
),
typography: Roboto,
density: 0,
));
}
// The theme applies Material 3 tokens globally.
// Components read from --mat-sys-* variables.
// Dark mode switches automatically via light-dark().
The theme configuration generates the Material 3 tokens. The light-dark() function handles automatic dark mode based on the operating system preference .
// ============================================
// PART 6: THE COMPONENT TEST WITH HARNESS
// ============================================
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { MatDialogHarness } from '@angular/material/dialog/testing';
import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
describe('TaskListComponent', () => {
let fixture: ComponentFixture<TaskListComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [TaskListComponent],
}).compileComponents();
fixture = TestBed.createComponent(TaskListComponent);
fixture.detectChanges();
});
it('opens the task dialog', async () => {
const loader = TestbedHarnessEnvironment.loader(fixture);
const openButton = fixture.nativeElement.querySelector('button');
openButton.click();
fixture.detectChanges();
const dialog = await loader.getHarness(MatDialogHarness);
expect(await dialog.isOpen()).toBe(true);
});
});
Angular Material provides test harnesses for its components. The MatDialogHarness is used to interact with the dialog in tests without depending on the internal DOM structure .
// ============================================
// PART 7: THE ACCESSIBILITY FEATURES
// ============================================
// The mat-dialog automatically:
// - Moves focus into the dialog on open
// - Traps Tab focus within the dialog
// - Restores focus to the trigger on close
// - Sets role="dialog" and aria-modal="true"
// - Connects the title via aria-labelledby
// The mat-table supports:
// - Keyboard navigation between cells
// - aria-sort on sorted columns
// - Screen reader announcements for sort changes
// The mat-sidenav supports:
// - aria-expanded on the toggle button
// - Focus management when opening/closing
The accessibility behaviors are built into the components. The application does not need to add ARIA attributes or focus management manually .
// ============================================
// PART 8: THE CUSTOM COMPONENT WITH CDK
// ============================================
import { OverlayModule, Overlay } from '@angular/cdk/overlay';
import { FocusMonitor } from '@angular/cdk/a11y';
@Component({
selector: 'app-custom-dropdown',
imports: [OverlayModule],
template: `
<button (click)="toggle()" cdkOverlayOrigin #trigger="cdkOverlayOrigin">
{{ selected() }}
</button>
<ng-template
cdkConnectedOverlay
[cdkConnectedOverlayOrigin]="trigger"
[cdkConnectedOverlayOpen]="isOpen()"
(detach)="close()"
>
<div class="dropdown-panel" role="listbox">
@for (option of options; track option) {
<div role="option" (click)="select(option)">{{ option }}</div>
}
</div>
</ng-template>
`,
})
export class CustomDropdownComponent {
private overlay = inject(Overlay);
private focusMonitor = inject(FocusMonitor);
options = ['Option 1', 'Option 2', 'Option 3'];
isOpen = signal(false);
selected = signal('Select...');
toggle() { this.isOpen.update((v) => !v); }
close() { this.isOpen.set(false); }
select(option: string) {
this.selected.set(option);
this.close();
}
}
When Angular Material does not have the component you need, the CDK provides the primitives. The cdkConnectedOverlay directive positions the dropdown relative to the trigger. The FocusMonitor can detect how the trigger received focus .
// ============================================
// PART 9: THE BUNDLE OPTIMIZATION
// ============================================
// Import only the modules the application uses.
// Bad: imports the entire library
// import { MatButtonModule } from '@angular/material';
// Good: imports only the button module
import { MatButtonModule } from '@angular/material/button';
// The build only includes the code for the imported modules.
// Unused components are tree-shaken out of the bundle.
Importing individual modules keeps the bundle small. Each component has its own entry point under @angular/material/ .
// ============================================
// PART 10: THE DECISION MATRIX
// ============================================
// Use Angular Material when:
// - The component exists in the library
// - The Material Design look is acceptable
// - Accessibility and form integration matter
// - The application wants the Angular team to maintain the UI
// Use the CDK when:
// - The component does not exist in Material
// - The visual design must be custom
// - The behavior is needed without the styling
// - A third-party library is not an option
// Use a custom component when:
// - The interaction pattern is application-specific
// - The CDK primitives are insufficient
// - The component is simple enough to build directly
The decision is about reuse. Angular Material provides the most reuse with the least customization. The CDK provides the behavior without the styling. A custom component provides the most customization with the most code to maintain.
Quick Reference
The Component Categories
| Category | Components |
|---|---|
| Form controls | MatFormField, MatInput, MatSelect, MatCheckbox, MatRadioButton, MatSlideToggle, MatDatepicker, MatTimepicker |
| Navigation | MatToolbar, MatSidenav, MatDrawer, MatTabs, MatMenu, MatStepper |
| Layout | MatCard, MatList, MatGridList, MatExpansionPanel, MatDivider |
| Data display | MatTable, MatPaginator, MatSort, MatChips, MatTree |
| Feedback | MatDialog, MatSnackBar, MatProgressBar, MatProgressSpinner, MatTooltip |
| Buttons | MatButton, MatButtonToggle, MatBadge, MatIcon |
The CDK Primitives
| Primitive | Purpose | Used By |
|---|---|---|
| Overlay | Position floating content | Select, Menu, Dialog, Tooltip |
| A11y | Focus management, announcements | Dialog, Menu, List |
| DragDrop | Draggable lists and grids | Custom lists, kanban boards |
| Portal | Render content in different DOM location | Dialog, Menu, Tooltip |
| Scrolling | Virtual scrolling for large lists | Custom tables and lists |
The Theming System
| Feature | Description |
|---|---|
| Design tokens | CSS variables prefixed with --mat-sys- |
mat.theme | Generates the token set from a configuration |
light-dark() | Native CSS for automatic dark mode |
| Override mixins | Per-component token overrides |
| Color palettes | mat.$azure-palette, mat.$blue-palette, etc. |
The Module Imports
| Import Path | Provides |
|---|---|
@angular/material/toolbar | MatToolbarModule |
@angular/material/sidenav | MatSidenavModule |
@angular/material/table | MatTableModule |
@angular/material/dialog | MatDialogModule |
@angular/material/form-field | MatFormFieldModule |
@angular/material/button | MatButtonModule |
Best Practices
✅ Do This:
// Import only the modules used
import { MatButtonModule } from '@angular/material/button'; // ✅
// Use the M3 theming API
@include mat.theme((color: (primary: mat.$azure-palette))); // ✅
// Use test harnesses for Material components
const dialog = await loader.getHarness(MatDialogHarness); // ✅
// Use the CDK when Material does not have the component
import { OverlayModule } from '@angular/cdk/overlay'; // ✅
❌ Don’t Do This:
// Don't import the entire library
import { MatButtonModule } from '@angular/material'; // ❌
// Don't override component styles with deep selectors
::ng-deep .mat-mdc-button { background: red; } // ❌
// Don't use third-party timepicker in v19
// Use mat-timepicker instead. // ❌
// Don't skip the theme configuration
// Components render unstyled without it. // ❌
Common Pitfalls
| Pitfall | Why It Happens | Fix |
|---|---|---|
| Components unstyled | Theme not configured | Add mat.theme to styles.scss |
| Bundle too large | Importing entire library | Import individual modules |
| Dialog focus not trapped | Using a custom dialog | Use MatDialog |
| Table not sortable | MatSort not connected | Connect sort to data source |
| Timepicker missing | Using third-party library | Use mat-timepicker (v19+) |
| Dark mode not working | color-scheme not set | Add color-scheme: light dark |
Real-World Examples
1. Toolbar
<mat-toolbar color="primary">Title</mat-toolbar>
2. Sidenav
<mat-sidenav-container>
<mat-sidenav mode="side" opened>Nav</mat-sidenav>
<mat-sidenav-content>Content</mat-sidenav-content>
</mat-sidenav-container>
3. Form Field
<mat-form-field>
<mat-label>Name</mat-label>
<input matInput />
</mat-form-field>
4. Table
<table mat-table [dataSource]="dataSource" matSort>...</table>
5. Dialog
this.dialog.open(MyDialogComponent, { width: '400px' });
6. Snackbar
this.snackBar.open('Saved', 'Dismiss', { duration: 3000 });
7. Button
<button mat-raised-button color="primary">Save</button>
8. Icon
<mat-icon>menu</mat-icon>
9. Theme
@include mat.theme((color: (primary: mat.$azure-palette)));
10. CDK Overlay
<ng-template cdkConnectedOverlay [cdkConnectedOverlayOrigin]="trigger">...</ng-template>
Visual
The Material and CDK Layers
┌──────────────────────────────────────────────┐
│ ANGULAR MATERIAL │
│ Visual design (M3 tokens, styles) │
│ ├─ MatButton, MatDialog, MatTable │
│ └─ Built on top of the CDK │
├──────────────────────────────────────────────┤
│ COMPONENT DEV KIT (CDK) │
│ Behavior without visual design │
│ ├─ Overlay (positioning) │
│ ├─ A11y (focus, announcements) │
│ ├─ DragDrop (draggable lists) │
│ ├─ Portal (DOM placement) │
│ └─ Scrolling (virtual scroll) │
├──────────────────────────────────────────────┤
│ ANGULAR FRAMEWORK │
│ Components, DI, forms, router │
└──────────────────────────────────────────────┘
The Component Catalog
┌──────────────────────────────────────────────┐
│ FORM CONTROLS │
│ FormField, Input, Select, Checkbox, │
│ RadioButton, SlideToggle, Datepicker, │
│ Timepicker │
│ │
│ NAVIGATION │
│ Toolbar, Sidenav, Drawer, Tabs, Menu, │
│ Stepper │
│ │
│ LAYOUT │
│ Card, List, GridList, ExpansionPanel, │
│ Divider │
│ │
│ DATA DISPLAY │
│ Table, Paginator, Sort, Chips, Tree │
│ │
│ FEEDBACK │
│ Dialog, SnackBar, ProgressBar, │
│ ProgressSpinner, Tooltip │
│ │
│ BUTTONS │
│ Button, ButtonToggle, Badge, Icon │
│ │
└──────────────────────────────────────────────┘
The Theming Flow
┌──────────────────────────────────────────────┐
│ M3 THEMING │
│ │
│ mat.theme(( │
│ color: (primary, tertiary), │
│ typography: Roboto, │
│ density: 0 │
│ )) │
│ │ │
│ ▼ │
│ Generates --mat-sys-* CSS variables │
│ │ │
│ ▼ │
│ Components read tokens from variables │
│ │ │
│ ▼ │
│ light-dark() switches automatically │
│ │
└──────────────────────────────────────────────┘
The Decision Matrix
┌──────────────────────────────────────────────┐
│ WHEN TO USE WHAT │
│ │
│ Angular Material: │
│ ├─ Component exists in the library │
│ ├─ Material Design look is acceptable │
│ └─ Accessibility is required │
│ │
│ CDK: │
│ ├─ Component does not exist in Material │
│ ├─ Custom visual design needed │
│ └─ Behavior without styling │
│ │
│ Custom component: │
│ ├─ Application-specific interaction │
│ └─ CDK primitives insufficient │
│ │
└──────────────────────────────────────────────┘
Summary
| Item | Value |
|---|---|
| Library | @angular/material |
| Foundation | Component Dev Kit (@angular/cdk) |
| Design system | Material Design 3 (M3) |
| Theming | CSS variables (--mat-sys-*), mat.theme |
| Dark mode | light-dark() function, color-scheme |
| New in v19 | Timepicker, 2D drag-and-drop, tab reordering |
| Import pattern | Individual modules per component |
| Test harnesses | Provided for all components |
| Accessibility | Built-in (ARIA, focus management) |
| Form integration | Reactive Forms, ngModel, formControl |
Key takeaways:
- Angular Material is the official component library for Angular. It implements Material Design 3 and integrates deeply with the framework’s forms, change detection, and dependency injection. The components are built and maintained by the Angular team .
- The CDK is the foundation. Angular Material components are thin layers of Material styling on top of CDK primitives. The CDK provides overlay positioning, focus management, drag-and-drop, portals, and virtual scrolling. When Material does not have the component you need, the CDK has the primitives to build it .
- The component catalog covers form controls, navigation, layout, data display, feedback, and buttons. Each component is imported individually to keep the bundle small. The
mat-timepickeris new in v19 and replaces third-party time selection libraries . - Material 3 theming is based on design tokens. The
mat.themeAPI generates CSS variables that define every color, typography size, and shape. Thelight-dark()function handles automatic dark mode switching. Component override mixins allow granular customization . - Accessibility is built into the components.
MatDialogtraps focus and restores it on close.MatMenusupports keyboard navigation.MatTableannounces sort changes. The application does not need to add ARIA attributes or focus management manually . - Test harnesses make Material components testable. The
@angular/cdk/testingpackage provides harnesses for every Material component. The harnesses interact with the components through their public API, not their internal DOM structure . - The decision is about reuse. Use Angular Material when the component exists and the Material Design look is acceptable. Use the CDK when the component does not exist or the design must be custom. Build a custom component only when the CDK primitives are insufficient.
Remember: Angular Material is a design system implementation, not just a component library. It provides the visual design on top of the CDK’s behavior. The components are accessible, testable, and integrated with Angular’s forms and change detection. The theming system is based on design tokens, and the M3 API generates them from a configuration. Use the components when they fit. Use the CDK when they do not. The library is maintained by the Angular team, so the components stay current with the framework.
Stop using slow, ad-bloated tool sites! 🤮
🔎 Search “KandZ Tools” on Google to use many professional utilities for free.
KandZ.me is the ultimate minimalist hub for:
✅ Finance (Mortgage, Interest, Inflation)
✅ Tech (Base64, JSON, Dev Suite, IP)
✅ Health (BMI, BMR, TDEE)
✅ Productivity (Timer, Workspace, QR)
⚡️ Fast & Private
🔒 No data leaves your device
💎 100% Free
🔗 Use it now: https://tools.kandz.me
🔖 Bookmark it—you’ll need it later!