<dark-mode-toggle>
Element
A custom element that allows you to easily put a Dark Mode 🌒 toggle
or switch on your site, so you can initially adhere to your users' preferences according to
prefers-color-scheme
,
but also allow them to (optionally permanently) override their system setting for just your site.
📚 Read all(!) about dark mode in the related article
Hello Darkness, My Old Friend.
Installation
Install from npm:
npm install --save dark-mode-toggle
Or, alternatively, use a <script type="module">
tag (served from unpkg's CDN):
<script type="module" src="https://unpkg.com/dark-mode-toggle"></script>
(See the original HD version so you can pause.)
Usage
There are two ways how you can use <dark-mode-toggle>
:
① Using different stylesheets per color scheme that are conditionally loaded
The custom element assumes that you have organized your CSS in different files
that you load conditionally based on the media
attribute in the stylesheet's
corresponding link
element. This is a great performance pattern,
as you don't force people to download CSS that they don't need
based on their current theme preference, yet non-matching stylesheets still get loaded,
but don't compete for bandwidth in the critical rendering path.
You can also have more than one file per theme.
The example below illustrates the principle.
<main>
<h1>Hi there</h1>
<img src="https://googlechromelabs.github.io/dark-mode-toggle/demo/cat.jpg"
alt="Sitting cat in front of a tree" width="320" height="195">
<p>Check out the dark mode toggle in the upper right corner!</p>
</main>
<aside>
<dark-mode-toggle
id="dark-mode-toggle-1"
legend="Theme Switcher"
appearance="switch"
dark="Dark"
light="Light"
remember="Remember this"
></dark-mode-toggle>
</aside>
② Using a CSS class that you toggle
If you prefer to not split your CSS in different files based on the color scheme,
you can instead work with a class that you toggle, for example class="dark"
.
You can see this in action in this demo.
import * as DarkModeToggle from 'https://googlechromelabs.github.io/dark-mode-toggle/src/dark-mode-toggle.mjs';
const toggle = document.querySelector('dark-mode-toggle');
const body = document.body;
toggle.mode === 'dark' ? body.classList.add('dark') : body.classList.remove('dark');
toggle.addEventListener('colorschemechange', () => {
body.classList.toggle('dark', toggle.mode === 'dark');
});
Demo
See the custom element in action in the
interactive demo.
It shows four different kinds of synchronized <dark-mode-toggle>
s.
If you use Chrome on an Android device, pay attention to the address bar's
theme color, and also note how the favicon changes.
Properties
Properties can be set directly on the custom element at creation time, or
dynamically via JavaScript.
👉 Note that the dark and light icons are set via CSS variables, see
Style Customization below.
Name | Required | Values | Default | Description |
---|
mode | No | Any of "dark" or "light" | Defaults to whatever the user's preferred color scheme is according to prefers-color-scheme , or "light" if the user's browser doesn't support the media query. | If set overrides the user's preferred color scheme. |
appearance | No | Any of "toggle" or "switch" | Defaults to "toggle" . | The "switch" appearance conveys the idea of a theme switcher (light/dark), whereas "toggle" conveys the idea of a dark mode toggle (on/off). |
permanent | No | true if present | Defaults to not remember the last choice. | If present remembers the last selected mode ("dark" or "light" ), which allows the user to permanently override their usual preferred color scheme. |
legend | No | Any string | Defaults to no legend. | Any string value that represents the legend for the toggle or switch. |
light | No | Any string | Defaults to no label. | Any string value that represents the label for the "light" mode. |
dark | No | Any string | Defaults to no label. | Any string value that represents the label for the "dark" mode. |
remember | No | Any string | Defaults to no label. | Any string value that represents the label for the "remember the last selected mode" functionality. |
Events
colorschemechange
: Fired when the color scheme gets changed.permanentcolorscheme
: Fired when the color scheme should be permanently remembered or not.
Complete Example
Interacting with the custom element:
const darkModeToggle = document.querySelector('dark-mode-toggle');
darkModeToggle.mode = 'dark';
darkModeToggle.mode = 'light';
darkModeToggle.legend = 'Dark Mode';
darkModeToggle.light = 'off';
darkModeToggle.dark = 'on';
darkModeToggle.appearance = 'switch';
darkModeToggle.appearance = 'toggle';
darkModeToggle.remember = 'Remember this';
darkModeToggle.setAttribute('permanent', '');
darkModeToggle.removeAttribute('permanent');
Reacting on color scheme changes:
document.addEventListener('colorschemechange', (e) => {
console.log(`Color scheme changed to ${e.detail.colorScheme}.`);
});
Reacting on "remember the last selected mode" functionality changes:
document.addEventListener('permanentcolorscheme', (e) => {
console.log(`${e.detail.permanent ? 'R' : 'Not r'
}emembering the last selected mode.`);
});
Style Customization
You can style the custom element with ::part()
.
See the demo's
CSS source code
for some concrete examples.
The exposed parts and their names can be seen below:
<form part="form">
<fieldset part="fieldset">
<legend part="legend"></legend>
<input part="lightRadio" id="l" name="mode" type="radio">
<label part="lightLabel" for="l"></label>
<input part="darkRadio" id="d" name="mode" type="radio">
<label part="darkLabel" for="d"></label>
<input part="toggleCheckbox" id="t" type="checkbox">
<label part="toggleLabel" for="t"></label>
<aside part="aside">
<input part="permanentCheckbox" id="p" type="checkbox">
<label part="permanentLabel" for="p"></label>
</aside>
</fieldset>
</form>
Additionally, you can use a number of exposed CSS variables, as listed in the following:
CSS Variable Name | Default | Description |
---|
--dark-mode-toggle-light-icon | No icon | The icon for the light state in background-image: notation. |
--dark-mode-toggle-dark-icon | No icon | The icon for the dark state in background-image: notation. |
--dark-mode-toggle-icon-size | 1rem | The icon size in CSS length data type notation. |
--dark-mode-toggle-remember-icon-checked | No icon | The icon for the checked "remember the last selected mode" functionality in background-image: notation. |
--dark-mode-toggle-remember-icon-unchecked | No icon | The icon for the unchecked "remember the last selected mode" functionality in background-image: notation. |
--dark-mode-toggle-color | User-Agent stylesheet text color | The main text color in color: notation. |
--dark-mode-toggle-background-color | User-Agent stylesheet background color | The main background color in background-color: notation. |
--dark-mode-toggle-legend-font | User-Agent <legend> font | The font of the legend in shorthand font: notation. |
--dark-mode-toggle-label-font | User-Agent <label> font | The font of the labels in shorthand font: notation. |
--dark-mode-toggle-remember-font | User-Agent <label> font | The font of the "remember the last selected mode" functionality label in shorthand font: notation. |
--dark-mode-toggle-icon-filter | No filter | The filter for the dark icon (so you can use all black or all white icons and just invert one of them) in filter: notation. |
--dark-mode-toggle-remember-filter | No filter | The filter for the "remember the last selected mode" functionality icon (so you can use all black or all white icons and just invert one of them) in filter: notation. |
--dark-mode-toggle-active-mode-background-color | No background color | The background color for the currently active mode in background-color: notation. |
Hacking on <dark-mode-toggle>
The core custom element code lives in
src/dark-mode-toggle.mjs
.
You can start hacking and testing your changes by running npm run start
and then navigating to http://localhost:8080/demo/.
No build step required 🎉, this happens automatically upon npm publish
ing.
If for whatever reason you want to build locally, run npm run build
.
You can lint by running npm run lint
.
The HTML and the CSS used by <dark-mode-toggle>
is hard-coded as a template literal
in the file src/dark-mode-toggle.mjs
.
For optimal performance, the contents of this literal are hand-minified.
If you need to tweak the HTML or the CSS, find the unminified template literal contents
in src/template-contents.tpl
and copy them over to src/dark-mode-toggle.mjs
.
Once your changes are done, commit them to both the *.tpl
file (in unminified form)
and the *.mjs
file (in minified form).
(This is actually just making a strong argument for
CSS Modules and
HTML Modules
that would allow for proper tools integration).
Proudly used on…
Notes
This is not an official Google product.
Acknowledgements
Thanks to all contributors
for making <dark-mode-toggle>
even better!
Usage video by Tomek Sułkowski.
License
Copyright 2019 Google LLC
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.