DEV Community

Cover image for Changing website theme with CSS variables
Petr Tcoi
Petr Tcoi

Posted on • Updated on

Changing website theme with CSS variables

Hello everyone. My name is Petr Tcoi. I have good experience in self-development of commercial websites. As an example and my resume, I have a website calledpetrtcoi.com. Here's the link to GitHub.

If you need a specialist, feel free to contact me!

Right now there is a cat on the site. I will make a decent photo and post it soon.

Why CSS

The ability to switch the website between different color schemes implies the ability to dynamically change the styles of most of its components. In the minimal version, it is the text color and background color.

Simplifying, this can be achieved in two ways:

  • through JavaScript, by changing the properties of the corresponding variables;
  • through CSS, by changing the values of CSS variables;

The first option requires a redraw of all components associated with the new theme. This can negatively affect performance. Moreover, additional project configuration is required since the properties of the new theme need to be passed to all site components.

The second option does not have this drawback. We simply change the values of CSS variables responsible for displaying the theme, and the site is updated automatically without redrawing any components. This method is not only faster but also easier to implement.

The screenshot below shows the result of changing the theme on my site. At the bottom, you can notice that one component was still rewritten - this is the Switcher itself, which changes the theme.

Image description

Setting up a theme with CSS

To implement this feature, I extracted variables related to the theme into a separate file.

/* src/theme/theme.css */
:root {
  --color-text-main: #bab7b7;
  --background: #1b1b1b;
  --change-theme-duration: 0.5s;
}

[data-theme="light"] {
  --color-text-main: #1b1b1b;
  --background: white;
}

Enter fullscreen mode Exit fullscreen mode

Variables for text and background colors are set here. In the basic version - for a dark theme, below - for a light theme "light".

For beauty, the characteristic --change-theme-duration: 0.5s; has also been added so that the colors do not change instantly, but with a slight delay.

Next, in the same folder, we will place an auxiliary file with a list of possible topics. We have two.

// src/theme/theme.enum.ts
export enum Theme {
    light = 'light',
    dark = 'dark'
}
Enter fullscreen mode Exit fullscreen mode

The CSS variables of the theme are written in the file with global styles.

/* src/css/globals.css */
html,
body {
  /* other styles  */
  color: var(--color-text-main);
  background-color: var(--background);
  transition: background-color var(--change-theme-duration);
}


Enter fullscreen mode Exit fullscreen mode

Ready!

All that is required now is to attach a handler to our <ThemeSwitcher /> component, which will set the attribute we require on the root element.

// src/assets/ui-components/ThemeSwitcher/ThemeSwitcher.tsx
....
import { Theme } from '../../../theme/theme.enum'
....
const toggleTheme: React.ChangeEventHandler<HTMLInputElement> = (event) => {
    const theme = event.target.checked ? Theme.dark : Theme.light
    document.documentElement.setAttribute("data-theme", theme)
    return
}
....

Enter fullscreen mode Exit fullscreen mode

The result

Image description

Pitfalls. Part 1.

Implementing theme switching in this way blurs the line of responsibility. It turns out that part of the application logic is still being handled through JavaScript, while another part is already being handled through CSS. This may create some difficulties as the project continues to grow, especially if this method is abused.

On the other hand, this method is too efficient to ignore. I think it will be enough to do the following:

  • Later on, I will connect Redux to the project and such global effects will be carried out through it. Everything will be neatly located in one place and it will be easy to find the necessary piece of code.
  • I will try to limit these CSS tricks to the boundaries of individual elements.

Pitfalls. Part 2.

The main advantage of the CSS approach also led to the complexity of its testing. Since the standard library for unit tests @testing-library simply does not see what parameters are hidden behind class names, it is not able to check what color is set for an element, whether it is in the visible area, etc.

As a result, I used e2e testing with Playwright to test the theme switching. It is not as convenient as fast unit tests, but it works.

More details about testing I will write in my next post.
Thank you for your attention.

Top comments (0)