Designing & Implementing a UI Theme Switcher

Theme Switcher Gif

When designing my site, I knew from pretty early on in the design process that I wanted to use a dark theme. Why exactly? Well, dark themes have become rather popular in recent times. There are a few known benefits to them;

  • They save battery > It takes less screen power to produce dark colours. That ultimately means it consumes significantly less battery power (provided your phone has an OLED display!)
  • Omits less blue light > A dark theme omits less blue light. Blue light can affect your sleep patterns. It has also been suggested that it can cause eye strain.
  • Reduces screen glare > When in a dark room, the high contrast between a light screen and the user's environment can cause an irritating screen glare. A dark theme will significantly reduce this glare.
  • User preference > Some users prefer reading light text on a dark background to the other way around. Having the option is nice as long as the toggle is designed & implemented in an accessible manner.

After creating an initial dark-themed colour palette and designing an early iteration of the site, I decided that I wanted to give the user the option of switching between a dark and light theme as they desired. A theme toggle would give users control over how they view the site. Many people are familiar with them at this stage. It's essentially a toggle button with two states. One for the light theme and one for the dark one. I figured it'd also be an exciting challenge from both a design and implementation perspective.

Designing a theme switcher

Here’s a few best practices for how to create a theme switcher:

  • Allow the user to control their selected theme (fairly self-explanatory!)(fairly self-explanatory!)
  • The colours used in each theme don't have to be directly derived from each other. Tailor the colours to what works best in your case!
  • Retain the user's theme selection for when they're navigating the site & for when they return to the site later.
  • Keep the accessibility of the toggle button in mind. Is it clear what the action does? Do users that are utilising a screen-reader need to know about the functionality? In the HTML, you can add the aria-hidden attribute to hide the toggle button element from a screen reader.

While we’re on the subject, here are a few best practices for creating a dark-themed UI;

  • Don't use a pure black #000000 background, In my case I used (#1b1e21) for the site's darkest background colour.
  • Avoid using colours with too much saturation on a dark background. They can vibrate on a darker surface, making them harder to read when used for text colours.
  • On that note, always keep accessible color contrast ratios in a dark theme, just as you would in a light theme.
  • The higher the elevation of an element, the lighter the surface (or background colour).

Note: Another option would have been to configure the site to leverage whatever the user’s OS system level preference is (accessible via the prefers-color-scheme CSS media feature). Some sites, like developer.mozilla now give the user three theming options - light, dark, and their OS system level preference (or 'default'). Perhaps that could be an exciting and valuable future iteration to implement.

Choosing light-theme colours

Having focused on a dark-themed colour palette in the site's initial design, the most time-consuming aspect of this feature was to develop a new colour palette for the light theme. Unfortunately, this wasn't as simple as 'flipping' the colour palette I'd already defined for the dark theme. It took some tweaking and tinkering to settle on a light-themed colour palette that worked stylistically while also being accessible. I still tried to keep some consistency with the original dark-themed colour palette. Many dark theme colours were either repurposed or slightly modified for use in the light theme.

It's worth noting that there are also subtle differences in best practices for how you use each theme. For a dark theme, the page background should be the darkest colour on the UI. Other page components' surfaces should become lighter the higher their artificial elevation is from the background. For a light theme, it's a bit different. The background shouldn't be the lightest element on the page. Instead, it's recommended that you use a slightly tinted page background colour. The page components' surfaces become lighter the higher their artificial elevation is from the background.


How does it work from an implementation perspective?

As it turns out, many things start with a google search! In my case, I found this helpful article by Gulshan Saini that gave me an idea of how I wanted to implement the feature. I adapted & modified it to suit my specific use case better. It's probably worth noting that many methods of implementing the theme switch functionality exist. This is just the method I chose to implement due to its simplicity. However, it did come with a few drawbacks that I'll touch on later.

I wont make this a full tutorial, but essentially the theme toggle is an HTML <input> element with the type="checkbox" attribute. There's also a styled ‘toggle’ element that sits alongside on top of it. When a user interacts with it its checked attribute is changed. When the user toggles the checkbox, it adds either a theme="dark" (for checked) or theme="light" (for unchecked) attribute to the ‘document’ HTML element. It’s set to theme="dark" by default.

This attribute's value is then used to load the appropriate global colour variables for the whole site (set in :root). The :checked & :unchecked pseudo-classes were used to style the toggle based on what theme was selected.

The user's theme selection is saved to local browser storage. The value of that saved theme attribute is loaded into the HTML when a new page loads. This ensures that the selected theme persists if a user navigates to a different page on the site. Usefully, this saved preference in local storage also applies if a user returns to the site at a later stage - the site will remember what theme the user had previously set and will load it accordingly.

One drawback of this implementation method is that it led to some confusing colour variable names regarding the light theme. For example, the colour variable grey900, used for the darkest greyscale colour in the dark theme, had to be assigned to the lightest background colour in the light theme. This isn't perfect. Seeing a light colour assigned to grey900 is confusing & not the most readable code! Other implementation methods would have solved this; I may look at changing my implementation later.

If you have any feedback or questions, feel free to reach out!