Taco Steemers

A personal blog.
☼ / ☾

Dark and light web themes: consider using a hybrid CSS/JS implementation

Instead of using either CSS media queries for operating system theme preferences or a Javacript-based theme selector we can use both. An automatic CSS-based switching and JS-based switching where the user can choose.

An excellent article on website dark mode and light mode implementation can be found here on ccs-tricks . It describes a style sheet-based implementation and a Javascript-based implementation. The style sheet-based implementation uses the user's operating system preferences to automatically select a dark or light theme. The Javascript-based implementation allows a user to select the theme they want to use.

Adding to that article, I want to advocate for a hybrid approach where we use both. An automatic CSS-based switching and JS-based switching where the user can choose.

The advantage

The advantage is we can add theme selection, and the default theme can be the preferred theme as configured on the operating system level. This way we have all options open for users that browse with JS on. Users that browse with JS disabled will still get the style that they have selected as the preferred style in their operating system preferences.

The themeswitcher control

The tutorial linked to at the beginning of this article shows an example implementation. The tutorial code removes CSS classes and adds CSS classes when the user switches to a different theme.

My own implementation changes the entire stylesheet. This is done by changing the stylesheet href.

The stylesheet is linked as follows: <link rel="stylesheet" id="css_colors" href="/css/colors/1.css" />

It can be changed with Javascript in the following way: document.getElementById("css_colors").href="/css/colors/2.css";

The manual themeswitcher on this website is currently a big dropdown control. That is not necessary. It can be a button, an anchor link or a simple icon.

The downside to my hybrid approach

The downside to my hybrid approach is that there is some duplication. For the CSS-only functionality we need a stylesheet that uses switching based on media queries. For the JS-based functionality we need to be able to load a stylesheet specific to the chosen theme. The contents of these stylesheets would partially overlap. As a proponent of the 'do not repeat yourself' idea this is a downside. Duplicated source code makes it easy to create inconsistencies.

Solution to the downside

To avoid having to write duplicate stylesheet contents we can generate the required stylesheets from de-duplicated input files. My scripts for generating the css can be found here .

Let's take a look at how this would work.

The three color stylesheets can be generated from three input files:

  1. A file containing the CSS rules that apply the color variables
  2. A file containing the light mode color variables
  3. A file containing the dark mode color variables.

We might have four stylesheet files for the website:

  • The general stylesheet that does not contain color information
  • The general color stylesheet that contains both light and dark mode color information, for operating system preference support. This is input files 1, 2 and 3 combined in a specific structure.
  • The light mode stylesheet for the javascript switching support. This is input file 1.
  • The dark mode stylesheet for the javascript switching support. This is input file 2.

Based on this idea, the general color stylesheet would look like this:

/* This section is identical to the light mode CSS file contents */
:root {
    color-scheme: light dark;

    --code-text-color: black;
    --background-color: white;

@media (prefers-color-scheme: dark) {
    /* This section is identical to the dark mode CSS file contents */
    :root {
        color-scheme: light dark;

        --text-color: white;
        --background-color: black;

/* This section contains the rules for applying the color variables */
body {
    color: var(--text-color);
    background-color: var(--background-color);


If we only support the automatic theme selection a user might be forced to use a theme that they don't want to use. The user might prefer their operating system controls in a dark theme, but that does not guarantee that they want to see our website in our website's dark theme. For that reason I prefer having the automatic system as well as giving the user the option to choose a theme. It takes a bit more work to support both, but it helps make our websites more accessible.