From 6b267780b58727b50262cabf722b5c4582224dee Mon Sep 17 00:00:00 2001 From: Sagnik Pradhan Date: Fri, 1 Oct 2021 17:20:49 +0530 Subject: [PATCH 1/6] feat: added prefers-color-scheme support + read media query for theme preference + improved typing of useLocalStorage hook --- src/ThemeProvider.tsx | 41 ++++++++++++++++++++++++++++++----- src/hooks/useLocalStorage.tsx | 10 ++++++--- 2 files changed, 42 insertions(+), 9 deletions(-) diff --git a/src/ThemeProvider.tsx b/src/ThemeProvider.tsx index f637c08..fab7627 100644 --- a/src/ThemeProvider.tsx +++ b/src/ThemeProvider.tsx @@ -1,12 +1,13 @@ -import React, { createContext, FC, useMemo } from "react" +import React, { createContext, FC, useEffect, useMemo, useState } from "react" import { ThemeProvider as BaseThemeProvider } from "styled-components" import { darkTheme, lightTheme } from "./design/themes" import { useLocalStorage } from "./hooks/useLocalStorage" +type ThemeType = "dark" | "light" + export interface IThemeContext { - theme: "dark" | "light" - setTheme: () => void + theme: ThemeType toggleTheme: () => void } @@ -17,19 +18,47 @@ interface IScopedDownChildren { export const ThemeContext = createContext(null) const ThemeProvider: FC = ({ children }) => { - const [theme, setTheme] = useLocalStorage("theme", "dark") + // User's explicitly set theme + const [localTheme, setLocalTheme] = useLocalStorage( + "theme", + "unset" + ) + + // App's current theme + const [theme, setTheme] = useState( + localTheme === "unset" ? "dark" : localTheme + ) const themeObject = useMemo( () => (theme === "dark" ? darkTheme : lightTheme), [theme] ) + useEffect(() => { + if (localTheme === "unset") { + const prefersLightTheme = window.matchMedia( + "(prefers-color-scheme: light)" + ) + + setTheme(prefersLightTheme.matches ? "light" : "dark") + + prefersLightTheme.onchange = ({ matches }) => + setTheme(matches ? "light" : "dark") + + return () => { + prefersLightTheme.onchange = null + } + } + }, []) + const contextValue = useMemo( () => ({ theme, - setTheme, + toggleTheme: () => { - setTheme(theme === "light" ? "dark" : "light") + const newTheme = theme === "light" ? "dark" : "light" + setTheme(newTheme) + setLocalTheme(newTheme) }, }), [theme, setTheme] diff --git a/src/hooks/useLocalStorage.tsx b/src/hooks/useLocalStorage.tsx index 8cea92a..849eb33 100644 --- a/src/hooks/useLocalStorage.tsx +++ b/src/hooks/useLocalStorage.tsx @@ -1,9 +1,13 @@ /* globals window */ import { useEffect, useState } from "react" -export const useLocalStorage = (name: string, initialValue: string) => { +export const useLocalStorage = ( + name: string, + initialValue: Value +) => { const windowGlobal = typeof window !== "undefined" && window - const [value, setValue] = useState(() => { + + const [value, setValue] = useState(() => { if (windowGlobal) { const currentValue = windowGlobal.localStorage.getItem(name) return currentValue ? JSON.parse(currentValue) : initialValue @@ -17,5 +21,5 @@ export const useLocalStorage = (name: string, initialValue: string) => { } }, [name, value, windowGlobal]) - return [value, setValue] + return [value, setValue] as const } From b6b295fced8e48530fbdaa83c26799fd831db7a3 Mon Sep 17 00:00:00 2001 From: Sagnik Pradhan Date: Fri, 1 Oct 2021 18:33:17 +0530 Subject: [PATCH 2/6] fix: hopefully fix for #334 --- gatsby-ssr.js | 34 ++++++++++++++++++++++++++++------ src/ThemeProvider.tsx | 6 +++--- 2 files changed, 31 insertions(+), 9 deletions(-) diff --git a/gatsby-ssr.js b/gatsby-ssr.js index b17b8fc..0a97870 100644 --- a/gatsby-ssr.js +++ b/gatsby-ssr.js @@ -1,7 +1,29 @@ -/** - * Implement Gatsby's SSR (Server Side Rendering) APIs in this file. - * - * See: https://www.gatsbyjs.org/docs/ssr-apis/ - */ +import React from "react" -// You can delete this file if you're not using it +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" + } + + document.documentElement.style.setProperty( + "--initial-theme", + getInitialTheme() + ) +})() + ` + + return