From 9c04f9559f1e00c452237177176c9e4e5c76e82a Mon Sep 17 00:00:00 2001 From: Johannes Kirschbauer Date: Fri, 17 May 2024 13:52:46 +0200 Subject: [PATCH] fix: dark mode flickering by using native css vars --- website/src/app/layout.tsx | 5 +- website/src/app/page.tsx | 2 +- .../components/ClientSideLayoutContext.tsx | 74 +++++++++++++++---- website/src/components/HighlightBaseline.tsx | 15 ++-- .../functionOfTheDay/functionOfTheDay.tsx | 35 ++++----- website/src/components/layout/Background.tsx | 6 +- website/src/components/layout/header.tsx | 7 +- .../markdownPreview/MarkdownPreview.tsx | 15 ++-- website/src/components/themeRegistry.tsx | 72 ------------------ website/src/styles/globals.css | 5 -- website/src/styles/theme/common.ts | 4 +- website/src/styles/theme/darkThemeOptions.ts | 22 ------ website/src/styles/theme/index.tsx | 3 +- website/src/styles/theme/lightThemeOptions.ts | 20 ----- website/src/styles/theme/themeOptions.ts | 50 +++++++++++++ 15 files changed, 150 insertions(+), 185 deletions(-) delete mode 100644 website/src/components/themeRegistry.tsx delete mode 100644 website/src/styles/theme/darkThemeOptions.ts delete mode 100644 website/src/styles/theme/lightThemeOptions.ts create mode 100644 website/src/styles/theme/themeOptions.ts diff --git a/website/src/app/layout.tsx b/website/src/app/layout.tsx index aa322c4..27b13e3 100644 --- a/website/src/app/layout.tsx +++ b/website/src/app/layout.tsx @@ -1,4 +1,3 @@ -import { CssBaseline } from "@mui/material"; import "../styles/globals.css"; import { AppRouterCacheProvider } from "@mui/material-nextjs/v13-appRouter"; import localFont from "next/font/local"; @@ -26,7 +25,7 @@ export default function RootLayout({ children: React.ReactNode; }) { return ( - + {/* */} - + {/* */} {children} diff --git a/website/src/app/page.tsx b/website/src/app/page.tsx index 9760cf8..83f362d 100644 --- a/website/src/app/page.tsx +++ b/website/src/app/page.tsx @@ -4,7 +4,7 @@ import { LandingPageLayout } from "@/components/layout"; import { FilterProvider } from "@/components/layout/filterContext"; import { SearchInput } from "@/components/searchInput"; import { Box, Typography, Link } from "@mui/material"; - +import type {} from "@mui/material/themeCssVarsAugmentation"; import localFont from "next/font/local"; import { Suspense } from "react"; diff --git a/website/src/components/ClientSideLayoutContext.tsx b/website/src/components/ClientSideLayoutContext.tsx index 2f2d850..c2e734e 100644 --- a/website/src/components/ClientSideLayoutContext.tsx +++ b/website/src/components/ClientSideLayoutContext.tsx @@ -1,28 +1,70 @@ "use client"; -import { - CssBaseline, - ThemeProvider, - createTheme, - useMediaQuery, -} from "@mui/material"; -import { darkThemeOptions, lightThemeOptions } from "@/styles/theme"; -import { ReactNode } from "react"; +import { Button, CssBaseline, useMediaQuery } from "@mui/material"; +import { cssThemeOptions } from "@/styles/theme"; +import { ReactNode, useEffect, useState } from "react"; import { Toaster } from "react-hot-toast"; +import { + Experimental_CssVarsProvider as CssVarsProvider, + experimental_extendTheme as extendTheme, + getInitColorSchemeScript, + useColorScheme, +} from "@mui/material/styles"; -const darkTheme = createTheme(darkThemeOptions); -const lightTheme = createTheme(lightThemeOptions); +const theme = extendTheme(cssThemeOptions); + +const Mode = () => { + const userPrefersDarkmode = useMediaQuery("(prefers-color-scheme: dark)"); + const { mode, setMode } = useColorScheme(); + + const [mounted, setMounted] = useState(false); + + useEffect( + () => { + setMounted(true); + setMode(userPrefersDarkmode ? "dark" : "light"); + }, + // eslint-disable-next-line react-hooks/exhaustive-deps + [userPrefersDarkmode] + ); + + if (!mounted) { + // for server-side rendering + // learn more at https://github.com/pacocoursey/next-themes#avoid-hydration-mismatch + return null; + } + + return ( + + ); +}; export const ClientSideLayoutContext = ({ children, }: { children: ReactNode; }) => { - const userPrefersDarkmode = useMediaQuery("(prefers-color-scheme: dark)"); return ( - - - - {children} - + // + <> + {getInitColorSchemeScript()} + + + + + {children} + + + // ); }; diff --git a/website/src/components/HighlightBaseline.tsx b/website/src/components/HighlightBaseline.tsx index c79eeb9..8f2121d 100644 --- a/website/src/components/HighlightBaseline.tsx +++ b/website/src/components/HighlightBaseline.tsx @@ -1,17 +1,18 @@ "use client"; -import { useTheme } from "@mui/material"; +import { useColorScheme } from "@mui/material"; import { useEffect } from "react"; export const HighlightBaseline = () => { - const theme = useTheme(); + const { mode } = useColorScheme(); useEffect(() => { - if (theme.palette.mode === "dark") { - // @ts-ignore - don't check type of css module - import("highlight.js/styles/github-dark.css"); + console.log({ mode }); + // @ts-ignore - don't check type of css module + import("highlight.js/styles/github-dark.css"); + if (mode === "dark") { } else { // @ts-ignore - don't check type of css module - import("highlight.js/styles/github.css"); + // import("highlight.js/styles/github.css"); } - }, [theme]); + }, [mode]); return <>; }; diff --git a/website/src/components/functionOfTheDay/functionOfTheDay.tsx b/website/src/components/functionOfTheDay/functionOfTheDay.tsx index a542cf1..6691a30 100644 --- a/website/src/components/functionOfTheDay/functionOfTheDay.tsx +++ b/website/src/components/functionOfTheDay/functionOfTheDay.tsx @@ -6,7 +6,7 @@ import { CardContent, CardHeader, Divider, - useTheme, + styled, } from "@mui/material"; import { useMemo, useState } from "react"; import seedrandom from "seedrandom"; @@ -37,10 +37,20 @@ function getRandomIntInclusive(min: number, max: number, config?: Config) { return Math.floor(randomNumber * (max - min + 1) + min); // The maximum is inclusive and the minimum is inclusive } +const FunctionCard = styled( + Card, + {} +)(({ theme }) => ({ + width: "100%", + borderImageSlice: 1, + borderImageSource: `linear-gradient(to left, ${theme.vars.palette.info.light},${theme.vars.palette.error.main})`, + // : `linear-gradient(to left, ${info.light},${info.dark})`, + borderWidth: 4, + borderStyle: "solid", +})); + export const FunctionOfTheDay = () => { - const { - palette: { info, error }, - } = useTheme(); + // const { mode } = useColorScheme(); const todaysIdx = useMemo( () => getRandomIntInclusive(0, data.length - 1), @@ -74,20 +84,7 @@ export const FunctionOfTheDay = () => { }; return ( - + { Next - + ); }; diff --git a/website/src/components/layout/Background.tsx b/website/src/components/layout/Background.tsx index b5ad893..81f812b 100644 --- a/website/src/components/layout/Background.tsx +++ b/website/src/components/layout/Background.tsx @@ -1,9 +1,8 @@ "use client"; -import { Box, useTheme } from "@mui/material"; +import { Box } from "@mui/material"; import { ReactNode } from "react"; export const Background = ({ children }: { children: ReactNode }) => { - const theme = useTheme(); return ( { flexDirection: "column", overflowX: "hidden", overflowY: "scroll", - bgcolor: - theme.palette.mode === "light" ? "rgb(242, 248, 253)" : "#070c16", + bgcolor: "background.paper", }} > {children} diff --git a/website/src/components/layout/header.tsx b/website/src/components/layout/header.tsx index 660751d..ac61e7b 100644 --- a/website/src/components/layout/header.tsx +++ b/website/src/components/layout/header.tsx @@ -1,5 +1,5 @@ "use client"; -import { Box, LinearProgress, Link, useTheme } from "@mui/material"; +import { Box, LinearProgress, Link } from "@mui/material"; import { SearchInput } from "../searchInput"; import { Filter } from "../filter"; import { Suspense } from "react"; @@ -13,8 +13,6 @@ const fira = localFont({ }); export const Header = () => { - const theme = useTheme(); - return ( <> { width: "100%", py: 1.2, zIndex: 1000, - backgroundColor: - theme.palette.mode === "light" ? "primary.main" : "#101010", + backgroundColor: "header.default", display: "grid", gridTemplateColumns: { xs: "1fr", diff --git a/website/src/components/markdownPreview/MarkdownPreview.tsx b/website/src/components/markdownPreview/MarkdownPreview.tsx index 305d6fd..e547710 100644 --- a/website/src/components/markdownPreview/MarkdownPreview.tsx +++ b/website/src/components/markdownPreview/MarkdownPreview.tsx @@ -1,5 +1,5 @@ "use client"; -import { useTheme } from "@mui/material"; +import { useColorScheme } from "@mui/material"; import nix from "highlight.js/lib/languages/nix"; import haskell from "highlight.js/lib/languages/haskell"; import bash from "highlight.js/lib/languages/bash"; @@ -23,16 +23,17 @@ interface MarkdownPreviewProps { } export const MarkdownPreview = (props: MarkdownPreviewProps) => { const { description } = props; - const theme = useTheme(); + const { mode } = useColorScheme(); useEffect(() => { - if (theme.palette.mode === "dark") { - // @ts-ignore - don't check type of css module - import("highlight.js/styles/github-dark.css"); + console.log({ mode }); + // @ts-ignore - don't check type of css module + import("highlight.js/styles/github-dark.css"); + if (mode === "dark") { } else { // @ts-ignore - don't check type of css module - import("highlight.js/styles/github.css"); + // import("highlight.js/styles/github.css"); } - }, [theme]); + }, [mode]); return ( <> diff --git a/website/src/components/themeRegistry.tsx b/website/src/components/themeRegistry.tsx deleted file mode 100644 index 4b4baf5..0000000 --- a/website/src/components/themeRegistry.tsx +++ /dev/null @@ -1,72 +0,0 @@ -"use client"; -import { darkThemeOptions, lightThemeOptions } from "@/styles/theme"; -import createCache from "@emotion/cache"; -import { CacheProvider } from "@emotion/react"; -import { useMediaQuery } from "@mui/material"; -import CssBaseline from "@mui/material/CssBaseline"; -import { ThemeProvider, createTheme } from "@mui/material/styles"; -import { useServerInsertedHTML } from "next/navigation"; -import React, { ReactNode } from "react"; - -const lightTheme = createTheme(lightThemeOptions); -const darkTheme = createTheme(darkThemeOptions); - -// This implementation is from emotion-js -// https://github.com/emotion-js/emotion/issues/2928#issuecomment-1319747902 -export default function ThemeRegistry(props: { - children: ReactNode; - options: any; -}) { - const userPrefersDarkmode = useMediaQuery("(prefers-color-scheme: dark)"); - - const { options, children } = props; - - const [{ cache, flush }] = React.useState(() => { - const cache = createCache(options); - cache.compat = true; - const prevInsert = cache.insert; - let inserted: string[] = []; - cache.insert = (...args) => { - const serialized = args[1]; - if (cache.inserted[serialized.name] === undefined) { - inserted.push(serialized.name); - } - return prevInsert(...args); - }; - const flush = () => { - const prevInserted = inserted; - inserted = []; - return prevInserted; - }; - return { cache, flush }; - }); - - useServerInsertedHTML(() => { - const names = flush(); - if (names.length === 0) { - return null; - } - let styles = ""; - for (const name of names) { - styles += cache.inserted[name]; - } - return ( -