BrowserStateService: A Single Point of Truth for Dark Mode State in Angular
netsi1964 🙏🏻
Posted on March 17, 2023
As more and more users prefer dark mode on their devices, web developers have to keep up with this trend by providing dark mode support on their web applications. This can be achieved in several ways, but maintaining the state of dark mode across the application can be a daunting task, especially as it grows larger. A good solution is to use a single point of truth in a service that maintains the state of dark mode across all components. This is where the BrowserStateService comes in.
TLDR
- The BrowserStateService is an Angular service that maintains the state of dark mode across an Angular application.
- It listens for changes to the dark class on the body element, and provides an observable that components can subscribe to in order to observe changes to the state of dark mode.
- The service provides methods for toggling the mode and setting it to a specific value.
- It listens for changes to the user's preferred color scheme at the operating system level, and sets the initial mode accordingly.
- Using this service can help maintain a single point of truth for the state of dark mode across an Angular application.
The code
browserstate.service.ts
looks like this:
import { BehaviorSubject } from 'rxjs';
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root',
})
export class BrowserStateService {
private isDarkModeSubject = new BehaviorSubject<boolean>(false);
public isDarkMode$ = this.isDarkModeSubject.asObservable();
constructor() {
// Check if 'dark' class is present on body element on initialization
this.isDarkModeSubject.next(document.body.classList.contains('dark'));
// Watch for changes to 'dark' class on body element
new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
if (mutation.attributeName === 'class') {
const isDark = document.body.classList.contains('dark');
this.isDarkModeSubject.next(isDark);
}
});
}).observe(document.body, { attributes: true });
// Listen for user's preferred color scheme
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
mediaQuery.addEventListener('change', (event) => {
const isDark = event.matches;
this.setDarkMode(isDark);
});
// Set initial mode based on user's preferred color scheme
if (mediaQuery.matches) {
this.setDarkMode(true);
}
}
toggleMode() {
const isDark = !this.isDarkModeSubject.value;
this.setDarkMode(isDark);
}
setDarkMode(isDark: boolean) {
if (isDark) {
document.body.classList.add('dark');
} else {
document.body.classList.remove('dark');
}
this.isDarkModeSubject.next(isDark);
}
}
How to use
To use the BrowserStateService in your Angular application, follow these steps:
1) Create a new file named browser-state.service.ts
in your Angular project's src/app
directory.
2) Copy the code from the example in the previous section and paste it into the browser-state.service.ts
file.
3) Import the service in any component where you want to observe or change the state of dark mode:
import { BrowserStateService } from './browser-state.service';
4) Inject the service in the component's constructor:
constructor(private browserStateService: BrowserStateService) {}
You can also import it as public
if you for instance want to use it in you template like this: {{ browserStateService.isDarkKode$ || async }}
.
5) Use the isDarkMode$
observable to observe changes to the state of dark mode, and the setDarkMode()
and toggleMode()
methods to change the state of dark mode:
// Subscribe to changes to the state of dark mode
this.browserStateService.isDarkMode$.subscribe(isDarkMode => {
// Do something based on the value of isDarkMode
});
// Set the state of dark mode
this.browserStateService.setDarkMode(true);
// Toggle the state of dark mode
this.browserStateService.toggleMode();
Example of Use
Here's an example of how you can use the BrowserStateService in an Angular component:
import { Component, OnInit } from "@angular/core";
import { BrowserStateService } from "./browser-state.service";
@Component({
selector: "app-my-component",
template: `
<div [ngClass]="{ 'dark-mode': isDarkMode }">
<p>Some text in the component.</p>
<button (click)="toggleMode()">Toggle Mode</button>
</div>
`,
})
export class MyComponent implements OnInit {
isDarkMode: boolean;
constructor(private browserStateService: BrowserStateService) {}
ngOnInit() {
// Subscribe to changes to the
// state of dark mode
this.browserStateService.isDarkMode$.subscribe((isDarkMode) => {
this.isDarkMode = isDarkMode;
});
}
toggleMode() {
this.browserStateService.toggleMode();
}
}
In this example, we're using the ngClass
directive to conditionally apply the dark-mode
class to the div
element based on the value of the isDarkMode
property. We're also using a button to call the toggleMode()
method of the BrowserStateService
to toggle the state of dark mode.
Conclusion
The BrowserStateService
is a simple and effective way to maintain the state of dark mode across an Angular application. By using a single point of truth in a service, you can avoid the complexity of managing the state of dark mode across multiple components, and provide a more consistent user experience. The BrowserStateService
listens for changes to the dark
class on the body
element, and provides methods for toggling the mode and setting it to a specific value. Additionally, it listens for changes to the user's preferred color scheme at the operating system level, and sets the initial mode accordingly.
Thank you to ChatGPT for providing the code for this useful Angular service.
Posted on March 17, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.