How we shipped dark mode in just 2 days šŸŒ—

Peter HraŔka
Slido developers blog
7 min readMay 13, 2021

Iā€™ve been thinking, talking and experimenting with dark mode here in Slido literally for a few years now. So when someone finally asked me: ā€œWe need dark mode, ASAP. Can you help?ā€, I knew I was ready.

Now, Iā€˜d like to give this superpower to you.

ā€¦and to be completely frank with you, we really did get dark mode to production in just 2 days, but we also reserved additional 8 days in advance, to get rid of the tech debt that weā€™ve introduced in those 2 days.

The technical solution

First, letā€™s figure out, how to define and maintain 2 color palettes at the same time. There are multiple ways but the most reasonable one, and the one that you should go for, is native CSS Variables.

CSS Variables

With modern CSS, thanks to CSS Variables, you can define colors as variables and change them in real time.

So how do we take advantage of them?

First, we define 2 color palettes ā€” one for light, and one for dark mode, on the :root element like so:

:root {
--bg-color: #FFFFFF; // background for light mode šŸŒ•
}
.dark-mode {
--bg-color: #000000; // background for dark mode šŸŒ‘
}

Then, we use these colors in our CSS/SCSS files:

body {
background: var(--bg-color); // the magic šŸŒ—
}

And finally, we toggle dark mode whenever necessary by adding dark-mode class on the body element.

if (isDarkModeEnabled) {
documeny.body.classList.add('dark-mode');
} ...

All of this is pretty simple (as most of the code should be), and here is an example of what it could look like:

IE11 + CSS Variables = šŸ’”

A huge topic for us, when it came to dark mode, was IE11 support. For a long time, we were considering CSS variables to be the best option for color customisability, but IE11 with its non-existing support for CSS variables stood in the way.

Sure, nowdays you can polyfill them for IE11, for example, by using ponyfill or postcss-custom-properties, but in our experience, the implementation and maintenance cost for either of these just wasnā€™t worth it. Especially so, if we are talking about a browser, which we ultimately want to stop supporting.

We therefore decided to simply not support dark mode on IE11 (and to only display the default light mode on this prehistoric browser).

By the way, we werenā€™t the only ones to make such a decision. For example, StackOverflow went a bit further and dropped support for IE11 altogether because of the dark mode that they have introduced last year.

So the plan is to replace all colors in our CSS files with CSS variables. But in that case, IE11 wonā€™t know what color to display, right? Yeah, but to fix that, weā€™ve used postcss-custom-properties, which generates fallbacks for IE11.

These fallbacks then look something like this:

background: #FFFFFF;
background: var(--white-1);

This way, IE is able to read the first line and ignore the other, while the rest of the browsers can take advantage of the CSS variable on the second line.

Alternative approaches

Even though CSS variables are superior to other approaches, letā€™s mention other approaches that have popped up in our minds, as they might prove helpful to you.

  1. Generate multiple sets of CSS files out of your SCSS files (one for each color mode). ā€” With this approach, you will have no troubles with IE, but you will end up with 2 files, which could get quite big.
  2. Tailwind ā€”If youā€™ve heard about or have been considering using Tailwind in your project, you should know that the support for dark mode has been added in version 2.0. However, it requires you to define the desired dark mode colors on each element, and can get quite verbose.
  3. ā€œDark Mode Hack Fileā€ ā€”Think of a single file that contains just the diff between light and dark mode, so that when this file is loaded, it transforms all light colors into dark ones.

Why do we mention this ā€œhackā€ approach, you ask?

Hacking dark mode in 2 days

For business reasons, we needed to make the dark mode available as quickly as possible, and the ā€œDark Mode Hack Fileā€ approach is essentially what enabled us to ship dark mode in 2 just days. Well that is cool, isnā€™t it?

Yeah, but among Slido developers, we often use the phrase ā€œThere is nothing more permanent than a temporary solution.ā€ Therefore, we knew that we had to redo it properly right away or else it would be weighing us down.

Despite the ā€œDark Mode Hack Fileā€ approach being ugly, it was a good stepping stone towards a proper solution ā€” it helped us understand the complexity of the project and weā€™ve identified all the CSS rules that needed adjusting for dark mode.

Colors

When it comes to colors, here are some tips regarding 3 topics: Choosing them quickly, choosing them properly, and taking advantage of the situation and cleaning them up.

Creating dark palette quickly

If you are implementing dark mode on an existing project with a light palette already defined, chances are that you can just use a clever trick to switch color from light to its dark alternative.

In our case, the majority of the colors were just different shades of grey, defined in RGBA color space as black color with a variable alpha channel.

Generating the first iteration of our dark mode palette was as simple as switching the R, G and B channels from 0 to 255 and leaving the A (alpha) channel untouched.

Flipping colors in RGBA color space

Similar trick can be applied on colors in HSL (hue, saturation, lightness), by fiddling with the lightness channel.

Creating dark palette properly

In reality, switching 0 to 255 wonā€™t always do the trick. When doing things properly, you want consistency, cleanliness and simplicity.

Creating a proper palette is therefore all about creating a system.

Our palette

In our case, the designers have created a palette that goes from black to white for each color, in the same number of steps. Each color got its name and each existing color was mapped to this palette.

Having enough colors in the palette is important so that designers always have enough colors to choose from. This way, the palette doesnā€™t become obsolete so quickly. Names are useful for making the communication between developers and designers easier.

The cleanup

An opportunity to dig deep into your CSS files and colors doesnā€™t come often. Therefore, if possible, take advantage of such a situation and try cleaning the styles up a bit.

One of the candidates for such clean-up would be the amount of different shades of a color, that should have been the same. (E.g. 3 different border colors in 3 different places.)

We tackled this problem by listing all the colors weā€™ve been using in our codebase, grouping them by similarity and pinpointing them to a new color from our new color palette.

The result was a cleaner, more pleasant look.

Detecting light/dark mode

It might not seem obvious at first, but despite having just 2 color palettes, most applications are supporting 3 different modes:

  • Automatic ā€” based on OS or browser
  • Always light
  • Always dark

In our case, we wanted to go easy on our users. We hid the ā€œAutomaticā€ option and went for a toggle with 2 positions instead. By default, our application is in auto mode and the toggle simply displays which theme is active. But as soon as you touch it, you will forever (well, almost forever) be using that mode. Just for the sake of simplicity.

You can detect whether the userā€™s browser/OS prefers a dark theme with:

if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
// The user prefers dark mode šŸŒ‘
}

ā€¦and it is always nice to surprise your users with dark mode if they prefer it.

The result

Surprisingly, working on the dark mode was even more fun, than I originally anticipated!

It brought a lot of delight to our costumers, and I canā€™t wait until the pandemic is over, so that we can take advantage of dark mode in dark conference rooms, where we were previously blinding our participants with the original light theme.

And last but not least ā€” huge thanks for help goes to Adam Ciganik, Milos Lajtman, Michal Mikulasik, Maros Holly and Janka Kovacikova. šŸ’š

Is there anything we missed? Let us know!

--

--