diff --git a/gatsby-ssr.js b/gatsby-ssr.js
index 33c7ee9..b17b8fc 100644
--- a/gatsby-ssr.js
+++ b/gatsby-ssr.js
@@ -1,31 +1,7 @@
-import React from "react"
+/**
+ * Implement Gatsby's SSR (Server Side Rendering) APIs in this file.
+ *
+ * See: https://www.gatsbyjs.org/docs/ssr-apis/
+ */
-const ThemeScript = () => {
- const themeScript = `
-(function() {
- function getInitialTheme() {
- const userSetTheme = window.localStorage.getItem("theme")
- const prefersLightTheme = window.matchMedia(
- "(prefers-color-scheme: light)"
- )
-
- if (userSetTheme && userSetTheme !== '"unset"') return userSetTheme
- else if (prefersLightTheme.matches === true) return "light"
- else return "dark"
- }
-
- if (typeof document === "undefined") return;
-
- document.documentElement.style.setProperty(
- "--initial-theme",
- getInitialTheme()
- )
-})()
- `
-
- return
-}
-
-export const onRenderBody = ({ setPreBodyComponents }) => {
- setPreBodyComponents()
-}
+// You can delete this file if you're not using it
diff --git a/src/ThemeProvider.tsx b/src/ThemeProvider.tsx
index 57d69b9..8747f85 100644
--- a/src/ThemeProvider.tsx
+++ b/src/ThemeProvider.tsx
@@ -1,8 +1,10 @@
-import React, { createContext, FC, useEffect, useMemo, useState } from "react"
+import React, { createContext, FC, useMemo } from "react"
import { ThemeProvider as BaseThemeProvider } from "styled-components"
import { darkTheme, lightTheme } from "./design/themes"
+import { useIsBrowser } from "./hooks/useIsBrowser"
import { useLocalStorage } from "./hooks/useLocalStorage"
+import { useMedia } from "./hooks/useMedia"
type ThemeType = "dark" | "light"
@@ -17,20 +19,20 @@ interface IScopedDownChildren {
export const ThemeContext = createContext(null)
-const ThemeProvider: FC = ({ children }) => {
- // User's explicitly set theme
- const [localTheme, setLocalTheme] = useLocalStorage(
- "theme",
- "unset"
+function useTheme() {
+ const preferredTheme = useMedia(
+ ["(prefers-color-scheme: light)", "(prefers-color-scheme: dark)"],
+ ["light", "dark"],
+ "dark"
)
- // App's current theme
- const [theme, setTheme] = useState(
- typeof document !== "undefined"
- ? (document.documentElement.style.getPropertyValue(
- "--initial-theme"
- ) as ThemeType)
- : "dark"
+ const [userSelectedTheme, setUserSelectedTheme] = useLocalStorage<
+ ThemeType | "unset"
+ >("user-selected-theme", "unset")
+
+ const theme = useMemo(
+ () => (userSelectedTheme !== "unset" ? userSelectedTheme : preferredTheme),
+ [preferredTheme, userSelectedTheme]
)
const themeObject = useMemo(
@@ -38,37 +40,30 @@ const ThemeProvider: FC = ({ children }) => {
[theme]
)
- useEffect(() => {
- if (localTheme === "unset") {
- const prefersLightTheme = window.matchMedia(
- "(prefers-color-scheme: light)"
- )
+ return {
+ theme,
+ themeObject,
+ setTheme: setUserSelectedTheme,
+ }
+}
- prefersLightTheme.onchange = ({ matches }) =>
- setTheme(matches ? "light" : "dark")
-
- return () => {
- prefersLightTheme.onchange = null
- }
- }
- }, [localTheme])
+const ThemeProvider: FC = ({ children }) => {
+ const isBrowser = useIsBrowser()
+ const { theme, themeObject, setTheme } = useTheme()
const contextValue = useMemo(
() => ({
theme,
-
- toggleTheme: () => {
- const newTheme = theme === "light" ? "dark" : "light"
- setTheme(newTheme)
- setLocalTheme(newTheme)
- },
+ toggleTheme: () => setTheme(theme === "light" ? "dark" : "light"),
}),
- [theme, setTheme, setLocalTheme]
+ [theme, setTheme]
)
return (
- {children}
+
+ {isBrowser ? children : undefined}
+
)
}
diff --git a/src/hooks/useIsBrowser.tsx b/src/hooks/useIsBrowser.tsx
new file mode 100644
index 0000000..cd598b6
--- /dev/null
+++ b/src/hooks/useIsBrowser.tsx
@@ -0,0 +1,11 @@
+import { useEffect, useState } from "react"
+
+export function useIsBrowser() {
+ const isBrowserCheck = () => typeof window !== "undefined"
+
+ const [isBrowser, setIsBrowser] = useState(isBrowserCheck)
+
+ useEffect(() => setIsBrowser(isBrowserCheck), [])
+
+ return isBrowser
+}
diff --git a/src/hooks/useMedia.tsx b/src/hooks/useMedia.tsx
new file mode 100644
index 0000000..6442aa5
--- /dev/null
+++ b/src/hooks/useMedia.tsx
@@ -0,0 +1,35 @@
+import { useEffect, useState } from "react"
+
+export function useMedia(
+ queries: string[],
+ values: Value[],
+ defaultValue: Value
+) {
+ const [currentValue, setCurrentValue] = useState(defaultValue)
+
+ useEffect(() => {
+ const windowGlobal = typeof window !== "undefined" && window
+
+ if (!windowGlobal) return
+
+ const mediaQueries = queries.map((query) => windowGlobal.matchMedia(query))
+
+ const getCurrentValue = () => {
+ const index = mediaQueries.findIndex((value) => value.matches)
+ return values[index] || defaultValue
+ }
+
+ setCurrentValue(getCurrentValue)
+
+ const handler = () => setCurrentValue(getCurrentValue)
+
+ mediaQueries.forEach((query) => query.addEventListener("change", handler))
+
+ return () =>
+ mediaQueries.forEach((query) =>
+ query.removeEventListener("change", handler)
+ )
+ }, [queries, values, defaultValue])
+
+ return currentValue
+}