[React] How to implement color themes using SCSS

Jay Kim
4 min readOct 21, 2023

This article provides a guide on implementing a dark theme and a light theme in a React application.

Implementation

React

First of all, let’s create a React context. It will have a boolean flag to determine the selected theme and a function for toggling themes.

import React, { createContext, useState } from "react";

interface ContextProps {
darkTheme: boolean;
toggleTheme: () => void;
}

export const ThemeContext = createContext<ContextProps>({
darkTheme: true,
toggleTheme: () => {},
});

interface Props {
children?: React.ReactNode;
}

const ThemeProvider: React.FC<Props> = ({ children }) => {
const [darkTheme, setDarkTheme] = useState(true);

const toggleThemeHandler = () => {
setDarkTheme((prevState) => !prevState);
};

return (
<ThemeContext.Provider
value={{
darkTheme: darkTheme,
toggleTheme: toggleThemeHandler,
}}
>
{children}
</ThemeContext.Provider>
);
};

export default ThemeProvider;

Don’t forget to use the context.

import React from "react";
import ReactDOM from "react-dom/client";
import "./index.scss";
import App from "./App";
import ThemeProvider from "./context/ThemeContext";

const root = ReactDOM.createRoot(
document.getElementById("root") as HTMLElement
);
root.render(
<ThemeProvider>
<App />
</ThemeProvider>
);

The main idea is to set the data-theme attribute to dark or light depending on the chosen theme.

import React, { useContext, useEffect } from "react";
import { ThemeContext } from "./context/ThemeContext";

import styles from "./App.module.scss";

function App() {
const { darkTheme, toggleTheme } = useContext(ThemeContext);

useEffect(() => {
document.documentElement.setAttribute(
"data-theme",
darkTheme ? "dark" : "light"
);
}, [darkTheme]);

return (
<div className={styles.container}>
<div className={styles.card}>
<h2>Welcome to the app</h2>
<p className={styles.text__primary}>Primary texts</p>
<p className={styles.text__secondary}>Secondary texts</p>
</div>
<button className={styles.button} onClick={toggleTheme}>
Toggle Theme
</button>
</div>
);
}

export default App;

If everything is set up correctly, clicking Toggle Theme button will toggle the data-theme attribute.

Dark theme enabled
Light theme enabled

SCSS

In this part, we will create two SCSS files.

File structure

Let’s create a new file _colors.scss to define some colors that we intend to use. Typically, these color choices will come from the design system (ideally provided by product designers!).

/* Dark Theme Colors */
$neutral-0: #FFFFFF;
$neutral-500: #949497;
$neutral-700: #626264;
$neutral-900: #1A1A1C;

$dark-overlay-8: #2B2B2E;

$primary-500: #846bda;

/* Light Theme Colors */
$white-1: #FFFFFF;
$white-2: #F8F8F8;

$black-1: #232323;

$grey-2: #B4B4B7;
$grey-3: #949497;

$blue-1: #1274CF;

Next, let’s create _themes.scss file where we will declare some CSS variables based on the data-theme attribute. We will utilize the colors declared in _colors.scss.

@use 'colors' as *;

html[data-theme="dark"] {
--background-color: #{$neutral-900};
--color: #{$neutral-0};

--primary-color: #{$primary-500};
--secondary-color: #{$neutral-500};

--card-background-color: #{$dark-overlay-8};
--card-border-color: #{$neutral-700};
}

html[data-theme="light"] {
--background-color: #{$white-2};
--color: #{$black-1};

--primary-color: #{$blue-1};
--secondary-color: #{$grey-3};

--card-background-color: #{$white-1};
--card-border-color: #{$grey-2};
}

Depending on the selected theme, it is necessary to update both the background-color and default font color. In index.scss, import _themes.scss and assign the values of these properties to the CSS variables previously defined in the _themes.scss.

@use 'scss/themes' as *;

body {
background-color: var(--background-color);
color: var(--color);
overscroll-behavior: none;

margin: 0;
font-family: 'Roboto', sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}

From here, additional styling can be applied to each component. The core concept remains same: utilization of CSS variables declared in the _themes.scss.

.container {
width: 100vw;
height: 100vh;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
gap: 20px;
}

.card {
width: 40vw;
height: 40vh;
background-color: var(--card-background-color);
border: 1px solid var(--card-border-color);
border-radius: 4px;
padding: 16px;
display: flex;
flex-direction: column;
}

.text {
&__primary {
color: var(--primary-color);
}

&__secondary {
color: var(--secondary-color);
}
}

.button {
cursor: pointer;
width: 196px;
padding: 12px 16px;

font-size: 1.2rem;

border-radius: 4px;
border: 1px solid var(--primary-color);
outline: none;

color: var(--primary-color);
background-color: transparent;
}

Demo

Improvement

It would be better to store the selected theme in the local storage, allowing it to serve as the default theme the next time a user visits the application.

--

--