How to implement switchable dynamic custom themes with Angular Material

I needed to implement a function that allows users to dynamically switch between different themes in an app that I develop at the moment. As the title suggests, I use Angular Material components and Material themes.

In this article, I’ll give you a step-by-step guide on how you can choose between different themes in an Angular app dynamically.

You don’t need to set up a new Angular project just to implement this feature. Any existing app can easily be converted so that you can apply different themes to it.

Take care of the components

Before you begin, make sure that you use the same type of components all throughout the app. Try not to mix Angular and, for example, Bootstrap components, because that will only cause you a lot of trouble later on. However, it certainly is possible to combine those two, but it will require some extra work to make it look good.

Also, make sure to import all necessary Angular Material components (like buttons, cards, toggles, etc.) in your app.module.ts file!

A demo app

Throughout this article, I’ll use the following sample app that I quickly put together. It only uses a couple of standard examples from the Angular documentation:

Figure 1: The example app for this article with the default-theme applied

A custom light-theme

Let’s begin by creating a custom light theme that gets displayed by default. For that, we’ll utilize the global styles.scss stylesheet so that we only have to define it once:

// Import library functions for theme creation.
@import '~@angular/material/theming';

// Include non-theme styles for core.
@include mat-core();

// Define the application's custom theme
$primary: mat-palette($mat-blue);
$accent:  mat-palette($mat-pink, A200, A100, A400);
$theme: mat-light-theme($primary, $accent);

// Regular (light theme)
// Include theme styles for Angular Material components
// and include theme styles for your custom components.
@include angular-material-theme($theme);

The code above defines the color palette for the default design. In this case, I wanted to use blue as the primary and pink for the accent color. The next line creates a theme with a light background and those two colors. In the last line of the snippet, we assign the theme to the entire app. If you only want to assign it to a certain component, you can do that too (I’ll discuss that option a bit later).

When you save this file and reload the page, the demo app should now look like this:

Figure 2: The example app with the light theme applied

A custom dark theme

Next, we’ll define a custom dark theme. The procedure is almost the same as for the light theme. You only have to change one line:

// Use mat-dark-theme instead of mat-light-theme
$dark-theme: mat-dark-theme($primary, $accent);

You can either change the primary and accent color for each style or use the same ones, which I wouldn’t recommend. So, let’s define a new palette for the dark theme:

// Define the application's dark theme
$primary-dark: mat-palette($mat-deep-purple);
$accent-dark:  mat-palette($mat-green, A200, A100, A400);

// Use mat-dark-theme instead of mat-light-theme
$dark-theme: mat-dark-theme($primary-dark, $accent-dark);

Refer to this page to see a list of pre-defined colors in Angular Material.

My custom dark theme for this app looks like this:

Figure 3: The example app’s dark theme

How to change themes dynamically

Once you defined all the themes of your app, it’s time to change its appearance! To do so, create the following class somewhere underneath the theme definitions in the styles.scss file:

.dark {
    @include angular-material-theme($dark-theme);
}

This will include the contents of the dark theme inside the dark class, which means that you can easily assign it to and remove it from your app to switch its theme. To do that, you only need to add the following code to the HTML file of your app’s main component:

<div class="mat-app-background" [class.dark]="darkThemeSelected">
    <app-top-navbar></app-top-navbar>
    <app-example-component></app-example-component>
</div>

As you can see, my app.component.html file contains two components. I put those in a div, which we’ll use to apply our dark css-class to. Note, that I added an extra class to the div, which is needed to change the background color of the app when you change the theme.

Anyway, in the app’s main typescript file, add the following variable:

darkThemeSelected : boolean = false;

As well as the following method that we’ll use to tell the app to switch themes:

applyTheme(darkThemeSelected : boolean)
{
  this.darkThemeSelected = darkThemeSelected;
}

Next, we’ll need to tell the main app component that it should switch to a different theme. In this app, the top-header bar contains a Material toggle switch, which a user can utilize to switch between themes. Therefore, we need to propagate a change from that component to the app’s main component. We can do that with Events and EventEmitters:

I added the following event to the top-menu-bar component in the app.component.html file:

<div class="mat-app-background" [class.dark]="darkThemeSelected">
    <app-top-navbar (sliderChanged)="applyTheme($event)"></app-top-navbar>
    <app-example-component></app-example-component>
</div>

As you can see, I defined a custom event (sliderChanged) so that the applyTheme function, we defined earlier, gets called every time the slider gets moved.

In the top-menu bar, I had to output the appropriate EventEmitter:

@Output() sliderChanged = new EventEmitter<boolean>();

The slider element (in the top-menu-bar’s HTML) looks like this:

<mat-slide-toggle color="primary" (change)="onSliderChange()" [(ngModel)]="sliderEnabled">
    Enable Dark Theme
  </mat-slide-toggle>

Note, that onSliderChange() gets called whenever a user clicks the slider, and its state is stored in the sliderEnabled variable. Inside of the onSliderChange()-method, we only need to emit the new state of the slider, so that the parent component can receive the update:

private sliderEnabled : boolean = false;

onSliderChange()
{
  this.sliderChanged.emit(this.sliderEnabled);
}

And that’s it! This will propagate the toggle slider’s change event all the way to the main app component. The user should now be able to do the following:

Note, that this is a good solution for this simple app. However, if your app gets more complicated, it might make more sense to write a custom service that handles the style-changes.

What about custom components?

If your app contains custom components, you need to create a mix-in in their component-specific scss file:

// Define a mixin that accepts a theme and outputs the theme-specific styles.
@mixin vehicle-grid-theme($theme, $primary-font)
{
    // Extract the palettes you need from the theme definition.
    $primary: map-get($theme, primary);
    $accent: map-get($theme, accent);

    // Define any styles affected by the theme.
    .vehicleCardHeader {
        padding-left: 15px;
        padding-right: 15px;
        padding-top: 30px;
        padding-bottom: 5px;
        background-color:mat-color($primary);
    }

    .primary-text {
        color:$primary-font;
    }
}

And then you have to import and include that in the app’s global style.scss file right underneath the global themes we defined earlier:

// Other stuff in the global styles.scss
@include angular-material-theme($theme);
@include vehicle-grid-theme($theme, #fff);
// ...

Note: This is an example from another app that I’m working on and it’s not related to the example app!

Dynamically styling bootstrap compononents

As stated before, it’s possible to use components from different providers in a single app. One way to apply styles to other elements is to use css variables and assign their values to the components. However, that’s just a note, and I won’t explain that approach here.

Download the demo app

You can download the demo app here so that you can take a look at the code if you got stuck somewhere.

Summary

So, to recap the necessary steps:

  1. Create at least two custom styles in the global styles.scss file
  2. Make sure to import the alternative one in an extra css-class
  3. Add that css-class to a div that contains your app
  4. Add a way to choose the styles (in this example a toggle switch)
  5. Set up an event handler for the switch
  6. Propagate the event to the app’s main component
  7. Switch the theme when the event fires by setting the css-class

Dynamic themes are a cool way to allow users to customize their experience in your app, and it’s not too complicated to implement, so it’s definitely worth a try!

Leave your two cents, comment here!

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.