This small library manages a class on the <body>
element, of the form user-prefers-theme-(light|dark)
. It reflects the user's system theme setting, or their preference for the current origin. A callback can be registered so that you can update a theme UI on your page when either preference is updated.
How might you use it?
Simply load the script and it's functionally the same as using the prefers-color-scheme
media query (the class on <body>
will reflect changes to the system setting).
Call the API to set your visitor's preferred theme: their preference will be saved and the class on <body>
will be updated if need be.
Register a callback so you can update theme-related UI on your page in response to system or visitor preference changes.
Some design notes…
No UI is provided; you're free to devise one that suits.
Light mode is assumed to be the default (the no-preference
CSS value is taken to mean light mode).
The user's choice will persist (localStorage
is used).
The API can be found below.
Here's a basic UI. You can manually switch between light and dark mode at system level and then check this page's appearance and the readout below.
Most of the tests are automatic, with several running when this page loads. Some of them might cause a flash in your browser, so they must be instigated manually. Finally, some tests need your help in changing the system theme setting. Test results can be found in the console.
Internet Explorer 11
Firefox 68 (macOS, Windows)
Chrome 75 (macOS, Windows)
Safari 12 (macOS and iOS)
Test notes:
Only Firefox and Safari reflect the system's theme setting. (Chrome and Edge 76 are expected to support the system theme setting, though.)
The first test requiring user assistance doesn't work in Safari 12 (macOS), nor Firefox 68 on Windows, but it does work in Firefox on macOS, so these are assumed to be problems with Safari and Windows. Outside of the test, the system setting is tracked.
Load user-prefers-theme.js
via a script
tag—either in the <head>
element with the defer
attribute set (recommended, as it allows the script to be downloaded in parallel), or at the end of your document's <body>
. It will start managing the class applied to the <body>
element.
The following functions are exported.
window.userPrefersThemeListener(callback)
Register a callback that will be passed either the system theme setting, or the user's preference for the current origin.
The callback will be called immediately, and whenever the system or user preference changes (the latter takes precedence, as described below). It is passed the string 'light' or 'dark'.
Calling this again replaces any previously-registered callback.
window.userPrefersTheme('light'|'dark')
Switch to the given theme, and save the user's preference for it (on this origin). The preference is persisted indefinitely, and stops the automatic tracking of the system theme setting.
Values other than 'light' or 'dark' will cause an error to be thrown.
If the theme requested is the same as the current one, the user's preference will be saved, but the callback won't be run.
window.userPrefersThemeNeither()
Used to remove an existing preference for light or dark theme on this origin, and return to tracking the system setting.
If the current system setting and previously user-preferred theme differ, then the class on <body>
will be updated and the callback run.
The prefers-color-scheme
media query is used in an event-driven way, via JavaScript, to detect the system preference, and when it changes.
The user's direct preference is managed and stored using localStorage
.