DEV Community

Cover image for CSS variables vs ThemeContext
Late Night Coder
Late Night Coder

Posted on

4 1

CSS variables vs ThemeContext

The light mode and dark mode are gaining popularity and more apps are offering these theme switching. This theme switching looks cool but is difficult to implement and hard to get right. There are many libraries(emotion.js) that let you do this with ease by giving a ThemeProvider which is nothing but a React component that provides theme context. These libraries use CSS-in-JS which is a beautiful way of writing CSS with javascript.

I have been using CSS-in-JS for most of my projects and I’m in love with it but over time CSS has improved, the browsers have matured and support for CSS is better than before. The cost of implementing theme switching with CSS-in-JS libraries is considerably more than using browser standard CSS variables.

Let’s take the example of CSS-in-JS theme switching.

import { jsx, ThemeProvider } from "@emotion/react";
import styled from "@emotion/styled";
import { useState } from "react";

const themes = {
  light: {
    colors: {
      primary: "#48ff00",
      background: "#fff"
    }
  },
  dark: {
    colors: {
      primary: "#ff0000",
      background: "#000"
    }
  }
};

const Heading1 = styled.h1(({ theme }) => ({
  color: theme.colors.primary,
  backgroundColor: theme.colors.background
}));

const Paragraph = styled.p(({ theme }) => ({
  color: theme.colors.primary,
  backgroundColor: theme.colors.background
}));

const Div = styled.div(({ theme }) => ({
  backgroundColor: theme.colors.background
}));

const Button = styled.button(({ theme }) => ({
  color: theme.colors.primary,
  backgroundColor: theme.colors.background
}));

export default function App() {
  const [isLight, setIsLight] = useState(true);
  const activeTheme = isLight ? "light" : "dark";

  return (
    <ThemeProvider theme={themes[activeTheme]}>
      <Div>
        <Div>
          <Button onClick={() => setIsLight((prev) => !prev)}>
            {activeTheme}
          </Button>
        </Div>
        <Heading1>CSS In JS</Heading1>
        <Paragraph>
          Emotion is a library designed for writing css 
        styles with JavaScript. It provides powerful 
        and predictable style composition in addition 
        to agreat developer experience with features 
        such as source maps, labels,and testing utilities. 
        Both string and object styles are supported.
        </Paragraph>
      </Div>
    </ThemeProvider>
  );
}
Enter fullscreen mode Exit fullscreen mode

That’s the beauty of CSS-in-js it’s just javascript. The developer experience is pretty amazing with such API. However the user experience takes a hit when there are many components on the page, so switching the theme takes a while sometimes a noticeable delay. This leads to a poor user experience which is bad for our brand and business. Here is codesandbox for the CSS-in-JS example.

Now let’s do it with CSS variables.

import { jsx } from "@emotion/react";
import styled from "@emotion/styled";
import { useState, useEffect } from "react";
import "./theme.css";

/*
  theme.css

  body[data-theme="light"] {
    --color--primary: #48ff00;
    --color--background: #fff;
  }

  body[data-theme="dark"] {
    --color-primary: #ff0000;
    --color-background: #000;
  }
*/

const Heading1 = styled.h1({
  color: "var(--color-primary)",
  backgroundColor: "var(--color-background)"
});

const Paragraph = styled.p({
  color: "var(--color-primary)",
  backgroundColor: "var(--color-background)"
});
const Div = styled.div({
  backgroundColor: "var(--color-background)"
});

const Button = styled.button({
  color: "var(--color-primary)",
  backgroundColor: "var(--color-background)"
});

function ThemeToggler() {
  const [isLight, setIsLight] = useState("light");

  useEffect(() => {
    document.body.dataset.theme = isLight ? "light" : "dark";
  }, [isLight]);

  return (
    <Button onClick={() => setIsLight((prev) => !prev)}>
      {isLight ? "light" : "dark"}
    </Button>
  );
}

export default function App() {
  return (
    <Div>
      <Div>
        <ThemeToggler />
      </Div>
      <Heading1>CSS Variable</Heading1>
      <Paragraph>
        Emotion is a library designed for writing css 
        styles with JavaScript. It provides powerful 
        and predictable style composition in addition 
        to agreat developer experience with features 
        such as source maps, labels,and testing utilities. 
        Both string and object styles are supported.
      </Paragraph>
    </Div>
  );
}
Enter fullscreen mode Exit fullscreen mode

Here the developer experience may suffer because of loss of static typing on theme object but the user experience is considerably better. Also, a developer doesn’t need to learn API styled.button(({**theme**}) => ({ ...styles })) where we create a function accepting theme and returning styles. Here is a link to codesandbox.

React profiler matrix⚛️

CSS-in-JS way of theme switching

CSS-in-JS way of theme switching

CSS variables of theme switching

CSS variable way of theme switching

By seeing the above two screenshots it is very clear that using CSS variable is better than using CSS-in-JS way. A better developer experience can be achieved by a hybrid of two. Following gives you the ability for static type on theme object as theme.colors.primary.

import { jsx } from "@emotion/react";
import styled from "@emotion/styled";
import { useState, useEffect } from "react";
import { theme } from "./theme";
import "./theme.css";
/*
  theme.css

  body[data-theme="light"] {
    --color--primary: #48ff00;
    --color--background: #fff;
  }

  body[data-theme="dark"] {
    --color-primary: #ff0000;
    --color-background: #000;
  }
*/

/*
  theme.js
  export const theme = {
    colors: {
      primary: "var(--color-primary)",
      background: "var(--color-background)"
    }
  };
*/

const Heading1 = styled.h1({
  color: theme.colors.primary,
  backgroundColor: theme.colors.background
});

const Paragraph = styled.p({
  color: theme.colors.primary,
  backgroundColor: theme.colors.background
});

const Div = styled.div({
  backgroundColor: theme.colors.background
});

const Button = styled.button({
  color: theme.colors.primary,
  backgroundColor: theme.colors.background
});

function ThemeToggler() {
  const [isLight, setIsLight] = useState("light");

  useEffect(() => {
    document.body.dataset.theme = isLight ? "light" : "dark";
  }, [isLight]);

  return (
    <Button onClick={() => setIsLight((prev) => !prev)}>
      {" "}
      {isLight === "light" ? "dark" : "light"}
    </Button>
  );
}

export default function App() {
  return (
    <Div>
      <Div>
        <ThemeToggler />
      </Div>
      <Heading1>CSS var and CSS in JS</Heading1>
      <Paragraph>
        Emotion is a library designed for writing css 
        styles with JavaScript. It provides powerful 
        and predictable style composition in addition 
        to agreat developer experience with features 
        such as source maps, labels,and testing utilities. 
        Both string and object styles are supported.
      </Paragraph>
    </Div>
  );
}
Enter fullscreen mode Exit fullscreen mode

Conclusion

CSS-in-JS is awesome but it comes with the cost of injecting styles with every render and theme switching using ThemeContext is not performant especially if there are a large number of components on a screen. Theme switching is very performant with CSS variables. Let’s use more CSS variables to develop awesome web apps themes.

Credit: Image by ailonwebs.com

Hostinger image

Get n8n VPS hosting 3x cheaper than a cloud solution

Get fast, easy, secure n8n VPS hosting from $4.99/mo at Hostinger. Automate any workflow using a pre-installed n8n application and no-code customization.

Start now

Top comments (0)

AWS Security LIVE!

Join us for AWS Security LIVE!

Discover the future of cloud security. Tune in live for trends, tips, and solutions from AWS and AWS Partners.

Learn More

AWS GenAI LIVE!

GenAI LIVE! is a dynamic live-streamed show exploring how AWS and our partners are helping organizations unlock real value with generative AI.

Tune in to the full event

DEV is partnering to bring live events to the community. Join us or dismiss this billboard if you're not interested. ❤️