Skip to content

Commit

Permalink
fix(app): themes sync (#210)
Browse files Browse the repository at this point in the history
  • Loading branch information
polubis committed Jun 20, 2024
1 parent 92e22b2 commit 757b4ee
Show file tree
Hide file tree
Showing 3 changed files with 82 additions and 38 deletions.
52 changes: 52 additions & 0 deletions gatsby-ssr.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import React from 'react';
import type { GatsbySSR } from 'gatsby';
// Based on following implementation:
// https://github.com/insin/gatsby-plugin-dark-mode
export const onRenderBody: GatsbySSR['onRenderBody'] = ({
setPreBodyComponents,
}) => {
setPreBodyComponents([
<script
id="dark-mode"
key="dark-mode"
dangerouslySetInnerHTML={{
__html: `
void function() {
window.__onThemeChange = function() {}
var preferredTheme
try {
preferredTheme = localStorage.getItem('theme')
} catch (err) { }
function setTheme(newTheme) {
if (preferredTheme && document.body.classList.contains(preferredTheme)) {
document.body.classList.replace(preferredTheme, newTheme)
} else {
document.body.classList.add(newTheme)
}
window.__theme = newTheme
preferredTheme = newTheme
window.__onThemeChange(newTheme)
}
window.__setPreferredTheme = function(newTheme) {
setTheme(newTheme)
try {
localStorage.setItem('theme', newTheme)
} catch (err) {}
}
var darkQuery = window.matchMedia('(prefers-color-scheme: dark)')
darkQuery.addListener(function(e) {
window.__setPreferredTheme(e.matches ? 'dark' : 'light')
})
setTheme(preferredTheme || (darkQuery.matches ? 'dark' : 'light'))
}()
`,
}}
/>,
]);
};
7 changes: 4 additions & 3 deletions src/components/navigation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,16 @@ const Navigation = ({ className, children }: NavigationProps) => {
<nav className="flex gap-3 w-full items-center">
{children}
<ThemeProvider>
{({ theme, setTheme }) => (
{({ theme, set }) => (
<Button
i={1}
s={2}
title="Change theme"
className="ml-auto"
onClick={() => setTheme(theme === `light` ? `dark` : `light`)}
disabled={theme === null}
onClick={() => set(theme === `light` ? `dark` : `light`)}
>
{theme === `light` ? <BiMoon /> : <BiSun />}
{theme === `dark` ? <BiSun /> : <BiMoon />}
</Button>
)}
</ThemeProvider>
Expand Down
61 changes: 26 additions & 35 deletions src/design-system/theme-provider.tsx
Original file line number Diff line number Diff line change
@@ -1,59 +1,50 @@
import { isServer } from 'development-kit/ssr-csr';
import { useIsomorphicLayoutEffect } from 'development-kit/use-isomorphic-layout-effect';
import React from 'react';
import { isClient } from 'development-kit/ssr-csr';

const themes = [`light`, `dark`] as const;

type Theme = (typeof themes)[number];
type NullableTheme = Theme | null;

declare global {
interface Window {
__theme: Theme;
__setPreferredTheme(theme: Theme): void;
__onThemeChange(theme: Theme): void;
}
}

interface ThemeContext {
theme: Theme;
setTheme(theme: Theme): void;
theme: NullableTheme;
set(theme: Theme): void;
}

interface ThemeProviderProps {
children: (context: ThemeContext) => React.ReactNode;
children(context: ThemeContext): React.ReactNode;
}

const isTheme = (theme: string | null): theme is Theme =>
theme !== null && themes.includes(theme as Theme);

const readThemeFromLS = (): Theme => {
if (isServer()) {
return `light`;
}

const theme = localStorage.getItem(`theme`);

if (!isTheme(theme)) return `light`;

return theme;
};
const getInitialTheme = (): NullableTheme =>
isClient() ? window.__theme : null;

const ThemeProvider = ({ children }: ThemeProviderProps) => {
const [theme, setTheme] = React.useState(readThemeFromLS);

useIsomorphicLayoutEffect(() => {
localStorage.setItem(`theme`, theme);
const [theme, setTheme] = React.useState<NullableTheme>(null);

if (document.body.classList.contains(`light`)) {
document.body.classList.replace(`light`, theme);
return;
}
const set: ThemeContext['set'] = React.useCallback((theme) => {
window.__setPreferredTheme(theme);
}, []);

if (document.body.classList.contains(`dark`)) {
document.body.classList.replace(`dark`, theme);
return;
}
React.useEffect(() => {
window.__onThemeChange = () => {
setTheme(window.__theme);
};

document.body.classList.add(theme);
}, [theme]);
setTheme(getInitialTheme());
}, []);

return children({
theme,
setTheme,
set,
});
};

export type { ThemeProviderProps };
export { ThemeProvider };

0 comments on commit 757b4ee

Please sign in to comment.