| |

Angular 90 ๐Ÿ…ฐ๏ธ Angular Material โ€” Theming

โฑ๏ธ Estimated read: 15 min | |

Angular Material’s theming system was completely rewritten in version 19. The old approach โ€” defining palettes, composing them into a theme map, and passing that map to component mixins โ€” was replaced with a design token system based on Material Design 3 (M3) . The new API generates CSS variables from a single theme configuration and gives you native CSS mechanisms for switching between light and dark mode without duplicating component styles .

The transition is significant. Code written for Angular Material 18 and earlier does not work with the v19+ theming API without modification. The old mat.define-light-theme, mat.define-dark-theme, and mat.all-component-themes functions were replaced with mat.theme, mat.system-classes, and per-component override mixins . The new system is more flexible, more aligned with the M3 specification, and more predictable once you understand the token model.

Key point: The new theming system is built on system tokens โ€” CSS variables prefixed with --mat-sys- that define every color, typography size, and shape in the design system. The mat.theme mixin generates these tokens from a configuration. Components read from the tokens. To customize a component, you override the tokens for that component or globally. To switch between light and dark mode, you change the color-scheme CSS property, and the light-dark() CSS function selects the appropriate token values automatically .


Why the theming system changed

The old theming system had structural limitations that became apparent as Material Design 3 matured. The rewrite addresses them.

The duplication problem. In the old system, applying a dark theme required re-running the component theme mixins with the dark theme map. This generated duplicate CSS for every component โ€” the light styles and the dark styles โ€” even though only the colors changed . The new system generates the color tokens once, and the light-dark() function switches between the light and dark values natively. There is no duplication.

The customization problem. In the old system, customizing a single component required extracting the component’s mixin, understanding the theme map structure, and passing the right palette keys. The new system exposes system tokens directly. A custom component reads var(--mat-sys-primary) instead of extracting the primary palette from a Sass map . This is simpler and works without Sass.

The token problem. Material Design 3 is built on design tokens โ€” named values for every color, typography scale, and shape in the system. The old theming system did not expose these tokens. The new system does. Every M3 token is available as a CSS variable, and the mat.theme mixin generates them from a configuration .

The native CSS problem. The light-dark() CSS function and the color-scheme property are now supported in all modern browsers. They allow a single set of CSS rules to produce both light and dark mode output based on the browser’s preference. The old theming system predated this capability. The new system uses it .

The trade-off. The new system is not backward-compatible. Applications upgrading from Angular Material 18 to 19 must migrate their theme files. The migration is mechanical โ€” replace mat.define-light-theme with mat.theme, replace component mixins with token overrides โ€” but it is not automatic. The Angular Material migration guide provides the step-by-step instructions.


a. The mat.theme Mixin and Design Tokens

The mat.theme mixin is the entry point for theming. It accepts a configuration object with three optional keys: color, typography, and density. The mixin generates the system tokens and applies them to the html element .

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

html {
  color-scheme: light dark;

  @include mat.theme((
    color: (
      primary: mat.$azure-palette,
      tertiary: mat.$blue-palette,
    ),
    typography: Roboto,
    density: 0,
  ));
}

The color key accepts either a single palette or a map with primary, tertiary, and theme-type keys. When theme-type is omitted and color-scheme: light dark is set, the mixin generates both light and dark token values using the light-dark() function . The typography key accepts a font family name or a map with plain-family, brand-family, and weight values. The density key accepts a number where 0 is standard and negative values are more compact .

The generated tokens are CSS variables. The full set is prefixed with --mat-sys-. The color tokens include --mat-sys-primary, --mat-sys-on-primary, --mat-sys-surface, --mat-sys-on-surface, --mat-sys-error, and their container and variant forms . The typography tokens include --mat-sys-body-medium, --mat-sys-headline-large, and the rest of the M3 scale. The shape tokens include --mat-sys-corner-small, --mat-sys-corner-medium, and the elevation tokens.

To apply the surface colors to your application, you set the background and text color on the body:

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

Alternatively, the mat.system-classes() mixin generates utility classes that apply the tokens. The mat-bg-surface, mat-text-on-surface, and mat-font-body-medium classes can be applied directly in templates .

html {
  @include mat.theme((/* ... */));
  @include mat.system-classes();
}
<body class="mat-bg-surface mat-text-on-surface">

b. Color Palettes and Custom Palettes

Angular Material provides twelve prebuilt color palettes that can be used with mat.theme. The palettes are available as Sass variables: $red-palette, $green-palette, $blue-palette, $yellow-palette, $cyan-palette, $magenta-palette, $orange-palette, $chartreuse-palette, $spring-green-palette, $azure-palette, $violet-palette, and $rose-palette .

A single palette can be provided for the primary color, and the mixin generates the secondary and tertiary colors from it. Alternatively, a map can specify different palettes for primary and tertiary:

@include mat.theme((
  color: (
    primary: mat.$violet-palette,
    tertiary: mat.$orange-palette,
  ),
));

For custom colors, the Angular CLI schematic ng generate @angular/material:theme-color generates a custom palette file from a single color input. The schematic prompts for the primary color and optionally the secondary, tertiary, and neutral colors, and produces a Sass file with the generated palettes .

ng generate @angular/material:theme-color

The generated file contains the full palette definition with all the tones that M3 requires. The palette is then imported and used in the mat.theme configuration.


c. Light and Dark Mode

The new theming system handles light and dark mode through the color-scheme CSS property and the light-dark() CSS function. When color-scheme: light dark is set on the html element, the browser selects the appropriate color values based on the user’s system preference .

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

The light-dark() function is used inside the generated tokens. Each token has a light value and a dark value, and the browser selects the correct one based on the computed color-scheme .

To force a specific mode regardless of system preference, set color-scheme: light or color-scheme: dark on the html element. To allow the user to toggle between modes, the application can set the color-scheme property dynamically based on a stored preference .

// theme.service.ts
@Injectable({ providedIn: 'root' })
export class ThemeService {
  private document = inject(DOCUMENT);

  setTheme(theme: 'light' | 'dark'): void {
    this.document.documentElement.style.setProperty('color-scheme', theme);
  }

  initializeTheme(): void {
    const saved = localStorage.getItem('theme');
    if (saved === 'light' || saved === 'dark') {
      this.setTheme(saved);
    }
  }
}

The service sets the color-scheme property directly on the html element. The browser then switches the token values automatically. No CSS classes are needed, and no duplicate component styles are generated .

For applications that need to apply a theme to a specific subtree rather than globally, the mat.theme mixin can be included under a CSS selector. The tokens are scoped to that selector, and the components inside it use the scoped values.


Complete Example Session

This session builds a custom theme with a generated palette, a dark mode toggle, and a custom component that reads from the system tokens.

// ============================================
// PART 1: THE THEME FILE
// ============================================

// styles.scss
@use '@angular/material' as mat;
@use './theme-colors' as my-colors;

html {
  color-scheme: light dark;

  @include mat.theme((
    color: (
      primary: my-colors.$primary-palette,
      tertiary: mat.$blue-palette,
    ),
    typography: (
      plain-family: 'Open Sans',
      brand-family: 'Josefin Sans',
      bold-weight: 700,
      medium-weight: 500,
      regular-weight: 300,
    ),
    density: 0,
  ));

  @include mat.system-classes();
}

body {
  background: var(--mat-sys-surface);
  color: var(--mat-sys-on-surface);
  margin: 0;
  font-family: var(--mat-sys-body-medium);
}

The mat.theme mixin generates the system tokens. The mat.system-classes() mixin generates the utility classes. The body uses the surface tokens for background and text color .

// ============================================
// PART 2: THE GENERATED PALETTE
// ============================================

// theme-colors.scss (generated by ng generate @angular/material:theme-color)
@use 'sass:map';
@use '@angular/material' as mat;

$_palettes: (
  primary: (
    0: #000000,
    10: #21005d,
    20: #381e72,
    // ... all tones
  ),
  secondary: (/* ... */),
  tertiary: (/* ... */),
  neutral: (/* ... */),
  neutral-variant: (/* ... */),
  error: (/* ... */),
);

$_rest: (
  secondary: map.get($_palettes, secondary),
  neutral: map.get($_palettes, neutral),
  neutral-variant: map.get($_palettes, neutral-variant),
  error: map.get($_palettes, error),
);

$primary-palette: map.merge(map.get($_palettes, primary), $_rest);
$tertiary-palette: map.merge(map.get($_palettes, tertiary), $_rest);

The generated palette file contains the full M3 palette definition with all tones. The $_rest map merges the secondary, neutral, neutral-variant, and error palettes into the primary and tertiary palettes so that each is a complete palette .

// ============================================
// PART 3: THE THEME TOGGLE SERVICE
// ============================================

// theme.service.ts
import { Injectable, inject, DOCUMENT } from '@angular/core';

@Injectable({ providedIn: 'root' })
export class ThemeService {
  private document = inject(DOCUMENT);
  private readonly STORAGE_KEY = 'app-theme';

  initialize(): void {
    const saved = localStorage.getItem(this.STORAGE_KEY);
    if (saved === 'light' || saved === 'dark') {
      this.setTheme(saved);
      return;
    }

    // Follow system preference if no saved theme
    const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
    this.setTheme(prefersDark ? 'dark' : 'light');
  }

  setTheme(theme: 'light' | 'dark'): void {
    this.document.documentElement.style.setProperty('color-scheme', theme);
    localStorage.setItem(this.STORAGE_KEY, theme);
  }

  toggle(): 'light' | 'dark' {
    const current = this.document.documentElement.style.getPropertyValue('color-scheme') || 'light';
    const next = current === 'dark' ? 'light' : 'dark';
    this.setTheme(next);
    return next;
  }
}

The service sets the color-scheme property on the html element. The browser switches the token values automatically. No CSS classes are needed, and no component styles are duplicated .

// ============================================
// PART 4: THE THEME TOGGLE BUTTON
// ============================================

// theme-toggle.component.ts
import { Component, inject } from '@angular/core';
import { MatButtonModule } from '@angular/material/button';
import { MatIconModule } from '@angular/material/icon';
import { ThemeService } from './theme.service';

@Component({
  selector: 'app-theme-toggle',
  imports: [MatButtonModule, MatIconModule],
  template: `
    <button mat-icon-button (click)="toggleTheme()" aria-label="Toggle theme">
      <mat-icon>{{ isDark() ? 'light_mode' : 'dark_mode' }}</mat-icon>
    </button>
  `,
})
export class ThemeToggleComponent {
  private themeService = inject(ThemeService);

  isDark(): boolean {
    return document.documentElement.style.getPropertyValue('color-scheme') === 'dark';
  }

  toggleTheme(): void {
    this.themeService.toggle();
  }
}

The button toggles the color-scheme property. The icon changes based on the current mode. The theme switch is instantaneous and does not require a page reload .

// ============================================
// PART 5: THE CUSTOM COMPONENT WITH TOKENS
// ============================================

// custom-card.component.scss
.custom-card {
  background: var(--mat-sys-surface-container);
  color: var(--mat-sys-on-surface);
  border-radius: var(--mat-sys-corner-medium);
  padding: 16px;
  box-shadow: var(--mat-sys-level1);
}

.custom-card__title {
  font: var(--mat-sys-title-large);
  color: var(--mat-sys-primary);
}

.custom-card__body {
  font: var(--mat-sys-body-medium);
  color: var(--mat-sys-on-surface-variant);
}

.custom-card__error {
  color: var(--mat-sys-error);
}

The custom component reads from the system tokens directly. It does not need to extract values from a Sass theme map. The tokens are CSS variables, so they work in plain CSS and in component styles .

// ============================================
// PART 6: THE COMPONENT CLASS
// ============================================

// custom-card.component.ts
import { Component, input } from '@angular/core';

@Component({
  selector: 'app-custom-card',
  template: `
    <div class="custom-card">
      <h3 class="custom-card__title">{{ title() }}</h3>
      <p class="custom-card__body">{{ body() }}</p>
      @if (hasError()) {
        <p class="custom-card__error">{{ errorMessage() }}</p>
      }
    </div>
  `,
  styleUrl: './custom-card.component.scss',
})
export class CustomCardComponent {
  title = input.required<string>();
  body = input.required<string>();
  hasError = input(false);
  errorMessage = input('');
}

The component uses the token-based styles. The card background, title, body, and error colors all come from the M3 system tokens. When the user switches themes, the card updates automatically .

// ============================================
// PART 7: THE COMPONENT OVERRIDE
// ============================================

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

// Override the button's corner radius globally
html {
  @include mat.theme((/* ... */));

  @include mat.button-overrides((
    filled-container-shape: 4px,
    outlined-container-shape: 4px,
  ));
}

The mat.button-overrides mixin accepts a map of component token overrides. The filled-container-shape and outlined-container-shape tokens control the corner radius of the buttons. The override is applied globally, and only the button styles are affected .

// ============================================
// PART 8: THE COMPONENT-SPECIFIC THEME
// ============================================

// Only apply a custom shape to buttons inside the .compact-buttons container
.compact-buttons {
  @include mat.button-overrides((
    filled-container-shape: 2px,
    outlined-container-shape: 2px,
  ));
}

The override can be scoped to a CSS selector. Buttons inside .compact-buttons get the custom shape, while buttons elsewhere use the default. This is how per-component customization works without affecting the rest of the application .

// ============================================
// PART 9: THE TYPOGRAPHY CONFIGURATION
// ============================================

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

html {
  @include mat.theme((
    typography: (
      plain-family: 'Inter',
      brand-family: 'Playfair Display',
      bold-weight: 800,
      medium-weight: 600,
      regular-weight: 400,
    ),
  ));
}

The typography configuration accepts a single font family or a map. The plain-family is used for body text. The brand-family is used for headings and titles. The weight values control the bold, medium, and regular text .

// ============================================
// PART 10: THE DENSITY CONFIGURATION
// ============================================

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

html {
  @include mat.theme((
    density: -1,  // More compact than default
  ));
}

// Or apply density to a specific component
.compact-form {
  @include mat.form-field-density(-3);
}

The density scale ranges from 0 (default) to -5 (most compact). Negative values reduce the vertical spacing in components. The mat.form-field-density mixin applies density to a specific component without affecting the global theme .


Quick Reference

The mat.theme Configuration

KeyTypeExample
colorPalette or mapmat.$azure-palette or { primary, tertiary, theme-type }
typographyString or map'Roboto' or { plain-family, brand-family, bold-weight }
densityNumber0 (default), -1 (compact), -3 (dense)

The Prebuilt Palettes

PaletteVariable
Redmat.$red-palette
Greenmat.$green-palette
Bluemat.$blue-palette
Yellowmat.$yellow-palette
Cyanmat.$cyan-palette
Magentamat.$magenta-palette
Orangemat.$orange-palette
Chartreusemat.$chartreuse-palette
Spring Greenmat.$spring-green-palette
Azuremat.$azure-palette
Violetmat.$violet-palette
Rosemat.$rose-palette

The System Token Categories

CategoryExample Tokens
Primary--mat-sys-primary, --mat-sys-on-primary, --mat-sys-primary-container
Surface--mat-sys-surface, --mat-sys-on-surface, --mat-sys-surface-container
Error--mat-sys-error, --mat-sys-on-error, --mat-sys-error-container
Typography--mat-sys-body-medium, --mat-sys-headline-large, --mat-sys-title-small
Shape--mat-sys-corner-small, --mat-sys-corner-medium, --mat-sys-corner-large
Elevation--mat-sys-level0 through --mat-sys-level5

The Light/Dark Mode Configuration

ConfigurationEffect
color-scheme: light darkFollows system preference
color-scheme: lightForces light mode
color-scheme: darkForces dark mode
light-dark() functionSelects token value based on color-scheme

The Component Override Mixins

MixinComponent
mat.button-overridesButtons
mat.form-field-overridesForm fields
mat.dialog-overridesDialogs
mat.card-overridesCards
mat.table-overridesTables

Best Practices

โœ… Do This:

// Use mat.theme with color-scheme: light dark
html {
  color-scheme: light dark;
  @include mat.theme((color: mat.$azure-palette));
}                                                              // โœ…
// Use system tokens for custom components
.custom { background: var(--mat-sys-surface); }                // โœ…
// Use component override mixins for targeted changes
@include mat.button-overrides((filled-container-shape: 4px));  // โœ…
// Set color-scheme on the document element for theme switching
document.documentElement.style.setProperty('color-scheme', theme); // โœ…
// Use mat.system-classes for utility classes
@include mat.system-classes();                                 // โœ…

โŒ Don’t Do This:

// Don't use the old M2 theming API
@include mat.define-light-theme(...)                           // โŒ removed in v19
// Don't duplicate component themes for dark mode
.dark-theme { @include mat.all-component-themes($dark); }      // โŒ duplicates CSS
// Don't override component styles with deep selectors
::ng-deep .mat-mdc-button { ... }                              // โŒ
// Don't use @import in SCSS
@import '@angular/material';                                   // โŒ use @use

Common Pitfalls

PitfallWhy It HappensFix
Components unstyledmat.theme not includedAdd to styles.scss
Dark mode not workingcolor-scheme not setAdd color-scheme: light dark
Duplicate CSSUsing old theme mixinsUse mat.theme and tokens
Tokens undefinedmat.theme not applied to htmlInclude on html or a parent
Custom palette not appliedWrong palette variableCheck the generated file
Theme toggle requires reloadUsing classes instead of color-schemeSet color-scheme directly

Real-World Examples

1. Basic Theme

html { @include mat.theme((color: mat.$violet-palette)); }

2. Custom Palette Theme

@include mat.theme((color: (primary: my-colors.$primary-palette)));

3. Typography Configuration

@include mat.theme((typography: (plain-family: 'Inter')));

4. Density Configuration

@include mat.theme((density: -1));

5. Light/Dark Mode

html { color-scheme: light dark; }

6. Force Dark Mode

document.documentElement.style.setProperty('color-scheme', 'dark');

7. System Tokens

.custom { background: var(--mat-sys-surface); }

8. Utility Classes

<body class="mat-bg-surface mat-text-on-surface">

9. Component Override

@include mat.button-overrides((filled-container-shape: 4px));

10. Scoped Density

.compact { @include mat.form-field-density(-3); }

Visual

The Theming Architecture

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  mat.theme CONFIGURATION                     โ”‚
โ”‚    color, typography, density                โ”‚
โ”‚       โ”‚                                      โ”‚
โ”‚       โ–ผ                                      โ”‚
โ”‚  SYSTEM TOKENS (CSS variables)               โ”‚
โ”‚    --mat-sys-primary                         โ”‚
โ”‚    --mat-sys-surface                         โ”‚
โ”‚    --mat-sys-body-medium                     โ”‚
โ”‚       โ”‚                                      โ”‚
โ”‚       โ”œโ”€โ”€> Angular Material components        โ”‚
โ”‚       โ”œโ”€โ”€> Custom components                  โ”‚
โ”‚       โ””โ”€โ”€> Utility classes                    โ”‚
โ”‚                                              โ”‚
โ”‚  color-scheme: light dark                    โ”‚
โ”‚    โ””โ”€ light-dark() selects token values      โ”‚
โ”‚                                              โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

The Light/Dark Mode Flow

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  color-scheme: light dark                    โ”‚
โ”‚       โ”‚                                      โ”‚
โ”‚       โ–ผ                                      โ”‚
โ”‚  System preference: dark                     โ”‚
โ”‚       โ”‚                                      โ”‚
โ”‚       โ–ผ                                      โ”‚
โ”‚  Browser selects dark token values           โ”‚
โ”‚    --mat-sys-surface: #1a1a1a               โ”‚
โ”‚    --mat-sys-on-surface: #ffffff             โ”‚
โ”‚       โ”‚                                      โ”‚
โ”‚       โ–ผ                                      โ”‚
โ”‚  Components render with dark colors          โ”‚
โ”‚                                              โ”‚
โ”‚  No CSS duplication. No page reload.         โ”‚
โ”‚                                              โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

The Token Categories

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  --mat-sys-* TOKENS                          โ”‚
โ”‚                                              โ”‚
โ”‚  Color:                                      โ”‚
โ”‚    primary, on-primary, primary-container    โ”‚
โ”‚    surface, on-surface, surface-container    โ”‚
โ”‚    error, on-error, error-container          โ”‚
โ”‚                                              โ”‚
โ”‚  Typography:                                 โ”‚
โ”‚    body-medium, headline-large               โ”‚
โ”‚    title-small, label-large                  โ”‚
โ”‚                                              โ”‚
โ”‚  Shape:                                      โ”‚
โ”‚    corner-small, corner-medium               โ”‚
โ”‚    corner-large, corner-extra-large          โ”‚
โ”‚                                              โ”‚
โ”‚  Elevation:                                  โ”‚
โ”‚    level0 through level5                     โ”‚
โ”‚                                              โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

The Theme Toggle

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  THEME TOGGLE                                โ”‚
โ”‚                                              โ”‚
โ”‚  ThemeService.setTheme('dark')               โ”‚
โ”‚       โ”‚                                      โ”‚
โ”‚       โ–ผ                                      โ”‚
โ”‚  document.documentElement.style              โ”‚
โ”‚    .setProperty('color-scheme', 'dark')      โ”‚
โ”‚       โ”‚                                      โ”‚
โ”‚       โ–ผ                                      โ”‚
โ”‚  Browser switches all light-dark() values    โ”‚
โ”‚       โ”‚                                      โ”‚
โ”‚       โ–ผ                                      โ”‚
โ”‚  All components update instantly             โ”‚
โ”‚                                              โ”‚
โ”‚  No CSS classes. No duplicate styles.        โ”‚
โ”‚                                              โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Summary

ItemValue
Theming APImat.theme mixin
Design systemMaterial Design 3 (M3)
Token prefix--mat-sys-
Light/dark modecolor-scheme + light-dark()
Prebuilt palettes12 (red, green, blue, violet, etc.)
Custom paletteng generate @angular/material:theme-color
Typography configplain-family, brand-family, weights
Density config0 to -5
Utility classesmat.system-classes()
Component overridesmat.button-overrides, mat.form-field-overrides
Old APIRemoved in v19 (M2)

Key takeaways:

  • Angular Material 19 replaced the M2 theming system with a design token system based on Material Design 3. The old mat.define-light-theme and mat.all-component-themes functions are gone. The new API is mat.theme, which generates system tokens from a configuration .
  • The mat.theme mixin accepts color, typography, and density configuration. The color can be a single palette or a map with primary, tertiary, and theme-type. The typography can be a font family name or a map with plain and brand families. The density is a number from 0 to -5 .
  • System tokens are CSS variables prefixed with --mat-sys-. They define every color, typography scale, and shape in the design system. Components read from the tokens. Custom components use the same tokens with var(--mat-sys-primary) .
  • Light and dark mode are handled by color-scheme and light-dark(). Setting color-scheme: light dark on the html element makes the browser select the appropriate token values based on system preference. Setting color-scheme: light or color-scheme: dark forces a specific mode .
  • Theme switching does not require duplicate CSS. The light-dark() function is applied inside the tokens. When the color-scheme property changes, the browser switches all token values automatically. There is no need to re-run component theme mixins .
  • Custom palettes are generated with the Angular CLI. The ng generate @angular/material:theme-color schematic creates a full M3 palette file from a single color input. The generated file contains all the tones that M3 requires .
  • Component-specific customization uses override mixins. The mat.button-overrides, mat.form-field-overrides, and similar mixins accept a map of token overrides. The overrides can be applied globally or scoped to a CSS selector .

Remember: The Angular Material theming system is now based on design tokens. The mat.theme mixin generates the tokens. The color-scheme property switches between light and dark. The tokens are CSS variables that any component can read. This is simpler than the old M2 system, more aligned with the M3 specification, and more predictable once you understand the token model. Use the prebuilt palettes or generate a custom one. Set color-scheme: light dark for automatic mode switching. Use system tokens for custom components. Override component tokens when you need to customize a specific component. The theming system is the visual foundation of the entire application.

Was this helpful?


Stop using slow, ad-bloated tool sites! ๐Ÿคฎ

๐Ÿ”Ž Search “KandZ Tools” on Google to use many professional utilities for free.

KandZ.me is the ultimate minimalist hub for:
โœ… Finance (Mortgage, Interest, Inflation)
โœ… Tech (Base64, JSON, Dev Suite, IP)
โœ… Health (BMI, BMR, TDEE)
โœ… Productivity (Timer, Workspace, QR)

โšก๏ธ Fast & Private
๐Ÿ”’ No data leaves your device
๐Ÿ’Ž 100% Free

๐Ÿ”— Use it now: https://tools.kandz.me
๐Ÿ”– Bookmark itโ€”youโ€™ll need it later!