components Documentation

Component infrastructure and Material Design components for Angular

25,036 stars TypeScript TypeScript 1 file ยท ~29,399 tokens #angular#angular-components#material#material-design
RAW Doc

File: README.md

Official components for Angular



The Angular team builds and maintains both common UI components and tools to help you build your
own custom components. The team maintains several npm packages.

Package Description Docs
@angular/aria Collection of headless, accessible directives that implement common WAI-ARIA patterns Docs
@angular/cdk Library that helps you author custom UI components with common interaction patterns Docs
@angular/material Material Design UI components for Angular applications Docs
@angular/google-maps Angular components built on top of the Google Maps JavaScript API Docs
@angular/youtube-player Angular component built on top of the YouTube Player API Docs

Documentation, demos, and guides |
Frequently Asked Questions |
Community Google group |
Contributing |
StackBlitz Template

Getting started

See our Getting Started Guide if you're building your first project with Angular
Material.

Contributing

If you'd like to contribute, please follow our contributing guidelines. Please see
our help wanted label for a list of issues with good opportunities for
contribution. You can also use the good first issue label to find issues
if you are just starting to contribute to the project.

About the team

The Angular Components team is part of the Angular team at Google. The team includes both Google
employees and community contributors from around the globe.

Our team has two primary goals:

  • Build high-quality UI components that developers can drop into existing applications
  • Provide tools that help developers build their own custom components with common interaction
    patterns

What do we mean by "high-quality" components?

  • Internationalized and accessible so that all users can use them.
  • Straightforward APIs that don't confuse developers.
  • Behave as expected across a wide variety of use-cases without bugs.
  • Behavior is well-tested with both unit and integration tests.
  • Customizable within the bounds of the Material Design specification.
  • Performance cost is minimized.
  • Code is clean and well-documented to serve as an example for Angular developers.

Support policy

Angular Material and the CDK follow the same support and release policy as the Angular framework.
For details on supported versions and update practices, see the
Angular support policy and schedule.

Browser and screen reader support

The Angular Components team supports the most recent two versions of all major browsers:
Chrome (including Android), Firefox, Safari (including iOS), and Edge.

We aim for great user experience with the following screen readers:

  • Windows: NVDA and JAWS with FF / Chrome.
  • macOS: VoiceOver with Safari / Chrome.
  • iOS: VoiceOver with Safari
  • Android: Android Accessibility Suite (formerly TalkBack) with Chrome.
  • Chrome OS: ChromeVox with Chrome.

File: docs/README.md

Angular Components Docs Site

This is the repository for the Angular Components documentation site.

Versions of this site are also available for

Contributing

Please open bugs against the Angular Material and CDK components, directives, documentation
contents, API docs, and demos in the
Angular Components repo.

Please only open issues with the documentation site itself (not the content) in
this repo. This includes issues like the
navigation not working properly, examples or documentation not being presented in an accessible way,
issues with rendering or layout of the documentation pages, etc.

Where does the content come from?

The guides, examples, and docs content repo
angular/material2-docs-content contains the
documentation content and examples. They are generated from:

Development Server

  1. Run pnpm bazel run //docs:serve for a dev server. Navigate to http://localhost:4200/.

Build

Run pnpm bazel build //docs:build.production to build the project.

Running unit tests

  1. Run pnpm bazel test //docs/... to execute the unit tests via Karma.

Running e2e tests

  1. Run pnpm bazel test //docs/e2e:e2e_tests to execute the e2e tests via Selenium WebDriver.

Scenes Development server

  1. Run pnpm bazel run //docs/scenes:build.serve for a dev server. Navigate to http://localhost:4200/.

Build

Run pnpm bazel build //docs/scenes:build.production to build the project.

Running unit tests

  1. Run pnpm bazel test //docs/scenes/... to execute the unit tests via Karma.

Running Scenes e2e tests

  1. Run pnpm bazel test //docs/scenes/e2e:e2e_tests to execute the scenes screenshot e2e tests.

File: guides/creating-a-custom-form-field-control.md

Creating a custom form field control

It is possible to create custom form field controls that can be used inside <mat-form-field>. This
can be useful if you need to create a component that shares a lot of common behavior with a form
field, but adds some additional logic.

For example in this guide we'll learn how to create a custom input for inputting US telephone
numbers and hook it up to work with <mat-form-field>. Here is what we'll build by the end of this
guide:

In order to learn how to build custom form field controls, let's start with a simple input component
that we want to work inside the form field. For example, a phone number input that segments the
parts of the number into their own inputs. (Note: this is not intended to be a robust component,
just a starting point for us to learn.)

ts
class MyTel {
  constructor(public area: string, public exchange: string, public subscriber: string) {}
}

@Component({
  selector: 'example-tel-input',
  template: `
    
      
      &ndash;
      
      &ndash;
      
    
  `,
  styles: [`
    div {
      display: flex;
    }
    input {
      border: none;
      background: none;
      padding: 0;
      outline: none;
      font: inherit;
      text-align: center;
      color: currentColor;
    }
  `],
})
export class MyTelInput {
  parts: FormGroup;

  @Input()
  get value(): MyTel | null {
    let n = this.parts.value;
    if (n.area.length == 3 && n.exchange.length == 3 && n.subscriber.length == 4) {
      return new MyTel(n.area, n.exchange, n.subscriber);
    }
    return null;
  }
  set value(tel: MyTel | null) {
    tel = tel || new MyTel('', '', '');
    this.parts.setValue({area: tel.area, exchange: tel.exchange, subscriber: tel.subscriber});
  }

  constructor(fb: FormBuilder) {
    this.parts =  fb.group({
      'area': '',
      'exchange': '',
      'subscriber': '',
    });
  }
}

Providing our component as a MatFormFieldControl

The first step is to provide our new component as an implementation of the MatFormFieldControl
interface that the <mat-form-field> knows how to work with. To do this, we will have our class
implement MatFormFieldControl. Since this is a generic interface, we'll need to include a type
parameter indicating the type of data our control will work with, in this case MyTel. We then add
a provider to our component so that the form field will be able to inject it as a
MatFormFieldControl.

ts
@Component({
  ...
  providers: [{provide: MatFormFieldControl, useExisting: MyTelInput}],
})
export class MyTelInput implements MatFormFieldControl<MyTel> {
  ...
}

This sets up our component, so it can work with <mat-form-field>, but now we need to implement the
various methods and properties declared by the interface we just implemented. To learn more about
the MatFormFieldControl interface, see the
form field API documentation.

Implementing the methods and properties of MatFormFieldControl

`value`

This property allows someone to set or get the value of our control. Its type should be the same
type we used for the type parameter when we implemented MatFormFieldControl. Since our component
already has a value property, we don't need to do anything for this one.

`stateChanges`

Because the <mat-form-field> uses the OnPush change detection strategy, we need to let it know
when something happens in the form field control that may require the form field to run change
detection. We do this via the stateChanges property. So far the only thing the form field needs to
know about is when the value changes. We'll need to emit on the stateChanges stream when that
happens, and as we continue flushing out these properties we'll likely find more places we need to
emit. We should also make sure to complete stateChanges when our component is destroyed.

ts
stateChanges = new Subject<void>();

set value(tel: MyTel | null) {
  ...
  this.stateChanges.next();
}

ngOnDestroy() {
  this.stateChanges.complete();
}

`id`

This property should return the ID of an element in the component's template that we want the
<mat-form-field> to associate all of its labels and hints with. In this case, we'll use the host
element and just generate a unique ID for it.

ts
static nextId = 0;

@HostBinding() id = `example-tel-input-${MyTelInput.nextId++}`;

`placeholder`

This property allows us to tell the <mat-form-field> what to use as a placeholder. In this
example, we'll do the same thing as matInput and <mat-select> and allow the user to specify it
via an @Input(). Since the value of the placeholder may change over time, we need to make sure to
trigger change detection in the parent form field by emitting on the stateChanges stream when the
placeholder changes.

ts
@Input()
get placeholder() {
  return this._placeholder;
}
set placeholder(plh) {
  this._placeholder = plh;
  this.stateChanges.next();
}
private _placeholder: string;

`ngControl`

This property allows the form field control to specify the @angular/forms control that is bound
to this component. Since we haven't set up our component to act as a ControlValueAccessor, we'll
just set this to null in our component.

ts
ngControl: NgControl = null;

It is likely you will want to implement ControlValueAccessor so that your component can work with
formControl and ngModel. If you do implement ControlValueAccessor you will need to get a
reference to the NgControl associated with your control and make it publicly available.

The easy way is to add it as a public property to your constructor and let dependency injection
handle it:

ts
constructor(
  ...,
  @Optional() @Self() public ngControl: NgControl,
  ...,
) { }

Note that if your component implements ControlValueAccessor, it may already be set up to provide
NG_VALUE_ACCESSOR (in the providers part of the component's decorator, or possibly in a module
declaration). If so, you may get a cannot instantiate cyclic dependency error.

To resolve this, remove the NG_VALUE_ACCESSOR provider and instead set the value accessor directly:

ts
@Component({
  ...,
  providers: [
    ...,
    // Remove this.
    // {
    //   provide: NG_VALUE_ACCESSOR,
    //   useExisting: forwardRef(() => MatFormFieldControl),
    //   multi: true,
    // },
  ],
})
export class MyTelInput implements MatFormFieldControl<MyTel>, ControlValueAccessor {
  constructor(
    ...,
    @Optional() @Self() public ngControl: NgControl,
    ...,
  ) {

    // Replace the provider from above with this.
    if (this.ngControl != null) {
      // Setting the value accessor directly (instead of using
      // the providers) to avoid running into a circular import.
      this.ngControl.valueAccessor = this;
    }
  }
}

For additional information about ControlValueAccessor see the API docs.

`focused`

This property indicates whether the form field control should be considered to be in a
focused state. When it is in a focused state, the form field is displayed with a solid color
underline. For the purposes of our component, we want to consider it focused if any of the part
inputs are focused. We can use the focusin and focusout events to easily check this. We also
need to remember to emit on the stateChanges when the focused stated changes stream so change
detection can happen.

In addition to updating the focused state, we use the focusin and focusout methods to update the
internal touched state of our component, which we'll use to determine the error state.

ts
focused = false;

onFocusIn(event: FocusEvent) {
  if (!this.focused) {
    this.focused = true;
    this.stateChanges.next();
  }
}

onFocusOut(event: FocusEvent) {
  if (!this._elementRef.nativeElement.contains(event.relatedTarget as Element)) {
    this.touched = true;
    this.focused = false;
    this.onTouched();
    this.stateChanges.next();
  }
}

`empty`

This property indicates whether the form field control is empty. For our control, we'll consider it
empty if all the parts are empty.

ts
get empty() {
  let n = this.parts.value;
  return !n.area && !n.exchange && !n.subscriber;
}

`shouldLabelFloat`

This property is used to indicate whether the label should be in the floating position. We'll
use the same logic as matInput and float the placeholder when the input is focused or non-empty.
Since the placeholder will be overlapping our control when it's not floating, we should hide
the โ€“ characters when it's not floating.

ts
@HostBinding('class.floating')
get shouldLabelFloat() {
  return this.focused || !this.empty;
}
css
span {
  opacity: 0;
  transition: opacity 200ms;
}
:host.floating span {
  opacity: 1;
}

`required`

This property is used to indicate whether the input is required. <mat-form-field> uses this
information to add a required indicator to the placeholder. Again, we'll want to make sure we run
change detection if the required state changes.

ts
@Input()
get required() {
  return this._required;
}
set required(req: BooleanInput) {
  this._required = coerceBooleanProperty(req);
  this.stateChanges.next();
}
private _required = false;

`disabled`

This property tells the form field when it should be in the disabled state. In addition to reporting
the right state to the form field, we need to set the disabled state on the individual inputs that
make up our component.

ts
@Input()
get disabled(): boolean { return this._disabled; }
set disabled(value: BooleanInput) {
  this._disabled = coerceBooleanProperty(value);
  this._disabled ? this.parts.disable() : this.parts.enable();
  this.stateChanges.next();
}
private _disabled = false;

`errorState`

This property indicates whether the associated NgControl is in an error state. For example,
we can show an error if the input is invalid and our component has been touched.

ts
get errorState(): boolean {
  return this.parts.invalid && this.touched;
}

However, there are some error triggers that we can't subscribe to (e.g. parent form submissions),
to handle such cases we should re-evaluate errorState on every change detection cycle.

ts
/** Whether the component is in an error state. */
errorState: boolean = false;

constructor(
  ...,
  @Optional() private _parentForm: NgForm,
  @Optional() private _parentFormGroup: FormGroupDirective
) {
...
}

ngDoCheck() {
  if (this.ngControl) {
    this.updateErrorState();
  }
}

private updateErrorState() {
  const parentSubmitted = this._parentFormGroup?.submitted || this._parentForm?.submitted;
  const touchedOrParentSubmitted = this.touched || parentSubmitted;

  const newState = (this.ngControl?.invalid || this.parts.invalid) && touchedOrParentSubmitted;

  if (this.errorState !== newState) {
    this.errorState = newState;
    this.stateChanges.next(); // Notify listeners of state changes.
  }
}

Keep in mind that updateErrorState() must have minimal logic to avoid performance issues.

`controlType`

This property allows us to specify a unique string for the type of control in form field. The
<mat-form-field> will add a class based on this type that can be used to easily apply
special styles to a <mat-form-field> that contains a specific type of control. In this example
we'll use example-tel-input as our control type which will result in the form field adding the
class mat-form-field-type-example-tel-input.

ts
controlType = 'example-tel-input';

`setDescribedByIds(ids: string[])`

This method is used by the <mat-form-field> to set element ids that should be used for the
aria-describedby attribute of your control. The ids are controlled through the form field
as hints or errors are conditionally displayed and should be reflected in the control's
aria-describedby attribute for an improved accessibility experience.

The setDescribedByIds method is invoked whenever the control's state changes. Custom controls
need to implement this method and update the aria-describedby attribute based on the specified
element ids. Below is an example that shows how this can be achieved.

Note that the method by default will not respect element ids that have been set manually on the
control element through the aria-describedby attribute. To ensure that your control does not
accidentally override existing element ids specified by consumers of your control, create an
input called userAriaDescribedby like followed:

ts
@Input('aria-describedby') userAriaDescribedBy: string;

The form field will then pick up the user specified aria-describedby ids and merge
them with ids for hints or errors whenever setDescribedByIds is invoked.

ts
setDescribedByIds(ids: string[]) {
  const controlElement = this._elementRef.nativeElement
    .querySelector('.example-tel-input-container')!;
  controlElement.setAttribute('aria-describedby', ids.join(' '));
}

`onContainerClick(event: MouseEvent)`

This method will be called when the form field is clicked on. It allows your component to hook in
and handle that click however it wants. The method has one parameter, the MouseEvent for the
click. In our case we'll just focus the first if the user isn't about to click an anyways.

ts
onContainerClick(event: MouseEvent) {
  if ((event.target as Element).tagName.toLowerCase() != 'input') {
    this._elementRef.nativeElement.querySelector('input').focus();
  }
}

Improving accessibility

Our custom form field control consists of multiple inputs that describe segments of a phone
number. For accessibility purposes, we put those inputs as part of a div element with
role="group". This ensures that screen reader users can tell that all those inputs belong
together.

One significant piece of information is missing for screen reader users though. They won't be able
to tell what this input group represents. To improve this, we should add a label for the group
element using either aria-label or aria-labelledby.

It's recommended to link the group to the label that is displayed as part of the parent
<mat-form-field>. This ensures that explicitly specified labels (using <mat-label>) are
actually used for labelling the control.

In our concrete example, we add an attribute binding for aria-labelledby and bind it
to the label element id provided by the parent <mat-form-field>.

typescript
export class MyTelInput implements MatFormFieldControl<MyTel> {
  ...

  constructor(...
              @Optional() public parentFormField: MatFormField) {
html
@Component({
  selector: 'example-tel-input',
  template: `

Trying it out

Now that we've fully implemented the interface, we're ready to try our component out! All we need to
do is place it inside a <mat-form-field>

html
<mat-form-field>
  <example-tel-input></example-tel-input>
</mat-form-field>

We also get all the features that come with <mat-form-field> such as floating placeholder,
prefix, suffix, hints, and errors (if we've given the form field an NgControl and correctly report
the error state).

html
<mat-form-field>
  <example-tel-input placeholder="Phone number" required></example-tel-input>
  <mat-icon matPrefix>phone</mat-icon>
  <mat-hint>Include area code</mat-hint>
</mat-form-field>

File: guides/creating-a-custom-stepper-using-the-cdk-stepper.md

Creating a custom stepper using the CDK stepper

The CDK stepper allows you to build a custom stepper which you can completely style yourself without any specific Material Design styling.

In this guide, we'll learn how to build our own custom stepper using the CDK stepper. Here is what we'll build by the end of this guide:

Create our custom stepper component

Now we are ready to create our custom stepper component. Therefore, we need to create a new Angular component which extends CdkStepper:

custom-stepper.ts

ts
@Component({
  selector: 'app-custom-stepper',
  templateUrl: './custom-stepper.html',
  styleUrl: './custom-stepper.css',
  // This custom stepper provides itself as CdkStepper so that it can be recognized
  // by other components.
  providers: [{ provide: CdkStepper, useExisting: CustomStepper }]
})
export class CustomStepper extends CdkStepper {
  onClick(index: number): void {
    this.selectedIndex = index;
  }
}

After we've extended our component class from CdkStepper we can now access different properties from this class like linear, selectedIndex and steps which are defined in the API documentation.

This is the HTML template of our custom stepper component:

custom-stepper.html

html
<section class="container">
  <header><h2>Step {{selectedIndex + 1}}/{{steps.length}}</h2></header>

  
    
    <ng-container [ngTemplateOutlet]="selected.content"></ng-container>
  

  <footer class="step-navigation-bar">
    &larr;
    @for (step of steps; track step; let i = $index) {
      
        Step {{i + 1}}
      
    }
    &rarr;
  </footer>
</section>

In the app.css file we can now style the stepper however we want:

custom-stepper.css

css
.example-container {
  border: 1px solid black;
  padding: 10px;
  margin: 10px;
}

.example-step-navigation-bar {
  display: flex;
  justify-content: flex-start;
  margin-top: 10px;
}

.example-active {
  color: blue;
}

.example-step {
  background: transparent;
  border: 0;
  margin: 0 10px;
  padding: 10px;
  color: black;
}

.example-step.example-active {
  color: blue;
  border-bottom: 1px solid blue;
}

.example-nav-button {
  background: transparent;
  border: 0;
}

Using our new custom stepper component

Now we are ready to use our new custom stepper component and fill it with steps. Therefore, we can, for example, add it to our app.html and define some steps:

app.html

html
<app-custom-stepper>
  <cdk-step>This is any content of "Step 1"</cdk-step>
  <cdk-step>This is any content of "Step 2"</cdk-step>
</app-custom-stepper>

As you can see in this example, each step needs to be wrapped inside a <cdk-step> tag.

If you want to iterate over your steps and use your own custom component you can do it, for example, this way:

html
<app-custom-stepper>
  @for (step of mySteps; track step) {
    <cdk-step>
      <my-step-component [step]="$index"></my-step-component>
    </cdk-step>
  }
</app-custom-stepper>

Linear mode

The above example allows the user to freely navigate between all steps. The CdkStepper additionally provides the linear mode which requires the user to complete previous steps before proceeding.

A simple example without using forms could look like this:

app.html

html
<app-custom-stepper linear>
  <cdk-step editable="false" [completed]="completed">
    
    Complete step
  </cdk-step>
  <cdk-step editable="false">
    
  </cdk-step>
</app-custom-stepper>

app.ts

ts
export class App {
  completed = false;

  completeStep(): void {
    this.completed = true;
  }
}

File: guides/getting-started.md

Getting Started with Angular Material

This guide explains how to set up your Angular project to begin using Angular Material. It includes
information on prerequisites, installing Angular Material, and optionally displaying a sample
Material component in your application to verify your setup.

This guide assumes that the Angular CLI has already been installed.

Install Angular Material

Add Angular Material to your application by running the following command:

bash
ng add @angular/material

The ng add command will install Angular Material, the Component Dev Kit (CDK), and will ask you the following questions to determine which features to include:

  1. Choose a prebuilt theme name, or "custom" for a custom theme:

    You can choose from prebuilt material design themes or set up an extensible custom theme.

  2. Set up global Angular Material typography styles:

    Whether to apply the global typography styles to your application.

The ng add command will additionally perform the following actions:

  • Add project dependencies to package.json
  • Add the Roboto font to your index.html
  • Add the Material Design icon font to your index.html
  • Add a few global CSS styles to:
    • Remove margins from body
    • Set height: 100% on html and body
    • Set Roboto as the default application font

You're done! Angular Material is now configured to be used in your application.

Display a component

Let's display a slide toggle component in your app and verify that everything works.

You need to import the MatSlideToggle component by adding it to your component's imports:

ts
import {MatSlideToggle} from '@angular/material/slide-toggle';

@Component({
  imports: [MatSlideToggle],
})
class App {}

Add the <mat-slide-toggle> tag to the app.html like so:

html
<mat-slide-toggle>Toggle me!</mat-slide-toggle>

Run your local dev server:

bash
ng serve

Then point your browser to http://localhost:4200

You should see the Material slide toggle component on the page.

In addition to the installation schematic, Angular Material comes with
several other schematics (like nav, table,
address-form, etc.) that can be used to easily generate pre-built components in your application.


File: guides/material-2.md

Theming Angular Material 2

What is theming?

Angular Material's theming system lets you customize base, color, typography, and density styles for
components in your application. The theming system is based on Google's
Material Design 2 specification.

This guide refers to Material 2, the previous version of Material. For the
latest Material 3 documentation, see the theming guide.

For information on how to update, see the section
on how to migrate an app from Material 2 to Material 3.

This document describes the concepts and APIs for customizing colors. For typography customization,
see Angular Material Typography. For guidance on building components to be
customizable with this system, see Theming your own components.

Sass

Angular Material's theming APIs are built with Sass. This document assumes
familiarity with CSS and Sass basics, including variables, functions, and mixins.

You can use Angular Material without Sass by using a pre-built theme, described in
Using a pre-built theme below. However, using the library's Sass API
directly gives you the most control over the styles in your application.

Palettes

A palette is a collection of colors representing a portion of color space. Each value in this
collection is called a hue. In Material Design, each hues in a palette has an identifier number.
These identifier numbers include 50, and then each 100 value between 100 and 900. The numbers order
hues within a palette from lightest to darkest.

Angular Material represents a palette as a Sass map. This map contains the
palette's hues and another nested map of contrast colors for each of the hues. The contrast colors
serve as text color when using a hue as a background color. The example below demonstrates the
structure of a palette. See the Material Design color system for more background.

scss
$m2-indigo-palette: (
 50: #e8eaf6,
 100: #c5cae9,
 200: #9fa8da,
 300: #7986cb,
 // ... continues to 900
 contrast: (
   50: rgba(black, 0.87),
   100: rgba(black, 0.87),
   200: rgba(black, 0.87),
   300: white,
   // ... continues to 900
 )
);

Create your own palette

You can create your own palette by defining a Sass map that matches the structure described in the
Palettes section above. The map must define hues for 50 and each hundred between 100
and 900. The map must also define a contrast map with contrast colors for each hue.

You can use the Material Design palette tool to help choose the hues in your
palette.

Predefined palettes

Angular Material offers predefined palettes based on the 2014 version of the Material Design
spec. See the Material Design 2014 color palettes for a full list.

In addition to hues numbered from zero to 900, the 2014 Material Design palettes each include
distinct accent hues numbered as A100, A200, A400, and A700. Angular Material does not
require these hues, but you can use these hues when defining a theme as described in
Defining a theme below.

scss
@use '@angular/material' as mat;

$my-palette: mat.$m2-indigo-palette;

Themes

A theme is a collection of color, typography, and density options. Each theme includes three
palettes that determine component colors:

  • A primary palette for the color that appears most frequently throughout your application
  • An accent, or secondary, palette used to selectively highlight key parts of your UI
  • A warn, or error, palette used for warnings and error states

You can include the CSS styles for a theme in your application in one of two ways: by defining a
custom theme with Sass, or by importing a pre-built theme CSS file.

Custom themes with Sass

A theme file is a Sass file that calls Angular Material Sass mixins to output color,
typography, and density CSS styles.

Defining a theme

Angular Material represents a theme as a Sass map that contains your color, typography, and density
choices, as well as some base design system settings. See
Angular Material Typography for an in-depth guide to customizing typography. See
Customizing density below for details on adjusting component density.

Constructing the theme first requires defining your primary and accent palettes, with an optional
warn palette. The m2-define-palette Sass function accepts a color palette, described in the
Palettes section above, as well as four optional hue numbers. These four hues
represent, in order: the "default" hue, a "lighter" hue, a "darker" hue, and a "text" hue.
Components use these hues to choose the most appropriate color for different parts of
themselves.

scss
@use '@angular/material' as mat;

$my-primary: mat.m2-define-palette(mat.$m2-indigo-palette, 500);
$my-accent: mat.m2-define-palette(mat.$m2-pink-palette, A200, A100, A400);

// The "warn" palette is optional and defaults to red if not specified.
$my-warn: mat.m2-define-palette(mat.$m2-red-palette);

You can construct a theme by calling either m2-define-light-theme or m2-define-dark-theme with
the result from m2-define-palette. The choice of a light versus a dark theme determines the
background and foreground colors used throughout the components.

scss
@use '@angular/material' as mat;

$my-primary: mat.m2-define-palette(mat.$m2-indigo-palette, 500);
$my-accent: mat.m2-define-palette(mat.$m2-pink-palette, A200, A100, A400);

// The "warn" palette is optional and defaults to red if not specified.
$my-warn: mat.m2-define-palette(mat.$m2-red-palette);

$my-theme: mat.m2-define-light-theme((
 color: (
   primary: $my-primary,
   accent: $my-accent,
   warn: $my-warn,
 ),
 typography: mat.m2-define-typography-config(),
 density: 0,
));

Applying a theme to components

The core-theme Sass mixin emits prerequisite styles for common features used by multiple
components, such as ripples. This mixin must be included once per theme.

Each Angular Material component has a mixin for each theming dimension: base,
color, typography, and density. For example, MatButton declares button-base, button-color,
button-typography, and button-density. Each mixin emits only the styles corresponding to that
dimension of customization.

Additionally, each component has a "theme" mixin that emits all styles that depend on the theme
config. This theme mixin only emits color, typography, or density styles if you provided a
corresponding configuration to m2-define-light-theme or m2-define-dark-theme, and it always emits the
base styles.

Apply the styles for each of the components used in your application by including each of their
theme Sass mixins.

scss
@use '@angular/material' as mat;

$my-primary: mat.m2-define-palette(mat.$m2-indigo-palette, 500);
$my-accent: mat.m2-define-palette(mat.$m2-pink-palette, A200, A100, A400);

$my-theme: mat.m2-define-light-theme((
 color: (
   primary: $my-primary,
   accent: $my-accent,
 ),
 typography: mat.m2-define-typography-config(),
 density: 0,
));

// Emit theme-dependent styles for common features used across multiple components.
@include mat.core-theme($my-theme);

// Emit styles for MatButton based on `$my-theme`. Because the configuration
// passed to `m2-define-light-theme` omits typography, `button-theme` will not
// emit any typography styles.
@include mat.button-theme($my-theme);

// Include the theme mixins for other components you use here.

As an alternative to listing every component that your application uses, Angular Material offers
Sass mixins that includes styles for all components in the library: all-component-bases,
all-component-colors, all-component-typographies, all-component-densities, and
all-component-themes. These mixins behave the same as individual component mixins, except they
emit styles for core-theme and all 35+ components in Angular Material. Unless your application
uses every single component, this will produce unnecessary CSS.

scss
@use '@angular/material' as mat;

$my-primary: mat.m2-define-palette(mat.$m2-indigo-palette, 500);
$my-accent: mat.m2-define-palette(mat.$m2-pink-palette, A200, A100, A400);

$my-theme: mat.m2-define-light-theme((
 color: (
   primary: $my-primary,
   accent: $my-accent,
 ),
 typography: mat.m2-define-typography-config(),
 density: 0,
));

@include mat.all-component-themes($my-theme);

To include the emitted styles in your application, add your theme file to the styles array of
your project's angular.json file
.

Theming dimensions

Angular Material themes are divided along four dimensions: base, color, typography, and density.

Base

Common base styles for the design system. These styles don't change based on your configured
colors, typography, or density, so they only need to be included once per application. These
mixins include structural styles such as border-radius, border-width, etc. All components have a base
mixin that can be used to include its base styles. (For example,
@include mat.checkbox-base($theme))

Color

Styles related to the colors in your application. These style should be included at least once in
your application. Depending on your needs, you may need to include these styles multiple times
with different configurations. (For example, if your app supports light and dark theme colors.)
All components have a color mixin that can be used to include its color styles. (For example,
@include mat.checkbox-color($theme))

Typography

Styles related to the fonts used in your application, including the font family, size, weight,
line-height, and letter-spacing. These style should be included at least once in your application.
Depending on your needs, you may need to include these styles multiple times with different
configurations. (For example, if your app supports reading content in either a serif or sans-serif
font.) All components have a typography mixin that can be used to include its typography
styles. (For example, @include mat.checkbox-typography($theme))

Density

Styles related to the size and spacing of elements in your application. These style should be
included at least once in your application. Depending on your needs, you may need to include these
styles multiple times with different configurations. (For example, if your app supports both a
normal and compact mode). All components have a density mixin that can be used to include its
density styles. (For example, @include mat.checkbox-density($theme))

Theme mixin

All components also support a theme mixin that can be used to include the component's styles for all
theme dimensions at once. (For example, @include mat.checkbox-theme($theme)).

The recommended approach is to rely on the theme mixins to lay down your base styles, and if
needed use the single dimension mixins to override particular aspects for parts of your app (see the
section on Multiple themes in one file.)

Using a pre-built theme

Angular Material includes four pre-built theme CSS files, each with different palettes selected.
You can use one of these pre-built themes if you don't want to define a custom theme with Sass.

Theme Light or dark? Palettes (primary, accent, warn)
deeppurple-amber.css Light deep-purple, amber, red
indigo-pink.css Light indigo, pink, red
pink-bluegrey.css Dark pink, blue-grey, red
purple-green.css Dark purple, green, red

These files include the CSS for every component in the library. To include only the CSS for a subset
of components, you must use the Sass API detailed in Defining a theme above.
You can reference the source code for these pre-built themes to see examples of complete
theme definitions.

You can find the pre-built theme files in the "prebuilt-themes" directory of Angular Material's
npm package (@angular/material/prebuilt-themes). To include the pre-built theme in your
application, add your chosen CSS file to the styles array of your project's angular.json
file
.

Defining multiple themes

Using the Sass API described in Defining a theme, you can also define
multiple themes by repeating the API calls multiple times. You can do this either in the same
theme file or in separate theme files.

Multiple themes in one file

Defining multiple themes in a single file allows you to support multiple themes without having to
manage loading of multiple CSS assets. The downside, however, is that your CSS will include more
styles than necessary.

To control which theme applies when, @include the mixins only within a context specified via
CSS rule declaration. See the documentation for Sass mixins for further background.

scss
@use '@angular/material' as mat;

// Define a dark theme
$dark-theme: mat.m2-define-dark-theme((
 color: (
   primary: mat.m2-define-palette(mat.$m2-pink-palette),
   accent: mat.m2-define-palette(mat.$m2-blue-grey-palette),
 ),
  // Only include `typography` and `density` in the default dark theme.
  typography: mat.m2-define-typography-config(),
  density: 0,
));

// Define a light theme
$light-theme: mat.m2-define-light-theme((
 color: (
   primary: mat.m2-define-palette(mat.$m2-indigo-palette),
   accent: mat.m2-define-palette(mat.$m2-pink-palette),
 ),
));

// Apply the dark theme by default
@include mat.core-theme($dark-theme);
@include mat.button-theme($dark-theme);

// Apply the light theme only when the user prefers light themes.
@media (prefers-color-scheme: light) {
 // Use the `-color` mixins to only apply color styles without reapplying the same
 // typography and density styles.
 @include mat.core-color($light-theme);
 @include mat.button-color($light-theme);
}

Multiple themes across separate files

You can define multiple themes in separate files by creating multiple theme files per
Defining a theme, adding each of the files to the styles of your
angular.json. However, you must additionally set the inject option for each of these files to
false in order to prevent all the theme files from being loaded at the same time. When setting
this property to false, your application becomes responsible for manually loading the desired
file. The approach for this loading depends on your application.

Application background color

By default, Angular Material does not apply any styles to your DOM outside
its own components. If you want to set your application's background color
to match the components' theme, you can either:

  1. Put your application's main content inside mat-sidenav-container, assuming you're using
    MatSidenav, or
  2. Apply the mat-app-background CSS class to your main content root element (typically body).

Scoping style customizations

You can use Angular Material's Sass mixins to customize component styles within a specific scope
in your application. The CSS rule declaration in which you include a Sass mixin determines its scope.
The example below shows how to customize the color of all buttons inside elements marked with the
.my-special-section CSS class.

scss
@use '@angular/material' as mat;

.my-special-section {
 $special-primary: mat.m2-define-palette(mat.$m2-orange-palette);
 $special-accent: mat.m2-define-palette(mat.$m2-brown-palette);
 $special-theme: mat.m2-define-dark-theme((
   color: (primary: $special-primary, accent: $special-accent),
 ));

 @include mat.button-color($special-theme);
}

Customizing density

Angular Material's density customization is based on the
Material Design density guidelines. This system defines a scale where zero
represents the default density. You can decrement the number for more density and increment the
number for less density.

The density system is based on a density scale. The scale starts with the
default density of 0. Each whole number step down (-1, -2, etc.) reduces
the affected sizes by 4px, down to the minimum size necessary for a component to render
coherently.

Components that appear in task-based or pop-up contexts, such as MatDatepicker, don't change their
size via the density system. The Material Design density guidance explicitly
discourages increasing density for such interactions because they don't compete for space in the
application's layout.

You can apply custom density setting to the entire library or to individual components using their
density Sass mixins.

scss
// You can set a density setting in your theme to apply to all components.
$dark-theme: mat.m2-define-dark-theme((
  color: ...,
  typography: ...,
  density: -2,
));

// Or you can selectively apply the Sass mixin to affect only specific parts of your application.
.the-dense-zone {
  @include mat.button-density(-1);
}

Strong focus indicators

By default, most components indicate browser focus by changing their background color as described
by the Material Design specification. This behavior, however, can fall short of accessibility
requirements, such as WCAG, which require a stronger indication of browser focus.

Angular Material supports rendering highly visible outlines on focused elements. Applications can
enable these strong focus indicators via two Sass mixins:
strong-focus-indicators and strong-focus-indicators-theme.

The strong-focus-indicators mixin emits structural indicator styles for all components. This mixin
should be included exactly once in an application, similar to the core mixin described above.

The strong-focus-indicators-theme mixin emits only the indicator's color styles. This mixin should
be included once per theme, similar to the theme mixins described above. Additionally, you can use
this mixin to change the color of the focus indicators in situations in which the default color
would not contrast sufficiently with the background color.

The following example includes strong focus indicator styles in an application alongside the rest of
the custom theme API.

scss
@use '@angular/material' as mat;

@include mat.strong-focus-indicators();

$my-primary: mat.m2-define-palette(mat.$m2-indigo-palette, 500);
$my-accent: mat.m2-define-palette(mat.$m2-pink-palette, A200, A100, A400);

$my-theme: mat.m2-define-light-theme((
 color: (
   primary: $my-primary,
   accent: $my-accent,
 )
));

@include mat.all-component-themes($my-theme);
@include mat.strong-focus-indicators-theme($my-theme);

Customizing strong focus indicators

You can pass a configuration map to strong-focus-indicators to customize the appearance of the
indicators. This configuration includes border-style, border-width, and border-radius.

You also can customize the color of indicators with strong-focus-indicators-theme. This mixin
accepts either a theme, as described earlier in this guide, or a CSS color value. When providing a
theme, the indicators will use the default hue of the primary palette.

The following example includes strong focus indicator styles with custom settings alongside the rest
of the custom theme API.

scss
@use '@angular/material' as mat;

@include mat.strong-focus-indicators((
  border-style: dotted,
  border-width: 4px,
  border-radius: 2px,
));

$my-primary: mat.m2-define-palette(mat.$m2-indigo-palette, 500);
$my-accent: mat.m2-define-palette(mat.$m2-pink-palette, A200, A100, A400);

$my-theme: mat.m2-define-light-theme((
 color: (
   primary: $my-primary,
   accent: $my-accent,
 )
));

@include mat.all-component-themes($my-theme);
@include mat.strong-focus-indicators-theme(purple);

Theming and style encapsulation

Angular Material assumes that, by default, all theme styles are loaded as global CSS. If you want
to use Shadow DOM in your application, you must load the theme styles within each
shadow root that contains an Angular Material component. You can accomplish this by manually loading
the CSS in each shadow root, or by using Constructable Stylesheets.

User preference media queries

Angular Material does not apply styles based on user preference media queries, such as
prefers-color-scheme or prefers-contrast. Instead, Angular Material's Sass mixins give you the
flexibility to apply theme styles to based on the conditions that make the most sense for your
users. This may mean using media queries directly or reading a saved user preference.

Style customization outside the theming system

Angular Material supports customizing color, typography, and density as outlined in this document.
Angular strongly discourages, and does not directly support, overriding component CSS outside the
theming APIs described above. Component DOM structure and CSS classes are considered private
implementation details that may change at any time.

Theming your components

Reading values from a theme

As described in this guide, a theme is a Sass map that contains style values to
customize components. Angular Material provides APIs for reading values from this data structure.

Reading color values

To read color values from a theme, you can use the get-theme-color Sass function. This function
supports reading colors for both the app color palettes (primary, accent, and warn), as well as the
foreground and background palettes. get-theme-color takes three arguments: The theme to read from,
the name of the palette, and the name of the color.

Each of the color palettes (primary, accent, and warn) supports reading the following named colors:

Color Name Description
default The default color from this palette
lighter A lighter version of the color for this palette
darker A darker version of the color for this palette
text The text color for this palette
default-contrast A color that stands out against the this palette's default color
lighter-contrast A color that stands out against the this palette's lighter color
darker-contrast A color that stands out against the this palette's darker color
[hue] The [hue] color for the palette.[hue] can be one of: 50, 100, 200, 300, 400, 500, 600, 700, 800, 900, A100, A200, A400, A700
[hue]-contrast A color that stands out against the [hue] color for the palette.[hue] can be one of: 50, 100, 200, 300, 400, 500, 600, 700, 800, 900, A100, A200, A400, A700

The background palette supports reading the following named colors:

Color Name Description
status-bar The background color for a status bar
app-bar The background color for an app bar
background The background color for the app
hover The background color of a hover overlay
card The background color of a card
dialog The background color of a dialog
raised-button The background color of a raised button
selected-button The background color of a selected button
selected-disabled-button The background color of a selected disabled button
disabled-button The background color of a disabled button
focused-button The background color of a focused button
disabled-button-toggle The background color of a disabled button toggle
unselected-chip The background color of an unselected chip
disabled-list-option The background color of a disabled list option
tooltip The background color of a tooltip

The foreground palette supports reading the following named colors:

Color name Description
base The base foreground color, can be used to for color mixing or creating a custom opacity foreground color
divider The color of a divider
dividers (Alternate name for divider)
disabled-text The color for disabled text
disabled (Alternate name for disabled-text)
disabled-button The color for disabled button text
elevation The color elevation shadows
hint-text The color for hint text
secondary-text The color for secondary text
icon The color for icons
icons (Alternate name for icon)
text The color for text

In addition to reading particular colors, you can use the get-theme-type Sass function to
determine the type of theme (either light or dark). This function takes a single argument, the
theme, and returns either light or dark.

See the below example of reading some colors from a theme:

scss
$theme: mat.m2-define-dark-theme(...);

$primary-default: mat.get-theme-color($theme, primary, default);
$accent-a100: mat.get-theme-color($theme, accent, A100);
$warn-500-contrast: mat.get-theme-color($theme, warn, 500-contrast);
$foreground-text: mat.get-theme-color($theme, foreground, text);
$background-card: mat.get-theme-color($theme, background, card);
$type: mat.get-theme-type($theme);
$custom-background: if($type == dark, #030, #dfd);

Reading typography values

To read typography values from a theme, you can use the get-theme-typography Sass function. This
function supports reading typography properties from the typography levels defined in the theme.
There are two ways to call the function.

The first way to call it is by passing just the theme and the typography level to get a shorthand
font property based on the settings for that level. (Note: letter-spacing cannot be expressed in
the font shorthand, so it must be applied separately).

The second way to call it is by passing the theme, typography level, and the specific font property
you want: font-family, font-size, font-weight, line-height, or letter-spacing.

The available typography levels are:

Name Description
headline-1 One-off header, usually at the top of the page (e.g. a hero header).
headline-2 One-off header, usually at the top of the page (e.g. a hero header).
headline-3 One-off header, usually at the top of the page (e.g. a hero header).
headline-4 One-off header, usually at the top of the page (e.g. a hero header).
headline-5 Section heading corresponding to the <h1> tag.
headline-6 Section heading corresponding to the <h2> tag.
subtitle-1 Section heading corresponding to the <h3> tag.
subtitle-2 Section heading corresponding to the <h4> tag.
body-1 Base body text.
body-2 Secondary body text.
caption Smaller body and hint text.
button Buttons and anchors.

See the below example of reading some typography settings from a theme:

scss
$theme: mat.m2-define-dark-theme(...);

body {
  font: mat.get-theme-typography($theme, body-1);
  letter-spacing: mat.get-theme-typography($theme, body-1, letter-spacing);
}

Reading density values

To read the density scale from a theme, you can use the get-theme-density Sass function. This
function takes a theme and returns the density scale (0, -1, -2, -3, -4, or -5).

See the below example of reading the density scale from a theme:

scss
$theme: mat.m2-define-dark-theme(...);

$density-scale: mat.get-theme-density($theme);

Checking what dimensions are configured for a theme

Depending on how a theme was created, it may not have configuration data for all theming dimensions
(base, color, typography, density). You can check if a theme has a configuration for a particular
dimension by calling the theme-has Sass function, passing the theme and the dimension to check.

See the below example of checking the configured dimensions for a theme:

scss
$theme: mat.m2-define-dark-theme(...);

$has-base: mat.theme-has($theme, base);
$has-color: mat.theme-has($theme, color);
$has-typography: mat.theme-has($theme, typography);
$has-density: mat.theme-has($theme, density);

Separating theme styles

Angular Material components each have a Sass file that defines mixins for customizing
that component's color and typography. For example, MatButton has mixins for button-color and
button-typography. Each mixin emits all color and typography styles for that component,
respectively.

You can mirror this structure in your components by defining your own mixins. These mixins
should accept an Angular Material theme, from which they can read color and typography values. You
can then include these mixins in your application along with Angular Material's own mixins.

Customizing Typography

Including font assets

Angular Material's typography APIs let you specify any font-face. The default font-face value is
configured to Google's Roboto font with the 300, 400, and 500 font-weight styles. To use
Roboto, your application must load the font, which is not included with Angular Material. The
easiest way to load Roboto, or any other custom font, is by using Google Fonts. The following
snippet can be placed in your application's <head> to load Roboto from Google Fonts.

html
<link rel="preconnect" href="https://fonts.gstatic.com">
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500&display=swap" rel="stylesheet">

See Getting Started with the Google Fonts API for more about using Google Fonts. Also
note that, by default, the Angular CLI inlines assets from Google Fonts to reduce render-blocking
requests
.

Typography levels

A typography level is a collection of typographic styles that corresponds to a specific
part of an application's structure, such as a header. Each level includes styles for font family,
font weight, font size, and letter spacing. Angular Material uses the typography levels
from the 2018 version of the Material Design specification
, outlined in the
table below.

Name Description
headline-1 One-off header, usually at the top of the page (e.g. a hero header).
headline-2 One-off header, usually at the top of the page (e.g. a hero header).
headline-3 One-off header, usually at the top of the page (e.g. a hero header).
headline-4 One-off header, usually at the top of the page (e.g. a hero header).
headline-5 Section heading corresponding to the <h1> tag.
headline-6 Section heading corresponding to the <h2> tag.
subtitle-1 Section heading corresponding to the <h3> tag.
subtitle-2 Section heading corresponding to the <h4> tag.
body-1 Base body text.
body-2 Secondary body text.
caption Smaller body and hint text.
button Buttons and anchors.

Define a level

You can define a typography level with the m2-define-typography-level Sass function. This function
accepts, in order, CSS values for font-size, line-height, font-weight, font-family, and
letter-spacing. You can also specify the parameters by name, as demonstrated in the example below.

scss
@use '@angular/material' as mat;

$my-custom-level: mat.m2-define-typography-level(
  $font-family: Roboto,
  $font-weight: 400,
  $font-size: 1rem,
  $line-height: 1,
  $letter-spacing: normal,
);

Typography config

A typography config is a collection of all typography levels. Angular Material represents this
config as a Sass map. This map contains the styles for each level, keyed by name. You can create
a typography config with the m2-define-typography-config Sass function. Every parameter for
m2-define-typography-config is optional; the styles for a level will default to Material Design's
baseline if unspecified.

scss
@use '@angular/material' as mat;

$my-custom-typography-config: mat.m2-define-typography-config(
  $headline-1: mat.m2-define-typography-level(112px, 112px, 300, $letter-spacing: -0.05em),
  $headline-2: mat.m2-define-typography-level(56px, 56px, 400, $letter-spacing: -0.02em),
  $headline-3: mat.m2-define-typography-level(45px, 48px, 400, $letter-spacing: -0.005em),
  $headline-4: mat.m2-define-typography-level(34px, 40px, 400),
  $headline-5: mat.m2-define-typography-level(24px, 32px, 400),
  // ...
);

Typography configs and theming

You can provide a typography config when defining a theme to customize typographic styles. See the [theming guide][theming-system] for details on custom themes.

The following example shows a typical theme definition and a "kids theme" that only applies when
the ".kids-theme" CSS class is present. You can see the theming guide for more guidance on
defining multiple themes
.

scss
@use '@angular/material' as mat;

$my-primary: mat.m2-define-palette(mat.$indigo-palette, 500);
$my-accent: mat.m2-define-palette(mat.$pink-palette, A200, A100, A400);
$my-typography: mat.m2-define-typography-config();

$my-theme: mat.m2-define-light-theme((
 color: (
   primary: $my-primary,
   accent: $my-accent,
 ),
  typography: $my-typography,
));

@include mat.all-component-themes($my-theme);

.kids-theme {
  $kids-primary: mat.m2-define-palette(mat.$cyan-palette);
  $kids-accent: mat.m2-define-palette(mat.$yellow-palette);

  // Typography config based on the default, but using "Comic Sans" as the
  // default font family for all levels.
  $kids-typography: mat.m2-define-typography-config(
    $font-family: 'Comic Sans',
  );

  $kids-theme: mat.m2-define-light-theme((
   color: (
     primary: $kids-primary,
     accent: $kids-accent,
   ),
   typography: $kids-typography,
  ));

  @include mat.all-component-themes($kids-theme);
}

Each component also has a typography mixin that emits only the typography styles for that
component, based on a provided typography config. The following example demonstrates applying
typography styles only for the button component.

scss
@use '@angular/material' as mat;

$kids-typography: mat.m2-define-typography-config(
  // Specify "Comic Sans" as the default font family for all levels.
  $font-family: 'Comic Sans',
);

// Now we have sweet buttons with Comic Sans.
@include mat.button-typography($kids-typography);

Using typography styles in your application

In addition to styles shared between components, the typography-hierarchy mixin includes CSS
classes for styling your application. These CSS classes correspond to the typography levels in your
typography config. This mixin also emits styles for native header elements scoped within the
.mat-typography CSS class.

scss
@use '@angular/material' as mat;

// Use the default configuration.
$my-typography: mat.m2-define-typography-config();
@include mat.typography-hierarchy($my-typography);

The table below lists the CSS classes emitted and the native elements styled.

CSS class Level name Native elements
.mat-headline-1 headline-1 None
.mat-headline-2 headline-2 None
.mat-headline-3 headline-3 None
.mat-headline-4 headline-4 None
.mat-h1 or .mat-headline-5 headline-5 <h1>
.mat-h2 or .mat-headline-6 headline-6 <h2>
.mat-h3 or .mat-subtitle-1 subtitle-1 <h3>
.mat-h4 or .mat-body-1 body-1 <h4>
.mat-h5 None <h5>
.mat-h6 None <h6>
.mat-body or .mat-body-2 body-2 Body text
.mat-body-strong or .mat-subtitle-2 subtitle-2 None
.mat-small or .mat-caption caption None

In addition to the typographic styles, these style rules also include a margin-bottom for
headers and paragraphs. For body styles, text is styled within the provided CSS selector.

The .mat-h5 and .mat-h6 styles don't directly correspond to a specific Material Design
typography level. The .mat-h5 style uses the body-2 level with the font-size scaled down by
0.83. The .mat-h6 style uses the body-2 level with the font-size scaled down by 0.67.

The button and input typography levels do not map to CSS classes.

The following example demonstrates usage of the typography styles emitted by the
typography-hierarchy mixin.

html
<h1>Top header</h1>

  
  Introductory text

  
    
    <h2>Inner header</h2>

    
    Some inner text

Reading typography values from a config

It is possible to read typography properties from a theme for use in your own components. For more
information about this see our section on Theming your own components,

Step-by-step example

To illustrate participation in Angular Material's theming system, we can look at an example of a
custom carousel component. The carousel starts with a single file, carousel.scss, that contains
structural, color, and typography styles. This file is included in the styleUrls of the component.

scss
// carousel.scss

.my-carousel {
  display: flex;
  font-family: serif;
}

.my-carousel-button {
  border-radius: 50%;
  color: blue;
}

Step 1: Extract theme-based styles to a separate file

To change this file to participate in Angular Material's theming system, we split the styles into
two files, with the color and typography styles moved into mixins. By convention, the new file
name ends with -theme. Additionally, the file starts with an underscore (_), indicating that
this is a Sass partial file. See the Sass documentation for more information about
partial files.

scss
// carousel.scss

.my-carousel {
  display: flex;
}

.my-carousel-button {
  border-radius: 50%;
}
scss
// _carousel-theme.scss

@mixin color($theme) {
  .my-carousel-button {
    color: blue;
  }
}

@mixin typography($theme) {
  .my-carousel {
    font-family: serif;
  }
}

Step 2: Use values from the theme

Now that theme theme-based styles reside in mixins, we can extract the values we need from the
theme passed into the mixins.

scss
// _carousel-theme.scss

@use 'sass:map';
@use '@angular/material' as mat;

@mixin color($theme) {
  .my-carousel-button {
    // Read the 500 hue from the primary color palette.
    color: mat.get-theme-color($theme, primary, 500);
  }
}

@mixin typography($theme) {
  .my-carousel {
    // Get the headline font from the theme.
    font: mat.get-theme-typography($theme, headline-1);
  }
}

Step 3: Add a theme mixin

For convenience, we can add a theme mixin that includes both color and typography.
This theme mixin should only emit the styles for each color and typography, respectively, if they
have a config specified.

scss
// _carousel-theme.scss

@use 'sass:map';
@use '@angular/material' as mat;

@mixin color($theme) {
  .my-carousel-button {
    // Read the 500 hue from the primary color palette.
    color: mat.get-theme-color($theme, primary, 500);
  }
}

@mixin typography($theme) {
  .my-carousel {
    // Get the headline font from the theme.
    font: mat.get-theme-typography($theme, headline-1);
  }
}

@mixin theme($theme) {
  @if mat.theme-has($theme, color) {
    @include color($theme);
  }

  @if mat.theme-has($theme, typography) {
    @include typography($theme);
  }
}

Step 4: Include the theme mixin in your application

Now that you've defined the carousel component's theme mixin, you can include this mixin along with
the other theme mixins in your application.

scss
@use '@angular/material' as mat;
@use './path/to/carousel-theme' as carousel;

$my-primary: mat.m2-define-palette(mat.$m2-indigo-palette, 500);
$my-accent: mat.m2-define-palette(mat.$m2-pink-palette, A200, A100, A400);

$my-theme: mat.m2-define-light-theme((
  color: (
    primary: $my-primary,
    accent: $my-accent,
  ),
  typography: mat.m2-define-typography-config(
    $font-family: serif,
  ),
));

@include mat.all-component-themes($my-theme);
@include carousel.theme($my-theme);

How to migrate an app from Material 2 to Material 3

Angular Material does not offer an automated migration from M2 to M3 because the design of your app
is subjective. Material Design offers general principles and constraints to guide you, but
ultimately it is up to you to decide how to apply those in your app. That said, Angular Material's M3 themes were designed with maximum compatibility in mind.

Update your components that use Angular Material themes to be compatible with Material 3

In order to facilitate a smooth transition from M2 to M3, it may make sense to have your components
support both M2 and M3 themes. Once the entire app is migrated, the support for M2 themes can be
removed.

The simplest way to accomplish this is by checking the theme version and emitting different styles
for M2 vs M3. You can check the theme version using the get-theme-version function from
@angular/material. The function will return 0 for an M2 theme or 1 for an M3 theme (see
theme your own components using a Material 3 theme
for how to read values from an M3 theme).

scss
@use '@angular/material' as mat;

@mixin my-comp-theme($theme) {
  @if (mat.get-theme-version($theme) == 1) {
    // Add your new M3 styles here.
  } @else {
    // Keep your old M2 styles here.
  }
}

Pass a new M3 theme in your global theme styles

Replace your M2 theme with an M3 theme. For v19 and later, use the mat.theme
mixin as described in the theming guide:

scss
@use '@angular/material' as mat;

html {
  @include mat.theme((
    color: mat.$violet-palette,
    typography: Roboto,
    density: 0,
  ));
}

Alternatively, you can create a theme object with mat.define-theme and pass it
to individual component theme mixins. This approach is needed for backwards
compatibility helpers such as color-variants-backwards-compatibility.

Update usages of APIs that are not supported for Material 3 themes

Because Material 3 is implemented as an alternate theme for the same components used for Material 2,
the APIs for both are largely the same. However, there are a few differences to be aware of:

  • M3 expects that any @include of the -theme, -color, -typography, -density, or -base
    mixins should be wrapped in a selector. If your app includes such an @include at the root level,
    we recommend wrapping it in html { ... }
  • M3 has a different API for setting the color variant of a component (see
    using component color variants
    for more).
  • The backgroundColor property of <mat-tab-group> is not supported, and should not be used with
    M3 themes.
  • The appearance="legacy" variant of <mat-button-toggle> is not supported, and should not be
    used with M3 themes.
  • For M3 themes, calling all-component-typographies does not emit the typography-hierarchy
    styles, as this would violate M3's guarantee to not add selectors. Instead, the
    typography-hierarchy mixin must be explicitly called if you want these styles in your app.
  • The typography-hierarchy mixin outputs CSS class names that correspond to the names of M3
    typescale levels rather than M2 typescale levels. If you were relying on the M2 classes to style
    your app, you may need to update the class names.

(Optional) add backwards compatibility styles for color variants

We recommend not relying on the color="primary", color="accent", or color="warn" options
that are offered by a number of Angular Material components for M2 themes. However, if you want to
quickly update to M3 and are willing to accept the extra CSS generated for these variants, you can
enable backwards compatibility styles that restore the behavior of this API. Call the
color-variants-backwards-compatibility mixin from @angular/material with the M3 theme you want
to generate color variant styles for.

scss
@use '@angular/material' as mat;

$theme: mat.define-theme();

html {
  @include mat.all-component-themes($theme);
  @include mat.color-variants-backwards-compatibility($theme);
}

(Optional) add backwards compatibility styles for typography hierarchy

Calling the typography-hierarchy mixin with an M3 theme generates styles for CSS classes that
match the names of M3 typescale names (e.g. .mat-headline-large) rather than ones that match M2
typescale names (.mat-headline-1). If you were using the M2 class names in your app we recommend
updating all usages to one of the new class names. However, to make migration easier, the
typography-hierarchy mixin does support emitting the old class names in addition to the new ones.
We have made a best effort to map the M2 classes to reasonable equivalents in M3. To enable these
styles, pass an additional argument $back-compat: true to the mixin.

scss
@use '@angular/material' as mat;

$theme: mat.define-theme();

@include mat.typography-hierarchy($theme, $back-compat: true);

File: guides/schematics.md

Installation and Code Generation

Angular Material comes packaged with Angular CLI schematics to make
creating Material applications easier.

Install Schematics

Schematics are included with both @angular/cdk and @angular/material. Once you install the npm
packages, they will be available through the Angular CLI.

Using the command below will install Angular Material and the Component Dev Kit (CDK) in your project. Then it will run the installation schematic.

bash
ng add @angular/material

In case you just want to install the @angular/cdk, there are also schematics for the Component Dev Kit

bash
ng add @angular/cdk

The Angular Material ng add schematic helps you set up an Angular CLI project that uses Material. Running ng add will:

  • Ensure project dependencies are placed in package.json
  • Add either a prebuilt theme or a custom theme
  • Add Roboto fonts to your index.html
  • Add the Material Icon font to your index.html
  • Add global styles to
    • Remove margins from body
    • Set height: 100% on html and body
    • Make Roboto the default font of your app

Component schematics

In addition to the installation schematic, Angular Material comes with multiple schematics that can be
used to easily generate Material Design components:

Name Description
address-form Component with a form group that uses Material Design form controls to prompt for a shipping address
navigation Creates a component with a responsive Material Design sidenav and a toolbar for showing the app name
dashboard Component with multiple Material Design cards and menus which are aligned in a grid layout
table Generates a component with a Material Design data table that supports sorting and pagination
tree Component that interactively visualizes a nested folder structure by using the <mat-tree> component

Additionally, the Angular CDK also comes with a collection of component schematics:

Name Description
drag-drop Component that uses the @angular/cdk/drag-drop directives for creating an interactive to-do list

Address form schematic

Running the address-form schematic generates a new Angular component that can be used to get
started with a Material Design form group consisting of:

  • Material Design form fields
  • Material Design radio controls
  • Material Design buttons
bash
ng generate @angular/material:address-form <component-name>

The navigation schematic will create a new component that includes
a toolbar with the app name, and a responsive side nav based on Material
breakpoints.

bash
ng generate @angular/material:navigation <component-name>

Table schematic

The table schematic will create a component that renders an Angular Material `` which has
been pre-configured with a datasource for sorting and pagination.

bash
ng generate @angular/material:table <component-name>

Dashboard schematic

The dashboard schematic will create a new component that contains
a dynamic grid list of Material Design cards.

bash
ng generate @angular/material:dashboard <component-name>

Tree schematic

The tree schematic can be used to quickly generate an Angular component that uses the Angular
Material <mat-tree> component to visualize a nested folder structure.

bash
ng generate @angular/material:tree <component-name>

Drag and Drop schematic

The drag-drop schematic is provided by the @angular/cdk and can be used to generate a component
that uses the CDK drag and drop directives.

bash
ng generate @angular/cdk:drag-drop <component-name>

Material 3 Theme schematic

The theme-color schematic will generate a file with Material 3 palettes from the specified colors
that can be used in a theme file. It also generates high contrast color override mixins if
specified.

bash
ng generate @angular/material:theme-color

Learn more about this schematic in its documentation.


File: guides/theming.md

Theming

Angular Material lets you customize the appearance of your components by
defining a custom theme. Angular Materialโ€™s theming system is inspired by
Googleโ€™s Material Design.

This guide describes how to set up theming for your application using
Sass APIs introduced in Angular Material v19.

If your application depends on a version before v19, or if your application's
theme is applied using a theme config created with mat.define-theme,
mat.define-light-theme, or mat.define-dark-theme,
then you can refer to the theming guides at
v18.material.angular.dev/guides.

Getting Started

Your application needs to have a Sass theme file
that includes Angular Materialโ€™s mat.theme mixin.

The mat.theme mixin takes a map that defines color, typography, and density
values and outputs a set of CSS variables that control the component appearance
and layout. These variables are based on
Design Tokens.

The color variables are defined using the CSS color function
light-dark
so that your theme can switch between light and dark mode using the CSS property
color-scheme.

The following example theme file applies a violet color palette, Roboto font,
and standard density to the applicationโ€™s Angular Material components. It
targets the html selector to ensure the CSS variables are applied across the
entire application. The color-scheme is explicitly set to light dark so that
the end user's system preferences are used to determine whether the application
appears in light or dark mode.

scss
@use '@angular/material' as mat;

html {
  color-scheme: light dark;
  @include mat.theme((
    color: mat.$violet-palette,
    typography: Roboto,
    density: 0
  ));
}

You can use the following styles to apply the themeโ€™s surface background and
on-surface text colors as a default across your application:

scss
body {
  background: var(--mat-sys-surface);
  color: var(--mat-sys-on-surface);
}

You can also define a convenient set of CSS utility classes that let you apply
theme styles from your component templates.

scss
html {
  ...
  @include mat.system-classes();
}
html

The mat.theme mixin will only declare CSS variables for the categories
included in the input. For example, if typography is not defined, then
typography CSS variables will not be included in the output.

Color

The themeโ€˜s color determines the component color styles, such as the fill
color of checkboxes or ripple color of buttons. It depends on color palettes of
varying tones to build a color scheme. Check out the
Palettes
section to learn about available prebuilt palettes, or how to create custom
palettes.

You can set the color in one of two ways: as a single color palette, or as a
color map.

Single Color Palette

If you provide a single color palette, Angular Material uses its values for the
themeโ€™s primary, secondary, and tertiary colors. The CSS color values will be
defined using light-dark CSS color function. Your application styles should
define an explicit value declaration for the color-scheme CSS property.

Color Map

If you provide a color map, then the tertiary color palette can be configured
separately from the primary palette. The tertiary palette can be used to add a
distinct accent color to some components.

You can also set the theme-type to determine the color values are defined:

  • color-scheme - include both light and dark colors using the light-dark
    CSS color function
  • light - only define the light color values
  • dark - only define the dark color values

The light-dark CSS color function is
widely available for all major
browsers. However, if your application must support older browser versions or
non-major browsers, you should explicitly set the theme-type to either light
or dark.

The following example theme file applies a violet primary color and orange
tertiary color. The theme-type is set to light which means that only the light
color values will be set for the application. The typography is set to Roboto
with a standard density setting.

scss
@use '@angular/material' as mat;

html {
  @include mat.theme((
    color: (
      primary: mat.$violet-palette,
      tertiary: mat.$orange-palette,
      theme-type: light,
    ),
    typography: Roboto,
    density: 0
  ));
}

Typography

The mat.theme โ€˜s typography determines the text styles used in components,
such as the font for dialog titles or menu list items.

You can set the typography in one of two ways: as a single font family value, or
as a typography map.

Single Font Family Value

If you provide a font family, Angular Material uses it for all the text in its
components. The font weights used in components are set to 700 for bold text,
500 for medium text, and 400 for regular text.

Typography Map

If you provide a typography map, then distinct font families are set for plain
and brand text. The plain font family is typically used for most of the
applicationโ€™s text, while the brand font family is typically used for headings
and titles.

The typography map also sets specific weights for bold, medium, and regular
text.

The following example theme file applies the Roboto font family to plain text
and the Open Sans font family to brand text. It specifies that bold weight is
900, medium weight is 500, and regular weight is 300. The color scheme uses the
violet color palette with a standard density.

scss
@use '@angular/material' as mat;

html {
  @include mat.theme((
    color: mat.$violet-palette,
    typography: (
      plain-family: Roboto,
      brand-family: Open Sans,
      bold-weight: 900,
      medium-weight: 500,
      regular-weight: 300,
    ),
    density: 0,
  ));
}

Density

The mat.theme โ€˜s density value determines the spacing within components, such
as how much padding is used around a buttonโ€™s text or the height of form fields.

The density value accepts integers from 0 to -5, where 0 is the default spacing
and -5 is the most dense and compact layout. Each whole number step down (-1,
-2, etc.) reduces the affected sizes by 4px, down to the minimum size necessary
for a component to render itself coherently.

The following example theme file has a density setting of -2 which causes most
components to include less whitespace in their layout. The color scheme uses the
violet color palette and applies Roboto as the font-family.

scss
@use '@angular/material' as mat;

html {
  @include mat.theme((
    color: mat.$violet-palette,
    typography: Roboto,
    density: -2,
  ));
}

Setting the density below 0 can reduce accessibility and make navigation harder
for users of assistive technology.

Density customizations do not affect components that appear in task-based or
pop-up contexts, such as the date picker. The Material Design density guidance
explicitly discourages changes to density for such interactions because they
don't compete for space in the application's layout.

Prebuilt Themes

Angular Material includes eight prebuilt theme CSS files as an alternative to
defining a custom theme with Sass. Four of these themes use the current Material
3 design system, while the other four use the older Material 2 design system.

If you want your application to follow the modern Material 3 design language,
make sure to use one of the M3 themes. The M2 themes are provided for backwards
compatibility and will be removed in a future version.

Theme Design system Light or dark? Palettes (primary, tertiary)
azure-blue.css M3 Light azure, blue
rose-red.css M3 Light rose, red
cyan-orange.css M3 Dark cyan, orange
magenta-violet.css M3 Dark magenta, violet
deeppurple-amber.css M2 Light deep-purple, amber
indigo-pink.css M2 Light indigo, pink
pink-bluegrey.css M2 Dark pink, blue-grey
purple-green.css M2 Dark purple, green

You can find the prebuilt theme files in the prebuilt-themes directory of
Angular Materialโ€™s npm package (@angular/material/prebuilt-themes). To include
a prebuilt theme in your application, add the chosen CSS file to the styles
array of your projectโ€™s angular.json file:

json
"styles": [
  "@angular/material/prebuilt-themes/azure-blue.css"
]

You can reference the source code for these prebuilt themes to see
examples of complete theme definitions.

Color Palettes

A color palette is a set of similar colors with different hues ranging from
light to dark. The Angular Material theme uses color palettes to create a color
scheme to communicate an applicationโ€™s hierarchy, state, and brand.

Prebuilt Color Palettes

Angular Material provides twelve prebuilt color palettes that can be used for
your applicationโ€™s theme:

  • $red-palette
  • $green-palette
  • $blue-palette
  • $yellow-palette
  • $cyan-palette
  • $magenta-palette
  • $orange-palette
  • $chartreuse-palette
  • $spring-green-palette
  • $azure-palette
  • $violet-palette
  • $rose-palette

Custom Color Palettes

The Angular Material
palette generation schematic
builds custom color palettes based on a single color input for the primary
color, and optionally color inputs to further customize secondary, tertiary, and
neutral palettes:

bash
ng generate @angular/material:theme-color

Loading Fonts

You can use Google Fonts as one option to load fonts in your application. For
example, the following code in an applicationโ€™s <head> loads the font family
Roboto with the font weights 700, 500, and 400:

html
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Open+Sans:ital,wght@0,300..800;1,300..800&family=Roboto:wght@400;500;700&display=swap" rel="stylesheet">

Learn more about using fonts with
Google Fonts. By
default, projects created with the Angular CLI are
configured
to inline assets from Google Fonts to reduce render-blocking requests.

Supporting Light and Dark Mode

By default, the mat.theme mixin defines colors using the CSS color function
light-dark to make it easy for your application to switch between light and
dark mode. The light-dark function depends on the value of color-scheme
declared in your applicationโ€™s global styles. If your application does not
define a value for color-scheme, then the light colors will always be applied.

You can define color-scheme: light or color-scheme: dark to explicitly
define your applicationโ€™s mode. To set the mode depending on the userโ€™s system
preferences, use color-scheme: light dark as shown in the following example:

scss
@use '@angular/material' as mat;

html {
  color-scheme: light dark;
  @include mat.theme((
    color: mat.$violet-palette,
    typography: Roboto,
    density: 0
  ));
}

You can also use the strategy of defining color-scheme under a CSS selector so
that the mode depends on whether that class has been applied. In the following
example, the application always displays the light mode theme unless the class
โ€œdark-modeโ€ is added to the HTML body.

scss
@use '@angular/material' as mat;

html {
  color-scheme: light;
  @include mat.theme((
    color: mat.$violet-palette,
    typography: Roboto,
    density: 0
  ));
}

body.dark-mode {
  color-scheme: dark;
}

Angular Material does not automatically apply different styles or themes based
on user preference media queries, such as color-scheme, prefers-color-scheme
or prefers-contrast. Instead, Angular Material gives you the flexibility to
define your own queries to apply the styles that make sense for your users. This
may mean relying on color-scheme: light dark, defining custom media queries,
or reading a saved user preference to apply styles.

Multiple Themes

You can call the mat.theme mixin more than once to apply multiple different
color schemes in your application.

Context-specific Themes

The following example theme file customizes the theme for components in
different contexts. In this case, a cyan-based palette is applied to a container
of information about deleting data, causing buttons and other components to have
a unique and attention-grabbing style applied:

scss
@use '@angular/material' as mat;

html {
  @include mat.theme((
    color: mat.$violet-palette,
    typography: Roboto,
    density: 0,
  ));
}

.example-bright-container {
  @include mat.theme((
    color: mat.$cyan-palette,
  ));
}

Using Theme Styles

An applicationโ€™s custom components can use the CSS variables defined by
mat.theme to apply the themeโ€™s colors and typography.

The color variables are useful for emphasizing important text and actions,
providing stronger application branding, and ensuring strong contrast ratios
between surface and on-surface elements.

The typography variables are useful for creating clear information hierarchy and
text consistency through the application.

The following example styles demonstrate a component using the color and
typography variables to create an application-wide banner presenting important
information to the user:

scss
.my-component {
  background: var(--mat-sys-primary-container);
  color: var(--mat-sys-on-primary-container);
  border: 1px solid var(--mat-sys-outline-variant);
  font: var(--mat-sys-body-large);
}

Alternatively, you can use utility classes to achieve the same styles:

html

See the Theming your components guide for a
comprehensive list of these variables and classes, including examples of where they are used, and how
components can depend on them.

Customizing Tokens

Angular Material components also allow for narrowly targeted customization of
specific tokens through the overrides mixins. This enables fine-grained
adjustments to specific system-level theme CSS variables as well as individual
component tokens, such as a componentโ€™s border-color or title font size.

The overrides API validates that the customized tokens are correctly spelled
and can be used to ensure backwards compatibility if tokens are added, moved, or
renamed in future versions.

System Tokens

System-level tokens can be changed to different values through the
mat.theme-overrides mixin, which will redefine CSS variables that are used in
the application.

The following example applies a violet color palette for the application, but
alters the primary-container token to a specific shade of blue.

scss
@use '@angular/material' as mat;

html {
  color-scheme: light dark;
  @include mat.theme((
    color: mat.$violet-palette,
    typography: Roboto,
    density: 0
  ));

  .example-orange-primary-container {
    @include mat.theme-overrides((
      primary-container: #84ffff
    ));
  }
}

Alternatively, an optional override map can be provided in the mat.theme mixin
to replace values applied by the mixin:

scss
@use '@angular/material' as mat;

html {
  color-scheme: light dark;
  @include mat.theme((
    color: mat.$violet-palette,
    typography: Roboto,
    density: 0
  ), $overrides: (
    primary-container: orange,
  ));
}

Component Tokens

Each Angular Material component defines an overrides mixin that can be used to
customize tokenized styles for their color, typography, and density.

More information for each componentโ€™s override API, including their list of
available customizable tokens, can be found on their respective documentation
page under the Styling tab.

The following example demonstrates the Cardโ€™s overrides API to change the
background color to red, increase the corner border radius, and specify a larger
title font size.

scss
html {
  @include mat.card-overrides((
    elevated-container-color: red,
    elevated-container-shape: 32px,
    title-text-size: 2rem,
  ));
}

Direct Style Overrides

Angular Material supports customizing color, typography, and density as outlined
in this document. Angular strongly discourages, and does not directly support,
overriding component CSS outside the theming APIs described above. Component DOM
structure and CSS classes are considered private implementation details that may
change at any time. CSS variables used by the Angular Material components should
be defined through the overrides API instead of defined explicitly.

Strong focus indicators

By default, most components indicate browser focus by changing their background color as described
by the Material Design specification. This behavior, however, can fall short of accessibility
requirements, such as [WCAG 4.5:1][], which require a stronger indication of browser focus.

Angular Material supports rendering highly visible outlines on focused elements. Applications can
enable these strong focus indicators by calling mat.strong-focus-indicators().

scss
@use '@angular/material' as mat;

html {
  color-scheme: light dark;
  @include mat.theme((
    color: mat.$violet-palette,
    typography: Roboto,
    density: 0
  ));
  @include mat.strong-focus-indicators();
}

By default, the focus indicator uses the theme's secondary color, but you can customize this color
by calling the strong-focus-indicators-theme($color) mixin. Additionally, you can use
this mixin to change the color of the focus indicators in situations in which the default color
would not contrast sufficiently with the background color.

Customizing strong focus indicators

You can pass a configuration map to strong-focus-indicators to customize the appearance of the
indicators. This configuration includes border-color, border-style, border-width, and
border-radius.

The following example includes strong focus indicator styles with custom settings alongside the rest
of the custom theme API.

scss
@use '@angular/material' as mat;

@include mat.strong-focus-indicators((
  border-color: red,
  border-style: dotted,
  border-width: 4px,
  border-radius: 2px,
));

Shadow DOM

Angular Material assumes that, by default, all theme styles are loaded as global
CSS. If you want to use
Shadow DOM
in your application, you must load the theme styles within each shadow root that
contains an Angular Material component. You can accomplish this by manually
loading the CSS in each shadow root, or by using
Constructable Stylesheets.


File: guides/using-component-harnesses.md

Using Angular Material's component harnesses in your tests

The Angular CDK provides code for creating component test harnesses. A component harness is
a class that lets a test interact with a component via a supported API. Each harness's API
interacts with a component the same way a user would. By using the harness API, a test insulates
itself against updates to the internals of a component, such as changing its DOM structure. The
idea for component harnesses comes from the
PageObject pattern commonly used for integration
testing.

Angular Material offers test harnesses for many of its components. The Angular team strongly
encourages developers to use these harnesses for testing to avoid creating brittle tests that rely
on a component's internals.

This guide discusses the advantages of using component test harnesses and shows how to use them.

Benefits of component test harnesses

There are two primary benefits to using the Angular Material component harnesses in your tests:

  1. Harnesses make tests easier to read and understand with straightforward APIs.
  2. Harnesses make tests more robust and less likely to break when updating Angular Material.

The following sections will illustrate these benefits in more detail.

Which kinds of tests can use harnesses?

The Angular CDK's component harnesses are designed to work in multiple different test environments.
Support currently includes Angular's Testbed environment in Karma unit tests and Selenium WebDriver
end-to-end (e2e) tests. You can also support additional environments by creating custom extensions
of the CDK's HarnessEnvironment and TestElement classes.

Getting started

The foundation for all test harnesses lives in @angular/cdk/testing. Start by importing either
TestbedHarnessEnvironment or SeleniumWebDriverHarnessEnvironment based on whether you're writing a
unit test or an e2e test. From the HarnessEnvironment, you can get a HarnessLoader instance,
which you will use to load Angular Material component harnesses. For example, if we're writing unit
tests for a UserProfile component, the code might look like this:

ts
import {HarnessLoader} from '@angular/cdk/testing';
import {TestbedHarnessEnvironment} from '@angular/cdk/testing/testbed';

let loader: HarnessLoader;

describe('my-component', () => {
  beforeEach(async () => {
    TestBed.configureTestingModule({imports: [MyModule], declarations: [UserProfile]});
    fixture = TestBed.createComponent(UserProfile);
    loader = TestbedHarnessEnvironment.loader(fixture);
  });
}

This code creates a fixture for UserProfile and then creates a HarnessLoader for that fixture.
The HarnessLoader can then locate Angular Material components inside UserProfile and create
harnesses for them. Note that HarnessLoader and TestbedHarnessEnvironment are loaded from
different paths.

  • @angular/cdk/testing contains symbols that are shared regardless of the environment your tests
    are in.
  • @angular/cdk/testing/testbed contains symbols that are used only in Karma tests.
  • @angular/cdk/testing/selenium-webdriver (not shown above) contains symbols that are used only in
    Selenium WebDriver tests.

Loading an Angular Material harness

The HarnessLoader provides two methods that can be used to load harnesses, getHarness and
getAllHarnesses. The getHarness method gets a harness for the first instance
of the matching component, while getAllHarnesses gets a list of harnesses, one
for each instance of the corresponding component. For example, suppose UserProfile contains three
MatButton instances. We could load harnesses for them as follows:

ts
import {MatButtonHarness} from '@angular/material/button/testing';

...

it('should work', async () => {
  const buttons = await loader.getAllHarnesses(MatButtonHarness); // length: 3
  const firstButton = await loader.getHarness(MatButtonHarness); // === buttons[0]
});

Notice the example code uses async and await syntax. All component harness APIs are
asynchronous and return Promise objects. Because of this, the Angular team recommends using the
ES2017 async/await syntax
with your tests.

The example above retrieves all button harnesses and uses an array index to get the harness for a
specific button. However, if the number or order of buttons changes, this test will break. You can
write a less brittle test by instead asking for only a subset of harnesses inside UserProfile.

You can load harnesses for a sub-section of the DOM within UserProfile with the getChildLoader
method on HarnessLoader. For example, say that we know UserProfile has a div,
, and we want the button inside that specific . We can accomplish this
with the following code:

ts
it('should work', async () => {
  const footerLoader = await loader.getChildLoader('.footer');
  const footerButton = await footerLoader.getHarness(MatButtonHarness);
});

You can also use the static with method implemented on all Angular Material component harnesses.
This method creates a HarnessPredicate, an object that filters loaded harnesses based on the
provided constraints. The particular constraint options vary depending on the harness class, but all
harnesses support at least:

  • selector - CSS selector that the component must match (in addition to its host selector, such
    as [mat-button])
  • ancestor - CSS selector for a some ancestor element above the component in the DOM

In addition to these standard options, MatButtonHarness also supports

  • text - String text or regular expressions that matches the text content of the button

Using this method we could locate buttons as follows in our test:

ts
it('should work', async () => {
  // Harness for mat-button whose id is 'more-info'.
  const info = await loader.getHarness(MatButtonHarness.with({selector: '#more-info'}));
  // Harness for mat-button whose text is 'Cancel'.
  const cancel = await loader.getHarness(MatButtonHarness.with({text: 'Cancel'}));
  // Harness for mat-button with class 'confirm' and whose text is either 'Ok' or 'Okay'.
  const okButton = await loader.getHarness(
      MatButtonHarness.with({selector: '.confirm', text: /^(Ok|Okay)$/}));
});

Using a harness to interact with an Angular Material component

The Angular Material component harnesses generally expose methods to either perform actions that a
real user could perform or to inspect component state that a real user might perceive. For
example, MatButtonHarness has methods to click, focus, and blur the mat-button, as well as
methods to get the text of the button and its disabled state. Because MatButton is a very simple
component, these harness methods might not seem very different from working directly with the DOM.
However, more complex harnesses like MatSelectHarness have methods like open and isOpen which
capture more knowledge about the component's internals.

A test using the MatButtonHarness to interact with a mat-button might look like the following:

ts
it('should mark confirmed when ok button clicked', async () => {
  const okButton = await loader.getHarness(MatButtonHarness.with({selector: '.confirm'});
  expect(fixture.componentInstance.confirmed).toBe(false);
  expect(await okButton.isDisabled()).toBe(false);
  await okButton.click();
  expect(fixture.componentInstance.confirmed).toBe(true);
});

Note that the code above does not call fixture.detectChanges(), something you commonly see in
unit tests. The CDK's component harnesses automatically invoke change detection after performing
actions and before reading state. The harness also automatically waits for the fixture to be stable,
which will cause the test to wait for setTimeout, Promise, etc.

Comparison with and without component harnesses

Consider an `` component that you want to test. It allows a user to
choose an issue type and display the necessary form create report for that issue type. You need a
test to verify that when the user chooses an issue type the proper report displays. First consider
what the test might look like without using component harnesses:

ts
describe('issue-report-selector', () => {
  let fixture: ComponentFixture;

  beforeEach(async () => {
    TestBed.configureTestingModule({
      imports: [IssueReportSelectorModule],
      declarations: [IssueReportSelector],
    });

    fixture = TestBed.createComponent(IssueReportSelector);
    fixture.detectChanges();
  });

  it('should switch to bug report template', async () => {
    expect(fixture.debugElement.query('bug-report-form')).toBeNull();
    const selectTrigger = fixture.debugElement.query(By.css('.mat-select-trigger'));
    selectTrigger.triggerEventHandler('click', {});
    fixture.detectChanges();
    await fixture.whenStable();
    const options = document.querySelectorAll('.mat-select-panel mat-option');
    options[1].click(); // Click the second option, "Bug".
    fixture.detectChanges();
    await fixture.whenStable();
    expect(fixture.debugElement.query('bug-report-form')).not.toBeNull();
  });
});

The same test, using the Angular Material component harnesses might look like the following:

ts
describe('issue-report-selector', () => {
  let fixture: ComponentFixture;
  let loader: HarnessLoader;

  beforeEach(async () => {
    TestBed.configureTestingModule({
      imports: [IssueReportSelectorModule],
      declarations: [IssueReportSelector],
    });

    fixture = TestBed.createComponent(IssueReportSelector);
    fixture.detectChanges();
    loader = TestbedHarnessEnvironment.loader(fixture);
  });

  it('should switch to bug report template', async () => {
    expect(fixture.debugElement.query('bug-report-form')).toBeNull();
    const select = await loader.getHarness(MatSelectHarness);
    await select.open();
    const bugOption = await select.getOption({text: 'Bug'});
    await bugOption.click();
    expect(fixture.debugElement.query('bug-report-form')).not.toBeNull();
  });
});

Tests that are easier to read and understand

The code above shows that adopting the harnesses in tests can make them easier to understand.
Specifically in this example, it makes the "open the mat-select" logic more obvious. An unfamiliar
reader may not know what clicking on .mat-select-trigger does, but await select.open() is
self-explanatory.

The harnesses also make clear which option should be selected. Without the harness, you need a comment that
explains what options[1] means. With MatSelectHarness, however, the filter API makes the code
self-documenting.

Finally, the repeated calls to detectChanges and whenStable() can obfuscate the underlying
intent of the test. By using the harness APIs, you eliminate these calls, making the test more
concise.

Tests that are more robust

Notice that the test without harnesses directly uses CSS selectors to query elements within
<mat-select>, such as .mat-select-trigger. If the internal DOM of <mat-select> changes, these
queries may stop working. While the Angular team tries to minimize this type of change, some
features and bug fixes ultimately require restructuring the DOM. By using the Angular Material
harnesses, you avoid depending on internal DOM structure directly.

In addition to DOM structure, component asynchronicity often offers a challenge when updating
components. If a component changes between synchronous and asynchronous, downstream unit tests may
break due to expectations around timing. Tests then require the addition or removal of some
arcane combination of whenStable, flushMicroTasks, tick, or detectChanges. Component
harnesses, however, avoid this problem by normalizing the asynchronicity of all component behaviors
with all asynchronous APIs. When a test uses these harnesses, changes to asynchronicity become
far more manageable.

Both DOM structure and asynchronicity are implementation details of Angular Material's components.
When tests depend on the implementation details, they become a common source of failures due to
library changes. Angular CDK's test harnesses makes component library updates easier for both
application authors and the Angular team, as the Angular team only has to update the harness once
for everyone.