diff --git a/package.json b/package.json
index f81c005b1c4a5f641d84b83b67611b2529741433..2ddf6be7c07768145ba11d65141e940875e50a4e 100644
--- a/package.json
+++ b/package.json
@@ -17,9 +17,11 @@
     "axios": "^0.24.0",
     "react": "^17.0.0",
     "react-dom": "^17.0.0",
-    "react-router-dom": "^6.0.2"
+    "react-router-dom": "^6.0.2",
+    "recoil": "^0.5.2"
   },
   "devDependencies": {
+    "@tauri-apps/cli": "^1.0.0-beta.10",
     "@types/react": "^17.0.0",
     "@types/react-dom": "^17.0.0",
     "@vitejs/plugin-react": "^1.1.1",
diff --git a/src/assets/styles/index.scss b/src/assets/styles/index.scss
index 3d5ba1142b651027dbe0995b4e7490a3b999d7c2..71218148733759a42d086e748041ab460b5db194 100644
--- a/src/assets/styles/index.scss
+++ b/src/assets/styles/index.scss
@@ -1,7 +1,3 @@
-html {
-  background-color: #fefefe;
-}
-
 body {
   margin: 0;
   font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen",
diff --git a/src/assets/styles/layout.scss b/src/assets/styles/layout.scss
index 5f52266af3a5ad783b56213683c9fbb14e1372f4..1f17d8fe6708b971249d5ad3670fb9bc2e2697c0 100644
--- a/src/assets/styles/layout.scss
+++ b/src/assets/styles/layout.scss
@@ -6,7 +6,14 @@
   &__sidebar {
     position: relative;
     height: 100vh;
-    flex: 1 1 25%;
+    flex: 1 0 25%;
+    max-width: 250px;
+  }
+
+  &__content {
+    flex: 1 1 75%;
+    padding: 20px 30px;
+    box-sizing: border-box;
   }
 
   &__traffic {
@@ -19,10 +26,4 @@
       margin: 0 auto;
     }
   }
-
-  &__content {
-    flex: 1 1 75%;
-    padding: 20px 30px;
-    box-sizing: border-box;
-  }
 }
diff --git a/src/components/list-item-link.tsx b/src/components/list-item-link.tsx
index 95fc5272a5e1bbd037fa86508c2a6f0411652803..bc1bd31b5b154c00f509d02799b3588c3c9a6550 100644
--- a/src/components/list-item-link.tsx
+++ b/src/components/list-item-link.tsx
@@ -12,16 +12,41 @@ const ListItemLink = (props: LinkProps) => {
   return (
     <ListItem sx={{ py: 0.5, maxWidth: 250, mx: "auto" }}>
       <ListItemButton
-        sx={{
-          borderRadius: 2,
-          textAlign: "center",
-          bgcolor: match ? "rgba(91,92,157,0.15)" : "transparent",
-        }}
+        sx={[
+          {
+            color: "primary",
+            borderRadius: 2,
+            textAlign: "center",
+          },
+          (theme) => {
+            if (!match) return {};
+
+            if (theme.palette.mode === "light") {
+              return {
+                bgcolor: "rgba(91,92,157,0.15)",
+                "&:hover": { bgcolor: "rgba(91,92,157,0.15)" },
+              };
+            }
+
+            return {
+              bgcolor: "rgba(91,92,157,0.35)",
+              "&:hover": { bgcolor: "rgba(91,92,157,0.35)" },
+            };
+          },
+        ]}
         onClick={() => navigate(to)}
       >
         <ListItemText
           primary={children}
-          sx={{ color: match ? "primary.main" : "text.primary" }}
+          sx={{
+            color: (theme) => {
+              if (!match) return "text.secondary";
+
+              const light = theme.palette.mode === "light";
+              if (match && light) return "primary.main";
+              return "primary.light";
+            },
+          }}
         />
       </ListItemButton>
     </ListItem>
diff --git a/src/components/palette-switch.tsx b/src/components/palette-switch.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..49b37f7bb4565de9e5d1b0379dce17e25493452d
--- /dev/null
+++ b/src/components/palette-switch.tsx
@@ -0,0 +1,51 @@
+import { styled, Switch } from "@mui/material";
+
+// From: https://mui.com/components/switches/
+const PaletteSwitch = styled(Switch)(({ theme }) => ({
+  width: 62,
+  height: 34,
+  padding: 7,
+  "& .MuiSwitch-switchBase": {
+    margin: 1,
+    padding: 0,
+    transform: "translateX(6px)",
+    "&.Mui-checked": {
+      color: "#fff",
+      transform: "translateX(22px)",
+      "& .MuiSwitch-thumb:before": {
+        backgroundImage: `url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" height="20" width="20" viewBox="0 0 20 20"><path fill="${encodeURIComponent(
+          "#fff"
+        )}" d="M4.2 2.5l-.7 1.8-1.8.7 1.8.7.7 1.8.6-1.8L6.7 5l-1.9-.7-.6-1.8zm15 8.3a6.7 6.7 0 11-6.6-6.6 5.8 5.8 0 006.6 6.6z"/></svg>')`,
+      },
+      "& + .MuiSwitch-track": {
+        opacity: 1,
+        backgroundColor: theme.palette.mode === "dark" ? "#8796A5" : "#aab4be",
+      },
+    },
+  },
+  "& .MuiSwitch-thumb": {
+    backgroundColor: theme.palette.mode === "dark" ? "#003892" : "#001e3c",
+    width: 32,
+    height: 32,
+    "&:before": {
+      content: "''",
+      position: "absolute",
+      width: "100%",
+      height: "100%",
+      left: 0,
+      top: 0,
+      backgroundRepeat: "no-repeat",
+      backgroundPosition: "center",
+      backgroundImage: `url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" height="20" width="20" viewBox="0 0 20 20"><path fill="${encodeURIComponent(
+        "#fff"
+      )}" d="M9.305 1.667V3.75h1.389V1.667h-1.39zm-4.707 1.95l-.982.982L5.09 6.072l.982-.982-1.473-1.473zm10.802 0L13.927 5.09l.982.982 1.473-1.473-.982-.982zM10 5.139a4.872 4.872 0 00-4.862 4.86A4.872 4.872 0 0010 14.862 4.872 4.872 0 0014.86 10 4.872 4.872 0 0010 5.139zm0 1.389A3.462 3.462 0 0113.471 10a3.462 3.462 0 01-3.473 3.472A3.462 3.462 0 016.527 10 3.462 3.462 0 0110 6.528zM1.665 9.305v1.39h2.083v-1.39H1.666zm14.583 0v1.39h2.084v-1.39h-2.084zM5.09 13.928L3.616 15.4l.982.982 1.473-1.473-.982-.982zm9.82 0l-.982.982 1.473 1.473.982-.982-1.473-1.473zM9.305 16.25v2.083h1.389V16.25h-1.39z"/></svg>')`,
+    },
+  },
+  "& .MuiSwitch-track": {
+    opacity: 1,
+    backgroundColor: theme.palette.mode === "dark" ? "#8796A5" : "#aab4be",
+    borderRadius: 20 / 2,
+  },
+}));
+
+export default PaletteSwitch;
diff --git a/src/main.tsx b/src/main.tsx
index a9fa3296aa510328ce68606cd85f4f1dfef0ba6b..1e732179c893b9d67608257eaaa751a313ee57b2 100644
--- a/src/main.tsx
+++ b/src/main.tsx
@@ -2,32 +2,17 @@ import "./assets/styles/index.scss";
 
 import React from "react";
 import ReactDOM from "react-dom";
+import { RecoilRoot } from "recoil";
 import { BrowserRouter } from "react-router-dom";
-import { createTheme, ThemeProvider } from "@mui/material";
 import Layout from "./pages/_layout";
 
-const theme = createTheme({
-  palette: {
-    mode: "light",
-    primary: {
-      main: "#5b5c9d",
-    },
-    text: {
-      primary: "#637381",
-      secondary: "#909399",
-    },
-  },
-});
-
-// console.log(theme);
-
 ReactDOM.render(
   <React.StrictMode>
-    <ThemeProvider theme={theme}>
+    <RecoilRoot>
       <BrowserRouter>
         <Layout />
       </BrowserRouter>
-    </ThemeProvider>
+    </RecoilRoot>
   </React.StrictMode>,
   document.getElementById("root")
 );
diff --git a/src/pages/_layout.tsx b/src/pages/_layout.tsx
index ee0907730916da494fc562d8892d13e98f6f5011..04465382ed7939de0e136c6e6309796f0b7c91c1 100644
--- a/src/pages/_layout.tsx
+++ b/src/pages/_layout.tsx
@@ -1,5 +1,14 @@
+import { useMemo } from "react";
 import { Route, Routes } from "react-router-dom";
-import { List, Paper, Typography } from "@mui/material";
+import { useRecoilValue } from "recoil";
+import {
+  createTheme,
+  List,
+  Paper,
+  ThemeProvider,
+  Typography,
+} from "@mui/material";
+import { atomPaletteMode } from "../states/setting";
 import LogPage from "../pages/log";
 import HomePage from "../pages/home";
 import ProxyPage from "../pages/proxy";
@@ -10,6 +19,8 @@ import ListItemLink from "../components/list-item-link";
 import Traffic from "../components/traffic";
 
 const Layout = () => {
+  const paletteMode = useRecoilValue(atomPaletteMode);
+
   const routers = [
     {
       label: "代理",
@@ -33,41 +44,61 @@ const Layout = () => {
     },
   ];
 
+  const theme = useMemo(() => {
+    const bgcolor = paletteMode === "light" ? "#f5f5f5" : "#000";
+    document.documentElement.style.background = bgcolor;
+
+    return createTheme({
+      palette: {
+        mode: paletteMode,
+        primary: {
+          main: "#5b5c9d",
+        },
+        text: {
+          primary: "#637381",
+          secondary: "#909399",
+        },
+      },
+    });
+  }, [paletteMode]);
+
   return (
-    <Paper square elevation={0} className="layout">
-      <div className="layout__sidebar">
-        <Typography
-          variant="h3"
-          component="h1"
-          sx={{ my: 2, px: 2, textAlign: "center", userSelect: "none" }}
-        >
-          Clash Verge
-        </Typography>
+    <ThemeProvider theme={theme}>
+      <Paper square elevation={0} className="layout">
+        <div className="layout__sidebar">
+          <Typography
+            variant="h3"
+            component="h1"
+            sx={{ my: 2, px: 2, textAlign: "center", userSelect: "none" }}
+          >
+            Clash Verge
+          </Typography>
 
-        <List sx={{ userSelect: "none" }}>
-          {routers.map((router) => (
-            <ListItemLink key={router.label} to={router.link}>
-              {router.label}
-            </ListItemLink>
-          ))}
-        </List>
+          <List sx={{ userSelect: "none" }}>
+            {routers.map((router) => (
+              <ListItemLink key={router.label} to={router.link}>
+                {router.label}
+              </ListItemLink>
+            ))}
+          </List>
 
-        <div className="layout__traffic">
-          <Traffic />
+          <div className="layout__traffic">
+            <Traffic />
+          </div>
         </div>
-      </div>
 
-      <div className="layout__content">
-        <Routes>
-          <Route path="/" element={<HomePage />} />
-          <Route path="/proxy" element={<ProxyPage />} />
-          <Route path="/profiles" element={<ProfilesPage />} />
-          <Route path="/log" element={<LogPage />} />
-          <Route path="/connections" element={<ConnectionsPage />} />
-          <Route path="/setting" element={<SettingPage />} />
-        </Routes>
-      </div>
-    </Paper>
+        <div className="layout__content">
+          <Routes>
+            <Route path="/" element={<HomePage />} />
+            <Route path="/proxy" element={<ProxyPage />} />
+            <Route path="/profiles" element={<ProfilesPage />} />
+            <Route path="/log" element={<LogPage />} />
+            <Route path="/connections" element={<ConnectionsPage />} />
+            <Route path="/setting" element={<SettingPage />} />
+          </Routes>
+        </div>
+      </Paper>
+    </ThemeProvider>
   );
 };
 
diff --git a/src/pages/setting.tsx b/src/pages/setting.tsx
index be8d857e4a0eae51063dc0eb235a6dbc16c13950..f367828f9fe848fa8e1fa26e1cbf5095cfee573e 100644
--- a/src/pages/setting.tsx
+++ b/src/pages/setting.tsx
@@ -1,5 +1,24 @@
+import { Box } from "@mui/system";
+import { useRecoilState } from "recoil";
+import { atomPaletteMode } from "../states/setting";
+import PaletteSwitch from "../components/palette-switch";
+
 const SettingPage = () => {
-  return <h1>Setting</h1>;
+  const [mode, setMode] = useRecoilState(atomPaletteMode);
+
+  return (
+    <Box>
+      <h1>Setting</h1>
+
+      <Box>
+        <PaletteSwitch
+          checked={mode !== "light"}
+          onChange={(_e, c) => setMode(c ? "dark" : "light")}
+          inputProps={{ "aria-label": "controlled" }}
+        />
+      </Box>
+    </Box>
+  );
 };
 
 export default SettingPage;
diff --git a/src/states/setting.ts b/src/states/setting.ts
new file mode 100644
index 0000000000000000000000000000000000000000..56ceffd2c8f02c24fa8b116072ca0082febe2540
--- /dev/null
+++ b/src/states/setting.ts
@@ -0,0 +1,6 @@
+import { atom } from "recoil";
+
+export const atomPaletteMode = atom<"light" | "dark">({
+  key: "atomPaletteMode",
+  default: "light",
+});