Building the N26 Dark Mode

When we launched N26 for Web in early July, the new dark mode was a particularly well received feature. Its white-on-dark-grey palette offers reduced contrast and is easier on the eyes at night.

What is invisible to the outside observer however, is that by the time we started working on dark mode, the rest of the application was already close to being finished.

Since we started working on the dark mode close to launch we were looking for ways to build it without introducing too many changes to the existing codebase.

Our first attempt

At first, we tried simply inverting the colors of the existing application using the CSS filter property. This approach is similar to Firefox’s built in dark mode:

html, img, video {
filter: invert(1) hue-rotate(180deg);

However, while we appreciated the simplicity of this solution, it just didn’t look quite right:

Contrast between different UI elements seemed to disappear and we couldn’t prevent it form also inverting background images, leading to some awkward looking spots.

How it’s built

So instead we embraced a different approach:

We were already using a specific palette of brand colors. Each color was imported as a JavaScript constant and featured a vibrant name, such as VENICE_SPRING or PARIS_SUNRISE.

In order to make dark mode happen we recategorised our color palette. Instead of describing colors by their tone we instead opted for functional descriptions, names that describe the purpose each color serves in the UI.

We then stored and accessed this palette using CSS custom properties:

:root {
--primary-color: rgb(27, 27, 27);
--secondary-color: rgb(104, 104, 104);
--primary-background-color: rgb(251, 251, 251);
--secondary-background-color: rgb(255, 255, 255);
--primary-accent-shade: rgb(201, 201, 201);
--secondary-accent-shade: rgb(245, 245, 245);
--mild-overlay: rgba(0, 0, 0, 0.075);
--distinct-overlay: rgba(0, 0, 0, 0.175);
.my-card {
color: var(--primary-color);
background-color: var(--secondary-background-color);

Applying the dark mode then becomes as easy as overwriting these custom properties and the entire UI will update.

:root {
--primary-color: rgb(255, 255, 255);
--secondary-color: rgb(201, 201, 201);
--primary-background-color: rgb(32, 32, 32);
--secondary-background-color: rgb(45, 45, 45);
--primary-accent-shade: rgb(136, 136, 136);
--secondary-accent-shade: rgb(61, 61, 61);
--mild-overlay: rgba(0, 0, 0, 0.3);
--distinct-overlay: rgba(0, 0, 0, 0.4);

Using a functional palette of colors means we don’t have to think about different themes when developing features. In other words, it just works.

Design Concerns

A close inspection of our dark palette reveals that it isn’t simply the opposite of our light colors. Instead some specific tweaks were applied to make dark mode look good and contrasted. Let’s take quick look at them:

First off compared to our initial attempt the finished dark mode uses dark greys as opposed to harsh black. Users typically appreciate dark modes for offering a more soothing experience and this reduced contrast helps with that.

Secondly you’ll notice that for light shades, colors are not inverted but instead shifted towards a darker tone. In the light mode we tend to use a bright background color to draw focus to an element, such as a card. This is because attention is naturally drawn to lighter elements, and the same rules still apply in dark mode:

Lastly you’ll notice that ` — mild-overlay` and ` — distinct-overlay` (both translucent blacks, used for shadows) are more than twice as strong in the dark palette. This is because human vision is more sensitive to brightness differences among light shades than among dark shades. Hence a stronger shadow is needed to achieve the same visual effect.

Wrapping Up

Adding a dark mode to our existing codebase turned out to be a lot easier and maintainable than we initially thought. Reception has been enthusiastic, with our statistics showing that more than 40% of our users have tried out the dark mode.

Categorising colors according to their purpose helps us build a consistent UI and makes it super easy to maintain themes. In fact, if you’re a tech-savvy user of N26 and like easter-eggs, you might be able to find a secret, additional theme in the N26 for Web application. 💖

Interested in joining one of our teams as we grow?

If you’d like to join us on the journey of building the mobile bank the world loves to use have a look at some of the roles we’re looking for here.