From 02ab5fc0bf05dfbffe437c884e2cbd1a85416993 Mon Sep 17 00:00:00 2001 From: Sargam Ghagre Date: Mon, 6 Jul 2026 13:49:32 +0530 Subject: [PATCH 1/4] feat(web): add global dark/light theme toggle --- web/src/app/globals.css | 21 +++++++++++++++++++++ web/src/app/layout.tsx | 9 ++++++--- web/src/components/nav.tsx | 4 ++++ web/src/components/theme-provider.tsx | 17 +++++++++++++++++ web/src/components/theme-toggle.tsx | 24 ++++++++++++++++++++++++ 5 files changed, 72 insertions(+), 3 deletions(-) create mode 100644 web/src/components/theme-provider.tsx create mode 100644 web/src/components/theme-toggle.tsx diff --git a/web/src/app/globals.css b/web/src/app/globals.css index 464b4d1f..7e5ecb43 100644 --- a/web/src/app/globals.css +++ b/web/src/app/globals.css @@ -16,6 +16,27 @@ --alert-soft: #fbe4e4; } +.dark { + --paper: #0f1115; + --paper-deep: #1a1d23; + + --ink: #f5f5f5; + --ink-soft: #d0d0d0; + --ink-faint: #8d8d8d; + + --rule: #f5f5f5; + + --accent: #5b7fff; + --accent-press: #3d63ff; + --accent-soft: #1c2648; + + --ok: #37c871; + --ok-soft: #163222; + + --alert: #ff6b6b; + --alert-soft: #3a1717; +} + @theme inline { --color-paper: var(--paper); --color-paper-deep: var(--paper-deep); diff --git a/web/src/app/layout.tsx b/web/src/app/layout.tsx index cc466260..3cad3c7b 100644 --- a/web/src/app/layout.tsx +++ b/web/src/app/layout.tsx @@ -3,6 +3,7 @@ import localFont from "next/font/local"; import "./globals.css"; import { SmoothScroll } from "@/components/smooth-scroll"; import { ColdStartWarmup } from "@/components/cold-start-warmup"; +import { ThemeProvider } from "@/components/theme-provider"; // Satoshi Variable (Fontshare, ITF) โ€” humanist grotesk for body / UI. const satoshi = localFont({ @@ -44,9 +45,11 @@ export default function RootLayout({ - - - {children} + + + + {children} + ); diff --git a/web/src/components/nav.tsx b/web/src/components/nav.tsx index 9a57f2e8..77f7a095 100644 --- a/web/src/components/nav.tsx +++ b/web/src/components/nav.tsx @@ -1,4 +1,5 @@ import Link from "next/link"; +import { ThemeToggle } from "./theme-toggle"; import { WalletWidget } from "./wallet-widget"; const CHAIN_NAME_LOWER = ( @@ -23,6 +24,7 @@ export function Nav() { Docs + View source โ†— + + diff --git a/web/src/components/theme-provider.tsx b/web/src/components/theme-provider.tsx new file mode 100644 index 00000000..ae90794f --- /dev/null +++ b/web/src/components/theme-provider.tsx @@ -0,0 +1,17 @@ +"use client"; + +import { ThemeProvider as NextThemesProvider } from "next-themes"; +import { ReactNode } from "react"; + +export function ThemeProvider({ children }: { children: ReactNode }) { + return ( + + {children} + + ); +} \ No newline at end of file diff --git a/web/src/components/theme-toggle.tsx b/web/src/components/theme-toggle.tsx new file mode 100644 index 00000000..cd554b64 --- /dev/null +++ b/web/src/components/theme-toggle.tsx @@ -0,0 +1,24 @@ +"use client"; + +import { useTheme } from "next-themes"; +import { useEffect, useState } from "react"; + +export function ThemeToggle() { + const { theme, setTheme } = useTheme(); + const [mounted, setMounted] = useState(false); + + useEffect(() => setMounted(true), []); + + if (!mounted) return null; + + return ( + + ); +} \ No newline at end of file From c65013973a1bb1d54f9968f992720ec8388347fd Mon Sep 17 00:00:00 2001 From: AnkanMisra Date: Mon, 6 Jul 2026 19:35:55 +0530 Subject: [PATCH 2/4] fix: repair theme toggle build Add the missing next-themes dependency and lockfile entry so the Vercel build can resolve the theme provider imports. Fix the theme toggle lint, hydration, and accessibility issues by deferring mount state through requestAnimationFrame, suppressing the expected root hydration warning, and exposing pressed state and labels. Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-authored-by: codex --- web/bun.lock | 3 +++ web/package.json | 1 + web/src/app/layout.tsx | 2 +- web/src/components/theme-provider.tsx | 4 ++-- web/src/components/theme-toggle.tsx | 14 ++++++++++---- 5 files changed, 17 insertions(+), 7 deletions(-) diff --git a/web/bun.lock b/web/bun.lock index aadebcc1..34eb6ef3 100644 --- a/web/bun.lock +++ b/web/bun.lock @@ -12,6 +12,7 @@ "ethers": "^6.16.0", "lenis": "^1.3.0", "next": "16.1.1", + "next-themes": "^0.4.6", "posthog-js": "^1.382.0", "react": "19.2.3", "react-dom": "19.2.3", @@ -916,6 +917,8 @@ "next": ["next@16.1.1", "", { "dependencies": { "@next/env": "16.1.1", "@swc/helpers": "0.5.15", "baseline-browser-mapping": "^2.8.3", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", "styled-jsx": "5.1.6" }, "optionalDependencies": { "@next/swc-darwin-arm64": "16.1.1", "@next/swc-darwin-x64": "16.1.1", "@next/swc-linux-arm64-gnu": "16.1.1", "@next/swc-linux-arm64-musl": "16.1.1", "@next/swc-linux-x64-gnu": "16.1.1", "@next/swc-linux-x64-musl": "16.1.1", "@next/swc-win32-arm64-msvc": "16.1.1", "@next/swc-win32-x64-msvc": "16.1.1", "sharp": "^0.34.4" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", "@playwright/test": "^1.51.1", "babel-plugin-react-compiler": "*", "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "sass": "^1.3.0" }, "optionalPeers": ["@opentelemetry/api", "@playwright/test", "babel-plugin-react-compiler", "sass"], "bin": { "next": "dist/bin/next" } }, "sha512-QI+T7xrxt1pF6SQ/JYFz95ro/mg/1Znk5vBebsWwbpejj1T0A23hO7GYEaVac9QUOT2BIMiuzm0L99ooq7k0/w=="], + "next-themes": ["next-themes@0.4.6", "", { "peerDependencies": { "react": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc", "react-dom": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc" } }, "sha512-pZvgD5L0IEvX5/9GWyHMf3m8BKiVQwsCMHfoFosXtXBMnaS0ZnIJ9ST4b4NqLVKDEm8QBxoNNGNaBv2JNF6XNA=="], + "node-releases": ["node-releases@2.0.27", "", {}, "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA=="], "object-assign": ["object-assign@4.1.1", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="], diff --git a/web/package.json b/web/package.json index cb142942..d9714125 100644 --- a/web/package.json +++ b/web/package.json @@ -22,6 +22,7 @@ "ethers": "^6.16.0", "lenis": "^1.3.0", "next": "16.1.1", + "next-themes": "^0.4.6", "posthog-js": "^1.382.0", "react": "19.2.3", "react-dom": "19.2.3" diff --git a/web/src/app/layout.tsx b/web/src/app/layout.tsx index 3cad3c7b..2e5d9ac2 100644 --- a/web/src/app/layout.tsx +++ b/web/src/app/layout.tsx @@ -41,7 +41,7 @@ export default function RootLayout({ children: React.ReactNode; }>) { return ( - + diff --git a/web/src/components/theme-provider.tsx b/web/src/components/theme-provider.tsx index ae90794f..79142e0d 100644 --- a/web/src/components/theme-provider.tsx +++ b/web/src/components/theme-provider.tsx @@ -1,7 +1,7 @@ "use client"; import { ThemeProvider as NextThemesProvider } from "next-themes"; -import { ReactNode } from "react"; +import type { ReactNode } from "react"; export function ThemeProvider({ children }: { children: ReactNode }) { return ( @@ -14,4 +14,4 @@ export function ThemeProvider({ children }: { children: ReactNode }) { {children} ); -} \ No newline at end of file +} diff --git a/web/src/components/theme-toggle.tsx b/web/src/components/theme-toggle.tsx index cd554b64..56cd7ead 100644 --- a/web/src/components/theme-toggle.tsx +++ b/web/src/components/theme-toggle.tsx @@ -7,18 +7,24 @@ export function ThemeToggle() { const { theme, setTheme } = useTheme(); const [mounted, setMounted] = useState(false); - useEffect(() => setMounted(true), []); + useEffect(() => { + const frame = requestAnimationFrame(() => setMounted(true)); + + return () => cancelAnimationFrame(frame); + }, []); if (!mounted) return null; return ( ); -} \ No newline at end of file +} From fcc304f34edb934a5bc1041d4b1decf290db78b2 Mon Sep 17 00:00:00 2001 From: AnkanMisra Date: Mon, 6 Jul 2026 19:39:32 +0530 Subject: [PATCH 3/4] fix: keep theme toggle responsive Allow the header action row to wrap on narrow screens and keep the theme toggle compact by showing icon-only text below the small breakpoint. Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-authored-by: codex --- web/src/components/nav.tsx | 9 ++++----- web/src/components/theme-toggle.tsx | 7 +++++-- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/web/src/components/nav.tsx b/web/src/components/nav.tsx index 77f7a095..02045210 100644 --- a/web/src/components/nav.tsx +++ b/web/src/components/nav.tsx @@ -9,14 +9,14 @@ const CHAIN_NAME_LOWER = ( export function Nav() { return (
-
+
MicroAI Paygate x402 ยท {CHAIN_NAME_LOWER} -
diff --git a/web/src/components/theme-toggle.tsx b/web/src/components/theme-toggle.tsx index 56cd7ead..f028cefb 100644 --- a/web/src/components/theme-toggle.tsx +++ b/web/src/components/theme-toggle.tsx @@ -22,9 +22,12 @@ export function ThemeToggle() { aria-label={ theme === "light" ? "Switch to dark mode" : "Switch to light mode" } - className="border border-ink bg-paper px-3 py-1.5 font-mono text-[10px] uppercase tracking-[0.12em] text-ink transition-all duration-300 hover:bg-ink hover:text-paper" + className="inline-flex items-center gap-1 border border-ink bg-paper px-2.5 py-1.5 font-mono text-[10px] uppercase tracking-[0.12em] text-ink transition-all duration-300 hover:bg-ink hover:text-paper sm:px-3" > - {theme === "light" ? "๐ŸŒ™ Dark" : "โ˜€ Light"} + + + {theme === "light" ? "Dark" : "Light"} + ); } From 5f04f10dc8acfb7a92d2624e92f3c548f295047b Mon Sep 17 00:00:00 2001 From: AnkanMisra Date: Mon, 6 Jul 2026 19:43:39 +0530 Subject: [PATCH 4/4] fix: use icons for theme toggle Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-authored-by: codex --- web/bun.lock | 3 +++ web/package.json | 1 + web/src/components/theme-toggle.tsx | 16 +++++++++++----- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/web/bun.lock b/web/bun.lock index 34eb6ef3..c16db1d9 100644 --- a/web/bun.lock +++ b/web/bun.lock @@ -11,6 +11,7 @@ "@types/mdx": "^2.0.13", "ethers": "^6.16.0", "lenis": "^1.3.0", + "lucide-react": "^1.23.0", "next": "16.1.1", "next-themes": "^0.4.6", "posthog-js": "^1.382.0", @@ -815,6 +816,8 @@ "lru-cache": ["lru-cache@11.5.1", "", {}, "sha512-RPimw/7aMdv2oqRrxKwvZXcPfwBrn/JZ2xYcY9Hus/6LaS3VOAKVWKWgNLCFSiOm1ESXinjsDlidVU7JlnCN2A=="], + "lucide-react": ["lucide-react@1.23.0", "", { "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-38BpJcD0JhFosxHApP/BYsBetLpQFRoTRzEzstM/XCc3jsAG7wqaY1lgVwxiUe3xqYE+lNxo2PkCmYwXWrwwIw=="], + "lz-string": ["lz-string@1.5.0", "", { "bin": { "lz-string": "bin/bin.js" } }, "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ=="], "magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="], diff --git a/web/package.json b/web/package.json index d9714125..9ffc5231 100644 --- a/web/package.json +++ b/web/package.json @@ -21,6 +21,7 @@ "@types/mdx": "^2.0.13", "ethers": "^6.16.0", "lenis": "^1.3.0", + "lucide-react": "^1.23.0", "next": "16.1.1", "next-themes": "^0.4.6", "posthog-js": "^1.382.0", diff --git a/web/src/components/theme-toggle.tsx b/web/src/components/theme-toggle.tsx index f028cefb..94305e4d 100644 --- a/web/src/components/theme-toggle.tsx +++ b/web/src/components/theme-toggle.tsx @@ -1,11 +1,13 @@ "use client"; +import { Moon, Sun } from "lucide-react"; import { useTheme } from "next-themes"; import { useEffect, useState } from "react"; export function ThemeToggle() { const { theme, setTheme } = useTheme(); const [mounted, setMounted] = useState(false); + const isLightTheme = theme === "light"; useEffect(() => { const frame = requestAnimationFrame(() => setMounted(true)); @@ -17,16 +19,20 @@ export function ThemeToggle() { return ( );