Arrow left
Blog Homepage

How to Improve Your Design System for Dark Mode Theming

How to Improve Your Design System for Dark Mode Theming

You’d be hard-pressed to find a developer that doesn’t prefer dark mode. Whether it’s because it looks darn cool, eases eye strain, or matches the moody feel of a dark coding dungeon (cough, home office, cough), dark mode is a tried and true fan favourite of app theming. So, even we’ll admit it seemed a bit bizarre that ZenHub didn’t have dark mode given that we’re a developer-focused product. And as users of our own product, it’s something our developers would have loved to see!

So, after plenty of tweets and requests.

Image of Twitter users requesting dark mode in ZenHub

We heard you loud and clear. The time for dark mode was now! But when we went to update our design system to allow for theming, we realized that we had some major design debt. So, we set off on a combo project. Clean up our design system to make way for dark mode.

We learned some tough lessons along the way, so we thought we’d compile them for you. In this blog, one of our Senior Product Designers, Mayra Pulido, will walk you through our approach, what we learned, and some best practices your team can adopt when theming for dark mode.

Identifying current design system challenges

When looking at our design team’s process, it was obvious we had design debt, meaning, our component library in Figma was off from what you’d see in production. How did this happen? As designers, it’s natural that we want to design with the best user experience in mind. And even though we have feedback sessions with developers to discuss complexity and project scope, sometimes our design can stray from what’s in the design system. Before long, you’re left with design debt that’s significantly slowing down development time, creating bottlenecks, and doubling the workload.

For full transparency, here are the two main issues we recognized within our own design system.

Challenge 1: An unclear color library

Our color library was out of sync with production, sometimes we would use hex codes and developers would have to find the variable.

Image of the color library we used while designing vs the colour scales that were available to use in production.

Challenge 2: Our design system didn’t match what was in production

Components in our design system were ahead of production. Meaning, our design system had the patterns the design team wanted to be implemented in the product but weren’t in production yet. This became a problem for our development teams as refactoring components creates added risk and could make QA difficult since the component that should be tested in production may not meet the expected behaviour.

Like many other fast-growing companies with a small but scrappy design team, there was no time (or budget) to spend resources on updating our design system. Regardless, our design system structure was becoming a blocker to our development teams being able to ship faster.

How we revamped our design system

While the challenges in our design system were obvious, finding the time to retroactively go back and amend these issues was the true challenge. However, we knew this work needed to be done sooner rather than later to make room for dark mode.

Here’s what our team did to solve our problem.


  1. We pulled out our color library for everyone to take a good look at. The team was shocked at the inconsistency between how many colors existed in production vs. what we thought we had available in our library. Taking a closer look at this helped us learn what terminology we were using, what was working, and what wasn't.
  2. We started looking at the code. It’s so important for designers (especially those designing systems) to learn the technical aspect of your component library. If you don’t have access to a development library, such as Storybook, the best way to find out what your engineering team is dealing with is by diving into the repository. The inspect element is your friend. Hot tip: if you don't know where to start, look at usage data to know which components are essential to the user and start there.
  3. Schedule some time with an engineer, listen to the challenges they are facing and inconsistencies they’ve noticed. They are working within your system every day and are likely to know where there are obvious gaps.


  1. Stop designing with components that are ahead of the state of production. Use the design system as your source of truth.
  2. Stop only living in Figma! Get involved with the dev team. Communicate what has changed, and learn their language. Need advice on how to do this effectively? Check out the blog post we just wrote on reducing silos between your development and dev teams.


  1. Design systems are a team effort. And, we hate to break it to you but it's likely never going to feel good enough! Embrace the true iterative nature of software design and development, and make a plan for how you can continue to iterate on your design system. And of course, continue being awesome.

Design principles we followed

As we alluded to before, the main reason we finally got around to improving our design system was in anticipation of bringing dark mode to ZenHub *applause*. Dark mode is something we all know developers love and was highly requested by our users, so it was only a matter of time before we brought this to our product!

Once starting this project, we quickly found there are a lot of resources out there about ‘do’s and don’ts for dark mode’, but one statement that stood out to me was:

“At higher levels of elevation, components express depth by displaying lighter surface colors” - material design

Let’s peek behind the curtain and walk through some principles we used when designing our dark mode with real examples from our design system.

Dark Mode Design Principle 1: Higher elevation, lighter surface

In light mode, we often use drop shadows to show depth. In dark mode, drop shadows are not always the best approach. Imagine you have a light source on top of a table, all elements closer to you will look lighter and anything in the background will be darker. This also applies to UI design.

GIF illustrating the difference between using shadows in light mode, and shade in dark mode to show elevation

We took this rule and applied it to our ZenHub Board. As you can see below, the issues on our board are lighter in color than the pipelines, creating depth and hierarchy for the user.

Image of the ZenHub board in dark mode with different shades to give depth.
Images of the ZenHub color gradients for both light and dark mode.
Backgrounds and containers in light mode might get darker or use gradients. In dark mode, they get lighter.

Dark Mode Design Principle 2: Less saturated colors improve legibility and reduce visual vibration

When we were building out the new color scale, our visual designer created a spectrum of colors with shades in increments of lightness for each color in our brand. For example:

Image of a complete color gradient for shades of blue on ZenHub

However, what we found worked for light mode, didn’t necessarily work for dark mode. This is because saturated colors in dark interfaces don’t pass WCAG's accessibility standard. Saturated colors produce optical vibrations against a dark background, which can induce eye strain – which is why we try not to use white text on black backgrounds. When designing for dark mode, modifying lightness in your color scale and reducing saturation will give you the best result.

Image showing that a less saturated blue works better for text on a dark background

In this example, our primaries (red and blue) had to be adjusted with a lighter shade to improve the legibility of the text and to pass the necessary contrast ratio.

Avoid using saturated colors that visually vibrate against a dark background. Less saturated colors from your color palette improve legibility and reduce visual vibration.

Developing for dark mode theming

In our design system, we have two layers of color variables: scales and themes.

Scales are where the raw color hex codes live. These values don’t change. Components won’t use these values directly, but the theme variables will point to them. Themes are the stable API for components to consume. They are pointers to the scales, and represent a distinct functional value in the system.

Developers should be able to easily read the name of a variable and understand what it’s used for. Themes are managed via a specific set of theme variables that are swapped by simply changing a data attribute at the root of the application. Each set of variables is bound to a data-theme value which is applied at the app root, but could also support theming specific components as well.

Image of the variables for day and night themes

An example of scales:

Image of the scale for grey

Now that theming is involved. text-primary in:

  • light theme points to grey-80
  • dark theme points to grey-0

The scale values never change, grey-80 is the same in all themes. But the text color references a different grey variable in the dark theme.

Image showing that the scales are the same across both light and dark mode

Applying to components:

Image of code applying the colors to components

Why functional vs. scale-based theming systems?

  1. Semantic libraries are easier to understand. If you work on a design team, you probably have had to explain your files to your colleague when they are working on them. Things like what color do we use for CTAs or which of these is used for the alerts?
  2. Experimentation is easier. If you have a complex file setup with semantic colors, it’s easy to change the ‘Brand / Background / Primary / Light’ color and experiment until you’ve got it right.

Design system and dark mode in Figma:

Ah, Figma. We all know it, and we all love it. Here’s how we transferred all these best practices into Figma.

Adding color variables in Figma

We moved from designing with hex codes to only using functional variables that are aligned with our code base, making it easier for the whole team to understand. This saves the developers time during handoffs and limits the margin of error for inconsistencies.

Image showing how the colors and theme variables were transferred to Figma

This also meant that converting a page from light mode to dark mode took only seconds. We only design in one theme, light theme, and if we want to see what it will look like in dark mode, we just switch variants from day to night. That sure makes it seem easy, right?

Image showing how we can test light and dark mode in ZenHub

To wrap it up

Revamping our design system to make room for dark mode definitely taught our design team some hard lessons. We hope that this blog provides some usable lessons learned that you can take back to your own team.

It’s important that when standardizing your design you don’t modify components that are currently used in development. It’s best to have a separate file where design improvements are explored. As always, continue to keep yourself and the team updated on new features within Figma that could be beneficial to your processes. While it will benefit your design and development, it also helps keep you motivated to try new things, celebrate what worked all within a framework that always keeps you on track and on standard.

Our final word of advice would be: Get yourself involved in the development process and become familiar with the different libraries available in production. By doing so, you can better create parity between Figma and production, encourage conversation, and build a consistent design system.



Newsletter Icon