Skip to content

Commit 59fc200

Browse files
committed
feat: add dark/light mode support
1 parent 03c4e38 commit 59fc200

16 files changed

Lines changed: 204 additions & 49 deletions

app/globals.css

Lines changed: 63 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,53 @@
44

55
:root {
66
color-scheme: light;
7+
--background: 210 40% 98%;
8+
--foreground: 222 47% 11%;
9+
--card: 0 0% 100%;
10+
--card-foreground: 222 47% 11%;
11+
--muted: 210 20% 96%;
12+
--muted-foreground: 215 16% 47%;
13+
--border: 214 32% 91%;
14+
--input: 214 32% 91%;
15+
--ring: 217 91% 60%;
16+
--primary: 217 91% 60%;
17+
--primary-foreground: 210 40% 98%;
18+
--secondary: 191 91% 42%;
19+
--secondary-foreground: 210 40% 98%;
20+
--accent: 199 89% 48%;
21+
--accent-foreground: 210 40% 98%;
22+
--destructive: 0 84% 60%;
23+
--destructive-foreground: 210 40% 98%;
24+
}
25+
26+
.dark {
27+
color-scheme: dark;
28+
--background: 222 47% 8%;
29+
--foreground: 210 40% 96%;
30+
--card: 222 47% 11%;
31+
--card-foreground: 210 40% 96%;
32+
--muted: 217 33% 17%;
33+
--muted-foreground: 215 20% 65%;
34+
--border: 217 33% 20%;
35+
--input: 217 33% 20%;
36+
--ring: 217 91% 60%;
37+
--primary: 217 91% 60%;
38+
--primary-foreground: 210 40% 98%;
39+
--secondary: 191 91% 42%;
40+
--secondary-foreground: 210 40% 98%;
41+
--accent: 199 89% 48%;
42+
--accent-foreground: 210 40% 98%;
43+
--destructive: 0 62% 40%;
44+
--destructive-foreground: 210 40% 98%;
745
}
846

947
* {
1048
@apply box-border;
49+
border-color: hsl(var(--border));
1150
}
1251

1352
body {
14-
@apply bg-slate-50 text-slate-900 antialiased;
53+
@apply bg-background text-foreground antialiased;
1554
font-family: "Inter", system-ui, -apple-system, "Segoe UI", sans-serif;
1655
background-image:
1756
radial-gradient(circle at 20% 20%, rgba(59, 130, 246, 0.08), transparent 35%),
@@ -21,22 +60,39 @@ body {
2160
}
2261

2362
.dark body {
24-
@apply bg-slate-950 text-slate-100;
63+
@apply bg-background text-foreground;
2564
background-image:
2665
radial-gradient(circle at 20% 20%, rgba(59, 130, 246, 0.08), transparent 35%),
2766
radial-gradient(circle at 80% 0%, rgba(124, 58, 237, 0.12), transparent 30%),
2867
linear-gradient(180deg, #0f172a 0%, #0b1221 40%, #0a0f1c 100%);
2968
}
3069

3170
.card {
32-
@apply bg-white/90 shadow-card rounded-2xl border border-slate-100 backdrop-blur;
71+
@apply bg-card/90 text-card-foreground shadow-card rounded-2xl border border-border backdrop-blur;
3372
transition: transform 180ms ease, box-shadow 180ms ease;
34-
box-shadow: 0 18px 48px rgba(15, 23, 42, 0.12);
35-
73+
box-shadow: 0 18px 48px rgba(15, 23, 42, 0.12);
3674
}
3775

3876

3977
.dark .card {
40-
@apply bg-slate-900/80 border-slate-800;
78+
@apply bg-card/80 border-border;
4179
box-shadow: 0 18px 48px rgba(0, 0, 0, 0.45);
42-
}
80+
}
81+
82+
html.theme-transition,
83+
html.theme-transition *,
84+
html.theme-transition *::before,
85+
html.theme-transition *::after {
86+
transition-property: background-color, border-color, color, fill, stroke, box-shadow;
87+
transition-duration: 460ms;
88+
transition-timing-function: cubic-bezier(0.32, 0, 0.2, 1);
89+
}
90+
91+
@media (prefers-reduced-motion: reduce) {
92+
html.theme-transition,
93+
html.theme-transition *,
94+
html.theme-transition *::before,
95+
html.theme-transition *::after {
96+
transition: none !important;
97+
}
98+
}

app/layout.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ export const metadata = {
99

1010
export default function RootLayout({ children }: { children: ReactNode }) {
1111
return (
12-
<html>
12+
<html lang="en" suppressHydrationWarning>
1313
<body>
1414
<Providers>{children}</Providers>
1515
</body>

app/page.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { ResultDashboard } from "../components/result-dashboard";
66
import { DashboardSkeleton } from "../components/skeletons";
77
import { UserResult } from "@/types/user-result";
88
import { LanguageSwitcher } from "@/components/language-switcher";
9+
import { ThemeToggle } from "@/components/theme-toggle";
910

1011
type ApiResponse = {
1112
success: boolean;
@@ -68,7 +69,7 @@ export default function HomePage() {
6869
return (
6970
<main className="min-h-screen flex flex-col">
7071
{" "}
71-
<header className="sticky top-0 z-50 w-full border-b bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60">
72+
<header className="sticky top-0 z-50 w-full border-b border-border bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60">
7273
<div className="container flex h-16 max-w-7xl items-center justify-between m-auto px-4">
7374
<div className="flex items-center gap-2 font-bold text-xl">
7475
<span className="bg-gradient-to-r from-primary to-primary/60 bg-clip-text text-transparent">
@@ -78,6 +79,7 @@ export default function HomePage() {
7879

7980
<div className="flex gap-4">
8081
<LanguageSwitcher />
82+
<ThemeToggle />
8183
</div>
8284
</div>
8385
</header>
@@ -122,7 +124,7 @@ export default function HomePage() {
122124
</div>
123125
)}
124126
</div>
125-
<footer className="border-t py-6 text-center text-sm text-muted-foreground">
127+
<footer className="border-t border-border py-6 text-center text-sm text-muted-foreground">
126128
<div className="container max-w-7xl mx-auto px-4">
127129
<span className="font-medium">DevImpact</span> — Compare GitHub developer metrics
128130
</div>

app/providers.tsx

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,14 @@
22

33
import { LanguageProvider } from "@/components/language-provider";
44
import { TooltipProvider } from "@/components/ui/tooltip";
5+
import { ThemeProvider } from "@/components/theme-provider";
56

67
export default function Providers({ children }: { children: React.ReactNode }) {
78
return (
8-
<LanguageProvider>
9-
<TooltipProvider>{children}</TooltipProvider>{" "}
10-
</LanguageProvider>
9+
<ThemeProvider>
10+
<LanguageProvider>
11+
<TooltipProvider>{children}</TooltipProvider>
12+
</LanguageProvider>
13+
</ThemeProvider>
1114
);
1215
}

components/compare-form.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,14 +68,14 @@ export function CompareForm({
6868
<CardContent>
6969
<div className="grid gap-3 md:grid-cols-2">
7070
<input
71-
className="h-11 rounded-lg border border-slate-200 px-3 text-sm focus:outline-none focus:ring-2 focus:ring-primary/60 focus:border-transparent bg-white"
71+
className="h-11 rounded-lg border border-input bg-background px-3 text-sm text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-primary/60 focus:border-transparent"
7272
ref={firstInputRef}
7373
placeholder={t("form.username1") }
7474
value={username1}
7575
onChange={(e) => setUsername1(e.target.value)}
7676
/>
7777
<input
78-
className="h-11 rounded-lg border border-slate-200 px-3 text-sm focus:outline-none focus:ring-2 focus:ring-primary/60 focus:border-transparent bg-white"
78+
className="h-11 rounded-lg border border-input bg-background px-3 text-sm text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-primary/60 focus:border-transparent"
7979
placeholder={t("form.username2") }
8080
value={username2}
8181
onChange={(e) => setUsername2(e.target.value)}

components/comparison-table.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ const {t} = useTranslation();
3232
</CardTitle>
3333
</CardHeader>
3434
<CardContent className="pt-6 space-y-4">
35-
<div className="flex justify-between items-center border-b pb-2">
35+
<div className="flex justify-between items-center border-b border-border pb-2">
3636
<span className="text-muted-foreground">{t("comparsion.final.score")}</span>
3737
<span className="text-2xl font-bold">{user.finalScore}</span>
3838
</div>

components/language-switcher.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ export function LanguageSwitcher() {
88
return (
99
<div className={cn("flex items-center gap-2 text-sm", dir === "rtl" && "flex-row-reverse")}>
1010
<select
11-
className="h-9 rounded-lg border border-slate-200 bg-white px-3 text-sm focus:outline-none focus:ring-2 focus:ring-primary/60"
11+
className="h-9 rounded-lg border border-input bg-background px-3 text-sm text-foreground focus:outline-none focus:ring-2 focus:ring-primary/60"
1212
value={locale}
1313
onChange={(e) => setLocale(e.target.value as any)}
1414
>

components/score-card.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,17 @@ export function ScoreCard({ title, value, highlight, subtitle }: ScoreCardProps)
1111
return (
1212
<div
1313
className={cn(
14-
"card p-4 flex flex-col gap-1 border bg-gradient-to-br from-white via-white to-slate-50 transition-all",
15-
highlight ? "border-blue-500/50 shadow-blue-200" : "border-slate-100"
14+
"card p-4 flex flex-col gap-1 border bg-gradient-to-br from-card via-card to-muted/40 transition-all",
15+
highlight ? "border-primary/50 shadow-blue-200 dark:shadow-blue-950/40" : "border-border"
1616
)}
1717
>
18-
<p className="text-xs uppercase tracking-wide text-slate-500">{title}</p>
18+
<p className="text-xs uppercase tracking-wide text-muted-foreground">{title}</p>
1919
<div className="flex items-baseline gap-2">
20-
<span className="text-2xl font-semibold text-slate-900">
20+
<span className="text-2xl font-semibold text-foreground">
2121
{value.toFixed(2)}
2222
</span>
2323
{subtitle && (
24-
<span className="text-xs text-slate-500 leading-tight">{subtitle}</span>
24+
<span className="text-xs text-muted-foreground leading-tight">{subtitle}</span>
2525
)}
2626
</div>
2727
</div>

components/skeletons.tsx

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
1+
import { Skeleton } from "@/components/ui/skeleton";
2+
13
export function DashboardSkeleton() {
24
return (
35
<div className="card p-6 animate-pulse space-y-4">
4-
<div className="h-5 bg-slate-200 rounded w-1/3" />
5-
<div className="h-4 bg-slate-200 rounded w-1/2" />
6+
<Skeleton className="h-5 w-1/3" />
7+
<Skeleton className="h-4 w-1/2" />
68
<div className="grid gap-3 md:grid-cols-2">
7-
<div className="h-24 bg-slate-200 rounded-lg" />
8-
<div className="h-24 bg-slate-200 rounded-lg" />
9+
<Skeleton className="h-24 rounded-lg" />
10+
<Skeleton className="h-24 rounded-lg" />
911
</div>
10-
<div className="h-64 bg-slate-200 rounded-xl" />
12+
<Skeleton className="h-64 rounded-xl" />
1113
</div>
1214
);
1315
}

components/theme-provider.tsx

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
"use client";
2+
3+
import { ThemeProvider as NextThemesProvider } from "next-themes";
4+
import type { ReactNode } from "react";
5+
6+
export function ThemeProvider({ children }: { children: ReactNode }) {
7+
return (
8+
<NextThemesProvider
9+
attribute="class"
10+
defaultTheme="system"
11+
enableSystem
12+
storageKey="devimpact-theme"
13+
>
14+
{children}
15+
</NextThemesProvider>
16+
);
17+
}

0 commit comments

Comments
 (0)