From 2abead43943bdea1228193c6618614bb73ee00ad Mon Sep 17 00:00:00 2001
From: Bayge <71931113+af-afk@users.noreply.github.com>
Date: Tue, 4 Nov 2025 03:55:49 +0900
Subject: [PATCH 1/2] feat(web): add dark mode toggle
---
web/src/app/globals.css | 61 +++++++++
web/src/app/layout.tsx | 5 +-
web/src/components/cardShadow.tsx | 2 +-
web/src/components/header.tsx | 20 ++-
web/src/components/mobileMenu.tsx | 2 +-
web/src/components/navMenu.tsx | 2 +-
web/src/components/pointsMenuButton.tsx | 2 +-
web/src/components/theme/themeScript.tsx | 25 ++++
.../components/theme/themeToggleButton.tsx | 31 +++++
web/src/components/themed/modal.tsx | 2 +-
web/src/providers/index.tsx | 31 +++--
web/src/providers/theme.tsx | 124 ++++++++++++++++++
web/tailwind.config.ts | 3 +
13 files changed, 288 insertions(+), 22 deletions(-)
create mode 100644 web/src/components/theme/themeScript.tsx
create mode 100644 web/src/components/theme/themeToggleButton.tsx
create mode 100644 web/src/providers/theme.tsx
diff --git a/web/src/app/globals.css b/web/src/app/globals.css
index 43183948..91ca97e5 100644
--- a/web/src/app/globals.css
+++ b/web/src/app/globals.css
@@ -2,6 +2,10 @@
@tailwind components;
@tailwind utilities;
+:root {
+ color-scheme: light dark;
+}
+
section {
flex-grow: 1;
}
@@ -10,6 +14,10 @@ section {
background-color: #cccccc; /* Background color of the track */
}
+.dark ::-webkit-scrollbar-track {
+ background-color: #1f1f1f;
+}
+
/* Scrollbar Thumb */
::-webkit-scrollbar-thumb {
background-color: #999999; /* Background color of the thumb */
@@ -17,6 +25,11 @@ section {
border: 2px solid #cccccc;
}
+.dark ::-webkit-scrollbar-thumb {
+ background-color: #4d4d4d;
+ border-color: #1f1f1f;
+}
+
/* Scrollbar Width */
::-webkit-scrollbar {
width: 7px; /* Set scrollbar width */
@@ -48,6 +61,10 @@ input[type="range"]::-webkit-slider-runnable-track {
background-color: #0c0c0c;
height: 9px;
}
+
+.dark input[type="range"]::-webkit-slider-runnable-track {
+ background-color: #f5f5f5;
+}
input[type="range"]:disabled {
opacity: 0.5;
}
@@ -72,6 +89,14 @@ input[type="range"]::-webkit-slider-thumb {
box-shadow: -2px -2px 0 rgba(68, 68, 68, 0.2) inset;
}
+.dark input[type="range"]::-webkit-slider-thumb {
+ border: 1px solid #f5f5f5;
+ background:
+ url("/icons/horizontal-arrows.svg") no-repeat center,
+ #1a1a1a;
+ box-shadow: -2px -2px 0 rgba(0, 0, 0, 0.35) inset;
+}
+
input[type="range"]:focus::-webkit-slider-thumb {
border: 1px solid #0c0c0c;
border-radius: 3px;
@@ -84,6 +109,10 @@ input[type="range"]::-moz-range-track {
height: 9px;
}
+.dark input[type="range"]::-moz-range-track {
+ background-color: #f5f5f5;
+}
+
/* slider thumb */
input[type="range"]::-moz-range-thumb {
border: none; /*Removes extra border that FF applies*/
@@ -100,6 +129,14 @@ input[type="range"]::-moz-range-thumb {
box-shadow: -2px -2px 0 rgba(68, 68, 68, 0.2) inset;
}
+.dark input[type="range"]::-moz-range-thumb {
+ border: 1px solid #f5f5f5;
+ background:
+ url("/icons/horizontal-arrows.svg") no-repeat center,
+ #1a1a1a;
+ box-shadow: -2px -2px 0 rgba(0, 0, 0, 0.35) inset;
+}
+
input[type="range"]:focus::-moz-range-thumb {
border: 1px solid #0c0c0c;
border-radius: 3px;
@@ -128,6 +165,12 @@ div[id^="radix"][role="dialog"][data-state="open"] {
@apply font-chicago;
}
+.dark div[id^="radix"][role="dialog"][data-state="open"] {
+ background-color: #1a1a1a;
+ border-color: #444444;
+ box-shadow: 5px 5px 0 rgba(0, 0, 0, 0.6);
+}
+
div[id^="radix"][role="dialog"][data-state="open"] button {
border-color: #0c0c0c;
border: 1px solid #0c0c0c;
@@ -138,6 +181,13 @@ div[id^="radix"][role="dialog"][data-state="open"] button {
2px 2px 0 rgba(0, 0, 0, 0.25);
}
+.dark div[id^="radix"][role="dialog"][data-state="open"] button {
+ border-color: #f5f5f5;
+ box-shadow:
+ -2px -2px 0 rgba(255, 255, 255, 0.1) inset,
+ 2px 2px 0 rgba(0, 0, 0, 0.4);
+}
+
div[id^="radix"][role="dialog"][data-state="open"] input {
border-color: #0c0c0c;
border: 1px solid #0c0c0c !important;
@@ -148,10 +198,21 @@ div[id^="radix"][role="dialog"][data-state="open"] input {
margin-right: 8px;
}
+.dark div[id^="radix"][role="dialog"][data-state="open"] input {
+ border-color: #f5f5f5 !important;
+ box-shadow:
+ -2px -2px 0 rgba(255, 255, 255, 0.08) inset,
+ 2px 2px 0 rgba(0, 0, 0, 0.4);
+}
+
div[style="pointer-events: auto;"][data-state="open"] {
@apply bg-9blueLight/70;
}
+.dark div[style="pointer-events: auto;"][data-state="open"] {
+ background-color: rgba(0, 0, 0, 0.6);
+}
+
div[id^="radix"][role="dialog"][data-state="open"] div[data-error] {
@apply border-none shadow-none focus:outline-none;
}
diff --git a/web/src/app/layout.tsx b/web/src/app/layout.tsx
index de39933f..1c8522ab 100644
--- a/web/src/app/layout.tsx
+++ b/web/src/app/layout.tsx
@@ -6,6 +6,7 @@ import GoogleAnalytics from "@/components/googleAnalytics";
import appConfig from "@/config";
import CustomToaster from "@/components/customToaster";
import CookieBanner from "@/components/cookieBanner";
+import ThemeScript from "@/components/theme/themeScript";
import Providers from "@/providers";
const chicago = localFont({
@@ -71,8 +72,10 @@ export default async function RootLayout({
arial.className,
arial.variable,
])}
+ suppressHydrationWarning
>
-
+
+
{children}
diff --git a/web/src/components/cardShadow.tsx b/web/src/components/cardShadow.tsx
index e8dc4689..7e2de55d 100644
--- a/web/src/components/cardShadow.tsx
+++ b/web/src/components/cardShadow.tsx
@@ -12,7 +12,7 @@ export default function ShadowCard({
{children}
diff --git a/web/src/components/header.tsx b/web/src/components/header.tsx
index b40d6c6b..1efb45b1 100644
--- a/web/src/components/header.tsx
+++ b/web/src/components/header.tsx
@@ -1,6 +1,9 @@
+"use client";
+
import ConnectButton from "@/components/connectButton";
import NavigationMenu from "@/components/navMenu";
import Link from "next/link";
+import { combineClass } from "@/utils/combineClass";
import HeaderLogo from "./headerLogo";
import CreateCampaingButton from "./createCampaign/createCampaignHeaderButton";
import DisclaimerButton from "./disclaimerButton";
@@ -9,10 +12,16 @@ import ReferralButton from "./referral/referralButton";
import MobileMenu from "./mobileMenu";
import SimpleModeButton from "./simple/simpleModeButton";
import AdvancedModeButton from "./advanced/advancedModeButton";
+import ThemeToggleButton from "./theme/themeToggleButton";
export default function Header({ simple = false }: { simple?: boolean }) {
return (
-
-
+
+
{simple ?
:
}
diff --git a/web/src/components/mobileMenu.tsx b/web/src/components/mobileMenu.tsx
index 53026288..0b4ada5b 100644
--- a/web/src/components/mobileMenu.tsx
+++ b/web/src/components/mobileMenu.tsx
@@ -18,7 +18,7 @@ export default function MobileMenu({ simple = false }: { simple?: boolean }) {
}, [pathname]);
return (
setIsMobileMenuOpen(true)}
>
diff --git a/web/src/components/navMenu.tsx b/web/src/components/navMenu.tsx
index c2a8230e..2945034b 100644
--- a/web/src/components/navMenu.tsx
+++ b/web/src/components/navMenu.tsx
@@ -28,7 +28,7 @@ export default function NavigationMenu({
key={item.page}
href={item.page}
className={combineClass(
- "font-chicago text-neutral-800 hover:underline",
+ "font-chicago text-neutral-800 transition-colors hover:underline dark:text-9gray",
item.disabled && "pointer-events-none opacity-30",
)}
>
diff --git a/web/src/components/pointsMenuButton.tsx b/web/src/components/pointsMenuButton.tsx
index 566993ab..3094001a 100644
--- a/web/src/components/pointsMenuButton.tsx
+++ b/web/src/components/pointsMenuButton.tsx
@@ -7,7 +7,7 @@ export default function PointMenuButton() {
return (