How to Implement Theme Switching with CSS Custom Properties (`var()`)

Introduction

In frontend development, the ability to switch between light and dark modes is becoming a necessity. This article explains how to efficiently implement this feature using CSS Custom Properties, also known as CSS variables (var()).

Why Use CSS Variables?

The biggest advantage of CSS variables is their maintainability and scalability. For example, by defining a theme color in one place, like --color-background-primary: #ffffff;, you can use that variable throughout your entire site.

:root {
  --color-background-primary: #ffffff;
  --color-text-primary: #000000;
}

body {
  background-color: var(--color-background-primary);
  color: var(--color-text-primary);
}

This way, if you want to switch themes, you only need to change the variable values by adding a class like [data-theme='dark'] to the :root or body tag.

Combining with SCSS

In my blog project "Nurohive," I use SCSS to implement theme switching. By combining SCSS variables and CSS variables, I can further organize and manage color definitions.

Here is an excerpt from _theme_variables.scss where theme colors are defined.

// nurohive-next/src/app/styles/_theme_variables.scss

// Define light mode theme colors
$light-theme: (
  color-background-primary: #f6f6f6,
  color-background-secondary: #ffffff,
  color-text-primary: #121212,
  color-link-hover: #007bff,
  color-border-primary: #e0e0e0,
);

// Define dark mode theme colors
$dark-theme: (
  color-background-primary: #121212,
  color-background-secondary: #1a1a1a,
  color-text-primary: #f6f6f6,
  color-link-hover: #007bff,
  color-border-primary: #333333,
);

// Mixin to output as CSS Custom Properties
@mixin set-theme($theme) {
  @each $key, $value in $theme {
    --#{$key}: #{$value};
  }
}

// Manage theme switching
[data-theme="light"] {
  @include set-theme($light-theme);
}

[data-theme="dark"] {
  @include set-theme($dark-theme);
}

This SCSS code uses a mixin to convert the $light-theme and $dark-theme maps into CSS custom properties. By doing so, simply toggling the [data-theme='dark'] attribute on the HTML tag will dynamically change the site's entire style.

Implementation Steps

Here are the specific implementation steps:

  1. Create SCSS files:
    • Create _theme_variables.scss and global-styles.scss.
  2. Define theme variables:
    • Define the light and dark mode colors as SCSS maps in _theme_variables.scss.
  3. Convert to CSS Custom Properties:
    • Use a mixin to convert the SCSS maps into CSS Custom Properties and link them to the [data-theme] attribute.
  4. Implement a useTheme component:
    • Use Next.js's context or useState to create a component that manages the theme state. This component will be responsible for dynamically adding attributes like [data-theme='dark'] to the <html> tag.
  5. Import into _app.tsx or layout.tsx:
    • Import the created global-styles.scss into layout.tsx to apply the styles to the entire site.

Conclusion

Using CSS Custom Properties makes the theme-switching feature dramatically simpler. Understanding this mechanism is essential for building a design system and creating maintainable frontend applications in future projects.

I hope this development log helps you on your own journey to "survive setup hell."