From e8c4d7f545db6b2fab74e38171462fcf86e92bac Mon Sep 17 00:00:00 2001 From: Yukko <17264509+yukkodesu@users.noreply.github.com> Date: Wed, 4 Jun 2025 22:19:51 +0800 Subject: [PATCH] Add theme_switcher and some theme (#2) * Add theme_switcher and some theme * Unify theme with both light & dark version * Use anchor instead of window.open --- frontend/src/components/navigator.tsx | 17 +- frontend/src/components/theme_switcher.tsx | 93 +++++++++ frontend/src/i18n.ts | 18 ++ frontend/src/index.css | 224 ++++++++++++++++++--- 4 files changed, 320 insertions(+), 32 deletions(-) create mode 100644 frontend/src/components/theme_switcher.tsx diff --git a/frontend/src/components/navigator.tsx b/frontend/src/components/navigator.tsx index 9ffa117..5847649 100644 --- a/frontend/src/components/navigator.tsx +++ b/frontend/src/components/navigator.tsx @@ -10,6 +10,7 @@ import { } from "react-icons/md"; import { useTranslation } from "react-i18next"; import UploadingSideBar from "./uploading_side_bar.tsx"; +import { ThemeSwitcher } from "./theme_switcher.tsx"; import { IoLogoGithub } from "react-icons/io"; import { useAppContext } from "./AppContext.tsx"; @@ -140,6 +141,7 @@ export default function Navigator() {
+ {app.isLoggedIn() && ( )} - + + {app.isLoggedIn() ? ( ) : ( diff --git a/frontend/src/components/theme_switcher.tsx b/frontend/src/components/theme_switcher.tsx new file mode 100644 index 0000000..657e218 --- /dev/null +++ b/frontend/src/components/theme_switcher.tsx @@ -0,0 +1,93 @@ +import { useEffect, useState } from "react"; +import { useTranslation } from "react-i18next"; +import { MdPalette } from "react-icons/md"; + +interface ThemeOption { + name: string; + displayName: string; + displayColor: string; +} + +const themeOptions: ThemeOption[] = [ + { + name: "pink", + displayName: "Nyne Pink", + displayColor: "oklch(65% 0.241 354.308)", + }, + { + name: "ocean", + displayName: "Ocean Breeze", + displayColor: "oklch(70% 0.18 220)", + }, + { + name: "mint", + displayName: "Mint Leaf", + displayColor: "oklch(65% 0.16 160)", + }, + { + name: "glow", + displayName: "Golden Glow", + displayColor: "oklch(70% 0.16 70)", + }, +]; + +export function ThemeSwitcher() { + const [currentTheme, setCurrentTheme] = useState("pink"); + + const { t } = useTranslation(); + + useEffect(() => { + const savedTheme = localStorage.getItem("theme") || "pink"; + setCurrentTheme(savedTheme); + applyTheme(savedTheme); + }, []); + + const applyTheme = (themeName: string) => { + const root = document.documentElement; + root.setAttribute("data-theme", themeName); + }; + + const handleThemeChange = (themeName: string) => { + setCurrentTheme(themeName); + applyTheme(themeName); + localStorage.setItem("theme", themeName); + const activeElement = document.activeElement as HTMLElement; + if (activeElement) { + activeElement.blur(); + } + }; + + return ( + + ); +} diff --git a/frontend/src/i18n.ts b/frontend/src/i18n.ts index d29119e..99be8c5 100644 --- a/frontend/src/i18n.ts +++ b/frontend/src/i18n.ts @@ -205,6 +205,12 @@ export const i18nData = { "Show more": "Show more", "Show less": "Show less", "You need to log in to comment": "You need to log in to comment", + + // Color Scheme Translation + "Nyne Pink": "Nyne Pink", + "Ocean Breeze": "Ocean Breeze", + "Mint Leaf": "Mint Leaf", + "Golden Glow": "Golden Glow", }, }, "zh-CN": { @@ -402,6 +408,12 @@ export const i18nData = { "Show more": "显示更多", "Show less": "显示更少", "You need to log in to comment": "您需要登录才能评论", + + // Color Scheme Translation + "Nyne Pink": "Nyne 粉", + "Ocean Breeze": "海蓝", + "Mint Leaf": "薄荷", + "Golden Glow": "微光", }, }, "zh-TW": { @@ -599,6 +611,12 @@ export const i18nData = { "Show more": "顯示更多", "Show less": "顯示更少", "You need to log in to comment": "您需要登入才能評論", + + // Color Scheme Translation + "Nyne Pink": "Nyne 粉", + "Ocean Breeze": "海藍", + "Mint Leaf": "薄荷", + "Golden Glow": "微光", }, }, }; diff --git a/frontend/src/index.css b/frontend/src/index.css index 2a77f8b..e910bd3 100644 --- a/frontend/src/index.css +++ b/frontend/src/index.css @@ -1,11 +1,12 @@ @import "tailwindcss"; @plugin "daisyui"; +/* Pink Theme */ @plugin "daisyui/theme" { - name: "light"; + name: "pink"; default: true; prefersdark: false; - color-scheme: "light"; + color-scheme: "light dark"; --color-base-100: oklch(99% 0.014 343.198); --color-base-200: oklch(97% 0.028 342.258); --color-base-300: oklch(84% 0.061 343.231); @@ -36,31 +37,57 @@ --noise: 0; } +@media (prefers-color-scheme: dark) { + [data-theme="pink"] { + --color-base-100: oklch(28% 0.022 277.508); + --color-base-200: oklch(24% 0.02 277.508); + --color-base-300: oklch(40% 0.153 2.432); + --color-base-content: oklch(97.747% 0.007 106.545); + --color-primary: oklch(75.461% 0.183 346.812); + --color-primary-content: oklch(15.092% 0.036 346.812); + --color-secondary: oklch(74.202% 0.148 301.883); + --color-secondary-content: oklch(14.84% 0.029 301.883); + --color-accent: oklch(83.392% 0.124 66.558); + --color-accent-content: oklch(16.678% 0.024 66.558); + --color-neutral: oklch(39.445% 0.032 275.524); + --color-neutral-content: oklch(87.889% 0.006 275.524); + --color-info: oklch(88.263% 0.093 212.846); + --color-info-content: oklch(17.652% 0.018 212.846); + --color-success: oklch(87.099% 0.219 148.024); + --color-success-content: oklch(17.419% 0.043 148.024); + --color-warning: oklch(95.533% 0.134 112.757); + --color-warning-content: oklch(19.106% 0.026 112.757); + --color-error: oklch(68.22% 0.206 24.43); + --color-error-content: oklch(13.644% 0.041 24.43); + } +} + +/* Ocean Theme */ @plugin "daisyui/theme" { - name: "dark"; + name: "ocean"; default: false; - prefersdark: true; - color-scheme: "dark"; - --color-base-100: oklch(28% 0.022 277.508); - --color-base-200: oklch(24% 0.02 277.508); - --color-base-300: oklch(40% 0.153 2.432); - --color-base-content: oklch(97.747% 0.007 106.545); - --color-primary: oklch(75.461% 0.183 346.812); - --color-primary-content: oklch(15.092% 0.036 346.812); - --color-secondary: oklch(74.202% 0.148 301.883); - --color-secondary-content: oklch(14.84% 0.029 301.883); - --color-accent: oklch(83.392% 0.124 66.558); - --color-accent-content: oklch(16.678% 0.024 66.558); - --color-neutral: oklch(39.445% 0.032 275.524); - --color-neutral-content: oklch(87.889% 0.006 275.524); - --color-info: oklch(88.263% 0.093 212.846); - --color-info-content: oklch(17.652% 0.018 212.846); - --color-success: oklch(87.099% 0.219 148.024); - --color-success-content: oklch(17.419% 0.043 148.024); - --color-warning: oklch(95.533% 0.134 112.757); - --color-warning-content: oklch(19.106% 0.026 112.757); - --color-error: oklch(68.22% 0.206 24.43); - --color-error-content: oklch(13.644% 0.041 24.43); + prefersdark: false; + color-scheme: "light dark"; + --color-base-100: oklch(98% 0.005 240); + --color-base-200: oklch(96% 0.01 240); + --color-base-300: oklch(92% 0.015 240); + --color-base-content: oklch(20% 0.01 240); + --color-primary: oklch(50% 0.2 240); + --color-primary-content: oklch(98% 0.005 240); + --color-secondary: oklch(60% 0.15 210); + --color-secondary-content: oklch(98% 0.005 210); + --color-accent: oklch(70% 0.18 220); + --color-accent-content: oklch(98% 0.005 220); + --color-neutral: oklch(25% 0.02 240); + --color-neutral-content: oklch(98% 0.005 240); + --color-info: oklch(65% 0.15 231); + --color-info-content: oklch(98% 0.005 231); + --color-success: oklch(60% 0.13 145); + --color-success-content: oklch(98% 0.005 145); + --color-warning: oklch(80% 0.15 90); + --color-warning-content: oklch(20% 0.05 90); + --color-error: oklch(60% 0.19 27); + --color-error-content: oklch(98% 0.005 27); --radius-selector: 1rem; --radius-field: 1.5rem; --radius-box: 1rem; @@ -71,6 +98,153 @@ --noise: 0; } +@media (prefers-color-scheme: dark) { + [data-theme="ocean"] { + --color-base-100: oklch(15% 0.02 240); + --color-base-200: oklch(12% 0.015 240); + --color-base-300: oklch(25% 0.03 240); + --color-base-content: oklch(90% 0.01 240); + --color-primary: oklch(70% 0.18 240); + --color-primary-content: oklch(15% 0.02 240); + --color-secondary: oklch(75% 0.15 210); + --color-secondary-content: oklch(15% 0.015 210); + --color-accent: oklch(80% 0.16 220); + --color-accent-content: oklch(15% 0.02 220); + --color-neutral: oklch(20% 0.025 240); + --color-neutral-content: oklch(85% 0.01 240); + --color-info: oklch(75% 0.14 231); + --color-info-content: oklch(15% 0.015 231); + --color-success: oklch(70% 0.12 145); + --color-success-content: oklch(15% 0.015 145); + --color-warning: oklch(85% 0.14 90); + --color-warning-content: oklch(15% 0.02 90); + --color-error: oklch(70% 0.18 27); + --color-error-content: oklch(15% 0.015 27); + } +} + +/* Mint Theme */ +@plugin "daisyui/theme" { + name: "mint"; + default: false; + prefersdark: false; + color-scheme: "light dark"; + --color-base-100: oklch(98% 0.005 140); + --color-base-200: oklch(96% 0.01 140); + --color-base-300: oklch(92% 0.015 140); + --color-base-content: oklch(20% 0.01 140); + --color-primary: oklch(45% 0.18 140); + --color-primary-content: oklch(98% 0.005 140); + --color-secondary: oklch(55% 0.15 120); + --color-secondary-content: oklch(98% 0.005 120); + --color-accent: oklch(65% 0.16 160); + --color-accent-content: oklch(98% 0.005 160); + --color-neutral: oklch(25% 0.02 140); + --color-neutral-content: oklch(98% 0.005 140); + --color-info: oklch(70% 0.131 231); + --color-info-content: oklch(98% 0.005 231); + --color-success: oklch(55% 0.15 145); + --color-success-content: oklch(98% 0.005 145); + --color-warning: oklch(80% 0.15 90); + --color-warning-content: oklch(20% 0.05 90); + --color-error: oklch(60% 0.19 27); + --color-error-content: oklch(98% 0.005 27); + --radius-selector: 1rem; + --radius-field: 1.5rem; + --radius-box: 1rem; + --size-selector: 0.25rem; + --size-field: 0.25rem; + --border: 1px; + --depth: 1; + --noise: 0; +} + +@media (prefers-color-scheme: dark) { + [data-theme="mint"] { + --color-base-100: oklch(15% 0.02 140); + --color-base-200: oklch(12% 0.015 140); + --color-base-300: oklch(25% 0.03 140); + --color-base-content: oklch(90% 0.01 140); + --color-primary: oklch(65% 0.16 140); + --color-primary-content: oklch(15% 0.02 140); + --color-secondary: oklch(70% 0.14 120); + --color-secondary-content: oklch(15% 0.015 120); + --color-accent: oklch(75% 0.15 160); + --color-accent-content: oklch(15% 0.02 160); + --color-neutral: oklch(20% 0.025 140); + --color-neutral-content: oklch(85% 0.01 140); + --color-info: oklch(75% 0.12 231); + --color-info-content: oklch(15% 0.015 231); + --color-success: oklch(70% 0.14 145); + --color-success-content: oklch(15% 0.015 145); + --color-warning: oklch(85% 0.14 90); + --color-warning-content: oklch(15% 0.02 90); + --color-error: oklch(70% 0.18 27); + --color-error-content: oklch(15% 0.015 27); + } +} + +/* Glow Theme */ +@plugin "daisyui/theme" { + name: "glow"; + default: false; + prefersdark: false; + color-scheme: "light dark"; + --color-base-100: oklch(98% 0.005 50); + --color-base-200: oklch(96% 0.01 50); + --color-base-300: oklch(92% 0.015 50); + --color-base-content: oklch(20% 0.01 50); + --color-primary: oklch(60% 0.18 50); + --color-primary-content: oklch(98% 0.005 50); + --color-secondary: oklch(65% 0.15 30); + --color-secondary-content: oklch(98% 0.005 30); + --color-accent: oklch(70% 0.16 70); + --color-accent-content: oklch(98% 0.005 70); + --color-neutral: oklch(25% 0.02 50); + --color-neutral-content: oklch(98% 0.005 50); + --color-info: oklch(70% 0.131 231); + --color-info-content: oklch(98% 0.005 231); + --color-success: oklch(60% 0.13 145); + --color-success-content: oklch(98% 0.005 145); + --color-warning: oklch(75% 0.16 60); + --color-warning-content: oklch(20% 0.05 60); + --color-error: oklch(60% 0.19 27); + --color-error-content: oklch(98% 0.005 27); + --radius-selector: 1rem; + --radius-field: 1.5rem; + --radius-box: 1rem; + --size-selector: 0.25rem; + --size-field: 0.25rem; + --border: 1px; + --depth: 1; + --noise: 0; +} + +@media (prefers-color-scheme: dark) { + [data-theme="glow"] { + --color-base-100: oklch(15% 0.02 50); + --color-base-200: oklch(12% 0.015 50); + --color-base-300: oklch(25% 0.03 50); + --color-base-content: oklch(90% 0.01 50); + --color-primary: oklch(75% 0.16 50); + --color-primary-content: oklch(15% 0.02 50); + --color-secondary: oklch(80% 0.14 30); + --color-secondary-content: oklch(15% 0.015 30); + --color-accent: oklch(85% 0.15 70); + --color-accent-content: oklch(15% 0.02 70); + --color-neutral: oklch(20% 0.025 50); + --color-neutral-content: oklch(85% 0.01 50); + --color-info: oklch(75% 0.12 231); + --color-info-content: oklch(15% 0.015 231); + --color-success: oklch(70% 0.12 145); + --color-success-content: oklch(15% 0.015 145); + --color-warning: oklch(85% 0.15 60); + --color-warning-content: oklch(15% 0.02 60); + --color-error: oklch(70% 0.18 27); + --color-error-content: oklch(15% 0.015 27); + } +} + html { width: 100%; height: 100%;