From 90c85103c3f528bc4f328a8745d1d2c0f391b0e0 Mon Sep 17 00:00:00 2001 From: Stephen Zhou Date: Sat, 26 Nov 2022 11:20:22 +0800 Subject: [PATCH] feat: dark mode support for auth page (#569) * feat: dark mode support for auth page * chore: update --- web/src/components/ApperanceDropdownMenu.tsx | 53 ++++++++++++++++++++ web/src/helpers/consts.ts | 3 ++ web/src/hooks/useApperance.ts | 28 +++++++++++ web/src/less/auth.less | 16 +++--- web/src/locales/en.json | 5 ++ web/src/locales/zh.json | 5 ++ web/src/pages/Auth.tsx | 7 ++- web/tailwind.config.js | 2 + 8 files changed, 111 insertions(+), 8 deletions(-) create mode 100644 web/src/components/ApperanceDropdownMenu.tsx create mode 100644 web/src/hooks/useApperance.ts diff --git a/web/src/components/ApperanceDropdownMenu.tsx b/web/src/components/ApperanceDropdownMenu.tsx new file mode 100644 index 00000000..5badb560 --- /dev/null +++ b/web/src/components/ApperanceDropdownMenu.tsx @@ -0,0 +1,53 @@ +import { Option, Select } from "@mui/joy"; +import { useTranslation } from "react-i18next"; + +import Icon from "./Icon"; +import { APPERANCE_OPTIONS } from "../helpers/consts"; +import useApperance, { Apperance } from "../hooks/useApperance"; + +const ApperanceDropdownMenu = () => { + const [apperance, setApperance] = useApperance(); + const { t } = useTranslation(); + + const apperanceOptionItems = [ + [ + APPERANCE_OPTIONS[0], + <> + +

{t("setting.apperance-option.follow-system")}

+ , + ], + [ + APPERANCE_OPTIONS[1], + <> + +

{t("setting.apperance-option.always-light")}

+ , + ], + [ + APPERANCE_OPTIONS[2], + <> + +

{t("setting.apperance-option.always-dark")}

+ , + ], + ] as const; + + return ( + + ); +}; + +export default ApperanceDropdownMenu; diff --git a/web/src/helpers/consts.ts b/web/src/helpers/consts.ts index aa989d4a..3f3f383a 100644 --- a/web/src/helpers/consts.ts +++ b/web/src/helpers/consts.ts @@ -22,3 +22,6 @@ export const IS_FOLDING_ENABLED_DEFAULT_VALUE = true; export const SETTING_IS_FOLDING_ENABLED_KEY = "setting_IS_FOLDING_ENABLED"; export const TAB_SPACE_WIDTH = 2; + +export const APPERANCE_OPTIONS = ["auto", "light", "dark"] as const; +export const APPERANCE_OPTIONS_STORAGE_KEY = "setting_APPERANCE_OPTIONS"; diff --git a/web/src/hooks/useApperance.ts b/web/src/hooks/useApperance.ts new file mode 100644 index 00000000..c020b388 --- /dev/null +++ b/web/src/hooks/useApperance.ts @@ -0,0 +1,28 @@ +import { useEffect } from "react"; +import { useColorScheme } from "@mui/joy/styles"; + +import { APPERANCE_OPTIONS, APPERANCE_OPTIONS_STORAGE_KEY } from "../helpers/consts"; +import useLocalStorage from "./useLocalStorage"; + +export type Apperance = typeof APPERANCE_OPTIONS[number]; + +const useApperance = () => { + const [apperance, setApperance] = useLocalStorage(APPERANCE_OPTIONS_STORAGE_KEY, APPERANCE_OPTIONS[0]); + + const { setMode } = useColorScheme(); + + useEffect(() => { + const root = document.documentElement; + if (apperance === "dark" || (apperance === "auto" && window.matchMedia("(prefers-color-scheme: dark)").matches)) { + root.classList.add("dark"); + setMode("dark"); + } else { + root.classList.remove("dark"); + setMode("light"); + } + }, [apperance]); + + return [apperance, setApperance] as const; +}; + +export default useApperance; diff --git a/web/src/less/auth.less b/web/src/less/auth.less index 899d9706..d3b75c6c 100644 --- a/web/src/less/auth.less +++ b/web/src/less/auth.less @@ -1,5 +1,5 @@ .page-wrapper.auth { - @apply flex flex-row justify-center items-center w-full h-screen bg-white; + @apply flex flex-row justify-center items-center w-full h-screen bg-white dark:bg-zinc-900; > .page-container { @apply w-80 max-w-full h-full py-4 flex flex-col justify-start items-center; @@ -11,15 +11,19 @@ @apply flex flex-col justify-start items-start w-full mb-4; > .title-container { - @apply w-full flex flex-row justify-between items-center; + @apply w-full flex flex-row justify-start items-center; > .logo-img { @apply h-20 w-auto; } + + > .logo-text { + @apply text-4xl tracking-wide text-black dark:text-white; + } } > .slogan-text { - @apply text-sm text-gray-700; + @apply text-sm text-gray-700 dark:text-gray-200; } } @@ -33,7 +37,7 @@ @apply absolute top-3 left-3 px-1 leading-10 flex-shrink-0 text-base cursor-text text-gray-400 bg-transparent transition-all select-none; &.not-null { - @apply text-sm top-0 z-10 leading-4 bg-white rounded; + @apply text-sm top-0 z-10 leading-4 bg-white dark:bg-zinc-800 rounded; } } @@ -41,7 +45,7 @@ @apply py-2; > input { - @apply w-full py-3 px-3 text-base shadow-inner rounded-lg border border-solid border-gray-400 hover:opacity-80; + @apply w-full py-3 px-3 text-base shadow-inner rounded-lg border border-solid border-gray-400 hover:opacity-80 dark:bg-zinc-800 dark:text-white; } } } @@ -58,7 +62,7 @@ @apply flex flex-row justify-center items-center px-1 py-2 text-sm rounded hover:opacity-80; &.signup-btn { - @apply px-3; + @apply px-3 dark:text-white dark:opacity-70; } &.signin-btn { diff --git a/web/src/locales/en.json b/web/src/locales/en.json index fa82ca1c..b78be1b8 100644 --- a/web/src/locales/en.json +++ b/web/src/locales/en.json @@ -160,6 +160,11 @@ "additional-script": "Additional script", "additional-style-placeholder": "Additional CSS codes", "additional-script-placeholder": "Additional JavaScript codes" + }, + "apperance-option": { + "follow-system": "Follow system", + "always-light": "Always light", + "always-dark": "Always dark" } }, "amount-text": { diff --git a/web/src/locales/zh.json b/web/src/locales/zh.json index 5e7cec45..14feab8d 100644 --- a/web/src/locales/zh.json +++ b/web/src/locales/zh.json @@ -160,6 +160,11 @@ "additional-script": "自定义脚本", "additional-style-placeholder": "自定义 CSS 代码", "additional-script-placeholder": "自定义 JavaScript 代码" + }, + "apperance-option": { + "follow-system": "跟随系统", + "always-light": "总是浅色", + "always-dark": "总是深色" } }, "amount-text": { diff --git a/web/src/pages/Auth.tsx b/web/src/pages/Auth.tsx index 49508487..02da8c85 100644 --- a/web/src/pages/Auth.tsx +++ b/web/src/pages/Auth.tsx @@ -9,6 +9,7 @@ import useLoading from "../hooks/useLoading"; import { globalService, userService } from "../services"; import Icon from "../components/Icon"; import toastHelper from "../components/Toast"; +import ApperanceDropdownMenu from "../components/ApperanceDropdownMenu"; import "../less/auth.less"; const validateConfig: ValidatorConfig = { @@ -113,7 +114,8 @@ const Auth = () => {
- + +

memos

{t("slogan")}

@@ -160,7 +162,7 @@ const Auth = () => { {!systemStatus?.host &&

{t("auth.host-tip")}

}
-
+
+
diff --git a/web/tailwind.config.js b/web/tailwind.config.js index 53e41590..222d4e6e 100644 --- a/web/tailwind.config.js +++ b/web/tailwind.config.js @@ -1,6 +1,8 @@ /* eslint-disable no-undef */ +/** @type {import('tailwindcss').Config} */ module.exports = { content: ["./index.html", "./src/**/*.{js,ts,tsx}"], + darkMode: "class", theme: { fontSize: { xs: ".75rem",