Last active
April 26, 2022 15:12
-
-
Save dustinmyers/bfe72e1a002a70e0106a5e913cda9f15 to your computer and use it in GitHub Desktop.
Initial function and context object for dark mode in a NextJS SSR App - To avoid the SSR dark mode "flicker", set styles on the root before the site is rendered. CSS variables are key here.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { COLORS, COLOR_MODE_KEY, INITIAL_COLOR_MODE_CSS_PROP } from '../themes/cssVariables' | |
export function setColorsByTheme() { | |
const colors = '🌈'; | |
const colorModeKey = '🔑'; | |
const colorModeCssProp = '⚡️'; | |
const mql = window.matchMedia('(prefers-color-scheme: dark)') | |
const prefersDarkFromMQ = mql.matches | |
const persistedPreference = localStorage.getItem(colorModeKey); | |
let colorMode = 'light'; | |
const hasUsedToggle = typeof persistedPreference === 'string'; | |
if (hasUsedToggle) { | |
colorMode = persistedPreference; | |
} else { | |
colorMode = prefersDarkFromMQ ? 'dark' : 'light'; | |
} | |
const root = document.documentElement | |
root.style.setProperty(colorModeCssProp, colorMode); | |
Object.entries(colors).forEach(([name, colorByTheme]) => { | |
const cssVarName = `--${name}` | |
root.style.setProperty(cssVarName, colorByTheme[colorMode]) | |
}) | |
} | |
export function MagicScriptTag() { | |
const boundFn = String(setColorsByTheme) | |
.replace("'🌈'", JSON.stringify(COLORS)) | |
.replace('🔑', COLOR_MODE_KEY) | |
.replace('⚡️', INITIAL_COLOR_MODE_CSS_PROP); | |
let calledFunction = `(${boundFn})()` | |
// eslint-disable-next-line react/no-danger | |
return <script dangerouslySetInnerHTML={{ __html: calledFunction }} /> | |
} | |
// if user doesn't have JavaScript enabled, set variables properly in a | |
// head style tag anyways (light mode) | |
export function FallbackStyles() { | |
const cssVariableString = Object.entries(COLORS).reduce( | |
(acc, [name, colorByTheme]) => { | |
return `${acc}\n--color-${name}: ${colorByTheme.light};` | |
}, | |
'' | |
) | |
const wrappedInSelector = `html { ${cssVariableString} }` | |
return <style>{wrappedInSelector}</style> | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React from "react"; | |
import { | |
COLORS, | |
COLOR_MODE_KEY, | |
INITIAL_COLOR_MODE_CSS_PROP, | |
BREAKPOINTS, | |
FONTS, | |
} from "../themes/cssVariables"; | |
export const UserThemeContext = React.createContext() | |
export const UserThemeProvider = ({ children }) => { | |
const [colorMode, rawSetColorMode] = React.useState(undefined); | |
React.useEffect(() => { | |
const root = window.document.documentElement; | |
const initialColorValue = root.style.getPropertyValue( | |
INITIAL_COLOR_MODE_CSS_PROP | |
); | |
rawSetColorMode(initialColorValue); | |
}, []); | |
const contextValue = React.useMemo(() => { | |
function setColorMode(newValue) { | |
const root = window.document.documentElement; | |
localStorage.setItem(COLOR_MODE_KEY, newValue); | |
Object.entries(COLORS).forEach(([name, colorByTheme]) => { | |
const cssVarName = `--${name}`; | |
root.style.setProperty(cssVarName, colorByTheme[colorMode]); | |
}); | |
Object.entries(BREAKPOINTS).forEach(([name, breakpoint]) => { | |
const cssVarName = `--${name}`; | |
root.style.setProperty(cssVarName, breakpoint[colorMode]); | |
}); | |
Object.entries(FONTS).forEach(([name, font]) => { | |
const cssVarName = `--${name}`; | |
root.style.setProperty(cssVarName, font[colorMode]); | |
}); | |
rawSetColorMode(newValue); | |
} | |
return { | |
colorMode, | |
setColorMode, | |
}; | |
}, [colorMode, rawSetColorMode]); | |
return ( | |
<UserThemeContext.Provider value={contextValue}> | |
{children} | |
</UserThemeContext.Provider> | |
); | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment