From 07b15039dada8e9ef3fcbbade234fd60881b76eb Mon Sep 17 00:00:00 2001 From: kurise <3067168909@qq.com> Date: Thu, 30 Apr 2026 17:50:54 +0800 Subject: [PATCH 1/3] =?UTF-8?q?feat(theme):=20=E6=96=B0=E5=A2=9E=20ThemeCo?= =?UTF-8?q?ntext=20=E4=B8=BB=E9=A1=8C=E7=8B=80=E6=85=8B=E7=AE=A1=E7=90=86?= =?UTF-8?q?=E8=88=87=20App=20=E5=85=A5=E5=8F=A3=E6=95=B4=E5=90=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.7 --- src/context/ThemeContext.jsx | 35 +++++++++++++++++++++++++++++++++++ src/main.jsx | 5 ++++- 2 files changed, 39 insertions(+), 1 deletion(-) create mode 100644 src/context/ThemeContext.jsx diff --git a/src/context/ThemeContext.jsx b/src/context/ThemeContext.jsx new file mode 100644 index 0000000..bed033e --- /dev/null +++ b/src/context/ThemeContext.jsx @@ -0,0 +1,35 @@ +import { createContext, useContext, useState, useEffect, useCallback } from 'react'; + +const ThemeContext = createContext(null); + +const STORAGE_KEY = 'salespilot-theme'; + +function getInitialTheme() { + if (typeof window === 'undefined') return 'dark'; + return localStorage.getItem(STORAGE_KEY) || 'dark'; +} + +export function ThemeProvider({ children }) { + const [theme, setTheme] = useState(getInitialTheme); + + useEffect(() => { + document.documentElement.dataset.theme = theme; + localStorage.setItem(STORAGE_KEY, theme); + }, [theme]); + + const toggleTheme = useCallback(() => { + setTheme((prev) => (prev === 'dark' ? 'light' : 'dark')); + }, []); + + return ( + + {children} + + ); +} + +export function useTheme() { + const ctx = useContext(ThemeContext); + if (!ctx) throw new Error('useTheme must be used within a ThemeProvider'); + return ctx; +} diff --git a/src/main.jsx b/src/main.jsx index 644bee6..2c4fa9f 100644 --- a/src/main.jsx +++ b/src/main.jsx @@ -1,10 +1,13 @@ import React from 'react'; import ReactDOM from 'react-dom/client'; +import { ThemeProvider } from './context/ThemeContext'; import App from './App'; import './index.css'; ReactDOM.createRoot(document.getElementById('root')).render( - + + + , ); From 40ee8741e0256b4a4e9a1249f71d9122644244a7 Mon Sep 17 00:00:00 2001 From: kurise <3067168909@qq.com> Date: Thu, 30 Apr 2026 17:51:00 +0800 Subject: [PATCH 2/3] =?UTF-8?q?feat(style):=20=E6=96=B0=E5=A2=9E=E6=B7=BA?= =?UTF-8?q?=E8=89=B2=E4=B8=BB=E9=A1=8C=20CSS=20=E8=AE=8A=E6=95=B8=E4=B8=A6?= =?UTF-8?q?=E4=BF=AE=E6=AD=A3=E5=85=83=E4=BB=B6=E6=A8=A3=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.7 --- src/index.css | 50 ++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 48 insertions(+), 2 deletions(-) diff --git a/src/index.css b/src/index.css index 507bc0c..0c8dab9 100644 --- a/src/index.css +++ b/src/index.css @@ -10,6 +10,7 @@ --color-bg-card: #1a2035; --color-bg-card-hover: #1f2847; --color-surface: #252d44; + --color-navbar-bg: rgba(10, 14, 26, 0.8); --color-primary: #6366f1; --color-primary-light: #818cf8; @@ -84,6 +85,34 @@ --navbar-height: 72px; } +/* --- Light Theme --- */ +[data-theme="light"] { + --color-bg: #f8fafc; + --color-bg-elevated: #f1f5f9; + --color-bg-card: #ffffff; + --color-bg-card-hover: #f8fafc; + --color-surface: #e2e8f0; + --color-navbar-bg: rgba(248, 250, 252, 0.85); + + --color-primary-light: #6366f1; + --color-primary-glow: rgba(99, 102, 241, 0.12); + + --color-accent-light: #0891b2; + + --color-text: #0f172a; + --color-text-secondary: #475569; + --color-text-muted: #94a3b8; + --color-text-inverse: #f1f5f9; + + --color-border: rgba(0, 0, 0, 0.08); + --color-border-hover: rgba(0, 0, 0, 0.15); + + --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.06); + --shadow-md: 0 4px 12px rgba(0, 0, 0, 0.08); + --shadow-lg: 0 8px 32px rgba(0, 0, 0, 0.1); + --shadow-glow: 0 0 40px var(--color-primary-glow); +} + /* --- Reset & Base --- */ *, *::before, @@ -256,7 +285,7 @@ button { right: 0; height: var(--navbar-height); z-index: 1000; - background: rgba(10, 14, 26, 0.8); + background: var(--color-navbar-bg); backdrop-filter: blur(16px); border-bottom: 1px solid var(--color-border); } @@ -320,6 +349,23 @@ button { width: 100%; } +.theme-toggle { + display: flex; + align-items: center; + justify-content: center; + width: 36px; + height: 36px; + border-radius: var(--radius-md); + color: var(--color-text-secondary); + transition: all var(--transition-fast); + flex-shrink: 0; +} + +.theme-toggle:hover { + color: var(--color-primary-light); + background: var(--color-primary-glow); +} + .navbar__toggle { display: none; flex-direction: column; @@ -432,7 +478,7 @@ button { font-weight: 800; line-height: 1.15; margin-bottom: var(--space-6); - background: linear-gradient(135deg, #fff 0%, var(--color-primary-light) 50%, var(--color-accent-light) 100%); + background: linear-gradient(135deg, var(--color-text) 0%, var(--color-primary-light) 50%, var(--color-accent-light) 100%); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text; From 133930654167c015aced5d2fde11db217e904a28 Mon Sep 17 00:00:00 2001 From: kurise <3067168909@qq.com> Date: Thu, 30 Apr 2026 17:51:06 +0800 Subject: [PATCH 3/3] =?UTF-8?q?feat(theme):=20=E6=96=BC=20Navbar=20?= =?UTF-8?q?=E6=96=B0=E5=A2=9E=E4=B8=BB=E9=A1=8C=E5=88=87=E6=8F=9B=E6=8C=89?= =?UTF-8?q?=E9=88=95=E4=B8=A6=E5=8A=A0=E5=85=A5=E9=98=B2=20FOUC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.7 --- index.html | 8 ++++++++ src/components/Navbar.jsx | 26 ++++++++++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/index.html b/index.html index 4aa07c4..7032304 100644 --- a/index.html +++ b/index.html @@ -6,6 +6,14 @@ SalesPilot CRM — 你的業務成長引擎 + diff --git a/src/components/Navbar.jsx b/src/components/Navbar.jsx index 5e205a8..785cabc 100644 --- a/src/components/Navbar.jsx +++ b/src/components/Navbar.jsx @@ -1,8 +1,10 @@ import { useState } from 'react'; import { NAV_LINKS, BRAND } from '../data/navigation'; +import { useTheme } from '../context/ThemeContext'; function Navbar() { const [menuOpen, setMenuOpen] = useState(false); + const { theme, toggleTheme } = useTheme(); return (
@@ -39,6 +41,30 @@ function Navbar() { ))} + 預約 Demo