Beautiful theme switch
This commit is contained in:
parent
2b3c4e2482
commit
068a3f8f1e
@ -10,15 +10,6 @@ interface ILayoutProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const Layout: NextPage<ILayoutProps> = ({ title, children }) => {
|
const Layout: NextPage<ILayoutProps> = ({ title, children }) => {
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (typeof window !== "undefined") {
|
|
||||||
const theme = window.localStorage.getItem("theme");
|
|
||||||
if(!theme || !document) return;
|
|
||||||
document.documentElement.setAttribute("data-theme", theme);
|
|
||||||
}
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return <>
|
return <>
|
||||||
<Head>
|
<Head>
|
||||||
<title>{title ?? "c0ntroller.de"}</title>
|
<title>{title ?? "c0ntroller.de"}</title>
|
||||||
|
@ -2,20 +2,11 @@
|
|||||||
import type { NextPage } from "next";
|
import type { NextPage } from "next";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { Terminal, Sun, Moon } from "phosphor-react";
|
import { Terminal, Sun, Moon } from "phosphor-react";
|
||||||
|
import ThemeSwitch from "./ThemeSwitch";
|
||||||
|
|
||||||
import styles from "../../styles/Blog/Navigation.module.scss";
|
import styles from "../../styles/Blog/Navigation.module.scss";
|
||||||
|
|
||||||
const Navigation: NextPage<{}> = () => {
|
const Navigation: NextPage<{}> = () => {
|
||||||
const switchTheme = () => {
|
|
||||||
if (typeof document === "undefined") return;
|
|
||||||
|
|
||||||
const current = document.documentElement.getAttribute("data-theme") || "dark";
|
|
||||||
const setTo = current === "dark" ? "light" : "dark";
|
|
||||||
document.documentElement.setAttribute("data-theme", setTo);
|
|
||||||
|
|
||||||
if (typeof window !== "undefined") window.localStorage.setItem("theme", setTo);
|
|
||||||
};
|
|
||||||
|
|
||||||
return <nav className={styles.navigation}>
|
return <nav className={styles.navigation}>
|
||||||
<Link href={"/"}>
|
<Link href={"/"}>
|
||||||
<a className={`nostyle ${styles.imgContainer}`}>
|
<a className={`nostyle ${styles.imgContainer}`}>
|
||||||
@ -29,10 +20,7 @@ const Navigation: NextPage<{}> = () => {
|
|||||||
<div className={styles.navLink}><Link href={"/"}><a className="nostyle">About me</a></Link></div>
|
<div className={styles.navLink}><Link href={"/"}><a className="nostyle">About me</a></Link></div>
|
||||||
<div className={styles.spacer}></div>
|
<div className={styles.spacer}></div>
|
||||||
<Terminal size={"1.5em"} />
|
<Terminal size={"1.5em"} />
|
||||||
<div className={styles.themeSwitch}>
|
<ThemeSwitch />
|
||||||
<Sun className={styles.lightTheme} size={"1.5em"} onClick={switchTheme} />
|
|
||||||
<Moon className={styles.darkTheme} size={"1.5em"} onClick={switchTheme} />
|
|
||||||
</div>
|
|
||||||
</nav>;
|
</nav>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
53
components/Blog/ThemeSwitch.tsx
Normal file
53
components/Blog/ThemeSwitch.tsx
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
import type { NextPage } from "next";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import { useTheme } from "next-themes";
|
||||||
|
import { Sun, Moon, FileJs } from "phosphor-react";
|
||||||
|
|
||||||
|
import styles from "../../styles/Blog/ThemeSwitch.module.scss";
|
||||||
|
|
||||||
|
interface FadeProperties {
|
||||||
|
sun?: string;
|
||||||
|
moon?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ThemeSwitch: NextPage<{ size?: string }> = ({ size }) => {
|
||||||
|
const [mounted, setMounted] = useState(false);
|
||||||
|
const [fadeProps, setFadeProps] = useState<FadeProperties>({});
|
||||||
|
const { theme, setTheme } = useTheme();
|
||||||
|
|
||||||
|
// Will be run when the component is rendered.
|
||||||
|
useEffect(() => {
|
||||||
|
setMounted(true);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const switchTheme = (theme: string) => {
|
||||||
|
|
||||||
|
if (theme === "dark") setFadeProps({
|
||||||
|
sun: styles.fadeIn,
|
||||||
|
moon: styles.fadeOut
|
||||||
|
});
|
||||||
|
else setFadeProps({
|
||||||
|
sun: styles.fadeOut,
|
||||||
|
moon: styles.fadeIn
|
||||||
|
});
|
||||||
|
|
||||||
|
setTheme(theme);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!mounted) {
|
||||||
|
return <div className={styles.switch} title="Theme switching needs JS to be enabled.">
|
||||||
|
<FileJs size={size || "1.5em"} />
|
||||||
|
</div>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const sunClasses = fadeProps.sun || (theme === "dark" ? styles.selected : undefined);
|
||||||
|
const moonClasses = fadeProps.moon || (theme === "light" ? styles.selected : undefined);
|
||||||
|
|
||||||
|
return <div className={styles.switch}>
|
||||||
|
<Sun size={size || "1.5em"} className={sunClasses} onClick={() => switchTheme("light")} />
|
||||||
|
<Moon size={size || "1.5em"} className={moonClasses} onClick={() => switchTheme("dark")} />
|
||||||
|
</div>;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ThemeSwitch;
|
17
package-lock.json
generated
17
package-lock.json
generated
@ -10,6 +10,7 @@
|
|||||||
"color": "^4.2.3",
|
"color": "^4.2.3",
|
||||||
"highlight.js": "^11.5.1",
|
"highlight.js": "^11.5.1",
|
||||||
"next": "12.1.0",
|
"next": "12.1.0",
|
||||||
|
"next-themes": "^0.2.1",
|
||||||
"node-fetch": "^3.2.0",
|
"node-fetch": "^3.2.0",
|
||||||
"phosphor-react": "^1.3.1",
|
"phosphor-react": "^1.3.1",
|
||||||
"react": "17.0.2",
|
"react": "17.0.2",
|
||||||
@ -3075,6 +3076,16 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/next-themes": {
|
||||||
|
"version": "0.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/next-themes/-/next-themes-0.2.1.tgz",
|
||||||
|
"integrity": "sha512-B+AKNfYNIzh0vqQQKqQItTS8evEouKD7H5Hj3kmuPERwddR2TxvDSFZuTj6T7Jfn1oyeUyJMydPl1Bkxkh0W7A==",
|
||||||
|
"peerDependencies": {
|
||||||
|
"next": "*",
|
||||||
|
"react": "*",
|
||||||
|
"react-dom": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/node-domexception": {
|
"node_modules/node-domexception": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz",
|
||||||
@ -6708,6 +6719,12 @@
|
|||||||
"use-subscription": "1.5.1"
|
"use-subscription": "1.5.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"next-themes": {
|
||||||
|
"version": "0.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/next-themes/-/next-themes-0.2.1.tgz",
|
||||||
|
"integrity": "sha512-B+AKNfYNIzh0vqQQKqQItTS8evEouKD7H5Hj3kmuPERwddR2TxvDSFZuTj6T7Jfn1oyeUyJMydPl1Bkxkh0W7A==",
|
||||||
|
"requires": {}
|
||||||
|
},
|
||||||
"node-domexception": {
|
"node-domexception": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz",
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
"color": "^4.2.3",
|
"color": "^4.2.3",
|
||||||
"highlight.js": "^11.5.1",
|
"highlight.js": "^11.5.1",
|
||||||
"next": "12.1.0",
|
"next": "12.1.0",
|
||||||
|
"next-themes": "^0.2.1",
|
||||||
"node-fetch": "^3.2.0",
|
"node-fetch": "^3.2.0",
|
||||||
"phosphor-react": "^1.3.1",
|
"phosphor-react": "^1.3.1",
|
||||||
"react": "17.0.2",
|
"react": "17.0.2",
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import type { AppProps } from "next/app";
|
import type { AppProps } from "next/app";
|
||||||
import Head from "next/head";
|
import Head from "next/head";
|
||||||
|
import { ThemeProvider } from "next-themes";
|
||||||
import "../styles/globals.scss";
|
import "../styles/globals.scss";
|
||||||
import { CommandsProvider } from "../lib/commands/ContextProvider";
|
import { CommandsProvider } from "../lib/commands/ContextProvider";
|
||||||
import { ModalFunctionProvider } from "../components/Terminal/contexts/ModalFunctions";
|
import { ModalFunctionProvider } from "../components/Terminal/contexts/ModalFunctions";
|
||||||
@ -33,11 +34,13 @@ function MyApp({ Component, pageProps }: AppProps) {
|
|||||||
<meta name="msapplication-TileImage" content="/mstile-310x310.png" />
|
<meta name="msapplication-TileImage" content="/mstile-310x310.png" />
|
||||||
<meta name="theme-color" content="#444444" />
|
<meta name="theme-color" content="#444444" />
|
||||||
</Head>
|
</Head>
|
||||||
|
<ThemeProvider>
|
||||||
<CommandsProvider>
|
<CommandsProvider>
|
||||||
<ModalFunctionProvider>
|
<ModalFunctionProvider>
|
||||||
<Component {...pageProps} />
|
<Component {...pageProps} />
|
||||||
</ModalFunctionProvider>
|
</ModalFunctionProvider>
|
||||||
</CommandsProvider>
|
</CommandsProvider>
|
||||||
|
</ThemeProvider>
|
||||||
</>;
|
</>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,13 +54,4 @@
|
|||||||
.spacer {
|
.spacer {
|
||||||
flex-grow: 2;
|
flex-grow: 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
.themeSwitch {
|
|
||||||
.lightTheme {
|
|
||||||
display: var(--blog_dark-el-display)
|
|
||||||
}
|
|
||||||
.darkTheme {
|
|
||||||
display: var(--blog_light-el-display)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
41
styles/Blog/ThemeSwitch.module.scss
Normal file
41
styles/Blog/ThemeSwitch.module.scss
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
.switch {
|
||||||
|
position: relative;
|
||||||
|
width: 1.5em;
|
||||||
|
height: 1.5em;
|
||||||
|
|
||||||
|
& > * {
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fadeOut {
|
||||||
|
animation: fadeOut 0.2s ease-in-out;
|
||||||
|
animation-fill-mode: forwards;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fadeIn {
|
||||||
|
animation: fadeIn 0.2s ease-in-out;
|
||||||
|
animation-fill-mode: forwards;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fadeOut {
|
||||||
|
from {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translate(0, 0);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translate(0, -100%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fadeIn {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translate(0, 100%);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translate(0, 0);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user