Black Sheep Code

Adding dark mode to a blog - how to avoid flashes of the wrong theme

I've added dark mode to this blog.

I followed this fantastic guide from LogRocket. I'm not going to repeat what they've done there, we'll just mention that they're making using of CSS variables, the @media (prefers-color-scheme: dark) query, and the package react-responsive for queries inside of React.

Here's a few additional things that I had to do.

Render conditional images client side.

I'm being a little bit clever with the main blog image on the front page - in light mode I show a sheep during the daytime and in dark mode he's in a dark room illuminated by the laptop screen.

Light mode image

Dark mode image

The server-side-rendered first render isn't aware of what the users preferred color scheme is, and so that image would aways come through as the light mode image.

The solution is to do the conditional check on the client side. We display nothing on first render (the server render) and the second render when we know what the user preference is, we display the image.

This introduces a slight problem of having an image pop of the image suddenly appearing. To mitigate this I use a CSS animation to make it fade in over 100ms.

Call me old fashioned, but I think this is the correct use of animations - to smooth out jankyness, or call attention to that something has changed. Animations for the sake of themselves are just distracting.

Conditionally use stylesheets

This blog uses highlight.js the plain markdown codeblocks. HighlightJS provides light and dark themes separately - they don't provide user responsive themes.

Using the user preference via react-responsive to determine the theme would have the same issue as the conditional rendering of the image above, the server isn't aware of the user preference, and you'd get a style pop when the page loads and the theme switches.

Fortunately there's a very handy way to conditionally apply CSS. Per this very handy issue thread - I import both themes, conditionally choose the one matching the user's preference with a media="(prefers-color-scheme: dark)" attribute on the link tag.

Other miscellaneous updates

I needed to update my utterances config

I also made updates to react-github-permalink to have it include a user preference responsive theme.

Spotted an error? Edit this page with Github