x
1
2
3
4
5
6
7
8
9
10
11
12
<div class="color-scheme-toggle" data-controller="popover" data-popover-placement-value="bottom-start"> <button type="button" class="btn btn--icon" data-popover-target="button" data-action="popover#toggle"> <span class="icon icon--sun" aria-hidden="true"></span> <span class="icon icon--moon" aria-hidden="true"></span> <span class="sr-only">Theme switcher</span> </button> <div popover class="popover menu" data-popover-target="menu"> <button type="button" class="btn menu__item" data-action="popover#hide color-scheme#setLight">Light</button> <button type="button" class="btn menu__item" data-action="popover#hide color-scheme#setDark">Dark</button> <button type="button" class="btn menu__item" data-action="popover#hide color-scheme#setSystem">System</button> </div></div>
1
2
3
4
5
6
7
8
9
10
11
12
13
<div class="color-scheme-toggle" data-controller="popover" data-popover-placement-value="bottom-start"> <button type="button" class="btn btn--icon" data-popover-target="button" data-action="popover#toggle"> <span class="icon icon--sun" aria-hidden="true"></span> <span class="icon icon--moon" aria-hidden="true"></span> <span class="sr-only">Theme switcher</span> </button> <div popover class="popover menu" data-popover-target="menu"> <button type="button" class="btn menu__item" data-action="popover#hide color-scheme#setLight">Light</button> <button type="button" class="btn menu__item" data-action="popover#hide color-scheme#setDark">Dark</button> <button type="button" class="btn menu__item" data-action="popover#hide color-scheme#setSystem">System</button> </div></div>
1
2
3
4
5
6
7
8
9
.color-scheme-toggle { .icon--sun { display: inline-flex; } .icon--moon { display: none; } [data-color-scheme="dark"] & { .icon--sun { display: none; } .icon--moon { display: inline-flex; } }}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
import { Controller } from "@hotwired/stimulus"const COLOR_SCHEME_DARK = "(prefers-color-scheme: dark)"export default class extends Controller { connect() { this.element.dataset.colorScheme = this.#colorScheme this.element.dataset.preferSystemColorScheme = this.#preferSystemColorScheme this.#setupSystemColorSchemeListener() } setLight() { this.element.dataset.colorScheme = "light" this.element.dataset.preferSystemColorScheme = false localStorage.setItem("color_scheme", "light") } setDark() { this.element.dataset.colorScheme = "dark" this.element.dataset.preferSystemColorScheme = false localStorage.setItem("color_scheme", "dark") } setSystem() { this.element.dataset.colorScheme = this.#systemColorScheme this.element.dataset.preferSystemColorScheme = true localStorage.removeItem("color_scheme") } get #colorScheme() { return localStorage.getItem("color_scheme") || this.#systemColorScheme } get #systemColorScheme() { return window.matchMedia(COLOR_SCHEME_DARK).matches ? "dark" : "light" } get #preferSystemColorScheme() { return !localStorage.getItem("color_scheme") } #setupSystemColorSchemeListener() { window.matchMedia(COLOR_SCHEME_DARK).addEventListener("change", () => { this.element.dataset.colorScheme = this.#colorScheme }) }}
Initialization
<html data-controller="color-scheme">...</html>