DEV Community

Cover image for Implementing Dark Mode (Part 2)
Matthew Foley for OpenSauced

Posted on

Implementing Dark Mode (Part 2)

This is Part 2 of the series on Implementing Dark Mode. This was done in open-sauced/open-sauced#1020, and handily demonstrated to me the wealth of learning opportunities in contributing to Open Source. I for one have learned a ton! On this one, I got the opportunity for learning in several areas. I was still pretty new to React (and I still am), so I had not yet used the Context API. For a lot of the same reasons, I hadn't used the styled-components library before.

Throughout the rest of these points, the thing to bear in mind is that for the most part, the app being in dark mode just means that the HTML body element has a CSS class name including "dark".

One implementation detail that I feel was a win was that the only React component that had any kind of interaction with the ThemeContext was a set of buttons that toggle the theme. I like to think this helps with separation of concerns. Here's a code snippet from the buttons component:

import React, {useContext} from "react";
import ThemeContext from "../ThemeContext";
import {FlexCenter} from "../styles/Grid";
import darkMode from "../images/darkMode.svg";
import lightMode from "../images/lightMode.svg";
import themeAuto from "../images/themeAuto.svg";
function ThemeButtonGroup() {
  const [theme, setTheme] = useContext(ThemeContext);
  return (
    <FlexCenter style={{marginRight:"0.5rem"}}>
      <a
        style={{margin:"0 .5rem"}}
        disabled={theme === "dark"}
        onClick={(event) => {
          event.preventDefault();
          setTheme("dark");
        }}>
        <img
          src={darkMode} 
          alt="dark mode" 
          style={{
            backgroundColor:(theme === "dark") 
            ? "#ccc" 
            : "transparent"
          }}/>
      </a>
// ...
    </FlexCenter>
  );
}
Enter fullscreen mode Exit fullscreen mode

Another implementation detail was related to coloring of images. Open Sauced makes use of many SVG images, of differing flavors. In the cases where SVG files are in the static assets of Open Sauced (e.g. <img alt="open sauced" className="svg" src={mortarBoard} />), the coloring of these is controlled using the filter CSS property. On the other hand, we also make use of @primer/octicons-react.

Here's a sample of one of these icons in component code:

import {SearchIcon} from "@primer/octicons-react";
// ...
<SearchIcon size="large" verticalAlign="middle" className="svg" />
Enter fullscreen mode Exit fullscreen mode

These inject code directly into markup as <svg>...</svg>, requiring use of CSS property fill.
Finally here's the CSS code snippet where the <img> and <svg> tags are handled differently.

body.dark img.svg {
  filter: invert();
}
body.dark svg.svg {
  fill: var(--lightestGrey);
}
Enter fullscreen mode Exit fullscreen mode

I referred quite a bit to this article: Color Control of SVGs.

One last fun implementation detail was working with our use of react-loading-skeleton (I love this effect, and I feel it really does work in keeping the user engaged and under the impression of the app working while data loads). To make this effect still work well in dark mode, I took the opportunity to crack open the source, and replicate a few key values as found in this snippet of our CSS.

body.dark .react-loading-skeleton {
  background-color: var(--backgroundGrey);
  background-image: linear-gradient(
    90deg,
    var(--backgroundGrey),
    var(--grey),
    var(--backgroundGrey)
  );
}
Enter fullscreen mode Exit fullscreen mode

Again, working on this PR really cemented my personal belief and experience that contributing to Open Source Software can provide amazing opportunities for learning by doing!

Oldest comments (5)

Collapse
 
ksengine profile image
Kavindu Santhusa • Edited
Collapse
 
mtfoley profile image
Matthew Foley

Thanks for sharing! I will have to remember your techniques here on the next project.

Collapse
 
0vortex profile image
TED Vortex (Teodor Eugen Duțulescu)

This technique was used in chrome and safari dark mode libraries, they have some adjustments but ultimately fall short due to different light mode contrast settings - it's really good as a generic setting, for example implementing your own cross site dark mode, but from a product perspective potentially hurts users and limits design.

Check out some open source projects doing exactly that: github.com/darkreader/darkreader - darkreader.org/help/en/

Collapse
 
ksengine profile image
Kavindu Santhusa

Totally agreed.

Collapse
 
nickytonline profile image
Nick Taylor

Nice!

Actor from Game of Thrones saying Nice!