---
title: Lights on or off?
description: >-
  Theming experiment with CSS Dark Mode, Geolocation and the Ambient Light
  Sensor API. And the winner is ...
language: English
url: 'https://voorhoe.de/en/blog/lights-on-or-off/'
---

Blog

# Lights on or off?

By [Vincent](/en/team/vincent.md)[James](/en/team/james.md)

18 March 2019

* [Using manual controls](#using-manual-controls)
* [Using geolocation](#using-geolocation)
* [Using CSS feature query](#using-css-feature-query)
* [Using ambient light sensor](#using-ambient-light-sensor)
* [Putting Everything Together](#putting-everything-together)
* [Takeaways](#takeaways)
* [Conclusion](#conclusion)

Experimenting with 'dark mode' and different ways to control the theme of your website

In projects like [Quantum Inspire](https://www.quantum-inspire.com) we’ve built dark and light themes. Quantum Inspire has a dark theme editor. The docs contain the same code snippet and graph components as the editor, but now they are in a light theme:

![Quantum Inspire editor in dark theme and Quantum Inspire docs in light theme](https://www.datocms-assets.com/6524/1550072159-quantuminspire.jpg)

Quantum Inspire editor in dark theme and Quantum Inspire docs in light theme

In these projects we, as designers and developers, controlled which pages get which theme. But how can we make this contextual and give this control to our users? In this article we’ll be experimenting with manual controls, geolocation, a new CSS feature query and the Ambient Light Sensor API.

For our experiment we built multiple **Dark Mode** demos, each based on a different technique. We want to explore their possibilities and limitations. This helps us choose the right implementation of theming in future projects. The demos can be found [here](https://dark-mode-experiment.netlify.com/).

## Using manual controls

Let’s start with manual controls. It’s one of the more common approaches to themes. All we need to do to get this to work is to toggle a class. Your styling could look something like: `body.theme--dark { color: #ffffff }` and `body.theme--light { color: #222222 }`. Using [CSS custom properties](https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_variables) we can make this even more powerful.

For our experiment, we created two buttons, one sets `theme--dark` on the `<body>` and the other `theme--light`. We can now write our styling using these selectors and re-assign different values to our CSS custom properties.

[play video](https://vimeo.com/317240749)

[Manual Controls - Demo](https://vimeo.com/317240749)

Source code (simplified):

```
document.getElementById('light-theme-button')
  .addEventListener('click', setLightTheme);
document.getElementById('dark-theme-button')
  .addEventListener('click', setDarkTheme);

function setLightTheme() {
  document.body.classList.add('theme--light');
  document.body.classList.remove('theme--dark');
}
  
function setDarkTheme() {
  document.body.classList.add('theme--dark');
  document.body.classList.remove('theme--light');
}
```

```
body.theme--light { --font-color: #283444; }
body.theme--dark { --font-color: #ffffff; }

body.theme { color: var(--font-color); }
```

View the full [manual controls source](https://github.com/voorhoede/experiment-dark-mode/tree/master/src/manual-control).

## Using geolocation

This is a fun one. We use the [Geolocation API](https://developer.mozilla.org/en-US/docs/Web/API/Geolocation_API) to read the user’s latitude and longitude values. We then combine these values with a `new Date()` object to calculate the sunrise and sunset for the user’s location. We used [SunCalc](https://github.com/mourner/suncalc/) for this. Based on these values we conditionally add the `theme--light` or `them--dark` class to the body. We update these values around every minute, so we can update the user’s location on the fly.

[play video](https://vimeo.com/317240737)

[Geolocation API + Sunrise:Sunset Times - Demo](https://vimeo.com/317240737)

Source code (simplified):

```
if ('geolocation' in navigator) {
  navigator.geolocation.getCurrentPosition(position => {
    const { latitude, longitude } = position.coords;
    setInterval(() => {
      const currentTime = new Date();
      const { sunrise, sunset } = SunCalc.getTimes(
        currentTime, latitude, longitude);
      updateTheme({ currentTime, sunrise, sunset });
    }, 60 * 1000); // Update every minute.
  });
}
  
function updateTheme({ currentTime, sunrise, sunset }) {
  (currentTime > sunrise && currentTime < sunset)
    ? setLightTheme()
    : setDarkTheme();
}
```

View the full [geolocation source](https://github.com/voorhoede/experiment-dark-mode/tree/master/src/geolocation).

The [browser support for the Geolocation API](https://developer.mozilla.org/en-US/docs/Web/API/Geolocation_API#Browser_compatibility) is really good:

![Geolocation browser support table](https://www.datocms-assets.com/6524/1550144768-geolocation-support.png)

Geolocation browser support table

## Using CSS feature query

This experiment only works on MacOS Mojave, and you must use the Safari Technology Preview browser. The code itself is straightforward, and no JavaScript is required. All you need to do is to swap your theme colours inside the `(prefers-color-scheme: dark)` media query.

[play video](https://vimeo.com/317240720)

[CSS Media Query - Demo](https://vimeo.com/317240720)

Source code (simplified):

```
:root {
  --background: #ffffff;
  --content: #283444;
  --content-alt: #515c6a;
}

@media (prefers-color-scheme: dark) {
  :root {
    --background: #283444;
    --content: #ffffff;
    --content-alt: #acb8c8;
  }
}
```

View the full [CSS feature query source](https://github.com/voorhoede/experiment-dark-mode/tree/master/src/media-query).

Keep in mind that this technique has, at the time of writing, very little browser support. Hopefully this will change with the next version of Safari and other mayor browsers in the future.

## Using ambient light sensor

In our last experiment we used the ambient light sensor that is (sometimes) present on the user’s device. Using ambient light, you could make a theme that smoothly changes depending on the current lighting conditions. Make sure you give your users an option to turn this off. Also keep in mind that this feature can be very distracting if a user is in an area where the lighting conditions often change, e.g. streets at night.

[play video](https://vimeo.com/324209946)

[Ambient Light - Demo](https://vimeo.com/324209946)

Source code (simplified):

```
if ('AmbientLightSensor' in window) {
  const sensor = new AmbientLightSensor();
  sensor.onreading = () => {
    const illuminance = sensor.illuminance;
    if (illuminance < 50) {
      setDarkTheme();
    } else if (illuminance > 60) {
      setLightTheme();
    }
  };
  sensor.start();
}
```

View the full [ambient light sensor source](https://github.com/voorhoede/experiment-dark-mode/tree/master/src/ambient-light).

As you can see below, this feature is not well supported yet. The [AmbientLightSensor API](https://developer.mozilla.org/en-US/docs/Web/API/AmbientLightSensor) is only available in some WebKit based browsers. You also need to turn on the *"Generic Sensor Extra Classes"* feature flag by typing "chrome://flags/#enable-generic-sensor-extra-classes" into the address bar and setting it to 'enabled'. Users will be asked for permission to use the sensors. Safari is currently working on an implementation.

![Ambient Light support table](https://www.datocms-assets.com/6524/1550147857-ambient-light-support.png)

Ambient Light support table

![Sensor permission pop-up in a web UI](https://www.datocms-assets.com/6524/1552660607-screen-shot-2019-03-15-at-14-44-30.png)

Sensor permission pop-up

For a more in-depth guide, consider reading '[Using the Ambient Light Sensor API to add brightness-sensitive dark mode to my website](https://blog.arnellebalane.com/using-the-ambient-light-sensor-api-to-add-brightness-sensitive-dark-mode-to-my-website-82223e754630)' by Arnelle Balane.

## Putting Everything Together

After experimenting with the mentioned techniques separately, we thought it would be interesting to put them all together into one single demo. In this video we show the 4 techniques combined in a menu. We give the user the option to select what theme they want. They can also select ‘auto’, in which case a theme is chosen based on OS settings, ambient light or their location.

[play video](https://vimeo.com/317240708)

[All techniques together | demo](https://vimeo.com/317240708)

## Takeaways

* There is a lot more involved with Dark Modes than simply inverting the colours. The process for choosing the new colours takes time, is complex and should be done by a designer. It does not only need to look right, but it also needs to be accessible.
* Most of the techniques used in this experiment are extremely new and not well supported. Therefore, each demo will only work on different browsers. In case you need to support every major browser, you will need to combine multiple techniques, make sure you always add Manual Controls as a fallback.
* While Geolocation is supported in most modern browsers, most users do not like to share their location. As an alternative, you could use the user’s IP address estimate their approximate position.
* The `AmbientLightSensor` API does not seem to work on the newer MacBooks. We weren’t able to figure out if this is a MacOS Mojave bug or if the API does not support the True Tone Sensor that’s commonly used in Apple devices.

## Conclusion

We should embrace the fact that ‘dark modes’ will show up in more and more places. Users should be able to decide how they want to experience your content. While most of the techniques mentioned above are quite new and not very well supported, some can be used right now. We do suggest you always provide some form of manual control or the ability to completely turn off dark mode. Some users might prefer the lighter theme.

You can find the code for all the experiments in the following [repository](https://github.com/voorhoede/experiment-dark-mode) or you can [play with the demos](https://dark-mode-experiment.netlify.com/) yourself.

[← All blog posts](/en/blog.md)

## Also in love with the web?

For us, that’s about technology and user experience. Fast, available for all, enjoyable to use. And fun to build. This is how our team bands together, adhering to the same values, to make sure we achieve a solid result for clients both large and small. Does that fit you?

[Join our team](/en/jobs.md)

[Return to top](#top)
