-
Notifications
You must be signed in to change notification settings - Fork 5
/
gatsby-ssr.js
76 lines (58 loc) · 2.26 KB
/
gatsby-ssr.js
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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
import React from "react";
import COLORS from "./src/utils/theme";
import { wrapRootElement as wrap } from "./wrap-root-element";
// Code to prevent flashing of Dark Mode adapted from
// https://www.joshwcomeau.com/react/dark-mode/
// https://github.com/joshwcomeau/dark-mode-minimal/blob/master/gatsby-ssr.js
function setColorsByTheme() {
const colors = "🌈";
const mql = window.matchMedia("(prefers-color-scheme: dark)");
const prefersDarkFromMQ = mql.matches;
const persistedPreference = localStorage.getItem("color-mode");
let colorMode = "light";
const hasUsedToggle = typeof persistedPreference === "string";
if (hasUsedToggle) {
colorMode = persistedPreference;
} else {
colorMode = prefersDarkFromMQ ? "dark" : "light";
}
const root = document.documentElement;
root.style.setProperty("--initial-color-mode", colorMode);
Object.entries(colors[colorMode]).forEach(([name, colorByTheme]) => {
const cssVar = `--color-${name}`;
root.style.setProperty(cssVar, colorByTheme);
});
}
const DarkModeScript = () => {
const boundFn = String(setColorsByTheme).replace(
'"🌈"',
JSON.stringify(COLORS)
);
const clientCode = `(${boundFn})()`;
// eslint-disable-next-line react/no-danger
return <script dangerouslySetInnerHTML={{ __html: clientCode }} />;
};
/**
* If the user has JS disabled, the injected script will never fire!
* This means that they won't have any colors set, everything will be default
* black and white.
* We can solve for this by injecting a `<style>` tag into the head of the
* document, which sets default values for all of our colors.
* Only light mode will be available for users with JS disabled.
*/
const FallbackStyles = () => {
// programatically create css variables from color list and use light by default
const cssVariableString = Object.entries(COLORS.light).reduce(
(acc, [name, colorByTheme]) => {
return `${acc}\n--color-${name}: ${colorByTheme};`;
},
""
);
const wrappedInSelector = `html { ${cssVariableString} }`;
return <style>{wrappedInSelector}</style>;
};
export const onRenderBody = ({ setPreBodyComponents, setHeadComponents }) => {
setHeadComponents(<FallbackStyles />);
setPreBodyComponents(<DarkModeScript />);
};
export const wrapRootElement = wrap;