Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unable to correctly switch between dark and light Angular Material themes

I am trying to implement the ability to switch between the Dark and Light Angular material theme. Here is the Stackblitz demo.

The issue is, if I set the Dark theme as default, then it is applied correctly but when I switch to the Light theme, then it is applied partially. Similarly, if I set the Light theme as default, then the Dark theme is applied partially.

enter image description here

As seen in the screenshot below, light-theme CSS is unable to override the default theme: enter image description here

What is the issue here?

src\app@theme\styles_variables.scss:

@use "@angular/material" as mat;


$light-primary-palette: (
  ...light-primary-colors-range...
);

$light-accent-palette: (
  ...light-accent-colors-range...
);

$dark-primary-palette: (
  ...dark-primary-colors-range...
);

$dark-accent-palette: (
  ...dark-accent-colors-range...
);

src\app@theme\styles_theme.scss:

@use 'sass:map';
@forward './variables';

@use '@angular/material' as mat;
@use './variables' as variables;

$light-primary: mat.define-palette(variables.$light-primary-palette);
$light-accent: mat.define-palette(variables.$light-accent-palette);
$light-warn: mat.define-palette(mat.$red-palette);
$light-theme: mat.define-light-theme(
  (
    color: (
      primary: $light-primary,
      accent: $light-accent,
      warn: $light-warn,
    ),
  )
);

$dark-primary: mat.define-palette(variables.$dark-primary-palette);
$dark-accent: mat.define-palette(variables.$dark-accent-palette);
$dark-warn: mat.define-palette(mat.$red-palette);
$dark-theme: mat.define-dark-theme(
  (
    color: (
      primary: $dark-primary,
      accent: $dark-accent,
      warn: $dark-warn,
    ),
  )
);


// Apply the dark theme by default
@include mat.all-component-themes($dark-theme);

// Include the light color styles inside of a block with a CSS class. You can make this
// CSS class whatever you want. In this example, any component inside of an element with
// `.light-theme` will be affected by this alternate light theme instead of the default dark theme.
.light-theme {
  @include mat.all-component-themes($light-theme);
}

src\styles.scss:

@use "./app/@theme/styles/theme";

src\app@theme\theme.service.ts:

export class ThemeService {
  private _lightTheme = new BehaviorSubject<boolean>(false);
  private _activeTheme = new BehaviorSubject<Theme>(Theme.Light);
  isLightTheme$ = this._lightTheme.asObservable();

  activeTheme$ = this._activeTheme.asObservable();

  setLightTheme(isLightTheme: boolean): void {
    this._lightTheme.next(isLightTheme);
    if (isLightTheme) {
      this._activeTheme.next(Theme.Dark);
    } else {
      this._activeTheme.next(Theme.Light);
    }
  }
}

src\app\app.module.ts:

export class AppModule {
  constructor(
    overlayContainer: OverlayContainer,
    private _themeService: ThemeService
  ) {
    this._themeService.isLightTheme$.subscribe((isLightTheme) => {
      if (isLightTheme) {
        overlayContainer.getContainerElement().classList.add('.light-theme');
      }
    });
  }
}

src\app@theme\components\header\header.component.html:

<mat-toolbar class="header" *subscribe="isLightTheme$; let isLightTheme">
  <span class="spacer"></span>
  <mat-slide-toggle
    [color]="'warn'"
    [checked]="isLightTheme"
    (change)="toggleDarkTheme($event.checked)"
    >{{ isLightTheme ? "Light Theme" : "Dark Theme" }}</mat-slide-toggle
  >
</mat-toolbar>

src\app@theme\components\header\header.component.ts:

export class HeaderComponent {
  isLightTheme$: Observable<boolean> = this._themeService.isLightTheme$;

  constructor(private _themeService: ThemeService) {}

  public toggleDarkTheme(checked: boolean) {
    this._themeService.setLightTheme(checked);
  }
}
like image 730
Saurabh Palatkar Avatar asked Oct 20 '25 14:10

Saurabh Palatkar


1 Answers

It looks like the issue is that you are not properly applying the light theme when toggling between the dark and light themes. In your AppModule constructor, you are adding a class of .light-theme instead of light-theme.

Additionally, you should remove the .light-theme class when the dark theme is active.

export class AppModule {
  constructor(
    overlayContainer: OverlayContainer,
    private _themeService: ThemeService
  ) {
    this._themeService.isLightTheme$.subscribe((isLightTheme) => {
      if (isLightTheme) {
        overlayContainer.getContainerElement().classList.add("light-theme");
      } else {
        overlayContainer.getContainerElement().classList.remove("light-theme");
      }
    });
  }
}

Another thing is related to your ThemeService because you use asObservable improperly. Please read this reply. Ideally the following code should work:

export class ThemeService {
  isLightTheme$ = new BehaviorSubject<boolean>(false);

  setLightTheme(isLightTheme: boolean): void {
    this.isLightTheme$.next(isLightTheme);
  }

  get activeTheme$(): Observable<Theme> {
    return this.isLightTheme$.pipe(
      map((isLightTheme) => (isLightTheme) ? Theme.Light : Theme.Dark)
    );
  }
}

Another important thing is to remove scss imported inside component styles (like @use '../../../@theme/styles/theme' as theme; inside admin.component.scss). Do it for all components where you added it.

This is a working StackBlitz

like image 128
Francesco Lisandro Avatar answered Oct 23 '25 02:10

Francesco Lisandro



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!