diff --git a/bun.lockb b/bun.lockb index ab436967..14216750 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/package.json b/package.json index 4bb55c3b..e6549492 100644 --- a/package.json +++ b/package.json @@ -60,6 +60,7 @@ "postcss": "^8.4.27", "prettier": "^3.0.0", "prettier-plugin-tailwindcss": "^0.5.1", + "tailwind": "^4.0.0", "tailwind-scrollbar": "^3.0.5", "tailwindcss": "^3.3.3", "typescript": "^5.1.6" diff --git a/src/app/components/UserMenu.tsx b/src/app/components/UserMenu.tsx index ccf8a6f0..09b9f936 100644 --- a/src/app/components/UserMenu.tsx +++ b/src/app/components/UserMenu.tsx @@ -5,13 +5,20 @@ import Image from "next/image"; import { signOut } from "next-auth/react"; import { Menu, Transition } from "@headlessui/react"; import { HiOutlineLogout } from "react-icons/hi"; +import { useTheme } from "~/app/providers/theme"; interface UserMenuProps { imageUrl: string | undefined; email: string; } +function classNames(...classes: string[]): string { + return classes.filter(Boolean).join(" "); +} + export default function UserMenu({ imageUrl, email }: UserMenuProps) { + const { theme, switchTheme } = useTheme(); + return (
@@ -49,18 +56,64 @@ export default function UserMenu({ imageUrl, email }: UserMenuProps) { leaveTo="transform opacity-0 scale-95" > -
- - {() => ( +
+
+
+ Theme +
+ + + + + + + + + +
+
+ - )} - + +
diff --git a/src/app/components/dashboard.tsx b/src/app/components/dashboard.tsx index 5e250ba3..d17ae3da 100644 --- a/src/app/components/dashboard.tsx +++ b/src/app/components/dashboard.tsx @@ -13,7 +13,7 @@ export default async function Dashboard(props: { children: React.ReactNode }) { } return ( -
+

diff --git a/src/app/layout.tsx b/src/app/layout.tsx index d869bcf8..7f18d142 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -6,6 +6,7 @@ import { headers } from "next/headers"; import { TRPCReactProvider } from "~/trpc/react"; import { ModalProvider } from "~/app/providers/modal"; import { BoardProvider } from "~/app/providers/board"; +import { ThemeProvider } from "./providers/theme"; const jakarta = Plus_Jakarta_Sans({ subsets: ["latin"], @@ -27,11 +28,13 @@ export default function RootLayout({ return ( - - - {children} - - + + + + {children} + + + ); diff --git a/src/app/providers/theme.tsx b/src/app/providers/theme.tsx new file mode 100644 index 00000000..797e2d5a --- /dev/null +++ b/src/app/providers/theme.tsx @@ -0,0 +1,70 @@ +"use client"; + +import React, { + createContext, + useContext, + useEffect, + useState, + type ReactNode, +} from "react"; + +interface ThemeContextProps { + theme: string; + switchTheme: (theme: string) => void; +} + +const ThemeContext = createContext(undefined); + +export const ThemeProvider: React.FC<{ children: ReactNode }> = ({ + children, +}) => { + const [theme, setTheme] = useState(""); + + const switchTheme = (theme: string) => { + if (theme === "system") { + localStorage.removeItem("theme"); + } else { + if (theme === "dark") { + document.documentElement.classList.add("dark"); + } else { + document.documentElement.classList.remove("dark"); + } + + localStorage.theme = theme; + } + setTheme(theme); + }; + + useEffect(() => { + if (!("theme" in localStorage)) { + if (window.matchMedia("(prefers-color-scheme: dark)").matches) { + document.documentElement.classList.add("dark"); + } else { + document.documentElement.classList.remove("dark"); + } + setTheme("system"); + } else { + if (localStorage.theme === "dark") { + document.documentElement.classList.add("dark"); + setTheme("dark"); + } else { + document.documentElement.classList.remove("dark"); + setTheme("light"); + } + } + }, []); + + return ( + + {theme.length ? children : null} + + ); +}; + +export const useTheme = (): ThemeContextProps => { + const context = useContext(ThemeContext); + if (!context) { + throw new Error("useTheme must be used within a ThemeProvider"); + } + return context; +}; diff --git a/tailwind.config.ts b/tailwind.config.ts index f482fd9e..44f9d0d7 100644 --- a/tailwind.config.ts +++ b/tailwind.config.ts @@ -2,6 +2,7 @@ import { type Config } from "tailwindcss"; export default { content: ["./src/**/*.tsx"], + darkMode: 'class', theme: { extend: { fontFamily: { @@ -10,6 +11,9 @@ export default { fontSize: { sm: "0.8rem", }, + boxShadow: { + '3xl': '0px 16px 70px rgba(0, 0, 0, 0.5)', + }, colors: { "dark-50": "#161616", "dark-100": "#1c1c1c", @@ -23,6 +27,17 @@ export default { "dark-900": "#7e7e7e", "dark-950": "#bbb", "dark-1000": "#ededed", + "light-50": "hsl(0deg 0% 98.8%)", + "light-100": "hsl(0deg 0% 97.3%)", + "light-200": "hsl(0deg 0% 95.3%)", + "light-300": "hsl(0deg 0% 92.9%)", + "light-400": "hsl(0deg 0% 91%)", + "light-500": "hsl(0deg 0% 88.6%)", + "light-700": "hsl(0deg 0% 85.9%)", + "light-800": "hsl(0deg 0% 78%)", + "light-900": "hsl(0deg 0% 56.1%)", + "light-950": "hsl(0deg 0% 52.2%)", + "light-1000": "hsl(0deg 0% 43.5%)", }, screens: { xxl: '1600px'