From f3c7f820caf841327ee419fa7f9df8489cdbf57d Mon Sep 17 00:00:00 2001 From: Alan Morel Date: Thu, 9 Nov 2023 19:10:11 -0500 Subject: [PATCH] feat: add support for themes --- app/layout.tsx | 9 ++++++--- package.json | 1 + pnpm-lock.yaml | 15 ++++++++++++++ src/components/Contacts.tsx | 4 ++++ src/components/other/ThemeSwitcher.tsx | 28 ++++++++++++++++++++++++++ src/helpers/client/Providers.tsx | 18 +++++++++++++++++ 6 files changed, 72 insertions(+), 3 deletions(-) create mode 100644 src/components/other/ThemeSwitcher.tsx create mode 100644 src/helpers/client/Providers.tsx diff --git a/app/layout.tsx b/app/layout.tsx index f9ef46e..03121ab 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -1,5 +1,6 @@ import "@/src/globals.css"; import Analytics from "@/src/helpers/client/Analytics"; +import Providers from "@/src/helpers/client/Providers"; import { withMetadata } from "@/src/helpers/server/MetadataHelper"; import { Inter } from "next/font/google"; import { ReactElement, ReactNode } from "react"; @@ -19,9 +20,11 @@ export default async function RootLayout(props: Props): Promise { const { children } = props; return ( - - {children} - + + + {children} + + ); } diff --git a/package.json b/package.json index 6d2670c..309a483 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,7 @@ "lucide-react": "^0.291.0", "mime-types": "^2.1.35", "next": "^14.0.0", + "next-themes": "^0.2.1", "nextjs-google-analytics": "^2.3.3", "pino": "^8.16.1", "pino-pretty": "^10.2.3", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1a0468e..2b28c2d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -41,6 +41,9 @@ dependencies: next: specifier: ^14.0.0 version: 14.0.0(react-dom@18.2.0)(react@18.2.0) + next-themes: + specifier: ^0.2.1 + version: 0.2.1(next@14.0.0)(react-dom@18.2.0)(react@18.2.0) nextjs-google-analytics: specifier: ^2.3.3 version: 2.3.3(next@14.0.0)(react@18.2.0) @@ -3380,6 +3383,18 @@ packages: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} dev: true + /next-themes@0.2.1(next@14.0.0)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-B+AKNfYNIzh0vqQQKqQItTS8evEouKD7H5Hj3kmuPERwddR2TxvDSFZuTj6T7Jfn1oyeUyJMydPl1Bkxkh0W7A==} + peerDependencies: + next: '*' + react: '*' + react-dom: '*' + dependencies: + next: 14.0.0(react-dom@18.2.0)(react@18.2.0) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + /next@14.0.0(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-J0jHKBJpB9zd4+c153sair0sz44mbaCHxggs8ryVXSFBuBqJ8XdE9/ozoV85xGh2VnSjahwntBZZgsihL9QznA==} engines: {node: '>=18.17.0'} diff --git a/src/components/Contacts.tsx b/src/components/Contacts.tsx index afb959a..a98ccce 100644 --- a/src/components/Contacts.tsx +++ b/src/components/Contacts.tsx @@ -3,6 +3,7 @@ import GitHubIcon from "@/src/components/icons/GitHubIcon"; import InstagramIcon from "@/src/components/icons/InstagramIcon"; import LinkedInIcon from "@/src/components/icons/LinkedInIcon"; import XIcon from "@/src/components/icons/XIcon"; +import ThemeSwitcher from "@/src/components/other/ThemeSwitcher"; import { ReactElement } from "react"; const contacts = [ @@ -51,6 +52,9 @@ export default function Contacts(): ReactElement { ))} +
  • + +
  • ); } diff --git a/src/components/other/ThemeSwitcher.tsx b/src/components/other/ThemeSwitcher.tsx new file mode 100644 index 0000000..34ebeb1 --- /dev/null +++ b/src/components/other/ThemeSwitcher.tsx @@ -0,0 +1,28 @@ +"use client"; + +import { Laptop2Icon, MoonIcon, SunIcon } from "lucide-react"; +import { useTheme } from "next-themes"; +import { ReactElement, useEffect, useState } from "react"; + +export default function ThemeSwitcher(): ReactElement { + const [mounted, setMounted] = useState(false); + const { theme, setTheme } = useTheme(); + + useEffect(() => { + setMounted(true); + }, []); + + function getIcon(): ReactElement { + if (!mounted || theme === "light") { + return setTheme("dark")} />; + } else if (theme === "system") { + return setTheme("light")} />; + } else if (theme === "dark") { + return setTheme("system")} />; + } + + return <>; + } + + return
    {getIcon()}
    ; +} diff --git a/src/helpers/client/Providers.tsx b/src/helpers/client/Providers.tsx new file mode 100644 index 0000000..a664e03 --- /dev/null +++ b/src/helpers/client/Providers.tsx @@ -0,0 +1,18 @@ +"use client"; + +import { ThemeProvider } from "next-themes"; +import { ReactElement, ReactNode } from "react"; + +interface Props { + children: ReactNode; +} + +export default function Providers(props: Props): ReactElement { + const { children } = props; + + return ( + + {children} + + ); +}