Theming your Angular apps using CSS Variables - Easy Solution!

Adithya Sreyaj - Apr 16 '21 - - Dev Community

Have you thought about how you can provide your users with different color schemes for your application? or Do you want your application to have a Dark theme?

Who doesn't like change? Even on our phones, sometimes we get bored of the look and feel, that we try out some new themes.

To provide multiple themes or not?

Theming in Angular

Sometimes it is always good to stick with the brand colors, this is especially the way to go for products that cater to consumers directly. Then there are apps that cater to other businesses, in this kind of application, it is good to have the option to customize the look and feel of the application for different businesses.

So the answer would be it depends on a lot of things. To name a few:

  • who are audience
  • does it bring in value

One really good example for when theming your application would make sense is for a School Management Software. Say the application is used by Teachers, Students, and Parents. We can give a different theme to the application based on the role.

Another good fit for providing custom themes would be applications that can be white-labeled. So for each of the users/businesses, they can set up their own themes to match their brand colors.

Even if you are going to provide multiple color themes for the application, it's probably a good idea to come with a Dark Mode for the application. More and more products are now supporting Dark theme.

CSS Custom Properties

The easiest way to theme your application is using CSS Variables / CSS custom properties. It makes theming so much easier than before when we had to do a lot of stuff, just to change some colors here and there.

But with CSS custom properties is so damn easy.

CSS Preprocessors had the concept of variables for a long time and CSS was still lagging behind on it until a few years back. Now its is supported in all modern browsers.

CSS Variable Browser Support

One really interesting thing about CSS custom properties is that they can be manipulated from JavaScript.

  1. Declare variables
--primaryColor: red;
--primaryFont: 'Poppins';
--primaryShadow: 0 100px 80px rgba(0, 0, 0, 0.07);
Enter fullscreen mode Exit fullscreen mode
  1. Usage
button {
    background: var(--primaryColor);
    font-family: var(--primaryFont);
    box-shadow: var(--primaryShadow);
}
Enter fullscreen mode Exit fullscreen mode

This is the most basic thing you should be knowing about CSS Custom Properties.

Theming in Angular

Starting off with a brand new Angular project with CLI v11.2.9. We now start by declaring some CSS variables for our application.

In the styles.scss file:

:root {
  --primaryColor: hsl(185, 57%, 35%);
  --secondaryColor: hsl(0, 0%, 22%);
  --textOnPrimary: hsl(0, 0%, 100%);
  --textOnSecondary: hsl(0, 0%, 90%);
  --background: hsl(0, 0%, 100%);
}
Enter fullscreen mode Exit fullscreen mode

We have declared a few variables and assigned the default colors for all of them. One thing to note, when you are going to be providing different colors is that you name the variables in a generic way. You shouldn't be naming it with the color's name.

Setting up the themes

I will be creating a theme.config.ts file all the themes will be configured. You can always make a static config like this or maybe get the config from an API response.
The latter is the better approach if you make changes to your themes often.

export const THEMES = {
  default: {
    primaryColor: 'hsl(185, 57%, 35%)',
    secondaryColor: 'hsl(0, 0%, 22%)',
    textOnPrimary: 'hsl(0, 0%, 100%)',
    textOnSecondary: 'hsl(0, 0%, 90%)',
    background: 'hsl(0, 0%, 100%)',
  },
  dark: {
    primaryColor: 'hsl(168deg 100% 29%)',
    secondaryColor: 'hsl(161deg 94% 13%)',
    textOnPrimary: 'hsl(0, 0%, 100%)',
    textOnSecondary: 'hsl(0, 0%, 100%)',
    background: 'hsl(0, 0%, 10%)',
  },
  netflix: {
    primaryColor: 'hsl(357, 92%, 47%)',
    secondaryColor: 'hsl(0, 0%, 8%)',
    textOnPrimary: 'hsl(0, 0%, 100%)',
    textOnSecondary: 'hsl(0, 0%, 100%)',
    background: 'hsl(0deg 0% 33%)',
  },
  spotify: {
    primaryColor: 'hsl(132, 65%, 55%)',
    secondaryColor: 'hsl(0, 0%, 0%)',
    textOnPrimary: 'hsl(229, 61%, 42%)',
    textOnSecondary: 'hsl(0, 0%, 100%)',
    background: 'hsl(0, 0%, 100%)',
  },
};

Enter fullscreen mode Exit fullscreen mode

This is the most basic way to do this. Maybe in the future, we can talk about how we can create a theme customizer where the user can completely change the look and feel of the application.

Theming service

We create a service and call it ThemeService. The logic for updating the themes will be handled by this service. We can inject the service into the application and then change the theme using a function we expose from the service.

import { DOCUMENT } from '@angular/common';
import { Inject, Injectable } from '@angular/core';
import { THEMES } from '../config/theme.config';

@Injectable({
  providedIn: 'root',
})
export class ThemeService {
  constructor(@Inject(DOCUMENT) private document: Document) {}

  setTheme(name = 'default') {
    const theme = THEMES[name];
    Object.keys(theme).forEach((key) => {
      this.document.documentElement.style.setProperty(`--${key}`, theme[key]);
    });
  }
}
Enter fullscreen mode Exit fullscreen mode

The service is very straightforward. There is a single function that we expose which changes the theme. How this works is basically by overriding the CSS variable values that we have defined in the styles.scss file.

We need to get access to the document, so we inject the Document token in the constructor.

The function takes the name of the theme to apply. What it does it, get the theme variables for the selected theme from our config file and then loop through it wherein we apply the new values to the variables.

Done!

Code and Demo

Click on the buttons to change the themes.

Repo

Do add your thoughts in the comments section.
Stay Safe ❤️

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .