diff --git a/src/components/layout/use-custom-theme.ts b/src/components/layout/use-custom-theme.ts
index fc72a5e94cd326acebf9b4b748b759f074ced1d2..de936a949c962c2722bfd43c7a436ae34384b4f1 100644
--- a/src/components/layout/use-custom-theme.ts
+++ b/src/components/layout/use-custom-theme.ts
@@ -1,49 +1,66 @@
-import useSWR from "swr";
-import { useMemo } from "react";
-import { createTheme } from "@mui/material";
-import { getVergeConfig } from "../../services/cmds";
-
-/**
- * wip: custome theme
- */
-export default function useCustomTheme() {
-  const { data } = useSWR("getVergeConfig", getVergeConfig);
-  const mode = data?.theme_mode ?? "light";
-
-  const theme = useMemo(() => {
-    // const background = mode === "light" ? "#f5f5f5" : "#000";
-    const selectColor = mode === "light" ? "#f5f5f5" : "#d5d5d5";
-
-    const rootEle = document.documentElement;
-    rootEle.style.background = "transparent";
-    rootEle.style.setProperty("--selection-color", selectColor);
-
-    const theme = createTheme({
-      breakpoints: {
-        values: { xs: 0, sm: 650, md: 900, lg: 1200, xl: 1536 },
-      },
-      palette: {
-        mode,
-        primary: { main: "#5b5c9d" },
-        text: { primary: "#637381", secondary: "#909399" },
-      },
-    });
-
-    const { palette } = theme;
-
-    setTimeout(() => {
-      const dom = document.querySelector("#Gradient2");
-      if (dom) {
-        dom.innerHTML = `
-        <stop offset="0%" stop-color="${palette.primary.main}" />
-        <stop offset="80%" stop-color="${palette.primary.dark}" />
-        <stop offset="100%" stop-color="${palette.primary.dark}" />
-        `;
-      }
-    }, 0);
-
-    return theme;
-  }, [mode]);
-
-  return { theme };
-}
+import useSWR from "swr";
+import { useMemo } from "react";
+import { createTheme } from "@mui/material";
+import { getVergeConfig } from "../../services/cmds";
+import { defaultTheme as dt } from "../../pages/_theme";
+
+/**
+ * custome theme
+ */
+export default function useCustomTheme() {
+  const { data } = useSWR("getVergeConfig", getVergeConfig);
+  const { theme_mode, theme_setting } = data ?? {};
+
+  const theme = useMemo(() => {
+    const mode = theme_mode ?? "light";
+    // const background = mode === "light" ? "#f5f5f5" : "#000";
+    const selectColor = mode === "light" ? "#f5f5f5" : "#d5d5d5";
+
+    const rootEle = document.documentElement;
+    rootEle.style.background = "transparent";
+    rootEle.style.setProperty("--selection-color", selectColor);
+
+    const setting = theme_setting || {};
+
+    const theme = createTheme({
+      breakpoints: {
+        values: { xs: 0, sm: 650, md: 900, lg: 1200, xl: 1536 },
+      },
+      palette: {
+        mode,
+        primary: { main: setting.primary_color || dt.primary_color },
+        secondary: { main: setting.secondary_color || dt.secondary_color },
+        info: { main: setting.info_color || dt.info_color },
+        error: { main: setting.error_color || dt.error_color },
+        warning: { main: setting.warning_color || dt.warning_color },
+        success: { main: setting.success_color || dt.success_color },
+        text: {
+          primary: setting.primary_text || dt.primary_text,
+          secondary: setting.secondary_text || dt.secondary_text,
+        },
+      },
+      typography: {
+        fontFamily: setting.font_family
+          ? `"${setting.font_family}", ${dt.font_family}`
+          : dt.font_family,
+      },
+    });
+
+    const { palette } = theme;
+
+    setTimeout(() => {
+      const dom = document.querySelector("#Gradient2");
+      if (dom) {
+        dom.innerHTML = `
+        <stop offset="0%" stop-color="${palette.primary.main}" />
+        <stop offset="80%" stop-color="${palette.primary.dark}" />
+        <stop offset="100%" stop-color="${palette.primary.dark}" />
+        `;
+      }
+    }, 0);
+
+    return theme;
+  }, [theme_mode, theme_setting]);
+
+  return { theme };
+}
diff --git a/src/components/setting/setting-theme.tsx b/src/components/setting/setting-theme.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..106896e2e5946355fea8a14ce951975549b20d76
--- /dev/null
+++ b/src/components/setting/setting-theme.tsx
@@ -0,0 +1,210 @@
+import useSWR from "swr";
+import { useEffect, useState } from "react";
+import { useLockFn } from "ahooks";
+import { useTranslation } from "react-i18next";
+import {
+  Button,
+  Dialog,
+  DialogActions,
+  DialogContent,
+  DialogTitle,
+  List,
+  ListItem,
+  ListItemText,
+  styled,
+  TextField,
+} from "@mui/material";
+import { getVergeConfig, patchVergeConfig } from "../../services/cmds";
+import { defaultTheme } from "../../pages/_theme";
+
+interface Props {
+  open: boolean;
+  onClose: () => void;
+  onError?: (err: Error) => void;
+}
+
+const Item = styled(ListItem)(() => ({
+  padding: "5px 2px",
+}));
+
+const Round = styled("div")(() => ({
+  width: "24px",
+  height: "24px",
+  borderRadius: "18px",
+  display: "inline-block",
+  marginRight: "8px",
+}));
+
+const SettingTheme = (props: Props) => {
+  const { open, onClose, onError } = props;
+
+  const { t } = useTranslation();
+  const { data: vergeConfig, mutate } = useSWR(
+    "getVergeConfig",
+    getVergeConfig
+  );
+
+  const { theme_setting } = vergeConfig ?? {};
+  const [theme, setTheme] = useState(theme_setting || {});
+
+  useEffect(() => {
+    setTheme({ ...theme_setting } || {});
+  }, [theme_setting]);
+
+  const textProps = {
+    size: "small",
+    autoComplete: "off",
+    sx: { width: 135 },
+  } as const;
+
+  const handleChange = (field: keyof typeof theme) => (e: any) => {
+    setTheme((t) => ({ ...t, [field]: e.target.value }));
+  };
+
+  const onSave = useLockFn(async () => {
+    try {
+      await patchVergeConfig({ theme_setting: theme });
+      mutate();
+    } catch (err: any) {
+      onError?.(err);
+    }
+  });
+
+  return (
+    <Dialog open={open} onClose={onClose}>
+      <DialogTitle>{t("Theme Setting")}</DialogTitle>
+
+      <DialogContent
+        sx={{ width: 400, maxHeight: 300, overflow: "auto", pb: 0 }}
+      >
+        <List sx={{ pt: 0 }}>
+          <Item>
+            <ListItemText primary="Primary Color" />
+
+            <Round
+              sx={{
+                background: theme.primary_color || defaultTheme.primary_color,
+              }}
+            />
+            <TextField
+              {...textProps}
+              value={theme.primary_color}
+              placeholder={defaultTheme.primary_color}
+              onChange={handleChange("primary_color")}
+            />
+          </Item>
+
+          <Item>
+            <ListItemText primary="Secondary Color" />
+
+            <Round
+              sx={{
+                background:
+                  theme.secondary_color || defaultTheme.secondary_color,
+              }}
+            />
+            <TextField
+              {...textProps}
+              value={theme.secondary_color}
+              placeholder={defaultTheme.secondary_color}
+              onChange={handleChange("secondary_color")}
+            />
+          </Item>
+
+          <Item>
+            <ListItemText primary="Info Color" />
+
+            <Round
+              sx={{
+                background: theme.info_color || defaultTheme.info_color,
+              }}
+            />
+            <TextField
+              {...textProps}
+              value={theme.info_color}
+              placeholder={defaultTheme.info_color}
+              onChange={handleChange("info_color")}
+            />
+          </Item>
+
+          <Item>
+            <ListItemText primary="Error Color" />
+
+            <Round
+              sx={{
+                background: theme.error_color || defaultTheme.error_color,
+              }}
+            />
+            <TextField
+              {...textProps}
+              value={theme.error_color}
+              placeholder={defaultTheme.error_color}
+              onChange={handleChange("error_color")}
+            />
+          </Item>
+
+          <Item>
+            <ListItemText primary="Warning Color" />
+
+            <Round
+              sx={{
+                background: theme.warning_color || defaultTheme.warning_color,
+              }}
+            />
+            <TextField
+              {...textProps}
+              value={theme.warning_color}
+              placeholder={defaultTheme.warning_color}
+              onChange={handleChange("warning_color")}
+            />
+          </Item>
+
+          <Item>
+            <ListItemText primary="Success Color" />
+
+            <Round
+              sx={{
+                background: theme.success_color || defaultTheme.success_color,
+              }}
+            />
+            <TextField
+              {...textProps}
+              value={theme.success_color}
+              placeholder={defaultTheme.success_color}
+              onChange={handleChange("success_color")}
+            />
+          </Item>
+
+          <Item>
+            <ListItemText primary="Font Family" />
+
+            <TextField
+              {...textProps}
+              value={theme.font_family}
+              onChange={handleChange("font_family")}
+            />
+          </Item>
+
+          <Item>
+            <ListItemText primary="Font Face" />
+
+            <TextField
+              {...textProps}
+              value={theme.font_face}
+              onChange={handleChange("font_face")}
+            />
+          </Item>
+        </List>
+      </DialogContent>
+
+      <DialogActions>
+        <Button onClick={onClose}>{t("Cancel")}</Button>
+        <Button onClick={onSave} variant="contained">
+          {t("Save")}
+        </Button>
+      </DialogActions>
+    </Dialog>
+  );
+};
+
+export default SettingTheme;
diff --git a/src/components/setting/setting-verge.tsx b/src/components/setting/setting-verge.tsx
index 19f19cd4331977883f177c4711ad1401d6b887d1..2241b96bc0b09108e2910aef76e66ae562ecf550 100644
--- a/src/components/setting/setting-verge.tsx
+++ b/src/components/setting/setting-verge.tsx
@@ -1,4 +1,5 @@
 import useSWR, { useSWRConfig } from "swr";
+import { useState } from "react";
 import { useTranslation } from "react-i18next";
 import {
   IconButton,
@@ -20,6 +21,7 @@ import { CmdType } from "../../services/types";
 import { version } from "../../../package.json";
 import PaletteSwitch from "./palette-switch";
 import GuardState from "./guard-state";
+import SettingTheme from "./setting-theme";
 
 interface Props {
   onError?: (err: Error) => void;
@@ -32,6 +34,8 @@ const SettingVerge = ({ onError }: Props) => {
 
   const { theme_mode, theme_blur, traffic_graph, language } = vergeConfig ?? {};
 
+  const [themeOpen, setThemeOpen] = useState(false);
+
   const onSwitchFormat = (_e: any, value: boolean) => value;
   const onChangeData = (patch: Partial<CmdType.VergeConfig>) => {
     mutate("getVergeConfig", { ...vergeConfig, ...patch }, false);
@@ -99,6 +103,17 @@ const SettingVerge = ({ onError }: Props) => {
         </GuardState>
       </SettingItem>
 
+      <SettingItem>
+        <ListItemText primary={t("Theme Setting")} />
+        <IconButton
+          color="inherit"
+          size="small"
+          onClick={() => setThemeOpen(true)}
+        >
+          <ArrowForward />
+        </IconButton>
+      </SettingItem>
+
       <SettingItem>
         <ListItemText primary={t("Open App Dir")} />
         <IconButton color="inherit" size="small" onClick={openAppDir}>
@@ -117,6 +132,8 @@ const SettingVerge = ({ onError }: Props) => {
         <ListItemText primary={t("Version")} />
         <Typography sx={{ py: "6px" }}>v{version}</Typography>
       </SettingItem>
+
+      <SettingTheme open={themeOpen} onClose={() => setThemeOpen(false)} />
     </SettingList>
   );
 };
diff --git a/src/pages/_theme.tsx b/src/pages/_theme.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..4b92a03d72a1cd32889f8798d6b500bc6513e8e8
--- /dev/null
+++ b/src/pages/_theme.tsx
@@ -0,0 +1,12 @@
+// default theme setting
+export const defaultTheme = {
+  primary_color: "#5b5c9d",
+  secondary_color: "#9c27b0",
+  primary_text: "#637381",
+  secondary_text: "#909399",
+  info_color: "#0288d1",
+  error_color: "#d32f2f",
+  warning_color: "#ed6c02",
+  success_color: "#2e7d32",
+  font_family: `"Roboto", "Helvetica", "Arial", sans-serif`,
+};
diff --git a/src/services/types.ts b/src/services/types.ts
index 37e2e14a84281333eeed9e11e5cf76f33d9dce72..b098c0fccb8cb8a30ebb3ddf0a53ce6108654506 100644
--- a/src/services/types.ts
+++ b/src/services/types.ts
@@ -130,6 +130,18 @@ export namespace CmdType {
     enable_system_proxy?: boolean;
     enable_proxy_guard?: boolean;
     system_proxy_bypass?: string;
+    theme_setting?: {
+      primary_color?: string;
+      secondary_color?: string;
+      primary_text?: string;
+      secondary_text?: string;
+      info_color?: string;
+      error_color?: string;
+      warning_color?: string;
+      success_color?: string;
+      font_face?: string;
+      font_family?: string;
+    };
   }
 
   type ClashConfigValue = any;