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
90 changes: 60 additions & 30 deletions src/app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,44 @@
@tailwind utilities;

:root {
color-scheme: light;
--background: #f8fafc;
--foreground: #1e293b;
--muted: #f1f5f9;
--muted-foreground: #64748b;
--card: #ffffff;
--card-foreground: #1e293b;
--card-muted: #f8fafc;
--border: #e5e7eb;
--accent: #3b82f6;
--accent-secondary: #60a5fa;
--ring: #3b82f6;
--focus-ring: #3b82f6;
--success: #22c55e;
--warning: #f59e0b;
--destructive: #ef4444;
--accent-soft: rgba(59, 130, 246, 0.1);
--accent-foreground: #ffffff;
--control: #f1f5f9;
--control-hover: #e2e8f0;
--tooltip: #1e293b;
--tooltip-foreground: #ffffff;
--destructive-muted: rgba(239, 68, 68, 0.1);
--destructive-muted-border: rgba(239, 68, 68, 0.28);
--destructive-foreground: #ffffff;
--shadow-soft: 0 4px 20px -2px rgba(59, 130, 246, 0.08), 0 2px 6px -1px rgba(0, 0, 0, 0.04);
--shadow-medium: 0 10px 30px -4px rgba(59, 130, 246, 0.12), 0 4px 10px -2px rgba(0, 0, 0, 0.06);
--chart-2: #22c55e;
--chart-3: #f59e0b;
--chart-4: #06b6d4;
--chart-5: #ec4899;
--chart-6: #eab308;
--chart-7: #8b5cf6;
--chart-8: #14b8a6;
}

html[data-theme="classic-dark"],
html.dark {
color-scheme: dark;
--background: #050816;
--foreground: #f5f7fb;
Expand Down Expand Up @@ -39,42 +77,34 @@
--chart-8: #40d9c2;
}

html[data-theme="classic-dark"],
html.dark {
color-scheme: dark;
--card: #111111;
--card-muted: #1a1a1a;
--border: #2a2a2a;
}

html[data-theme="modern-light-blue"] {
color-scheme: light;
--background: #f6f8fc;
--foreground: #0f172a;
--muted: #e9eef7;
--muted-foreground: #516079;
--background: #f8fafc;
--foreground: #1e293b;
--muted: #f1f5f9;
--muted-foreground: #64748b;
--card: #ffffff;
--card-foreground: #0f172a;
--card-muted: #eff4fb;
--border: #d9e2ef;
--accent: #2563eb;
--accent-secondary: #1d4ed8;
--ring: #9cc5ff;
--focus-ring: #2563eb;
--success: #059669;
--warning: #d97706;
--destructive: #dc2626;
--accent-soft: rgba(91, 141, 239, 0.12);
--card-foreground: #1e293b;
--card-muted: #f8fafc;
--border: #e5e7eb;
--accent: #3b82f6;
--accent-secondary: #60a5fa;
--ring: #3b82f6;
--focus-ring: #3b82f6;
--success: #22c55e;
--warning: #f59e0b;
--destructive: #ef4444;
--accent-soft: rgba(59, 130, 246, 0.1);
--accent-foreground: #ffffff;
--control: #edf2f8;
--control: #f1f5f9;
--control-hover: #e2e8f0;
--tooltip: #ffffff;
--tooltip-foreground: #0f172a;
--destructive-muted: rgba(220, 38, 38, 0.08);
--destructive-muted-border: rgba(220, 38, 38, 0.22);
--tooltip: #1e293b;
--tooltip-foreground: #ffffff;
--destructive-muted: rgba(239, 68, 68, 0.1);
--destructive-muted-border: rgba(239, 68, 68, 0.28);
--destructive-foreground: #ffffff;
--shadow-soft: 0 12px 30px -20px rgba(81, 96, 121, 0.2);
--shadow-medium: 0 18px 35px -24px rgba(81, 96, 121, 0.26);
--shadow-soft: 0 4px 20px -2px rgba(59, 130, 246, 0.08), 0 2px 6px -1px rgba(0, 0, 0, 0.04);
--shadow-medium: 0 10px 30px -4px rgba(59, 130, 246, 0.12), 0 4px 10px -2px rgba(0, 0, 0, 0.06);
--chart-2: #22c55e;
--chart-3: #f59e0b;
--chart-4: #06b6d4;
Expand Down
3 changes: 1 addition & 2 deletions src/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,7 @@ export default async function RootLayout({
let theme = validThemes.includes(stored || '') ? stored : null;

if (!theme) {
const systemPrefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
theme = systemPrefersDark ? 'classic-dark' : 'modern-light-blue';
theme = 'modern-light-blue';
}

const isDark = theme !== 'modern-light-blue';
Expand Down
33 changes: 15 additions & 18 deletions src/components/AppNavbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -107,24 +107,21 @@ export default function AppNavbar() {
</Link>

{/* Desktop nav - Includes links to Dashboard, Streaks, Pull Requests, Goals, etc. */}
<nav className="hidden items-center gap-1 md:flex rounded-full border border-white/5 bg-white/[0.02] px-2 py-1.5 shadow-sm" aria-label={t("main")}>
<nav className="hidden items-center gap-1 md:flex rounded-full border border-[var(--border)] bg-[var(--card)]/90 px-2 py-1.5 shadow-xs backdrop-blur-md" aria-label={t("main")}>
{navItems.map((item) => {
const active = isActivePath(pathname, item.href);
return (
<Link
key={item.href}
href={item.href}
onClick={(e) => handleAnchorClick(e, item.href)}
className="relative px-3 py-2 text-[12px] font-medium transition-colors duration-150"
className={`relative px-3.5 py-1.5 text-[12px] font-medium transition-all duration-150 rounded-full ${
active
? "bg-[var(--accent-soft)] text-[var(--accent)] font-semibold"
: "text-[var(--muted-foreground)] hover:text-[var(--foreground)] hover:bg-[var(--control-hover)]/60"
}`}
style={{
fontFamily: MONO,
color: active ? "var(--accent)" : "var(--muted-foreground)",
}}
onMouseEnter={(e) => {
if (!active) (e.currentTarget as HTMLAnchorElement).style.color = "var(--foreground)";
}}
onMouseLeave={(e) => {
if (!active) (e.currentTarget as HTMLAnchorElement).style.color = "var(--muted-foreground)";
}}
>
{item.label}
Expand All @@ -140,7 +137,7 @@ export default function AppNavbar() {
{/* Auth state - Shows user identity and settings when logged in, or generic sign-in when logged out */}
{isAuthenticated ? (
!isDashboardRoute && (
<div className="flex items-center gap-4 border-l border-white/10 pl-4">
<div className="flex items-center gap-4 border-l border-[var(--border)] pl-4">
<Link
href="/dashboard/settings"
className="text-[12px] font-medium text-[var(--muted-foreground)] transition-colors hover:text-[var(--foreground)]"
Expand All @@ -158,7 +155,7 @@ export default function AppNavbar() {
<button
type="button"
onClick={() => signOut({ callbackUrl: "/" })}
className="rounded-lg bg-red-500/10 px-3 py-1.5 text-[12px] font-medium text-red-400 transition-all hover:bg-red-500/20 hover:text-red-300"
className="rounded-lg bg-[var(--destructive-muted)] px-3 py-1.5 text-[12px] font-medium text-[var(--destructive)] transition-all hover:bg-[var(--destructive)] hover:text-white"
style={{ fontFamily: MONO }}
>
{t("signOutCta")}
Expand All @@ -170,8 +167,8 @@ export default function AppNavbar() {
!isPublicProfileRoute && (
<Link
href="/api/auth/signin/github?callbackUrl=/dashboard"
className="shrink-0 rounded-full px-5 py-2 text-[13px] font-semibold text-[var(--accent-foreground)] shadow-[0_0_20px_rgba(129,140,248,0.3)] transition-all hover:scale-105 hover:shadow-[0_0_25px_rgba(129,140,248,0.5)]"
style={{ fontFamily: MONO, background: "var(--accent)" }}
className="shrink-0 rounded-full px-5 py-2 text-[13px] font-semibold text-[var(--accent-foreground)] bg-[var(--accent)] shadow-xs transition-all hover:scale-[1.02] hover:shadow-sm"
style={{ fontFamily: MONO }}
>
{t("signInCta")} →
</Link>
Expand All @@ -183,7 +180,7 @@ export default function AppNavbar() {
<button
type="button"
onClick={() => setMobileOpen((o) => !o)}
className="inline-flex items-center justify-center rounded-lg border border-white/10 bg-white/5 p-2.5 text-[var(--foreground)] transition-colors hover:bg-white/10 md:hidden"
className="inline-flex items-center justify-center rounded-lg border border-[var(--border)] bg-[var(--card)] p-2.5 text-[var(--foreground)] transition-colors hover:bg-[var(--control-hover)] md:hidden shadow-xs"
aria-expanded={mobileOpen}
aria-controls="app-mobile-nav"
aria-label={mobileOpen ? t("closeMenu") : t("openMenu")}
Expand All @@ -196,8 +193,8 @@ export default function AppNavbar() {
{mobileOpen && (
<div
id="app-mobile-nav"
className="border-t border-[var(--border)] md:hidden"
style={{ background: "color-mix(in srgb, var(--background) 98%, transparent)", backdropFilter: "blur(24px)" }}
className="border-t border-[var(--border)] md:hidden shadow-md"
style={{ background: "color-mix(in srgb, var(--card) 98%, transparent)", backdropFilter: "blur(24px)" }}
>
<div className="mx-auto flex w-full max-w-7xl flex-col gap-2 px-4 py-5 sm:px-6">
{navItems.map((item) => {
Expand All @@ -222,14 +219,14 @@ export default function AppNavbar() {
{isAuthenticated && (
<Link
href="/dashboard/settings"
className="rounded-xl px-4 py-3.5 text-sm font-medium text-[var(--muted-foreground)] hover:bg-white/5 transition-colors"
className="rounded-xl px-4 py-3.5 text-sm font-medium text-[var(--muted-foreground)] hover:bg-[var(--control-hover)] transition-colors"
style={{ fontFamily: MONO }}
>
{t("settings")}
</Link>
)}

<div className="mt-4 border-t border-white/10 pt-4">
<div className="mt-4 border-t border-[var(--border)] pt-4">
{!isDashboardRoute && (
<div className="flex items-center justify-between px-4 py-2">
<span className="text-sm font-medium text-[var(--muted-foreground)]" style={{ fontFamily: MONO }}>
Expand Down
35 changes: 18 additions & 17 deletions src/components/DashboardSidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,49 +54,50 @@ export default function DashboardSidebar() {

return (
<>
<aside className="hidden lg:flex flex-col sticky top-8 ml-5 rounded-xl h-full gap-1 bg-[var(--control)] border-r border-[var(--border)] p-3 min-w-[48px] xl:min-w-[200px]">
<aside className="hidden lg:flex flex-col sticky top-8 ml-2 rounded-2xl gap-1 bg-[var(--card)] border border-[var(--border)] p-3 shadow-xs min-w-[48px] xl:min-w-[210px] backdrop-blur-md">
<div
className="text-lg flex items-center font-bold text-[var(--foreground)] mb-4 px-3">
Devtrack
className="text-sm font-extrabold tracking-wider uppercase text-[var(--accent)] mb-3 px-3 py-1 flex items-center gap-2">
<span className="flex h-5 w-5 items-center justify-center rounded-md bg-[var(--accent)]/10 text-xs font-bold">▲</span>
<span className="hidden xl:inline">DevTrack</span>
</div>
{sections.map(({ id, label, icon: Icon }) => (
<a
key={id}
href={`#${id}`}
title={label}
className={`flex items-center gap-2 px-3 py-2 rounded-lg text-sm transition-all hover:bg-[var(--background)] ${
className={`flex items-center gap-2.5 px-3 py-2 rounded-xl text-sm transition-all duration-150 ${
activeId === id
? "bg-[var(--background)] text-[var(--foreground)] font-semibold"
: "text-[var(--foreground)] opacity-60"
? "bg-[var(--accent-soft)] text-[var(--accent)] font-semibold shadow-xs"
: "text-[var(--muted-foreground)] hover:text-[var(--foreground)] hover:bg-[var(--control-hover)]"
}`}
>
<Icon size={18} />
<Icon size={18} className={activeId === id ? "text-[var(--accent)]" : "text-[var(--muted-foreground)]"} />
<span className="hidden xl:inline">{label}</span>
</a>
))}
</aside>
<button
onClick={() => setMobileOpen(true)}
className="lg:hidden fixed top-4 left-4 z-50 p-2 rounded-lg bg-[var(--control)]/80 backdrop-blur-sm border border-[var(--border)] shadow-md"
className="lg:hidden fixed top-4 left-4 z-50 p-2.5 rounded-xl bg-[var(--card)]/90 backdrop-blur-md border border-[var(--border)] shadow-sm text-[var(--foreground)]"
>
<Menu size={20} />
</button>
{mobileOpen && (
<div
className="lg:hidden fixed inset-0 z-40 bg-black/40 backdrop-blur-sm"
className="lg:hidden fixed inset-0 z-40 bg-slate-900/30 backdrop-blur-xs"
onClick={() => setMobileOpen(false)}
/>
)}
<div
className={`lg:hidden fixed top-0 left-0 h-full z-50 w-64 bg-[var(--control)]/90 backdrop-blur-md border-r border-[var(--border)] p-4 shadow-xl transition-transform duration-300 ${
className={`lg:hidden fixed top-0 left-0 h-full z-50 w-64 bg-[var(--card)] border-r border-[var(--border)] p-5 shadow-2xl transition-transform duration-300 ${
mobileOpen ? "translate-x-0" : "-translate-x-full"
}`}
>
<div className="flex justify-between items-center mb-4">
<span className="text-sm font-semibold text-[var(--foreground)]">
Navigate
<div className="flex justify-between items-center mb-6 border-b border-[var(--border)] pb-3">
<span className="text-xs font-bold uppercase tracking-wider text-[var(--muted-foreground)]">
Navigation
</span>
<button onClick={() => setMobileOpen(false)}>
<button onClick={() => setMobileOpen(false)} className="p-1 rounded-lg hover:bg-[var(--control-hover)] text-[var(--foreground)]">
<X size={20} />
</button>
</div>
Expand All @@ -106,10 +107,10 @@ export default function DashboardSidebar() {
key={id}
href={`#${id}`}
onClick={handleNavClick}
className={`flex items-center gap-3 px-3 py-2 rounded-lg text-sm transition-all hover:bg-[var(--background)] mb-1 ${
className={`flex items-center gap-3 px-3 py-2.5 rounded-xl text-sm transition-all mb-1 ${
activeId === id
? "bg-[var(--background)] text-[var(--foreground)] font-semibold"
: "text-[var(--foreground)] opacity-60"
? "bg-[var(--accent-soft)] text-[var(--accent)] font-semibold"
: "text-[var(--muted-foreground)] hover:text-[var(--foreground)] hover:bg-[var(--control-hover)]"
}`}
>
<Icon size={18} />
Expand Down
10 changes: 5 additions & 5 deletions src/components/EmptyState.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,20 +16,20 @@ export default function EmptyState({
actionHref,
}: EmptyStateProps) {
return (
<div className="flex flex-col items-center justify-center py-20 px-4 text-center">
<div className="text-6xl mb-6 select-none" role="img" aria-label={title}>
<div className="flex flex-col items-center justify-center py-16 px-6 text-center rounded-2xl border border-dashed border-[var(--border)] bg-[var(--card)]/50 backdrop-blur-xs shadow-xs my-4">
<div className="flex h-16 w-16 items-center justify-center rounded-2xl bg-[var(--accent-soft)] text-3xl mb-5 select-none shadow-xs border border-[var(--accent)]/20" role="img" aria-label={title}>
{icon}
</div>
<h2 className="text-xl font-semibold text-[var(--foreground)] mb-3">
<h2 className="text-lg font-bold tracking-tight text-[var(--foreground)] mb-2">
{title}
</h2>
<p className="text-[var(--muted-foreground)] max-w-sm mb-8 leading-relaxed">
<p className="text-sm text-[var(--muted-foreground)] max-w-sm mb-6 leading-relaxed">
{description}
</p>
{actionLabel && actionHref && (
<Link
href={actionHref}
className="inline-flex items-center gap-2 px-5 py-2.5 rounded-lg bg-[var(--foreground)] text-[var(--background)] font-medium text-sm hover:opacity-80 transition-opacity"
className="inline-flex items-center gap-2 px-5 py-2.5 rounded-xl bg-[var(--accent)] text-[var(--accent-foreground)] font-semibold text-sm shadow-xs hover:bg-[var(--accent)]/90 hover:shadow-sm transition-all active:scale-[0.98]"
>
{actionLabel} →
</Link>
Expand Down
9 changes: 5 additions & 4 deletions src/components/Footer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -110,10 +110,11 @@ export default function Footer() {

return (
<footer
className={`mt-auto border-t relative overflow-hidden ${isLanding
? "bg-transparent border-slate-900/40"
: "border-[var(--border)] bg-[var(--background)]"
}`}
className={`mt-auto border-t relative overflow-hidden ${
isLanding
? "bg-transparent border-[var(--border)]"
: "border-[var(--border)] bg-[var(--background)]"
}`}
aria-label="Site footer"
>
{/* Top gradient accent */}
Expand Down
4 changes: 1 addition & 3 deletions src/components/ThemeContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,7 @@ export const ThemeProvider: React.FC<{ children: React.ReactNode }> = ({
return;
}

// Respect prefers-color-scheme on first load
const systemPrefersDark = window.matchMedia("(prefers-color-scheme: dark)").matches;
updateTheme(systemPrefersDark ? "classic-dark" : "modern-light-blue");
updateTheme(DEFAULT_THEME);
} catch (e) {
updateTheme(DEFAULT_THEME);
}
Expand Down
10 changes: 5 additions & 5 deletions src/components/ui/button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@ const buttonVariants = cva(
variants: {
variant: {
default:
"bg-[var(--accent)] text-[var(--accent-foreground)] shadow hover:opacity-90",
"bg-[var(--accent)] text-[var(--accent-foreground)] shadow-xs hover:bg-[var(--accent)]/95 hover:shadow-sm hover:-translate-y-[0.5px] transition-all",
destructive:
"border border-[var(--destructive)]/50 bg-[var(--destructive)]/80 text-white shadow hover:bg-[var(--destructive)]",
"bg-[var(--destructive)] text-white shadow-xs hover:bg-[var(--destructive)]/90 hover:shadow-sm transition-all",
outline:
"border border-[var(--border)] bg-[var(--background)] shadow-sm hover:border-[var(--accent)] hover:text-[var(--accent)] hover:bg-[var(--card-muted)]/50",
"border border-[var(--border)] bg-[var(--card)] text-[var(--foreground)] shadow-xs hover:border-[var(--accent)] hover:text-[var(--accent)] hover:bg-[var(--accent-soft)]/20 transition-all",
secondary:
"bg-[var(--card-muted)] text-[var(--foreground)] border border-[var(--border)] shadow-sm hover:opacity-80",
ghost: "hover:text-[var(--destructive)] transition-colors",
"bg-[var(--control)] text-[var(--foreground)] border border-[var(--border)] shadow-xs hover:bg-[var(--control-hover)] hover:border-[var(--border)] transition-all",
ghost: "hover:bg-[var(--accent-soft)] hover:text-[var(--accent)] transition-colors text-[var(--foreground)]",
link: "text-[var(--accent)] underline-offset-4 hover:underline",
},
size: {
Expand Down
2 changes: 1 addition & 1 deletion src/components/ui/card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ const Card = React.forwardRef<
<div
ref={ref}
className={cn(
"rounded-xl border border-[var(--border)] bg-[var(--card)] text-[var(--card-foreground)] shadow-sm",
"rounded-2xl border border-[var(--border)] bg-[var(--card)] text-[var(--card-foreground)] shadow-[var(--shadow-soft)] transition-all duration-200 hover:shadow-[var(--shadow-medium)]",
className
)}
{...props}
Expand Down
4 changes: 2 additions & 2 deletions src/components/ui/select.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ const Select = React.forwardRef<HTMLSelectElement, SelectProps>(
<select
ref={ref}
className={cn(
"flex h-9 w-full rounded-md border border-[var(--border)] bg-[var(--control)] px-3 py-1 text-sm text-[var(--foreground)] shadow-sm transition-colors",
"focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-[var(--accent)]",
"flex h-10 w-full rounded-xl border border-[var(--border)] bg-[var(--card)] px-3.5 py-2 text-sm text-[var(--foreground)] shadow-xs transition-all duration-150 cursor-pointer",
"focus-visible:outline-none focus-visible:border-[var(--accent)] focus-visible:ring-2 focus-visible:ring-[var(--accent)]/30 hover:border-[var(--accent)]/50",
"disabled:cursor-not-allowed disabled:opacity-50",
className
)}
Expand Down
Loading
Loading