How to Implement Dark Mode in React, Angular, and Pre-processed CSS

Implementing dark mode has become essential in modern web applications, offering users a visually appealing and comfortable experience.

Perhaps you’re working with frameworks like React and Angular or pre-processed CSS tools like Sass and LESS, the process involves a series of practical steps to achieve smooth functionality and maintainable code.

This guide will take you through setting up your project in React, Angular, or Sass/LESS and configuring the tools and dependencies needed for theming.

Let’s get started and turn your dark-mode vision into reality!

Prerequisites

Before diving into the core content of this tutorial, let’s first make sure you’re equipped with the necessary tools and frameworks. You’ll need a few things set up depending on which stack or preprocessor you’re working with. Don’t worry – we’ll walk you through each one.

Tools and Frameworks

For React:

To follow along with React, you'll need a few key tools:

Node.js

React relies on Node.js for running JavaScript outside the browser and managing packages with npm. To install it, head over to the Node.js website and download the latest version. After installation, you can verify it by running:

```bash

node -v

npm -v

```

React

We’ll be using React, of course. If you haven’t already, you can install it by setting up a new React project using the following command:

```bash

npx create-react-app my-app

```

This will scaffold out the basic project structure and install all dependencies. Navigate into your new app folder:

```bash

cd my-app

```

Text Editor

A text editor is a must. While there are many options out there, we recommend VS Code because of its excellent support for JavaScript, React, and extensions.

For Angular

If Angular is your framework of choice, you’ll need the following:

  • Angular CLI: The Angular Command Line Interface (CLI) is a powerful tool for creating, building, and managing Angular applications. Install it globally by running:

```bash

npm install -g @angular/cli

```

  • Node.js: Just like with React, Angular requires Node.js to manage dependencies. Follow the same steps mentioned above to install it.

Basic Angular App: Create a new Angular application by running:

```bash

ng new my-angular-app

cd my-angular-app

```

This will generate a starter Angular project that’s ready to go.

For Sass/LESS

When working with CSS preprocessors like Sass or LESS, there are a couple of things you’ll need to set up:

  • Dart Sass (for Sass): Dart Sass is the reference implementation of Sass and is what we’ll be using here. Install it globally by running:

```bash

npm install -g sass

```

  • LESS.js (for LESS): Alternatively, if you’re using LESS, you’ll need to install the LESS compiler:

```bash

npm install -g less

```

Again, a simple HTML file with a linked LESS file is all you need to get started.

React Setup

  • Initialise a React App

To start, create a new React application. If you haven’t already, ensure Node.js is installed on your machine. Then, run the following command in your terminal:

```bash

npx create-react-app my-app

```

Replace my-app with your desired project name. This command sets up a fully functional React app with a default folder structure and all essential dependencies.

  • Install Dependencies

For theming, you can use libraries like styled-components or Tailwind CSS. Install them using the following commands:

For styled-components:

```bash

npm install styled-components

```

  • For Tailwind CSS, follow these steps:

- Install Tailwind via npm:

```bash

npm install -D tailwindcss postcss autoprefixer

npx tailwindcss init

```

- Configure the tailwind.config.js file and import Tailwind into your CSS:

```css

@tailwind base;

@tailwind components;

@tailwind utilities;

```

Once these are in place, you’re ready to start styling your React components with ease.

Angular Setup

Create an Angular Project

Ensure you have the Angular CLI installed. Then, create a new project by running:

```bash

ng new my-angular-app

```

Follow the prompts to set up the project, such as adding Angular routing and choosing your preferred CSS preprocessor.

  • Install Theme Libraries

Angular Material is a popular choice for UI theming. Install it with:

```bash

ng add @angular/material

```

The CLI will guide you through selecting a theme, configuring global typography, and adding animations.

For custom theming, consider importing your stylesheets or using additional libraries like ng-bootstrap or PrimeNG based on your project’s needs.

Sass/LESS Setup

  • Install the Compiler

If you’re using Sass, install the compiler globally or locally in your project:

```bash

npm install -g sass

```

For LESS:

```bash

npm install -g less

```

  • Compile Your Styles

Create your .scss or .less files and compile them to CSS using the following commands:

For Sass:

```bash

sass styles.scss styles.css

```

For LESS:

```bash

lessc styles.less styles.css

```

  • Integrate with Your Project

Link the compiled CSS file in your project’s HTML file, or import it directly into your application. This allows you to write modular and reusable styles, streamlining your workflow.

With these steps completed, your project environment is ready, and tailored to your preferred stack.

Designing the Theme Structure

When implementing themes in a web application, designing a flexible and scalable theme structure is the first step. Let’s break down the essentials for creating a theme structure, starting with CSS variables and moving into framework-specific configurations.

CSS Variables for Themes

CSS variables are an excellent choice for managing light and dark themes because they provide a reusable, declarative way to define and switch theme properties. Here’s how you can create and organize them effectively.

Defining CSS Variables

Start by defining CSS variables for key theme properties, such as colours for the background, text, and interactive elements. Place these variables within : root for the default theme (e.g., light) and within a custom class (e.g., .dark-theme) for the alternate theme.

```css

/* Light Theme */

: root {

--bg-color: #ffffff;

--text-color: #000000;

--accent-color: #007bff;

}

/* Dark Theme */

.dark-theme {

--bg-color: #121212;

--text-color: #e0e0e0;

--accent-color: #bb86fc;

}

```

These variables will act as your single source of truth for theming, making updates straightforward and consistent.

Applying Variables

You can use the variables in your CSS properties by referencing them with the var() function. This ensures your styles automatically adapt to the active theme.

```css

body {

background-color: var(--bg-color);

color: var(--text-color);

}

button {

background-color: var(--accent-color);

border: none;

color: var(--text-color);

}

```

Switching between themes becomes as simple as toggling the .dark-theme class on the root element (e.g., <html> or <body>), which we’ll cover later.

Framework-Specific Configurations

When working with frameworks like React, and Angular, or preprocessors like Sass/LESS, you can take your theming strategy to the next level by incorporating dynamic logic and reusable structures.

React: Using a Theme Object

In React, you can centralize theme configurations using a dedicated theme.js file. This file houses theme objects for light and dark themes.

```javascript

// theme.js

export const lightTheme = {

bgColor: "#ffffff",

textColor: "#000000",

accentColor: "#007bff",

};

export const darkTheme = {

bgColor: "#121212",

textColor: "#e0e0e0",

accentColor: "#bb86fc",

};

```

Use a context or state management solution to dynamically apply themes across your app. For example:

```javascript

import { useState } from "react";

import { lightTheme, darkTheme } from "./theme";

function App() {

const [theme, setTheme] = useState(lightTheme);

const toggleTheme = () => {

setTheme(theme === lightTheme ? darkTheme : lightTheme);

};

return (

<div style={{ backgroundColor: theme.bgColor, colour: theme.textColor }}>

<button onClick={toggleTheme}>Switch Theme</button>

</div>

);

}

```

This approach gives you precise control over theme behaviour and is particularly useful for component-based styling.

Angular: Theme Service

In Angular, creating a dedicated service to handle theme switching simplifies your logic. Define your themes in a JSON-like structure and use a service to toggle between them.

Theme Configuration Example:

```typescript

export const themes = {

light: {

"--bg-color": "#ffffff",

"--text-color": "#000000",

"--accent-color": "#007bff",

},

dark: {

"--bg-color": "#121212",

"--text-color": "#e0e0e0",

"--accent-color": "#bb86fc",

},

};

```

Theme Service Example:

```typescript

import { Injectable } from '@angular/core';

import { themes } from './themes';

@Injectable({ providedIn: 'root' })

export class ThemeService {

applyTheme(theme: string) {

const themeProperties = themes[theme];

for (const key in themeProperties) {

document.documentElement.style.setProperty(key, themeProperties[key]);

}

}

}

```

In your component, call the service method to switch themes:

```typescript

this.themeService.applyTheme('dark');

```

Sass/LESS: Variables and Mixins

Sass and LESS preprocessors allow you to define variables and mixins for themes, offering more flexibility for advanced designs.

Sass Example

```scss

$light-theme: (

bg-color: #ffffff,

text-color: #000000,

accent-color: #007bff,

);

$dark-theme: (

bg-color: #121212,

text-color: #e0e0e0,

accent-color: #bb86fc,

);

@mixin theme($theme) {

background-color: map-get($theme, bg-color);

color: map-get($theme, text-color);

}

body {

@include theme($light-theme);

}

.dark-theme {

@include theme($dark-theme);

}

```

Switching themes dynamically requires additional configuration using JavaScript to apply classes or variables.

Implementing Theme Switching

Switching between light and dark themes (or even custom themes) is an essential feature in modern web applications. Let’s explore how to implement this functionality in React, Angular, and Sass/LESS, catering to different development environments.

React Implementation: Managing Themes with Context API

In React, the Context API offers a robust way to manage global states like themes. Here’s how you can implement theme switching step by step:

  • Set Up Context for Theme State

Start by creating a ThemeContext to hold the theme's state and provide it across your application.

export cons ```jsx

import React, { createContext, useState, useContext } from 'react';

const ThemeContext = createContext();

export const ThemeProvider = ({ children }) => {

const [theme, setTheme] = useState('light');

const toggleTheme = () => {

setTheme((prevTheme) => (prevTheme === 'light' ? 'dark': 'light'));

};

return (

<ThemeContext.Provider value={{ theme, toggleTheme }}>

{children}

</ThemeContext.Provider>

);

};

t useTheme = () => useContext(ThemeContext);

```

  • Create a Theme Toggle Button

Add a button that allows users to switch themes.

```jsx

import React from 'react';

import { useTheme } from './ThemeProvider';

const ThemeToggleButton = () => {

const { theme, toggleTheme } = useTheme();

return (

<button onClick={toggleTheme}>

Switch to {theme === 'light'? 'Dark': 'Light'} Mode

</button>

);

};

export default ThemeToggleButton;

```

  • Apply Themes with Styled-Components

Use styled-components to dynamically apply styles based on the theme.

```jsx

import styled, { ThemeProvider as StyledThemeProvider } from 'styled-components';

import { useTheme } from './ThemeProvider';

const lightTheme = {

background: '#ffffff',

color: '#000000',

};

const darkTheme = {

background: '#000000',

colour: '#ffffff',

};

const AppContainer = styled.div`

background: ${(props) => props.theme.background};

color: ${(props) => props.theme.color};

min-height: 100vh;

display: flex;

justify-content: center;

align-items: center;

`;

const App = () => {

const { theme } = useTheme();

return (

<StyledThemeProvider theme={theme === 'light' ? lightTheme : darkTheme}>

<AppContainer>

<h1>Hello, Theme Switcher!</h1>

<ThemeToggleButton />

</AppContainer>

</StyledThemeProvider>

);

};

export default App;

```

Angular Implementation: Dynamic Theme Management with Services

In Angular, services are ideal for managing the application's theme state. Here’s how you can achieve this:

  • Create a Theme Service

The service will hold the theme state and provide methods to toggle it.

```typescript

import { Injectable } from '@angular/core';

@Injectable({ providedIn: 'root' })

export class ThemeService {

private theme = 'light';

toggleTheme() {

this.theme = this.theme === 'light' ? 'dark': 'light';

document.body.className = this.theme; // Apply a theme to the body

}

getTheme() {

return this.theme;

}

}

```

  • Add Theme Toggle Button

Create a button in your component to toggle the theme.

```typescript

import { Component } from '@angular/core';

import { ThemeService } from './theme.service';

@Component({

selector: 'app-theme-toggle',

template: <button (click)="toggleTheme()">Switch Theme</button>,

})

export class ThemeToggleComponent {

constructor(private themeService: ThemeService) {}

toggleTheme() {

this.themeService.toggleTheme();

}

}

```

  • Integrate Angular Material Theming (Optional)

Leverage Angular Material for built-in theme support.

```scss

@use '@angular/material' as mat;

$light-theme: mat.define-light-theme((

color: (primary: mat.$blue-palette, accent: mat.$pink-palette),

));

$dark-theme: mat.define-dark-theme($light-theme);

: root {

@include mat.core();

@include mat.all-component-themes($light-theme);

}

.dark {

@include mat.all-component-themes($dark-theme);

}

```

Sass/LESS Implementation: Style with Conditional Attributes

With Sass or LESS, you can dynamically update styles based on a custom data-theme attribute.

  • Set Up Theme Switching Logic

Use JavaScript to toggle a data-theme attribute on the HTML element.

```javascript

const toggleTheme = () => {

const htmlElement = document.documentElement;

const currentTheme = htmlElement.getAttribute('data-theme');

htmlElement.setAttribute('data-theme', currentTheme === 'light' ? 'dark': 'light');

};

document.querySelector('#theme-toggle-btn').addEventListener('click', toggleTheme);

```

  • Write Sass/LESS for Themes

Define styles for each theme.

```scss

[data-theme='light'] {

background: #ffffff;

color: #000000;

}

[data-theme='dark'] {

background: #000000;

color: #ffffff;

}

```

  • Include a Theme Toggle Button

Add a button to trigger the theme switch.

```html

<button id="theme-toggle-btn">Toggle Theme</button>

```

By using these methods, you can implement theme switching effectively in your chosen framework or styling methodology.

Debugging Common Issues

Even with careful planning, you may encounter issues during implementation. Here’s how to debug some common challenges:

Conflicting Styles

If you notice that some elements are not switching to dark mode, inspect them using browser DevTools:

- Right-click the problematic element and select “inspect”.

- Check for specificity conflicts in CSS, such as inline styles or styles applied via JavaScript.

Solution: Use the !important modifier sparingly to override problematic styles, or adjust your CSS selectors for better specificity.

```css

body.dark-mode .header {

background-color: #222 !important;

}

```

Incorrect State Management

Sometimes, toggling between dark and light modes may not persist or update correctly. This often results from incorrect use of state or local storage.

Example:

- Ensure that your JavaScript logic updates the classList on the <body> tag consistently.

- Verify that the user's preference is correctly stored and retrieved:

```javascript

const theme = localStorage.getItem('theme');

if (theme) {

document.body.classList.add(theme);

}

document.getElementById('toggle-dark-mode').addEventListener('click', () => {

const currentTheme = document.body.classList.contains('dark-mode') ? 'dark-mode' : 'light-mode';

localStorage.setItem('theme', currentTheme);

document.body.classList.toggle('dark mode);

});

```

Conclusion

Incorporating dark mode into your app or website can greatly enhance user experience, especially in low-light environments. Whether using frameworks like React and Angular or pre-processed CSS with Sass or LESS, the implementation is straightforward and flexible.

With just a few lines of code, you can give users control over their viewing preferences, making your project more modern and accessible.

0
Subscribe to my newsletter

Read articles from Onueze Mmasinachukwu directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Onueze Mmasinachukwu
Onueze Mmasinachukwu

I'm Onueze Mmasinachukwu, a Technical writer and a frontend developer. My articles are focused on web development. I love reading, writing and sharing my ideas with people.