Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 3 additions & 5 deletions app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,9 @@
--font-mono: var(--font-geist-mono);
}

@media (prefers-color-scheme: dark) {
:root {
--background: #2a2a2a;
--foreground: #ededed;
}
.dark {
--background: #18181b;
--foreground: #fafafa;
}

body {
Expand Down
3 changes: 2 additions & 1 deletion app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { Metadata } from "next";
import { Geist, Geist_Mono } from "next/font/google";
import "./globals.css";
import { ThemeProvider } from "./theme-provider";

const geistSans = Geist({
variable: "--font-geist-sans",
Expand All @@ -27,7 +28,7 @@ export default function RootLayout({
<body
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
>
{children}
<ThemeProvider>{children}</ThemeProvider>
</body>
</html>
);
Expand Down
25 changes: 22 additions & 3 deletions app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"use client";

import { useState, useEffect, DragEvent } from "react";
import { useTheme } from "./theme-provider";

interface Todo {
id: number;
Expand All @@ -20,6 +21,7 @@ const columns = [
];

export default function Home() {
const { theme, toggleTheme } = useTheme();
const [todos, setTodos] = useState<Todo[]>([]);
const [inputValue, setInputValue] = useState("");
const [isLoaded, setIsLoaded] = useState(false);
Expand Down Expand Up @@ -165,9 +167,26 @@ export default function Home() {
return (
<div className="flex min-h-screen flex-col bg-zinc-50 py-8 font-sans dark:bg-zinc-900">
<header className="px-6">
<h1 className="mb-6 text-center text-3xl font-bold text-zinc-900 dark:text-zinc-50">
Kanban Board
</h1>
<div className="mb-6 flex items-center justify-center gap-4">
<h1 className="text-3xl font-bold text-zinc-900 dark:text-zinc-50">
Kanban Board
</h1>
<button
onClick={toggleTheme}
className="rounded-lg border border-zinc-300 bg-white p-2 text-zinc-600 transition-colors hover:bg-zinc-100 dark:border-zinc-600 dark:bg-zinc-800 dark:text-zinc-400 dark:hover:bg-zinc-700"
aria-label={`Switch to ${theme === "light" ? "dark" : "light"} mode`}
>
{theme === "light" ? (
<svg className="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M20.354 15.354A9 9 0 018.646 3.646 9.003 9.003 0 0012 21a9.003 9.003 0 008.354-5.646z" />
</svg>
) : (
<svg className="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 11-8 0 4 4 0 018 0z" />
</svg>
)}
</button>
</div>

<div className="mx-auto mb-8 flex max-w-xl gap-2">
<input
Expand Down
61 changes: 61 additions & 0 deletions app/theme-provider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
"use client";

import { createContext, useContext, useEffect, useState } from "react";

type Theme = "light" | "dark";

interface ThemeContextType {
theme: Theme;
toggleTheme: () => void;
}

const ThemeContext = createContext<ThemeContextType | undefined>(undefined);

export function ThemeProvider({ children }: { children: React.ReactNode }) {
const [theme, setTheme] = useState<Theme>("light");
const [mounted, setMounted] = useState(false);

useEffect(() => {
const stored = localStorage.getItem("theme") as Theme | null;
if (stored) {
setTheme(stored);
} else if (window.matchMedia("(prefers-color-scheme: dark)").matches) {
setTheme("dark");
}
setMounted(true);
}, []);

useEffect(() => {
if (!mounted) return;

const root = document.documentElement;
if (theme === "dark") {
root.classList.add("dark");
} else {
root.classList.remove("dark");
}
localStorage.setItem("theme", theme);
}, [theme, mounted]);

const toggleTheme = () => {
setTheme((prev) => (prev === "light" ? "dark" : "light"));
};

if (!mounted) {
return <>{children}</>;
}

return (
<ThemeContext.Provider value={{ theme, toggleTheme }}>
{children}
</ThemeContext.Provider>
);
}

export function useTheme() {
const context = useContext(ThemeContext);
if (!context) {
throw new Error("useTheme must be used within a ThemeProvider");
}
return context;
}