Skip to content

Commit d28cf9f

Browse files
committed
theme code snippets
1 parent 9940e63 commit d28cf9f

File tree

3 files changed

+86
-16
lines changed

3 files changed

+86
-16
lines changed

landing/app/layout.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ import type { Metadata } from 'next';
33
import Head from 'next/head';
44
import { Geist, Geist_Mono } from 'next/font/google';
55

6+
import { ThemeProvider } from '@/components/ThemeProvider';
7+
68
import './globals.css';
79

810
const geistSans = Geist({
@@ -29,7 +31,7 @@ export default function RootLayout({ children }: { children: ReactNode }) {
2931
<body
3032
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
3133
>
32-
{children}
34+
<ThemeProvider>{children}</ThemeProvider>
3335
</body>
3436
</html>
3537
);

landing/components/CodeSnippet.tsx

Lines changed: 26 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,11 @@
22

33
import { Suspense } from 'react';
44
import dynamic from 'next/dynamic';
5-
import { docco } from 'react-syntax-highlighter/dist/esm/styles/hljs';
5+
import {
6+
docco,
7+
stackoverflowDark,
8+
} from 'react-syntax-highlighter/dist/esm/styles/hljs';
9+
import { useTheme } from '@/components/ThemeProvider';
610

711
const SyntaxHighlighter = dynamic(() => import('react-syntax-highlighter'));
812

@@ -11,18 +15,25 @@ type Props = {
1115
children: string;
1216
};
1317

14-
export const CodeSnippet = ({ type, children }: Props) => (
15-
<div className="my-2">
16-
<Suspense
17-
fallback={
18-
<div className="monospaced whitespace-pre-wrap bg-secondary px-2 py-[0.5em] border-l-4">
18+
export const CodeSnippet = ({ type, children }: Props) => {
19+
const theme = useTheme();
20+
21+
return (
22+
<div className="my-2">
23+
<Suspense
24+
fallback={
25+
<div className="monospaced whitespace-pre-wrap bg-secondary px-2 py-[0.5em] border-l-4">
26+
{children}
27+
</div>
28+
}
29+
>
30+
<SyntaxHighlighter
31+
language={type}
32+
style={theme === 'light' ? docco : stackoverflowDark}
33+
>
1934
{children}
20-
</div>
21-
}
22-
>
23-
<SyntaxHighlighter language={type} style={docco}>
24-
{children}
25-
</SyntaxHighlighter>
26-
</Suspense>
27-
</div>
28-
);
35+
</SyntaxHighlighter>
36+
</Suspense>
37+
</div>
38+
);
39+
};
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
'use client';
2+
3+
import {
4+
createContext,
5+
ReactNode,
6+
useContext,
7+
useLayoutEffect,
8+
useState,
9+
} from 'react';
10+
11+
export type Theme = 'light' | 'dark';
12+
13+
type ThemeProviderState = {
14+
theme: Theme;
15+
};
16+
17+
const ThemeContext = createContext<ThemeProviderState>({
18+
theme: 'light',
19+
});
20+
21+
export const ThemeProvider = ({ children }: { children?: ReactNode }) => {
22+
const [themeState, setThemeState] = useState<ThemeProviderState>({
23+
theme: 'light',
24+
});
25+
26+
useLayoutEffect(() => {
27+
const match = window.matchMedia('(prefers-color-scheme:dark)');
28+
29+
function onChange(event: { matches: boolean }) {
30+
setThemeState((themeState) => {
31+
const targetTheme = event.matches ? 'dark' : 'light';
32+
33+
if (themeState.theme === targetTheme) {
34+
return themeState;
35+
}
36+
37+
return {
38+
...themeState,
39+
theme: targetTheme,
40+
};
41+
});
42+
}
43+
44+
onChange(match);
45+
46+
match.addEventListener('change', onChange);
47+
return () => {
48+
match.removeEventListener('change', onChange);
49+
};
50+
}, []);
51+
52+
return <ThemeContext value={themeState}>{children}</ThemeContext>;
53+
};
54+
55+
export function useTheme(): Theme {
56+
return useContext(ThemeContext).theme;
57+
}

0 commit comments

Comments
 (0)