Light and dark themes with Angular, Bootstrap and SASS
Let's say you have an Angular single-page app and use Bootstrap for styling. You want to let your users choose between multiple themes (e.g. a light and dark theme). You also want the option to use custom SASS for each theme, as well as for all themes at once.
The solution
If you just want the code, it's on github. It's CC-0, so feel free to copy it into your project :)
There's also a demo here
1. Install bootstrap
If you haven't included bootstrap yet, or if you've been using it via a CDN, install it with npm:
npm install bootstrap
2. Create your themes
Create a separate file for each theme you want to include. I'm using SASS here, but if you prefer SCSS, it's basically the same process. As a simple example, let's create two files in the src
directory of our app, light.sass
and dark.sass
.
The light theme simply imports bootstrap with no changes:
@import '~bootstrap/scss/bootstrap'
The dark theme overrides bootstrap's default color variables:
$body-bg: #060606
$body-color: #d6d6d6
@import '~bootstrap/scss/bootstrap'
You can create as many themes as you like, just make sure to always @import
bootstrap at the end.
If you don't fancy making your own themes, you can find free ones at bootswatch. Simply download the _variables.scss
file for each theme you want, give it a unique name and place it inside your app!
3. Configure Angular
We want Angular to turn each of our themes into a separate CSS file, so we can switch between them at runtime. Add the following to your angular.json
:
{
//...
"projects": {
"my-project": {
"architect": {
"build": {
"styles": [
"src/styles.sass",
{
"input": "src/light.sass",
"bundleName": "light",
"inject": true
},
{
"input": "src/dark.sass",
"bundleName": "dark",
"inject": false
}
]
}
}
}
}
}
For each theme, add a bundle to the styles
array. This will tell angular to turn that SASS into a separate CSS file. The "inject"
property should be set to true
on your default theme (the one used for new visitors), and false
on all others.
4. Implement a ThemeService
Let's create a service to change the theme at runtime:
ng generate service Theme
theme.service.ts
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root'
})
export class ThemeService {
public static default = 'light';
public get current(): string {
return localStorage.getItem('theme') ?? ThemeService.default;
}
public set current(value: string) {
localStorage.setItem('theme', value);
this.style.href = `/${value}.css`;
}
private readonly style: HTMLLinkElement;
constructor() {
this.style = document.createElement('link');
this.style.rel = 'stylesheet';
document.head.appendChild(this.style);
if (localStorage.getItem('theme') !== undefined) {
this.style.href = `/${this.current}.css`;
}
}
}
This service lets us set the current theme, and then downloads the corresponding CSS file. The theme is saved to localStorage
, so the next time the user opens the page, their selected theme will be applied automatically.
If your app has a user login system, you could even store the selection in a database, so that it's synced across devices.
If you're using Angular CDK, the MediaMatcher
lets you check if the user has enabled a dark theme in their browser or operating system. You could then set your app to dark right away. However, not all browsers implement this feature, so it's a good idea to also let your users switch manually.
5. Add the theme switcher UI
Finally, we need some sort of UI that allows our users to select their favorite theme. In this case, since we only have two themes, a simple toggle button is enough. Let's generate a component for this:
ng generate component theme-switcher
theme-switcher.component.ts
import { Component, OnInit } from '@angular/core';
import { ThemeService } from "../theme.service";
@Component({
selector: 'app-theme-switcher',
templateUrl: './theme-switcher.component.html',
styleUrls: ['./theme-switcher.component.sass']
})
export class ThemeSwitcherComponent implements OnInit {
constructor(private theme: ThemeService) { }
ngOnInit(): void {
}
public switchTheme(): void {
if (this.theme.current === 'light') {
this.theme.current = 'dark';
} else {
this.theme.current = 'light';
}
}
}
theme-switcher.component.html
<button class="btn btn-secondary" (click)="switchTheme()">Switch theme</button>
We can now use this component anywhere on our page, for example on the homepage or in the navigation bar.
Example
This post shows only the essential steps. I've published a more complete example on GitHub, which uses 4 different themes, and mixes SASS and SCSS.
If something isn't working, let me know by opening an issue there!
Have fun coding :)