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")}
}
-
+
}
@@ -172,6 +174,7 @@ const Auth = () => {
+
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",