AGS Logo AGS Logo

Light or Dark
Which do you prefer?

Themes, specifically light versus dark, is one of those topics that seems to divide the internet, and the research[1][2] points to it being a personal preference situation rather than one option being superior to the other. Like many things, one size will not fit all. But before we get into the nitty-gritty of how to implement themes, let's go back a couple of weeks and looks at the events leading up to me writing this post.

User Feedback Matters

Regardless of if it's an internal tool or a product that will have users that are outside the company, at Andromeda, accessibility is a requirement. However, having a dark theme is not a WCAG requirement, nor had it really come up as a business requirement in the past, so we had never included it in any of our internal tools. However, dark mode is a very valuable UX feature...

I was working on a prototype for an internal tool, and during a discussion completely unrelated to the tool, someone on the team mentioned preferring dark mode so I figured I would add it to the prototype. I was in the middle of creating the theme anyway figured it would give me a chance to play with some ways to implement themes efficiently. It was a prototype after all, perfect time to play around with some things.

After a bit of conversation and rolling the prototype out for the team to test out, it turned out the overwhelming majority of the team actually prefers using the dark theme.

So why do I bring this up? The whole reason for Accessibility and UX is to create a positive experience for the users of the application. Unbeknownst to me, the team, which are the users of this application, have some very definite preferences and thus I was convinced to join the "Dark Side". By making sure that the tools had a light and dark mode, I was going to make the team's job easier and more comfortable. The moral of the story here is listen to the users, but also solicit the feedback. You may be surprised as to what you might learn.

So how do we implement themes? Like a great many things in CSS, there are many ways we can go about it. In this particular example, we are going to leverage CSS custom properties3 to build our themes.

Building Themes

CSS Custom Properties vs Variables

CSS custom properties are quite a bit different than the variables available via Sass, Less, and other pre-processed CSS languages. When using a pre-processed language variable, once the code is compiled, the variable is replaced with its value. I can no longer be reassigned. CSS custom properties however, stay variable and can be reassigned, which is exactly the functionality we are going to leverage.

prefers-color-scheme

We can automatically set the color scheme we present to a user based on their machine level settings. The media query @media (prefers-color-scheme: <theme>) which takes values dark or light, allows us to target the user's preferred color scheme and serve appropriate theme accordingly. This technique, however assumes that the user is aware that they set system level preferences, knows how to edit their settings, and does not allow for easily toggling between themes if a user has situational preferences.

Add a little JavaScript

We can also look up a user's system level default via JavaScript by calling the window matchMedia4 method window.matchMedia('(prefers-color-scheme: dark)') in conjunction with a toggle on the UI itself, we can set a default based on the user's system level preference and then still allow for manual toggling via a button on the UI. We can even save the theme chosen by the user in local storage for the next time they visit the site or use the application. All put together the JavaScript would be as follows (Listing 1):

Toggling theme using JavaScript

/**
 * On load set theme based on saved preferences or user settings
 */
(() => {
 'use strict';
 
 //  get theme from local storage
 let theme = localStorage.getItem('theme');

 //  if no theme and an user prefers dark mode, set theme to dark
 if (
   !theme 
   && window.matchMedia
   && window.matchMedia('(prefers-color-scheme: dark)').matches
 ) {
   theme = 'dark' 
 }

 //  if no theme or value stored in local storage is not either 'dark' or 'light' use light
 if (
   !theme
   || (theme !== 'light' && theme !== 'dark')
 ) { 
   theme = 'light' 
 }

 // now that we know which theme we want, go ahead and set it
 setTheme(theme) 
})();


/**
* set's the theme's class name on the body 
* @param theme (light or dark)
*/
function setTheme(theme) {

 //  save theme for next time
 localStorage.setItem(THEME_KEY, theme)

 //  add theme class to body 
 const body = document.querySelector('body')
 body.classList.remove('light', 'dark');
 body.classList.add(theme);
}

Although this method does require a little bit of JavaScript, it allows for toggling the theme from the UI while still respecting a user's defaults giving the user full control over what theme they want to use. This technique can also be expanded to work for more than 2 themes as well, which is can be advantageous in some use cases.

The CSS

With a class of light or dark being added to the body by our JavaScript we can now set our theme colors based on user choices. We create 2 rules, one for dark and one for light in which we assign colors to CSS custom properties, and then only use custom properties for colors when theming elements. Listing 2 shows an example of what this technique implemented in CSS might look like.

Applying Themes in CSS
.light {
  --background-color: #fafafa;
  --typography-color: rgba(44, 44, 44, 0.86);
  --primary-color: #154a4b;
  --primary-color-contrast: #ffffff;
}

.dark {
  --background-color: #303641;
  --typography-color: rgba(255, 255, 255, 0.86);
  --primary-color: #afcece;
  --primary-color-contrast: #1b2222;
}

body {
  font-family: Helvetica, Arial, sans-serif;
  background: var(--background-color);
  color: var(--typography-color);
}

If anytime we need to set a color, we make sure to use a custom property that is defined for all themes, the correct colors will be applied for each theme. This allows us to only have one set of styles regardless of the number of themes, keeps our code maintainable, and makes it quite easy to expand the number of themes if we choose to have more than just light and dark. It is worth noting that each theme should still have appropriate color contrast ratios to maintain easy readability and accessibility compliance.

A full working implementation of this code can be found on codepen.

Happy Coding!

1 Byrne, Sheri. “Dark UI themes are new and cool — but are they accessible? | by Sheri Byrne-Haber, CPACC.” UX Collective, 16 September 2019, https://uxdesign.cc/accessibility-and-dark-ui-themes-f01001339b65. Accessed 28 March 2022.

2 Sihan, Layth. “Dark mode: How accessible design raises the bar.” UX Collective, 8 August 2020, https://uxdesign.cc/dark-matters-342ff2c7cc. Accessed 28 March 2022.

3 mdn web docs. “Custom properties (--): CSS variables - CSS: Cascading Style Sheets | MDN.” MDN Web Docs, 18 March 2022, https://developer.mozilla.org/en-US/docs/Web/CSS/--. Accessed 28 March 2022.

4 mdn web docs. “Window.matchMedia() - Web APIs | MDN.” MDN Web Docs, 15 March 2022, https://developer.mozilla.org/en-US/docs/Web/API/Window/matchMedia. Accessed 29 March 2022.

License: CC BY-NC-ND 4.0 (Creative Commons)