Skip to content
Merged
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
2 changes: 2 additions & 0 deletions app/components/Navbar.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import Link from 'next/link';
import LogoutButton from './LogoutButton';
import ThemeToggle from './ThemeToggle';
import { GraduationCap, User, Hexagon } from 'lucide-react';

interface NavLink { href: string; label: string; }
Expand Down Expand Up @@ -43,6 +44,7 @@ export default function Navbar({ username, role, links }: Props) {
{username}
</span>
)}
<ThemeToggle />
<LogoutButton />
</div>
</div>
Expand Down
35 changes: 35 additions & 0 deletions app/components/ThemeToggle.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
'use client';

import { useEffect, useState } from 'react';
import { Sun, Moon } from 'lucide-react';

export default function ThemeToggle() {
const [theme, setTheme] = useState<'dark' | 'light'>('dark');

useEffect(() => {
const saved = localStorage.getItem('theme') as 'dark' | 'light' | null;
// Default to system preference if no saved choice
const system = window.matchMedia('(prefers-color-scheme: light)').matches ? 'light' : 'dark';
const initial = saved ?? system;
setTheme(initial);
document.documentElement.setAttribute('data-theme', initial);
}, []);

function toggle() {
const next = theme === 'dark' ? 'light' : 'dark';
setTheme(next);
document.documentElement.setAttribute('data-theme', next);
localStorage.setItem('theme', next);
}

return (
<button
onClick={toggle}
className="btn btn-ghost btn-sm"
title={theme === 'dark' ? 'Switch to light mode' : 'Switch to dark mode'}
aria-label="Toggle theme"
>
{theme === 'dark' ? <Sun size={13} /> : <Moon size={13} />}
</button>
);
}
33 changes: 32 additions & 1 deletion app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
--bg2: #0c0f14;
--bg3: #111520;
--bg4: #161b28;
--bg5: #ababab;
--border: rgba(255,255,255,0.06);
--border2: rgba(255,255,255,0.1);
--accent: #6366f1;
Expand All @@ -25,6 +24,28 @@
--radius-lg:14px;
}

/* ── Light (cream) theme ── */
[data-theme="light"] {
--bg: #f5f0e8;
--bg2: #faf7f2;
--bg3: #ede8df;
--bg4: #e4ddd2;
--border: rgba(0,0,0,0.08);
--border2: rgba(0,0,0,0.14);
--accent: #4f46e5;
--accent2: #6366f1;
--accent3: #4338ca;
--glow: rgba(99,102,241,0.15);
--glow2: rgba(99,102,241,0.06);
--green: #059669;
--red: #dc2626;
--orange: #d97706;
--text: #1c1917;
--text2: #44403c;
--text3: #78716c;
--text4: #a8a29e;
}

html { height: 100%; }
body {
min-height: 100%;
Expand Down Expand Up @@ -74,6 +95,9 @@ a:hover { color: var(--text); }
-webkit-backdrop-filter: blur(20px) saturate(180%);
border-bottom: 1px solid var(--border);
}
[data-theme="light"] .navbar {
background: rgba(245,240,232,0.9);
}
.navbar-inner {
display: flex;
align-items: center;
Expand Down Expand Up @@ -456,6 +480,13 @@ tbody tr:hover td { color: var(--text); }
0 24px 64px rgba(0,0,0,0.6),
0 0 80px rgba(99,102,241,0.06);
}
[data-theme="light"] .login-card {
background: var(--bg2);
box-shadow: 0 0 0 1px var(--border2), 0 24px 64px rgba(0,0,0,0.12);
}
[data-theme="light"] .login-page::before {
background: radial-gradient(ellipse, rgba(99,102,241,0.06) 0%, transparent 65%);
}
.login-logo { text-align: center; margin-bottom: 2.25rem; }
.login-logo-icon { display: flex; justify-content: center; margin-bottom: 0.75rem; }
.login-logo-title {
Expand Down
12 changes: 11 additions & 1 deletion app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,20 @@ export const metadata: Metadata = {
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<head>
{/* Prevent flash of wrong theme */}
<script dangerouslySetInnerHTML={{ __html: `
(function(){
var saved = localStorage.getItem('theme');
var system = window.matchMedia('(prefers-color-scheme: light)').matches ? 'light' : 'dark';
document.documentElement.setAttribute('data-theme', saved || system);
})();
`}} />
</head>
<body>
{children}
<Toaster
theme="dark"
theme="system"
position="bottom-right"
toastOptions={{
style: {
Expand Down
4 changes: 4 additions & 0 deletions app/login/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import { useState, Suspense } from 'react';
import { signIn } from 'next-auth/react';
import { useSearchParams } from 'next/navigation';
import ThemeToggle from '@/app/components/ThemeToggle';

import { Hexagon } from 'lucide-react';
import { toast } from 'sonner';
Expand Down Expand Up @@ -48,6 +49,9 @@ function LoginForm() { const searchParams = useSearchParams();

return (
<div className="login-page">
<div style={{ position: 'absolute', top: '1rem', right: '1rem', zIndex: 10 }}>
<ThemeToggle />
</div>
<div className="login-card">
<div className="login-logo">
<div className="login-logo-icon" style={{ marginBottom: '0.75rem' }}>
Expand Down
Loading