UPDATE:
A new version of this solution has been published here:
https://github.com/mirismaili/angular-material-dynamic-themes

ANSWER ARCHIVE:
Thanks @ K.Waite. I used his / her answer . I tried to improve this. The most important editing is to use .replace() instead of .add() for classList (in setTheme() ). There are also some other features that you can see below:

The most important parts:
In your styles.scss (or themes.scss if you have one):
@import ' ~@angular /material/theming'; @include mat-core(); @mixin define-css-classes($theme) { @include angular-material-theme($theme); $primary: map-get($theme, primary); $accent: map-get($theme, accent); $warn: map-get($theme, warn); $background: map-get($theme, background); $foreground: map-get($theme, foreground); // CSS THEME-DEPENDENT-STYLES ARE HERE: .theme-dependent-colors { background: mat-color($primary); color: mat-color($accent); } } /** * Define your custom themes in this map. * The 'key' of each member is the name of CSS class for that theme. * To better understand the schema of the map, see '@each' loop below and especially pay attention to 'map-has-key()' functions. */ $app-themes: ( indigo-pink : (primary-base: $mat-indigo, accent-base: $mat-pink), deeppurple-amber: (primary-base: $mat-deep-purple, accent-base: $mat-amber), pink-bluegrey : (primary-base: $mat-pink, accent-base: $mat-blue-gray, is-dark: true), purple-green : (primary-base: $mat-purple, accent-base: $mat-green, is-dark: true), ); @each $css-class, $theme in $app-themes { $primary: if(map-has-key($theme, primary), map-get($theme, primary), mat-palette(map-get($theme, primary-base))); $accent: if(map-has-key($theme, accent), map-get($theme, accent), mat-palette(map-get($theme, accent-base))); $warn: if(map-has-key($theme, warn), map-get($theme, warn), mat-palette( if(map-has-key($theme, warn-base), map-get($theme, warn-base), $mat-red) )); .
In typewriting (see here ):
import {Component, HostBinding} from '@angular/core'; import {OverlayContainer} from "@angular/cdk/overlay"; const THEME_DARKNESS_SUFFIX = '-dark'; export class AppComponent { @HostBinding('class') activeThemeCssClass: string; isThemeDark = false; activeTheme: string; setTheme(theme: string, darkness: boolean = null) { if (darkness === null) darkness = this.isThemeDark; else if (this.isThemeDark === darkness) { if (this.activeTheme === theme) return; } else this.isThemeDark = darkness; this.activeTheme = theme; const cssClass = darkness === true ? theme + THEME_DARKNESS_SUFFIX : theme; const classList = this.overlayContainer.getContainerElement().classList; if (classList.contains(this.activeThemeCssClass)) classList.replace(this.activeThemeCssClass, cssClass); else classList.add(cssClass); this.activeThemeCssClass = cssClass; } constructor(overlayContainer: OverlayContainer) { this.setThemeClass('indigo-pink', false);
See other stuff at stackblitz .
CAVEAT: adding 8 dynamic themes to the application (4 light sources + 4 features) in my case increased the size of the built-in styles.css by ~420 kB (compared to one theme of static material).