3

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);
  }
}
Saurabh Palatkar
  • 3,242
  • 9
  • 48
  • 107

1 Answers1

3

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