Start using next-themes (#115)

* Start using next-themes

* Fix with nonce

* Lint
This commit is contained in:
Samy Pessé
2024-01-24 11:39:59 +01:00
committed by GitHub
parent aec74d68fd
commit eabc46b86e
5 changed files with 34 additions and 35 deletions
BIN
View File
Binary file not shown.
+2 -1
View File
@@ -31,7 +31,8 @@
"jsontoxml": "^1.0.1",
"katex": "^0.16.9",
"memoizee": "^0.4.15",
"next": "^14.0.4",
"next": "^14.1.0",
"next-themes": "^0.2.1",
"object-hash": "^3.0.0",
"p-map": "^7.0.0",
"parse-cache-control": "^1.0.1",
+10 -4
View File
@@ -1,5 +1,7 @@
'use client';
import { CustomizationThemeMode } from '@gitbook/api';
import { ThemeProvider } from 'next-themes';
import React from 'react';
import { RecoilRoot } from 'recoil';
@@ -7,14 +9,18 @@ import { TranslateContext } from '@/intl/client';
import { TranslationLanguage } from '@/intl/translations';
export function ClientContexts(props: {
nonce: string;
language: TranslationLanguage;
forcedTheme: CustomizationThemeMode | undefined;
children: React.ReactNode;
}) {
const { children, language } = props;
const { nonce, children, forcedTheme, language } = props;
return (
<RecoilRoot>
<TranslateContext.Provider value={language}>{children}</TranslateContext.Provider>
</RecoilRoot>
<ThemeProvider nonce={nonce} attribute="class" enableSystem forcedTheme={forcedTheme}>
<RecoilRoot>
<TranslateContext.Provider value={language}>{children}</TranslateContext.Provider>
</RecoilRoot>
</ThemeProvider>
);
}
+12 -1
View File
@@ -6,6 +6,7 @@ import { fonts, ibmPlexMono } from '@/fonts';
import { getSpaceLanguage } from '@/intl/server';
import { getSpaceContent } from '@/lib/api';
import { hexToRgb, shadesOfColor } from '@/lib/colors';
import { getContentSecurityPolicyNonce } from '@/lib/csp';
import { tcls } from '@/lib/tailwind';
import { ClientContexts } from './ClientContexts';
@@ -27,9 +28,11 @@ export default async function SpaceRootLayout(props: {
});
const headerTheme = generateHeaderTheme(customization);
const language = getSpaceLanguage(customization);
const nonce = getContentSecurityPolicyNonce();
return (
<html
suppressHydrationWarning
lang={customization.internationalization.locale}
className={tcls(
customization.header.preset === CustomizationHeaderPreset.None
@@ -85,7 +88,15 @@ export default async function SpaceRootLayout(props: {
'dark:bg-dark',
)}
>
<ClientContexts language={language}>{children}</ClientContexts>
<ClientContexts
nonce={nonce}
language={language}
forcedTheme={
customization.themes.toggeable ? undefined : customization.themes.default
}
>
{children}
</ClientContexts>
</body>
</html>
);
+10 -29
View File
@@ -3,6 +3,7 @@
import Monitor from '@geist-ui/icons/monitor';
import Moon from '@geist-ui/icons/moon';
import Sun from '@geist-ui/icons/sun';
import { useTheme } from 'next-themes';
import React from 'react';
import { IconComponent } from '@/components/icons';
@@ -11,42 +12,22 @@ import { tcls } from '@/lib/tailwind';
type ThemeMode = 'light' | 'system' | 'dark';
const LOCALSTORAGE_KEY = 'color-theme';
/**
* Buttons to toggle between light/system/dark modes.
*/
export function ThemeToggler(props: {}) {
const [mode, setMode] = React.useState<ThemeMode>('system');
const language = useLanguage();
const onSwitchMode = (to: ThemeMode) => {
setMode(to);
window.localStorage.setItem(LOCALSTORAGE_KEY, to);
};
const [mounted, setMounted] = React.useState(false);
const { theme, setTheme } = useTheme();
React.useEffect(() => {
const value = window.localStorage.getItem(LOCALSTORAGE_KEY);
if (value && (value === 'light' || value === 'system' || value === 'dark')) {
setMode(value);
}
setMounted(true);
}, []);
React.useEffect(() => {
const applied =
mode === 'system'
? window.matchMedia('(prefers-color-scheme: dark)').matches
? 'dark'
: 'light'
: mode;
if (applied === 'light') {
document.documentElement.classList.remove('dark');
} else {
document.documentElement.classList.add('dark');
}
}, [mode]);
const onSwitchMode = (to: ThemeMode) => {
setTheme(to);
};
return (
<div
@@ -61,19 +42,19 @@ export function ThemeToggler(props: {}) {
)}
>
<ThemeButton
active={mode === 'light'}
active={mounted && theme === 'light'}
icon={Sun}
onClick={() => onSwitchMode('light')}
title={tString(language, 'switch_to_light_theme')}
/>
<ThemeButton
active={mode === 'system'}
active={mounted && theme === 'system'}
icon={Monitor}
onClick={() => onSwitchMode('system')}
title={tString(language, 'switch_to_system_theme')}
/>
<ThemeButton
active={mode === 'dark'}
active={mounted && theme === 'dark'}
icon={Moon}
onClick={() => onSwitchMode('dark')}
title={tString(language, 'switch_to_dark_theme')}