Skip to content

Commit 4bed60a

Browse files
authored
[examples] Add dark mode example for Material UI + Pigment CSS (#44480)
1 parent 6aaa61f commit 4bed60a

File tree

7 files changed

+110
-14
lines changed

7 files changed

+110
-14
lines changed

examples/material-ui-pigment-css-nextjs-ts/next-env.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@
22
/// <reference types="next/image-types/global" />
33

44
// NOTE: This file should not be edited
5-
// see https://nextjs.org/docs/pages/building-your-application/configuring/typescript for more information.
5+
// see https://nextjs.org/docs/app/building-your-application/configuring/typescript for more information.

examples/material-ui-pigment-css-nextjs-ts/next.config.mjs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ const nextConfig = {};
66

77
export default withPigment(nextConfig, {
88
theme: createTheme({
9-
cssVariables: true,
9+
cssVariables: {
10+
colorSchemeSelector: 'class',
11+
},
1012
colorSchemes: { light: true, dark: true },
1113
typography: {
1214
fontFamily: 'var(--font-roboto)',
Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,26 @@
11
import * as React from 'react';
22
import type { Metadata } from 'next';
3-
import { Roboto } from 'next/font/google';
3+
import { cookies } from 'next/headers';
44
import '@mui/material-pigment-css/styles.css';
5-
6-
const roboto = Roboto({
7-
subsets: ['latin'],
8-
weight: ['400', '500', '700'],
9-
display: 'swap',
10-
variable: '--font-roboto',
11-
});
5+
import { ColorSchemeProvider } from '../components/ColorSchemeProvider';
6+
import App from '../components/App';
127

138
export const metadata: Metadata = {
149
title: 'Material UI x Pigment CSS',
1510
description: 'Generated by create next app',
1611
};
17-
18-
export default function RootLayout({
12+
export default async function RootLayout({
1913
children,
2014
}: Readonly<{
2115
children: React.ReactNode;
2216
}>) {
17+
const cookieStore = await cookies();
18+
const { value: colorScheme = 'light' } = cookieStore.get('colorScheme') ?? {};
2319
return (
2420
<html lang="en">
25-
<body className={roboto.variable}>{children}</body>
21+
<ColorSchemeProvider colorScheme={colorScheme}>
22+
<App>{children}</App>
23+
</ColorSchemeProvider>
2624
</html>
2725
);
2826
}

examples/material-ui-pigment-css-nextjs-ts/src/app/page.tsx

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
1+
'use client';
12
import * as React from 'react';
23
import DefaultPropsProvider from '@mui/material/DefaultPropsProvider';
34
import CssBaseline from '@mui/material/CssBaseline';
5+
import IconButton from '@mui/material/IconButton';
46
import Container from '@mui/material-pigment-css/Container';
57
import Grid from '@mui/material-pigment-css/Grid';
68
import Stack from '@mui/material-pigment-css/Stack';
79
import Typography from '@mui/material/Typography';
810
import Chip from '@mui/material/Chip';
911
import { styled } from '@mui/material-pigment-css';
12+
import { useColorScheme } from '../components/ColorSchemeProvider';
1013

1114
const Title = styled('div')(({ theme }) => ({
1215
color: theme.vars.palette.text.primary,
@@ -16,6 +19,12 @@ const Title = styled('div')(({ theme }) => ({
1619
}));
1720

1821
export default function Home() {
22+
const { colorScheme, setColorScheme } = useColorScheme();
23+
24+
const toggleColorScheme = () => {
25+
setColorScheme(colorScheme === 'dark' ? 'light' : 'dark');
26+
};
27+
1928
return (
2029
<main sx={{ minHeight: '100lvh', display: 'grid', placeItems: 'center' }}>
2130
<DefaultPropsProvider
@@ -27,6 +36,11 @@ export default function Home() {
2736
>
2837
<CssBaseline />
2938
<Container>
39+
<div sx={{ position: 'absolute', top: 10, right: 10 }}>
40+
<IconButton sx={{ fontSize: 20, px: 1.5 }} onClick={toggleColorScheme}>
41+
{colorScheme === 'light' ? '🌙' : '🔆'}
42+
</IconButton>
43+
</div>
3044
<Grid container spacing={{ xs: 2, sm: 3, md: 4 }}>
3145
<Grid size={{ xs: 12, md: 6 }} sx={{ pl: 4.5 }}>
3246
<Chip
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
'use client';
2+
import * as React from 'react';
3+
import { Roboto } from 'next/font/google';
4+
import { useColorScheme } from './ColorSchemeProvider';
5+
6+
const roboto = Roboto({
7+
subsets: ['latin'],
8+
weight: ['400', '500', '700'],
9+
display: 'swap',
10+
variable: '--font-roboto',
11+
});
12+
13+
function App({ className, ...other }: React.PropsWithChildren<{ className?: string }>) {
14+
const { colorScheme } = useColorScheme();
15+
return <body {...other} className={`${roboto.variable} ${colorScheme}`} />;
16+
}
17+
18+
export default App;
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
'use client';
2+
import * as React from 'react';
3+
4+
const ColorSchemeContext = React.createContext<{
5+
colorScheme: string;
6+
setColorScheme: React.Dispatch<React.SetStateAction<string>>;
7+
}>({
8+
colorScheme: 'dark',
9+
setColorScheme: () => '',
10+
});
11+
12+
function setCookie(name: string, value: string, days: number = 100) {
13+
let expires = '';
14+
if (days) {
15+
const date = new Date();
16+
date.setTime(date.getTime() + days * 24 * 60 * 60 * 1000);
17+
expires = `; expires=${date.toUTCString()}`;
18+
}
19+
document.cookie = `${name}=${value || ''}${expires}; path=/`;
20+
}
21+
22+
export function ColorSchemeProvider({
23+
colorScheme: initialColorScheme,
24+
children,
25+
}: React.PropsWithChildren<{ colorScheme: string }>) {
26+
const [colorScheme, setColorScheme] = React.useState<string>(initialColorScheme);
27+
28+
const contextValue = React.useMemo(
29+
() => ({ colorScheme, setColorScheme }),
30+
[colorScheme, setColorScheme],
31+
);
32+
33+
// Set the colorScheme in localStorage
34+
React.useEffect(() => {
35+
setCookie('colorScheme', colorScheme);
36+
localStorage.setItem('colorScheme', colorScheme);
37+
}, [colorScheme]);
38+
39+
// Handle when localStorage has changed
40+
React.useEffect(() => {
41+
const handleStorage = (event: StorageEvent) => {
42+
const value = event.newValue;
43+
if (
44+
typeof event.key === 'string' &&
45+
event.key === 'colorScheme' &&
46+
typeof value === 'string'
47+
) {
48+
setColorScheme(value);
49+
}
50+
};
51+
// For syncing color-scheme changes between iframes
52+
window.addEventListener('storage', handleStorage);
53+
return () => {
54+
window.removeEventListener('storage', handleStorage);
55+
};
56+
}, [setColorScheme]);
57+
58+
return <ColorSchemeContext.Provider value={contextValue}>{children}</ColorSchemeContext.Provider>;
59+
}
60+
61+
export const useColorScheme = () => {
62+
return React.useContext(ColorSchemeContext);
63+
};

examples/material-ui-pigment-css-nextjs-ts/tsconfig.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@
1919
],
2020
"paths": {
2121
"@/*": ["./src/*"]
22-
}
22+
},
23+
"target": "ES2017"
2324
},
2425
"include": [
2526
"next-env.d.ts",

0 commit comments

Comments
 (0)