From 163f6cea08e83a5fd3bc4556682c862979d06523 Mon Sep 17 00:00:00 2001 From: QiShaoXuan Date: Mon, 26 Sep 2022 16:24:29 +0800 Subject: [PATCH 1/6] feat: add theme change handler --- src/components/simple-counter/index.ts | 5 ++-- src/pages/_app.tsx | 5 ++-- src/pages/index.tsx | 11 ++++++-- src/styles/hooks.ts | 4 +++ src/styles/index.ts | 7 ++++- src/styles/styled.ts | 3 ++ src/styles/theme.ts | 23 ++++++++------- src/styles/themeProvider.tsx | 39 ++++++++++++++++++++++++++ src/styles/types.ts | 21 ++++++++++++++ 9 files changed, 99 insertions(+), 19 deletions(-) create mode 100644 src/styles/hooks.ts create mode 100644 src/styles/styled.ts create mode 100644 src/styles/themeProvider.tsx create mode 100644 src/styles/types.ts diff --git a/src/components/simple-counter/index.ts b/src/components/simple-counter/index.ts index 83b6dbde95..6f99529c1b 100644 --- a/src/components/simple-counter/index.ts +++ b/src/components/simple-counter/index.ts @@ -1,7 +1,6 @@ -import { LitElement, css, html, unsafeCSS } from 'lit'; +import { LitElement, css, html } from 'lit'; import { customElement, property, state } from 'lit/decorators.js'; import * as React from 'react'; -import { theme } from '@/styles'; export const tagName = 'simple-counter'; @@ -28,7 +27,7 @@ export class Counter extends LitElement { static styles = css` .counter-container { display: flex; - color: ${unsafeCSS(theme.colors.primary)}; + color: var(--color-primary); } button { margin: 0 5px; diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx index 9908d21a12..e118fcf54f 100644 --- a/src/pages/_app.tsx +++ b/src/pages/_app.tsx @@ -1,12 +1,11 @@ import type { AppProps } from 'next/app'; -import { ThemeProvider } from '@emotion/react'; -import { theme } from '../styles'; +import { ThemeProvider } from '@/styles'; import '../../public/globals.css'; function MyApp({ Component, pageProps }: AppProps) { return ( - + ); diff --git a/src/pages/index.tsx b/src/pages/index.tsx index ef4fe01580..66d8048e7e 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -1,6 +1,5 @@ import type { NextPage } from 'next'; -import styled from '@emotion/styled'; - +import { styled, useTheme } from '@/styles'; import '@/components/simple-counter'; const Button = styled('div')(({ theme }) => { @@ -10,10 +9,18 @@ const Button = styled('div')(({ theme }) => { }); const Home: NextPage = () => { + const { changeMode, mode } = useTheme(); return (
+
); }; diff --git a/src/styles/hooks.ts b/src/styles/hooks.ts new file mode 100644 index 0000000000..94398c7f36 --- /dev/null +++ b/src/styles/hooks.ts @@ -0,0 +1,4 @@ +import { useContext } from 'react'; +import { ThemeContext } from './themeProvider'; + +export const useTheme = () => useContext(ThemeContext); diff --git a/src/styles/index.ts b/src/styles/index.ts index 7b1f54ecf9..f1c02991ff 100644 --- a/src/styles/index.ts +++ b/src/styles/index.ts @@ -1 +1,6 @@ -export * from './theme'; +export type { ThemeMode, ThemeProviderProps, AffineTheme } from './types'; + +export { styled } from './styled'; +export { ThemeProvider } from './themeProvider'; +export { lightTheme, darkTheme } from './theme'; +export { useTheme } from './hooks'; diff --git a/src/styles/styled.ts b/src/styles/styled.ts new file mode 100644 index 0000000000..11464cee29 --- /dev/null +++ b/src/styles/styled.ts @@ -0,0 +1,3 @@ +import emotionStyled from '@emotion/styled'; + +export const styled = emotionStyled; diff --git a/src/styles/theme.ts b/src/styles/theme.ts index 45f6a84ed4..7afc298e8c 100644 --- a/src/styles/theme.ts +++ b/src/styles/theme.ts @@ -1,17 +1,20 @@ import '@emotion/react'; +import { AffineTheme } from './types'; -interface AffineTheme { - colors: { - primary: string; - }; -} - -export const theme: AffineTheme = { +export const lightTheme: AffineTheme = { colors: { primary: '#0070f3', }, }; -declare module '@emotion/react' { - export interface Theme extends AffineTheme {} -} +export const darkTheme: AffineTheme = { + colors: { + primary: '#000', + }, +}; + +export const globalThemeConstant = (theme: AffineTheme) => { + return { + '--color-primary': theme.colors.primary, + }; +}; diff --git a/src/styles/themeProvider.tsx b/src/styles/themeProvider.tsx new file mode 100644 index 0000000000..383ae2dd74 --- /dev/null +++ b/src/styles/themeProvider.tsx @@ -0,0 +1,39 @@ +import { + ThemeProvider as EmotionThemeProvider, + Global, + css, +} from '@emotion/react'; +import { createContext, useState } from 'react'; +import type { PropsWithChildren } from 'react'; +import { ThemeMode, ThemeProviderProps, ThemeProviderValue } from './types'; +import { lightTheme, darkTheme, globalThemeConstant } from './theme'; + +export const ThemeContext = createContext({ + mode: 'light', + changeMode: () => {}, + theme: lightTheme, +}); + +export const ThemeProvider = ({ + defaultMode = 'light', + children, +}: PropsWithChildren) => { + const [mode, setMode] = useState(defaultMode); + const theme = mode === 'dark' ? darkTheme : lightTheme; + const changeMode = (themeMode: ThemeMode) => { + setMode(themeMode); + }; + + return ( + + + {children} + + ); +}; diff --git a/src/styles/types.ts b/src/styles/types.ts new file mode 100644 index 0000000000..e3a9ce0476 --- /dev/null +++ b/src/styles/types.ts @@ -0,0 +1,21 @@ +export type ThemeMode = 'light' | 'dark'; + +export type ThemeProviderProps = { + defaultMode?: ThemeMode; +}; + +export type ThemeProviderValue = { + theme: AffineTheme; + mode: ThemeMode; + changeMode: (newMode: ThemeMode) => void; +}; + +export interface AffineTheme { + colors: { + primary: string; + }; +} + +declare module '@emotion/react' { + export interface Theme extends AffineTheme {} +} From fb828c9cf4304b857798d3d0dd4aedf125342e53 Mon Sep 17 00:00:00 2001 From: QiShaoXuan Date: Tue, 27 Sep 2022 17:19:23 +0800 Subject: [PATCH 2/6] feat: add system theme judgment --- src/styles/themeProvider.tsx | 39 +++++++++++++++++++++++++++++++++--- src/styles/types.ts | 2 +- src/styles/utils.ts | 39 ++++++++++++++++++++++++++++++++++++ 3 files changed, 76 insertions(+), 4 deletions(-) create mode 100644 src/styles/utils.ts diff --git a/src/styles/themeProvider.tsx b/src/styles/themeProvider.tsx index 383ae2dd74..27f94ca682 100644 --- a/src/styles/themeProvider.tsx +++ b/src/styles/themeProvider.tsx @@ -3,10 +3,11 @@ import { Global, css, } from '@emotion/react'; -import { createContext, useState } from 'react'; +import { createContext, useEffect, useRef, useState } from 'react'; import type { PropsWithChildren } from 'react'; import { ThemeMode, ThemeProviderProps, ThemeProviderValue } from './types'; import { lightTheme, darkTheme, globalThemeConstant } from './theme'; +import { SystemTheme, LocalStorageThemeMode } from '@/styles/utils'; export const ThemeContext = createContext({ mode: 'light', @@ -15,15 +16,47 @@ export const ThemeContext = createContext({ }); export const ThemeProvider = ({ - defaultMode = 'light', + defaultThemeMode = 'light', children, }: PropsWithChildren) => { - const [mode, setMode] = useState(defaultMode); + const { current: localStorageThemeMode } = useRef( + new LocalStorageThemeMode() + ); + + const [mode, setMode] = useState(defaultThemeMode); const theme = mode === 'dark' ? darkTheme : lightTheme; + const changeMode = (themeMode: ThemeMode) => { setMode(themeMode); + // Remember the theme mode which user selected for next time + localStorageThemeMode.set(themeMode); }; + useEffect(() => { + const systemTheme = new SystemTheme(); + + // TODO: System theme mode and user theme mode which selected need to be prioritized + // If user has selected a theme mode, use it + const selectedThemeMode = localStorageThemeMode.get(); + if (selectedThemeMode) { + defaultThemeMode !== selectedThemeMode && setMode(selectedThemeMode); + } else { + // If user has not selected a theme mode, use system theme + const systemThemeMode = systemTheme.get(); + defaultThemeMode !== systemThemeMode && setMode(systemThemeMode); + } + + // When system theme changed, change the theme mode + systemTheme.onChange(() => { + // TODO: There may be should be provided a way to let user choose whether to + setMode(systemTheme.get()); + }); + + return () => { + systemTheme.dispose(); + }; + }, []); + return ( void> = []; + eventHandler = (e: Event) => { + this.eventList.forEach(fn => fn(e)); + }; + + constructor() { + this.media.addEventListener('change', this.eventHandler); + } + + get = (): ThemeMode => { + if (typeof window === 'undefined') { + return 'light'; + } + return this.media.matches ? 'light' : 'dark'; + }; + + onChange = (callback: () => void) => { + this.eventList.push(callback); + }; + + dispose = () => { + this.eventList = []; + this.media.removeEventListener('change', this.eventHandler); + }; +} + +export class LocalStorageThemeMode { + name = 'Affine-theme-mode'; + get = (): ThemeMode | null => { + return localStorage.getItem(this.name) as ThemeMode | null; + }; + set = (mode: ThemeMode) => { + localStorage.setItem(this.name, mode); + }; +} From 008c0d83e350ec77f231c3d8c6cf0e276ddf5323 Mon Sep 17 00:00:00 2001 From: QiShaoXuan Date: Wed, 28 Sep 2022 10:49:02 +0800 Subject: [PATCH 3/6] feat: add auto mode in theme selection --- src/pages/index.tsx | 7 ++++++ src/styles/themeProvider.tsx | 49 ++++++++++++++++++------------------ src/styles/types.ts | 5 ++-- src/styles/utils.ts | 6 +++-- 4 files changed, 39 insertions(+), 28 deletions(-) diff --git a/src/pages/index.tsx b/src/pages/index.tsx index 66d8048e7e..031ef59d08 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -21,6 +21,13 @@ const Home: NextPage = () => { > current theme mode :{mode}(click to change) + ); }; diff --git a/src/styles/themeProvider.tsx b/src/styles/themeProvider.tsx index 27f94ca682..1aa488e380 100644 --- a/src/styles/themeProvider.tsx +++ b/src/styles/themeProvider.tsx @@ -3,11 +3,16 @@ import { Global, css, } from '@emotion/react'; -import { createContext, useEffect, useRef, useState } from 'react'; +import { createContext, useEffect, useState } from 'react'; import type { PropsWithChildren } from 'react'; -import { ThemeMode, ThemeProviderProps, ThemeProviderValue } from './types'; +import { + Theme, + ThemeMode, + ThemeProviderProps, + ThemeProviderValue, +} from './types'; import { lightTheme, darkTheme, globalThemeConstant } from './theme'; -import { SystemTheme, LocalStorageThemeMode } from '@/styles/utils'; +import { SystemTheme, localStorageThemeMode } from '@/styles/utils'; export const ThemeContext = createContext({ mode: 'light', @@ -16,57 +21,53 @@ export const ThemeContext = createContext({ }); export const ThemeProvider = ({ - defaultThemeMode = 'light', + defaultTheme = 'light', children, }: PropsWithChildren) => { - const { current: localStorageThemeMode } = useRef( - new LocalStorageThemeMode() - ); - - const [mode, setMode] = useState(defaultThemeMode); - const theme = mode === 'dark' ? darkTheme : lightTheme; + const [theme, setTheme] = useState(defaultTheme); + const [mode, setMode] = useState('auto'); + const themeStyle = theme === 'light' ? lightTheme : darkTheme; const changeMode = (themeMode: ThemeMode) => { - setMode(themeMode); + themeMode !== mode && setMode(themeMode); // Remember the theme mode which user selected for next time localStorageThemeMode.set(themeMode); }; useEffect(() => { const systemTheme = new SystemTheme(); - - // TODO: System theme mode and user theme mode which selected need to be prioritized - // If user has selected a theme mode, use it const selectedThemeMode = localStorageThemeMode.get(); - if (selectedThemeMode) { - defaultThemeMode !== selectedThemeMode && setMode(selectedThemeMode); + + const themeMode = selectedThemeMode || mode; + if (themeMode === 'auto') { + setTheme(systemTheme.get()); } else { - // If user has not selected a theme mode, use system theme - const systemThemeMode = systemTheme.get(); - defaultThemeMode !== systemThemeMode && setMode(systemThemeMode); + setTheme(themeMode); } // When system theme changed, change the theme mode systemTheme.onChange(() => { // TODO: There may be should be provided a way to let user choose whether to - setMode(systemTheme.get()); + if (mode === 'auto') { + setTheme(systemTheme.get()); + } }); return () => { systemTheme.dispose(); }; - }, []); + }, [mode]); return ( - + - {children} + {children} ); }; diff --git a/src/styles/types.ts b/src/styles/types.ts index ccfb39290a..95c5bba951 100644 --- a/src/styles/types.ts +++ b/src/styles/types.ts @@ -1,7 +1,8 @@ -export type ThemeMode = 'light' | 'dark'; +export type Theme = 'light' | 'dark'; +export type ThemeMode = Theme | 'auto'; export type ThemeProviderProps = { - defaultThemeMode?: ThemeMode; + defaultTheme?: Theme; }; export type ThemeProviderValue = { diff --git a/src/styles/utils.ts b/src/styles/utils.ts index bdc0ef8f18..dd04d4fb0b 100644 --- a/src/styles/utils.ts +++ b/src/styles/utils.ts @@ -1,4 +1,4 @@ -import { ThemeMode } from '@/styles/types'; +import { Theme, ThemeMode } from '@/styles/types'; export class SystemTheme { media: MediaQueryList = window.matchMedia('(prefers-color-scheme: light)'); @@ -11,7 +11,7 @@ export class SystemTheme { this.media.addEventListener('change', this.eventHandler); } - get = (): ThemeMode => { + get = (): Theme => { if (typeof window === 'undefined') { return 'light'; } @@ -37,3 +37,5 @@ export class LocalStorageThemeMode { localStorage.setItem(this.name, mode); }; } + +export const localStorageThemeMode = new LocalStorageThemeMode(); From dec69d4f44d9721c93ad0e3807dd8e5577331de0 Mon Sep 17 00:00:00 2001 From: QiShaoXuan Date: Wed, 28 Sep 2022 17:16:18 +0800 Subject: [PATCH 4/6] chore: manage files --- src/pages/index.tsx | 14 ++++++++++--- src/styles/themeProvider.tsx | 20 +++++++++++-------- src/styles/utils/index.ts | 2 ++ src/styles/utils/localStorageThemeHelper.ts | 13 ++++++++++++ .../{utils.ts => utils/systemThemeHelper.ts} | 16 ++------------- 5 files changed, 40 insertions(+), 25 deletions(-) create mode 100644 src/styles/utils/index.ts create mode 100644 src/styles/utils/localStorageThemeHelper.ts rename src/styles/{utils.ts => utils/systemThemeHelper.ts} (61%) diff --git a/src/pages/index.tsx b/src/pages/index.tsx index 031ef59d08..4bda97e6c9 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -14,19 +14,27 @@ const Home: NextPage = () => {
+

current mode {mode}

+
); diff --git a/src/styles/themeProvider.tsx b/src/styles/themeProvider.tsx index 1aa488e380..95187d3415 100644 --- a/src/styles/themeProvider.tsx +++ b/src/styles/themeProvider.tsx @@ -12,7 +12,7 @@ import { ThemeProviderValue, } from './types'; import { lightTheme, darkTheme, globalThemeConstant } from './theme'; -import { SystemTheme, localStorageThemeMode } from '@/styles/utils'; +import { SystemThemeHelper, localStorageThemeHelper } from './utils'; export const ThemeContext = createContext({ mode: 'light', @@ -31,30 +31,34 @@ export const ThemeProvider = ({ const changeMode = (themeMode: ThemeMode) => { themeMode !== mode && setMode(themeMode); // Remember the theme mode which user selected for next time - localStorageThemeMode.set(themeMode); + localStorageThemeHelper.set(themeMode); }; useEffect(() => { - const systemTheme = new SystemTheme(); - const selectedThemeMode = localStorageThemeMode.get(); + setMode(localStorageThemeHelper.get() || 'auto'); + }, []); + + useEffect(() => { + const systemThemeHelper = new SystemThemeHelper(); + const selectedThemeMode = localStorageThemeHelper.get(); const themeMode = selectedThemeMode || mode; if (themeMode === 'auto') { - setTheme(systemTheme.get()); + setTheme(systemThemeHelper.get()); } else { setTheme(themeMode); } // When system theme changed, change the theme mode - systemTheme.onChange(() => { + systemThemeHelper.onChange(() => { // TODO: There may be should be provided a way to let user choose whether to if (mode === 'auto') { - setTheme(systemTheme.get()); + setTheme(systemThemeHelper.get()); } }); return () => { - systemTheme.dispose(); + systemThemeHelper.dispose(); }; }, [mode]); diff --git a/src/styles/utils/index.ts b/src/styles/utils/index.ts new file mode 100644 index 0000000000..cf6f6acd99 --- /dev/null +++ b/src/styles/utils/index.ts @@ -0,0 +1,2 @@ +export * from './systemThemeHelper'; +export * from './localStorageThemeHelper'; diff --git a/src/styles/utils/localStorageThemeHelper.ts b/src/styles/utils/localStorageThemeHelper.ts new file mode 100644 index 0000000000..4aee8eca0f --- /dev/null +++ b/src/styles/utils/localStorageThemeHelper.ts @@ -0,0 +1,13 @@ +import { ThemeMode } from '../types'; + +export class LocalStorageThemeHelper { + name = 'Affine-theme-mode'; + get = (): ThemeMode | null => { + return localStorage.getItem(this.name) as ThemeMode | null; + }; + set = (mode: ThemeMode) => { + localStorage.setItem(this.name, mode); + }; +} + +export const localStorageThemeHelper = new LocalStorageThemeHelper(); diff --git a/src/styles/utils.ts b/src/styles/utils/systemThemeHelper.ts similarity index 61% rename from src/styles/utils.ts rename to src/styles/utils/systemThemeHelper.ts index dd04d4fb0b..1c26cf8d4f 100644 --- a/src/styles/utils.ts +++ b/src/styles/utils/systemThemeHelper.ts @@ -1,6 +1,6 @@ -import { Theme, ThemeMode } from '@/styles/types'; +import { Theme } from '../types'; -export class SystemTheme { +export class SystemThemeHelper { media: MediaQueryList = window.matchMedia('(prefers-color-scheme: light)'); eventList: Array<(e: Event) => void> = []; eventHandler = (e: Event) => { @@ -27,15 +27,3 @@ export class SystemTheme { this.media.removeEventListener('change', this.eventHandler); }; } - -export class LocalStorageThemeMode { - name = 'Affine-theme-mode'; - get = (): ThemeMode | null => { - return localStorage.getItem(this.name) as ThemeMode | null; - }; - set = (mode: ThemeMode) => { - localStorage.setItem(this.name, mode); - }; -} - -export const localStorageThemeMode = new LocalStorageThemeMode(); From 0662b5f7a701e48b84e8e741a4a7661161d8b262 Mon Sep 17 00:00:00 2001 From: QiShaoXuan Date: Wed, 28 Sep 2022 17:17:00 +0800 Subject: [PATCH 5/6] feat: add emotion ssr in nextjs --- package.json | 1 + pnpm-lock.yaml | 121 +++++++++++++++++++++++++++++++++++++++- src/pages/_document.tsx | 53 ++++++++++++++++++ 3 files changed, 172 insertions(+), 3 deletions(-) create mode 100644 src/pages/_document.tsx diff --git a/package.json b/package.json index 35caa0df78..3156f0307f 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "dependencies": { "@emotion/css": "^11.10.0", "@emotion/react": "^11.10.4", + "@emotion/server": "^11.10.0", "@emotion/styled": "^11.10.4", "lit": "^2.3.1", "next": "12.3.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8d40c60e44..7022848f70 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -3,6 +3,7 @@ lockfileVersion: 5.4 specifiers: '@emotion/css': ^11.10.0 '@emotion/react': ^11.10.4 + '@emotion/server': ^11.10.0 '@emotion/styled': ^11.10.4 '@types/node': 18.7.18 '@types/react': 18.0.20 @@ -21,6 +22,7 @@ specifiers: dependencies: '@emotion/css': 11.10.0 '@emotion/react': 11.10.4_w5j4k42lgipnm43s3brx6h3c34 + '@emotion/server': 11.10.0_@emotion+css@11.10.0 '@emotion/styled': 11.10.4_yiaqs725o7pcd7rteavrnhgj4y lit: 2.3.1 next: 12.3.1_biqbaboplfbrettd7655fr4n2y @@ -202,6 +204,21 @@ packages: csstype: 3.1.1 dev: false + /@emotion/server/11.10.0_@emotion+css@11.10.0: + resolution: {integrity: sha512-MTvJ21JPo9aS02GdjFW4nhdwOi2tNNpMmAM/YED0pkxzjDNi5WbiTwXqaCnvLc2Lr8NFtjhT0az1vTJyLIHYcw==} + peerDependencies: + '@emotion/css': ^11.0.0-rc.0 + peerDependenciesMeta: + '@emotion/css': + optional: true + dependencies: + '@emotion/css': 11.10.0 + '@emotion/utils': 1.2.0 + html-tokenize: 2.0.1 + multipipe: 1.0.2 + through: 2.3.8 + dev: false + /@emotion/sheet/1.2.0: resolution: {integrity: sha512-OiTkRgpxescko+M51tZsMq7Puu/KP55wMT8BgpcXVG2hqXc0Vo0mfymJ/Uj24Hp0i083ji/o0aLddh08UEjq8w==} dev: false @@ -675,6 +692,10 @@ packages: fill-range: 7.0.1 dev: true + /buffer-from/0.1.2: + resolution: {integrity: sha512-RiWIenusJsmI2KcvqQABB83tLxCByE3upSP8QU3rJDMVFGPWLvPQJt/O1Su9moRWeH7d+Q2HYb68f6+v+tw2vg==} + dev: false + /call-bind/1.0.2: resolution: {integrity: sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==} dependencies: @@ -743,6 +764,10 @@ packages: requiresBuild: true dev: true + /core-util-is/1.0.3: + resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} + dev: false + /cosmiconfig/7.0.1: resolution: {integrity: sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ==} engines: {node: '>=10'} @@ -837,6 +862,12 @@ packages: esutils: 2.0.3 dev: true + /duplexer2/0.1.4: + resolution: {integrity: sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==} + dependencies: + readable-stream: 2.3.7 + dev: false + /emoji-regex/9.2.2: resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} dev: true @@ -1417,6 +1448,17 @@ packages: react-is: 16.13.1 dev: false + /html-tokenize/2.0.1: + resolution: {integrity: sha512-QY6S+hZ0f5m1WT8WffYN+Hg+xm/w5I8XeUcAq/ZYP5wVC8xbKi4Whhru3FtrAebD5EhBW8rmFzkDI6eCAuFe2w==} + hasBin: true + dependencies: + buffer-from: 0.1.2 + inherits: 2.0.4 + minimist: 1.2.6 + readable-stream: 1.0.34 + through2: 0.4.2 + dev: false + /ignore/5.2.0: resolution: {integrity: sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==} engines: {node: '>= 4'} @@ -1443,7 +1485,6 @@ packages: /inherits/2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} - dev: true /internal-slot/1.0.3: resolution: {integrity: sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==} @@ -1552,6 +1593,14 @@ packages: call-bind: 1.0.2 dev: true + /isarray/0.0.1: + resolution: {integrity: sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==} + dev: false + + /isarray/1.0.0: + resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} + dev: false + /isexe/2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} dev: true @@ -1681,7 +1730,6 @@ packages: /minimist/1.2.6: resolution: {integrity: sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==} - dev: true /ms/2.0.0: resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} @@ -1695,6 +1743,13 @@ packages: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} dev: true + /multipipe/1.0.2: + resolution: {integrity: sha512-6uiC9OvY71vzSGX8lZvSqscE7ft9nPupJ8fMjrCNRAUy2LREUW42UL+V/NTrogr6rFgRydUrCX4ZitfpSNkSCQ==} + dependencies: + duplexer2: 0.1.4 + object-assign: 4.1.1 + dev: false + /nanoid/3.3.4: resolution: {integrity: sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} @@ -1753,12 +1808,15 @@ packages: /object-assign/4.1.1: resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} engines: {node: '>=0.10.0'} - dev: true /object-inspect/1.12.2: resolution: {integrity: sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==} dev: true + /object-keys/0.4.0: + resolution: {integrity: sha512-ncrLw+X55z7bkl5PnUvHwFK9FcGuFYo9gtjws2XtSzL+aZ8tm830P60WJ0dSmFVaSalWieW5MD7kEdnXda9yJw==} + dev: false + /object-keys/1.1.1: resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} engines: {node: '>= 0.4'} @@ -1913,6 +1971,10 @@ packages: engines: {node: '>=10.13.0'} hasBin: true + /process-nextick-args/2.0.1: + resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} + dev: false + /prop-types/15.8.1: resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} dependencies: @@ -1950,6 +2012,27 @@ packages: loose-envify: 1.4.0 dev: false + /readable-stream/1.0.34: + resolution: {integrity: sha512-ok1qVCJuRkNmvebYikljxJA/UEsKwLl2nI1OmaqAu4/UE+h0wKCHok4XkL/gvi39OacXvw59RJUOFUkDib2rHg==} + dependencies: + core-util-is: 1.0.3 + inherits: 2.0.4 + isarray: 0.0.1 + string_decoder: 0.10.31 + dev: false + + /readable-stream/2.3.7: + resolution: {integrity: sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==} + dependencies: + core-util-is: 1.0.3 + inherits: 2.0.4 + isarray: 1.0.0 + process-nextick-args: 2.0.1 + safe-buffer: 5.1.2 + string_decoder: 1.1.1 + util-deprecate: 1.0.2 + dev: false + /regenerator-runtime/0.13.9: resolution: {integrity: sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==} @@ -2093,6 +2176,16 @@ packages: es-abstract: 1.20.2 dev: true + /string_decoder/0.10.31: + resolution: {integrity: sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==} + dev: false + + /string_decoder/1.1.1: + resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} + dependencies: + safe-buffer: 5.1.2 + dev: false + /strip-ansi/6.0.1: resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} engines: {node: '>=8'} @@ -2152,6 +2245,17 @@ packages: resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} dev: true + /through/2.3.8: + resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} + dev: false + + /through2/0.4.2: + resolution: {integrity: sha512-45Llu+EwHKtAZYTPPVn3XZHBgakWMN3rokhEv5hu596XP+cNgplMg+Gj+1nmAvj+L0K7+N49zBKx5rah5u0QIQ==} + dependencies: + readable-stream: 1.0.34 + xtend: 2.1.2 + dev: false + /to-fast-properties/2.0.0: resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==} engines: {node: '>=4'} @@ -2232,6 +2336,10 @@ packages: react: 18.2.0 dev: false + /util-deprecate/1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + dev: false + /v8-compile-cache/2.3.0: resolution: {integrity: sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==} dev: true @@ -2263,6 +2371,13 @@ packages: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} dev: true + /xtend/2.1.2: + resolution: {integrity: sha512-vMNKzr2rHP9Dp/e1NQFnLQlwlhp9L/LfvnsVdHxN1f+uggyVI3i08uD14GPvCToPkdsRfyPqIyYGmIk58V98ZQ==} + engines: {node: '>=0.4'} + dependencies: + object-keys: 0.4.0 + dev: false + /yallist/4.0.0: resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} dev: true diff --git a/src/pages/_document.tsx b/src/pages/_document.tsx new file mode 100644 index 0000000000..7bc23a3892 --- /dev/null +++ b/src/pages/_document.tsx @@ -0,0 +1,53 @@ +import createEmotionServer from '@emotion/server/create-instance'; +import { cache } from '@emotion/css'; + +import Document, { + Html, + Head, + Main, + NextScript, + DocumentContext, +} from 'next/document'; +import * as React from 'react'; + +export const renderStatic = async (html: string) => { + if (html === undefined) { + throw new Error('did you forget to return html from renderToString?'); + } + const { extractCritical } = createEmotionServer(cache); + const { ids, css } = extractCritical(html); + + return { html, ids, css }; +}; + +export default class AppDocument extends Document { + static async getInitialProps(ctx: DocumentContext) { + const page = await ctx.renderPage(); + const { css, ids } = await renderStatic(page.html); + const initialProps = await Document.getInitialProps(ctx); + return { + ...initialProps, + styles: ( + + {initialProps.styles} +