index.tsx

 1import 'typeface-roboto';
 2
 3import { createMuiTheme, CssBaseline, IconButton, responsiveFontSizes, ThemeProvider } from '@material-ui/core';
 4import { Brightness4, Brightness7 } from '@material-ui/icons';
 5import { useLocalStorage } from '@rehooks/local-storage';
 6import * as React from 'react';
 7import * as ReactDOM from 'react-dom';
 8
 9import { App } from './app';
10import { AboutButton } from './components/about';
11
12function useTheme() {
13    const [themeName, setThemeName] = useLocalStorage<'light' | 'dark'>('themeName', 'dark');
14
15    // Workaround for https://github.com/mui-org/material-ui/issues/20708.
16    //
17    // When in strict mode (development only), this is required to properly allow
18    // the theme to be changed, as unused styles are not cleaned up. Create a new theme
19    // each time, so that they're forced to be injected again at the end of the existing
20    // block of stylesheets (as the styling library will see them as "new" styles, rather than
21    // assuming they can just be reused).
22    //
23    // This is gross, as it means every time the button is clicked it's a slew of extra
24    // stylesheets (each overriding the previous), but in production the cleanup works
25    // so this extra work is "only" a performance hit. If the bug is ever fixed, we can
26    // simply store two global themes and swap between them.
27    const theme = responsiveFontSizes(
28        createMuiTheme({
29            palette: {
30                type: themeName,
31            },
32        })
33    );
34
35    const toggleTheme = () => {
36        if (themeName === 'light') {
37            setThemeName('dark');
38        } else {
39            setThemeName('light');
40        }
41    };
42
43    return { theme, toggleTheme, isDark: themeName === 'dark' };
44}
45
46const Root = (_props: {}) => {
47    const { theme, toggleTheme, isDark } = useTheme();
48
49    return (
50        <React.StrictMode>
51            <ThemeProvider theme={theme}>
52                <CssBaseline />
53                <div
54                    style={{
55                        position: 'absolute',
56                        top: 0,
57                        right: 0,
58                        margin: '0.5rem',
59                    }}
60                >
61                    <AboutButton style={{ marginRight: '0.5rem' }} />
62                    <IconButton size="small" onClick={toggleTheme}>
63                        {isDark ? <Brightness7 /> : <Brightness4 />}
64                    </IconButton>
65                </div>
66                <App />
67            </ThemeProvider>
68        </React.StrictMode>
69    );
70};
71
72ReactDOM.render(<Root />, document.getElementById('root'));