A script to toggle light and dark themes
Jour… Nuit… Jour…
file: assets/scripts/toggleTheme.js
1{{ $light := site.Params.theme.light.background | default "#ffffff" }}
2{{ $dark := site.Params.theme.dark.background | default "#000000" }}
3
4// Switch from one theme to another
5function toggleTheme() {
6 // The current theme is defined in <html data-theme="">
7 // "data-theme" is an attribute of <html> which is refered to as "document.documentElement"
8 // current theme is either "light" or "dark"
9 const currentTheme = document.documentElement.getAttribute("data-theme");
10 const isDark = currentTheme === "dark";
11 // The alternative theme is the theme that is not the current theme
12 // if current theme is "light" alternative theme is "dark"
13 // if current theme is "dark" alternative theme is "light"
14 const alternativeTheme = isDark ? "light" : "dark";
15 const message = isDark ? '{{ i18n "light_on" }}' : '{{ i18n "light_off" }}';
16 // The theme color is a RGB color value associated with the theme
17 // colors for both themes are defined in config.toml and defaults to values exposed above
18 // during compilation Hugo will replace the placeholders in the following expression by the actual values
19 // this metadata is used by Chrome to colorize its "chrome" on mobile (navigation bar)
20 // the theme color is exposed in a the page <head> as metadata : <meta name="theme-color" content="" />
21 const themeColor = document.querySelector("meta[name=theme-color]");
22 // The theme color me is a RGB color value associated with each theme
23 // they are defined in config.toml and defaults to values exposed above
24 // during compilation Hugo will replace the placeholders in the following expression by the actual values
25 // The alternative theme colot is the the RGB color associated to the alternative theme
26 const alternativeThemeColor = isDark ? "{{ $light }}" : "{{ $dark }}";
27 // Replace the current theme by the alternative theme
28 document.documentElement.setAttribute("data-theme", alternativeTheme);
29 console.log(message);
30 // Place a variable "theme" in browser's local storage (on the user device)
31 // the variable's value is the alternative theme ("dark" or "light" - the one we just set as current theme)
32 window.localStorage && window.localStorage.setItem("theme", alternativeTheme);
33 // Fill <meta name="theme-color" content="" /> with the alternative theme color
34 themeColor.setAttribute("content", alternativeThemeColor);
35}
36
37// Enable a specific theme
38function enableTheme(targetTheme) {
39 const currentTheme = document.documentElement.getAttribute("data-theme");
40 if (currentTheme !== targetTheme) {
41 toggleTheme();
42 }
43}
44
45// Set initial theme
46function initTheme(adminPref) {
47 // When visitors doesn't have JS, theme will be set according to his global preferences
48 // With JS we basically force a theme and the attribute must not be left empty
49 document.documentElement.setAttribute("data-theme", adminPref);
50 // Check user's global preference
51 const prefersDarkScheme = window.matchMedia("screen and (prefers-color-scheme: dark)");
52 const prefersLightScheme = window.matchMedia("screen and (prefers-color-scheme: light)");
53 const globalPref = prefersDarkScheme.matches ? "dark" : "light";
54 // Check user's local preference
55 const localPref = window.localStorage && window.localStorage.getItem("theme");
56 // Let local preference prevail
57 const userPref = !!localPref ? localPref : globalPref;
58 enableTheme(userPref);
59 // Observe change of mind
60 document.addEventListener("DOMContentLoaded", event => {
61 // Locally (on the website)
62 const toggle = document.querySelector(".theme");
63 toggle.addEventListener("click", toggleTheme, false);
64 // or globally (through the browser)
65 prefersDarkScheme.addListener(
66 e => e.matches && enableTheme("dark")
67 );
68 prefersLightScheme.addListener(
69 e => e.matches && enableTheme("light")
70 );
71 });
72}
73
74// Initialise Theme
75initTheme('{{ site.Params.theme.default | default "light" }}');
Comments