DEV Community

Wendell Adriel
Wendell Adriel

Posted on

Working with multiple CSS themes

CodeShare

I have a project called CodeShare, to create free and high-quality content in Portuguese, to reach more people in Brazil that didn’t have the opportunity to learn English yet.

So when I write a post there, I’ll post here as well, but in English (of course), so if you want to check the original version of this article (in Portuguese) you can check it here!

Introduction

In the first article of this series we saw how variables work on CSS and how we can use them to create themes for our projects. In this article, we will put this into practice creating some simple themes and seeing how to change between them.

Creating the Default Theme

As we saw before, the :root selector can be used to store the global variables of our themes. To create a default theme for our app, we will define some variables in this selector that will be used by our elements:

:root {
    --global-bg-color: #fff;
    --global-font-family: Verdana, sans-serif;
    --global-text-color: #222;
    --global-link-color: blue;
    --global-link-hover: lightblue;
}
Enter fullscreen mode Exit fullscreen mode

In the code above we defined some variables that will be used as the base values for our theme. That way we can use these values to style our elements. Since we're only working on an example, the number of variables we have are just a few, but in a large project, this list can be much bigger. Now, let's use our variables:

html {
    background: var(--global-bg-color);
    font-family: var(--global-font-family);
    color: var(--global-text-color);
}

a {
    color: var(--global-link-color);
    text-decoration: none;
    font-weight: 700;
}

a:hover {
    color: var(--global-link-hover);
}
Enter fullscreen mode Exit fullscreen mode

In the code above we used our variables to control the background and text colors, the font and also the color of the links. If in the middle of our project we want to customize any of these values, we just need to change the value of the variable that we want and all the elements that use it will update, pretty simple. Now let's create some elements to check our theme in action:

<!DOCTYPE html>
<html>
    <head>
        <title>Tematização com CSS</title>
        <meta charset="UTF-8" />

        <style>
            <!-- STYLE HERE -->
        </style>
    </head>

    <body>
        <h1>Tematização com variáveis CSS</h1>
        <p>
            Exemplo para ser usado em um artigo na
            <a href="https://codeshare.com.br" target="_blank">CodeShare</a>
        </p>
    </body>
</html>
Enter fullscreen mode Exit fullscreen mode

Creating a Night Mode Theme

Now that we already have our default theme, we can work in a new theme for our app. A lot of sites have a "Night Mode" to make it easier to read during the night. Let's create a theme for this.

There are some ways of working with themes, but my favorite is to use the data- attributes on HTML. These data- attributes are a convention used to define custom attributes for tags and to identify which theme we're using we will create a data-theme attribute. All the elements with this attribute and their children will be modified. Since our new theme will be for our Night Mode we will call it dark.

Since our elements are already using the variables that we created, to create a new theme we just need to update the values of the ones that we need to modify, that way the values that we change will overwrite the others and the ones we don't change will be inherited from the :root element. We will use the attribute selector on CSS to apply our theme. Since our theme is a Global Theme, we will link it to the <html> tag:

html[data-theme='dark'] {
  --global-bg-color: #444;
  --global-text-color: #ddd;
  --global-link-color: yellow;
  --global-link-hover: lightyellow;
}
Enter fullscreen mode Exit fullscreen mode

In the code above we changed the colors for the background, text and links. Now we just need to put the data-theme="dark" attribute in our <html> tag to see the result:

Letting the user switch themes

Now that we created two themes for our app, it would be interesting if we let the user switch between them right? It's not something hard to do, let's create this option. First, let's remove the data-theme="dark" attribute from our <html> tag and create the element that will let the user switch themes when clicked.

<!DOCTYPE html>
<html>
    ...
    <body>
        <span id="theme-toggle" class="theme-toggle"></span>
        ...
    </body>
</html>
Enter fullscreen mode Exit fullscreen mode

Now let's style this element, for that we will create two new variables. We will create this element as a fixed element, so the user can switch themes whenever he wants to. We will also assign different values for these variables when our dark theme is enabled, making that this element will also update its appearance:

:root {
    ...
    --global-theme-toggle-bg: #ccc;
    --global-theme-toggle-content: '🌞 THEME';
}

html[data-theme='dark'] {
    ...
    --global-theme-toggle-bg: #000;
    --global-theme-toggle-content: '🌝 THEME';
}

.theme-toggle {
    position: fixed;
    right: 20px;
    top: 20px;
    background: var(--global-theme-toggle-bg);
    padding: 10px;
    border-radius: 5px;
    font-size: 0.8rem;
    font-weight: 700;
    cursor: pointer;
}

.theme-toggle::after {
    content: var(--global-theme-toggle-content);
}
Enter fullscreen mode Exit fullscreen mode

Now that we have finished setting up the visual part of our component, let's add the needed action to make that our theme will switch when the element is clicked:

function toggleTheme () {
    const htmlTag = document.getElementsByTagName("html")[0]
    if (htmlTag.hasAttribute("data-theme")) {
        htmlTag.removeAttribute("data-theme")
        return
    }

    htmlTag.setAttribute("data-theme", "dark")
}

document
    .getElementById("theme-toggle")
    .addEventListener("click", toggleTheme);
Enter fullscreen mode Exit fullscreen mode

In the code above we created a function that will check if the <html> tag has a data-theme attribute and if so, it will remove it and if doesn't, it will add this attribute with the value dark to activate our Night Mode Theme. After that we create a listener in our element that when it gets clicked it will run the function that we just created:

Improving the Theme Switching

Congrats for us! We already let that our users can switch the theme of our app, but we still have some little details that we can improve. For example, when we switch the theme, the update is made in a very sudden way, it would be interesting to make this update in a smoother way and we can do that easily using the transition rule from CSS, check how simple it is:

html {
    ...
    transition: all 1s;
}

a {
    ...
    transition: all 1s;
}
Enter fullscreen mode Exit fullscreen mode

And it's done!!! See how now the transition between themes is being made much smoother and pleasing to the eye!!! But we can still improve something else. When the user enters in our app and change the theme and leave, we want that when he returns, the site will load the last chosen theme, for that we will use the localStorage to store this info. We will need to make a little change in our toggleTheme function and create one more function to help us:

function toggleTheme () {
    const htmlTag = document.getElementsByTagName('html')[0]
    if (htmlTag.hasAttribute('data-theme')) {
        htmlTag.removeAttribute('data-theme')
        return window.localStorage.removeItem("site-theme")
    }

    htmlTag.setAttribute('data-theme', 'dark')
    window.localStorage.setItem("site-theme", "dark")
}

function applyInitialTheme () {
    const theme = window.localStorage.getItem("site-theme")
    if (theme !== null) {
        const htmlTag = document.getElementsByTagName("html")[0]
        htmlTag.setAttribute("data-theme", theme)
    }
}

applyInitialTheme();

document
    .getElementById("theme-toggle")
    .addEventListener("click", toggleTheme);
Enter fullscreen mode Exit fullscreen mode

That's it, now we can offer a special experience for our users!!!

Applying Themes in Specific Zones

We saw above how to create and switch between two Global Themes, that means that they're applied in our application as a whole, but we can also apply themes to specific zones in our app. To show that, let's create a theme that will change only the font and let's apply that to some elements:

[data-theme='comic'] {
    --global-font-family: 'Comic Sans MS';
}

p, span {
    font-family: var(--global-font-family);
}
Enter fullscreen mode Exit fullscreen mode
<body>
    <span data-theme="comic" id="theme-toggle" class="theme-toggle"></span>
    <h1>Tematização com variáveis CSS</h1>
    <p>
        Exemplo para ser usado em um artigo na
        <a href="https://codeshare.com.br" target="_blank">CodeShare</a>
    </p>

    <p data-theme="comic">
        Exemplo de um tema aplicado em partes específicas da página! Esse texto
        tem o tema "comic"!
    </p>
</body>
Enter fullscreen mode Exit fullscreen mode

Conclusion

In our examples above, we worked with three simple themes, but in larger projects, the complexity of the themes can be much bigger. Thinking about that, I created a lib called CSS Theme Manager that helps in the management of themes based in CSS variables, check it out and if you find it interesting, use it in your projects!

GitHub logo WendellAdriel / css-theme-manager

Zero dependency lib to manage CSS themes easily for your app

CSS Theme Manager

license All Contributors

version downloads size gzip size

Watch on GitHub Star on GitHub Tweet

Zero dependency lib to manage CSS themes easily for your app

How to use

Install the package

npm install css-theme-manager --save
// or with yarn
yarn add css-theme-manager

Import it and init the CSS Theme Manager with a default theme A theme is an object with the name of your variables as keys and the value of the variables as the values.

import CSSThemeManager from 'css-theme-manager'

const themeManager = new CSSThemeManager({
  'bg-color': '#fff',
  'text-color': 'darkblue',
  'featured-font': 'Verdana, sans-serif'
})
Enter fullscreen mode Exit fullscreen mode

This will create and insert the given variables in the :root selector. All the variables created with this CSS Theme Manager will have a csstm- prefix. The code above will result in:

:root {
    --csstm-bg-color: '#fff';
    --csstm-text-color: 'darkblue';
    --csstm-featured-font: 'Verdana, sans-serif';
}
Enter fullscreen mode Exit fullscreen mode

Check the API Reference below to check all that you can…

I hope that you liked this article and if you do, don't forget about commenting and sharing this article with your friends!!! See ya! 😎

Top comments (0)