Make your Electron app Dark Mode compatible

Etienne Lemay
Missive App
Published in
4 min readSep 28, 2018

--

With the recent public release of macOS Mojave and as a developer, I’m sure you’ve already got a ton of requests to make a dark theme version of your app. We sure did.

We kept postponing development because of a major technical roadblock: We were using SASS variables and when it comes down to it, these are pretty much static. Or at least they are once compiled into CSS. We also didn’t want our CSS to get any bigger than it is or even deal with a different file per theme. We’re eventually looking for 100% customizable themes.

But I have good news for you, CSS Variables show a whopping 86.8% support rate. For Electron apps, that’s not even a concern 💯.

CSS Variables support | September 2018

Goal: 3 themes with minimal effort

Themes

light, dark and light-dark-sidebar are the initial themes we’re looking for. The beauty of the technique explained below is that we only need 2 base themes, the 3rd one being a mix of both and only requires one extra CSS selector.

Minimal effort

By minimal effort, not only do I mean being able to add new themes only by editing existing CSS Variables, but also from a performance perspective. The HTML is to not be made aware of the current theme and no re-rendering of any sort is to be required when changing theme.

Bonus: Quite easy to quickly debug when all you have to do is change an attribute on <html> via the web console 🐛.

The 3 main themes of Missive

We don’t want no FOUC!

What’s worse than seeing a light theme appear for a few milliseconds then switch to a dark one? Nothing 👏🏼 is 👏🏼 worse.

Electron

To avoid FOUC, we’ll use localStorage and a preloaded script on BrowserWindow. The preloaded script is loaded as soon as your app is launched and while <html> can’t be accessed yet, window can.

Using the systemPreferences API provided by Electron, we know if the OS is currently using dark mode and can subscribe to the theme change event.

Update localStorage from preload script

Then we’ll define the __setTheme function in index.html and make sure it’s called when launching the app. On the first run, the preloaded script already set localStorage.os_theme, so we can use that and set an attribute on <html> as soon as possible, especially before loading the styles. We’ll also preemptively read localStorage.user_theme in case you want to let users choose their own theme and not rely on the OS only.

Set <html> data-theme attribute

CSS

We’ll take advantage of a few techniques: CSS Variables, specificity and attribute selectors.

Theme-aware CSS Variables

Extra specificity selector [data-theme] [data-theme="light"]
That’s a way to make themes embeddable within each other. You could force any part of your app to have a given theme by setting the data-theme attribute.

Nota bene: Without that, even if you add data-theme="light" anywhere in your HTML, it would still use the <html data-theme="dark"> variables. Because for the same specificity, dark variables are being declared after in the CSS. That’s just good old cascading rules being applied.

We use that technique to dynamically create a thumbnail (HTML + CSS) of our theme variants.

Attribute selector ^="light"
That is so both light and light-dark-sidebar use the same base theme.

Scoping variables with [data-theme="light-dark-sidebar"] nav
That is to have theme variants without having to make the HTML aware of it. For that theme, the light theme is used, but the <nav> element is going to use the dark variables. That way, you don’t have to re-render any part of your HTML when changing the theme, it’s just a matter of updating the <html data-theme> and Voila!

Theme-aware CSS Variables playground

macOS Mojave

Don’t bother with transitions, even if that’s just CSS it’s 100% seamless on Mojave. macOS freezes the current frame and renders the new layout behind the scenes. When ready, it’ll crossfade the two layouts making your app look as native as Finder.

Give it a try and let us know how the dark mode feels. As you may already know our dirty little secret, you won’t be surprised to hear that this only required a full week worth of work time and a single developer to implement.

Dark theme is also available on Windows, iOS, Android and web browsers 🌒.

Missive dark theme available on all platforms

--

--