mirror of
https://github.com/hsjobeki/noogle.git
synced 2024-12-25 15:04:30 +03:00
fix: dark mode flickering by using native css vars
This commit is contained in:
parent
eb0f3a19f8
commit
9c04f9559f
@ -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 (
|
||||
<html lang="en" className={inter.className}>
|
||||
<html lang="en" className={inter.className} data-color-scheme="light">
|
||||
<head>
|
||||
{/* <link rel="icon" href="/favicon.png" /> */}
|
||||
<link
|
||||
@ -43,7 +42,7 @@ export default function RootLayout({
|
||||
}}
|
||||
>
|
||||
<ClientSideLayoutContext>
|
||||
<CssBaseline />
|
||||
{/* <CssBaseline /> */}
|
||||
{children}
|
||||
</ClientSideLayoutContext>
|
||||
</AppRouterCacheProvider>
|
||||
|
@ -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";
|
||||
|
||||
|
@ -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 (
|
||||
<Button
|
||||
variant="outlined"
|
||||
onClick={() => {
|
||||
if (mode === "light") {
|
||||
setMode("dark");
|
||||
} else {
|
||||
setMode("light");
|
||||
}
|
||||
}}
|
||||
>
|
||||
{mode === "light" ? "Change to Dark" : "Change to Light"}
|
||||
</Button>
|
||||
);
|
||||
};
|
||||
|
||||
export const ClientSideLayoutContext = ({
|
||||
children,
|
||||
}: {
|
||||
children: ReactNode;
|
||||
}) => {
|
||||
const userPrefersDarkmode = useMediaQuery("(prefers-color-scheme: dark)");
|
||||
return (
|
||||
<ThemeProvider theme={userPrefersDarkmode ? darkTheme : lightTheme}>
|
||||
<CssBaseline />
|
||||
<Toaster />
|
||||
{children}
|
||||
</ThemeProvider>
|
||||
// <ThemeProvider theme={userPrefersDarkmode ? darkTheme : lightTheme}>
|
||||
<>
|
||||
{getInitColorSchemeScript()}
|
||||
<CssVarsProvider theme={theme}>
|
||||
<CssBaseline />
|
||||
<Mode />
|
||||
<Toaster />
|
||||
{children}
|
||||
</CssVarsProvider>
|
||||
</>
|
||||
// </ThemeProvider>
|
||||
);
|
||||
};
|
||||
|
@ -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 <></>;
|
||||
};
|
||||
|
@ -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 (
|
||||
<Card
|
||||
elevation={0}
|
||||
sx={{
|
||||
width: "100%",
|
||||
my: 5,
|
||||
borderImageSlice: 1,
|
||||
borderImageSource:
|
||||
idx === todaysIdx
|
||||
? `linear-gradient(to left, ${info.light},${error.main})`
|
||||
: `linear-gradient(to left, ${info.light},${info.dark})`,
|
||||
borderWidth: 4,
|
||||
borderStyle: "solid",
|
||||
}}
|
||||
>
|
||||
<FunctionCard sx={{ my: 5 }}>
|
||||
<CardHeader
|
||||
component="h2"
|
||||
sx={{ py: 0 }}
|
||||
@ -129,6 +126,6 @@ export const FunctionOfTheDay = () => {
|
||||
Next
|
||||
</Button>
|
||||
</CardActions>
|
||||
</Card>
|
||||
</FunctionCard>
|
||||
);
|
||||
};
|
||||
|
@ -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 (
|
||||
<Box
|
||||
sx={{
|
||||
@ -12,8 +11,7 @@ export const Background = ({ children }: { children: ReactNode }) => {
|
||||
flexDirection: "column",
|
||||
overflowX: "hidden",
|
||||
overflowY: "scroll",
|
||||
bgcolor:
|
||||
theme.palette.mode === "light" ? "rgb(242, 248, 253)" : "#070c16",
|
||||
bgcolor: "background.paper",
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
|
@ -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 (
|
||||
<>
|
||||
<Box
|
||||
@ -24,8 +22,7 @@ export const Header = () => {
|
||||
width: "100%",
|
||||
py: 1.2,
|
||||
zIndex: 1000,
|
||||
backgroundColor:
|
||||
theme.palette.mode === "light" ? "primary.main" : "#101010",
|
||||
backgroundColor: "header.default",
|
||||
display: "grid",
|
||||
gridTemplateColumns: {
|
||||
xs: "1fr",
|
||||
|
@ -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 (
|
||||
<>
|
||||
|
@ -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 (
|
||||
<style
|
||||
key={cache.key}
|
||||
data-emotion={`${cache.key} ${names.join(" ")}`}
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: options.prepend ? `@layer emotion {${styles}}` : styles,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
return (
|
||||
<CacheProvider value={cache}>
|
||||
<ThemeProvider theme={userPrefersDarkmode ? darkTheme : lightTheme}>
|
||||
<CssBaseline />
|
||||
{children}
|
||||
</ThemeProvider>
|
||||
</CacheProvider>
|
||||
);
|
||||
}
|
@ -189,14 +189,9 @@ mark.noogle-marker {
|
||||
|
||||
|
||||
code {
|
||||
/* padding: 0.3rem; */
|
||||
background-color: #f0f1f2;
|
||||
}
|
||||
pre {
|
||||
/* padding: 0.3rem; */
|
||||
background-color: #f0f1f2;
|
||||
}
|
||||
code.hljs {
|
||||
background-color: #f0f1f2;
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { ThemeOptions } from "@mui/material/styles";
|
||||
import { CssVarsThemeOptions } from "@mui/material";
|
||||
|
||||
const commonOptions: Partial<ThemeOptions> = {
|
||||
const commonOptions: Partial<CssVarsThemeOptions> = {
|
||||
typography: {
|
||||
fontFamily: "inherit",
|
||||
h1: {
|
||||
|
@ -1,22 +0,0 @@
|
||||
import { ThemeOptions } from "@mui/material/styles";
|
||||
import { commonOptions } from "./common";
|
||||
|
||||
const darkThemeOptions: ThemeOptions = {
|
||||
...commonOptions,
|
||||
palette: {
|
||||
mode: "dark",
|
||||
background: {
|
||||
// default: "#0f192c",
|
||||
default: "#1b1d22",
|
||||
paper: "#17181c",
|
||||
},
|
||||
primary: {
|
||||
main: "#6586c8",
|
||||
},
|
||||
secondary: {
|
||||
main: "#40224e",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export { darkThemeOptions };
|
@ -1,2 +1 @@
|
||||
export { lightThemeOptions } from "./lightThemeOptions";
|
||||
export { darkThemeOptions } from "./darkThemeOptions";
|
||||
export { cssThemeOptions } from "./themeOptions";
|
||||
|
@ -1,20 +0,0 @@
|
||||
import { ThemeOptions } from "@mui/material/styles";
|
||||
import { commonOptions } from "./common";
|
||||
const lightThemeOptions: ThemeOptions = {
|
||||
...commonOptions,
|
||||
palette: {
|
||||
mode: "light",
|
||||
primary: {
|
||||
main: "#6586c8",
|
||||
},
|
||||
secondary: {
|
||||
main: "#6ad541",
|
||||
},
|
||||
background: {
|
||||
paper: "#fafafa",
|
||||
default: "#f0f1f2",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export { lightThemeOptions };
|
50
website/src/styles/theme/themeOptions.ts
Normal file
50
website/src/styles/theme/themeOptions.ts
Normal file
@ -0,0 +1,50 @@
|
||||
import { CssVarsThemeOptions } from "@mui/material/styles";
|
||||
import { commonOptions } from "./common";
|
||||
import type {} from "@mui/material/themeCssVarsAugmentation";
|
||||
|
||||
const cssThemeOptions: CssVarsThemeOptions = {
|
||||
...commonOptions,
|
||||
colorSchemes: {
|
||||
light: {
|
||||
palette: {
|
||||
mode: "light",
|
||||
primary: {
|
||||
main: "#6586c8",
|
||||
},
|
||||
secondary: {
|
||||
main: "#6ad541",
|
||||
},
|
||||
background: {
|
||||
paper: "#fafafa",
|
||||
default: "#f0f1f2",
|
||||
},
|
||||
// @ts-expect-error
|
||||
header: {
|
||||
default: "var(--mui-palette-primary-main)",
|
||||
},
|
||||
},
|
||||
},
|
||||
dark: {
|
||||
palette: {
|
||||
mode: "dark",
|
||||
background: {
|
||||
// default: "#0f192c",
|
||||
default: "#1b1d22",
|
||||
paper: "#17181c",
|
||||
},
|
||||
primary: {
|
||||
main: "#6586c8",
|
||||
},
|
||||
secondary: {
|
||||
main: "#40224e",
|
||||
},
|
||||
// @ts-expect-error
|
||||
header: {
|
||||
default: "#101010",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export { cssThemeOptions };
|
Loading…
Reference in New Issue
Block a user