Table of contents
Dark mode is increasingly appearing as an option on websites, but why not have it enable automatically? We can estimate the sunlight levels at a user's location, and apply the correct theme to soothe their eyes.
How do we do this?
The first step is finding the users' locations; the sun doesn't set everywhere at the same time, and other solar phases also differ depending on your location on the planet. For example, a user in Iceland could be set to dark mode at the same (local) time as a user in South Africa will be set to light:
Finding your users
There are multiple ways to find your users' locations, but for this example we'll stick with the Geolocation API, and retrieve their latitude & longitude from its coordinates object:
navigator.geolocation.getCurrentPosition(position => {
const { latitude, longitude } = position.coords
})
The Geolocation API requires users to accept permission in a browser dialog box, but if we don't want this we can find locations using a backend API.
Getting the time
How do we figure out the time of local sunsets & sunrises? We can't just use location, because the time of year affects solar phases too. If you play with the example below, you can see there's around 5 hours of variance between sunset in January and June in Ireland.
Theme timetable
Theme mode | Time period |
Dark | 12:00:00 AM - 7:51:32 AM |
Light | 7:51:32 AM - 3:27:20 PM |
Dark | 3:27:20 PM - 12:00:00 AM |
Even in equatorial nations, whilst the amount of sunlight barely changes across seasons, the time of the sunset still varies by around an hour, thanks to the elliptical nature of the earth's orbit. You can see this in Quito, Ecuador, one of the most longitudinally medial capital cities:
Theme timetable
Theme mode | Time period |
Dark | 12:00:00 AM - 6:14:32 AM |
Light | 6:14:32 AM - 6:22:23 PM |
Dark | 6:22:23 PM - 12:00:00 AM |
Getting local solar times
There's a handy NPM package named SunCalc (created by @mourner) that we can use to find the local solar times.
npm install suncalc
suncalc.getTimes()
takes three arguments, (date, latitude, longitude) and returns an object with a number of date properties attached.
We'll be using sunrise
and sunset
(the full syntax and return values can be found
on GitHub).
import SunCalc from 'suncalc'
const date = new Date('January 1, 2022 12:00:00')
const { sunrise, sunset } = SunCalc.getTimes(date, 10, -10)
// Sat Jan 01 2022 06:58:07
console.log(sunrise)
// Sat Jan 01 2022 18:31:08
console.log(sunset)
We can use getTimes()
in conjunction with the geolocation snippet from earlier and then compare with the users' current time to check
if it's currently before or after sunset.
import SunCalc from 'suncalc'
navigator.geolocation.getCurrentPosition(position => {
const { latitude, longitude } = position.coords
const now = new Date()
// Get sun phase times for today, in the current user's location
const { sunrise, sunset } = SunCalc.getTimes(now, longitude, latitude)
// Compare sunrise and sunset with current time
if (now < sunrise || now > sunset) {
// Dark mode: Before sunrise, or after sunset
...
} else {
// Light mode: Any other time
...
}
})
As simple as that! Here's a live demo that retrieves your current mode:
Theme timetable
Adding Twilight mode
We could even add more themes, such as a twilight theme, to be displayed before and after sunrise/sunset:
Theme timetable
Theme mode | Time period |
Dark | 12:00:00 AM - 9:28:14 AM |
Twilight | 9:28:14 AM - 12:28:26 PM |
Light | 12:28:26 PM - 2:20:56 PM |
Twilight | 2:20:56 PM - 5:21:09 PM |
Dark | 5:21:09 PM - 12:00:00 AM |
Technically speaking, twilight is the period of time between either dawn & sunrise, or sunset & dusk, however real twilight doesn't last too long, so I'm going to include golden hour as part of twilight mode:
Twilight mode will now be enabled for around twice the length of before, and will be enabled just either side of sunrise and sunset. Using SunCalc, it looks like this:
const { dawn, dusk, goldenHourEnd, goldenHour } = SunCalc.getTimes(now, longitude, latitude)
if (now < dawn || now > dusk) {
// Dark mode
} else if (now < goldenHourEnd || now > goldenHour) {
// Twilight mode
} else {
// Light mode
}
Test your current location:
Theme timetable
Accessibility first
Automatically setting the theme is a nice touch for most, but accessibility comes first, and if the user has a preference, it's always right to respect their choice. A number of people suffer from astigmatism and light text on a dark background can worsen a visual issue called halation, which looks something like this:
You can check for users' colour scheme preferences in CSS & JavaScript with the following snippets:
@media (prefers-color-scheme: dark) {
/* Dark mode preference */
}
if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
// Dark mode preference
}
Automatically selecting dark mode by local sunlight is quite simple, but remember—if you have a dark mode, it's good practice to include a switch.