diff --git a/docs/releases.md b/docs/releases.md
index cd7e37da..475b03fd 100644
--- a/docs/releases.md
+++ b/docs/releases.md
@@ -7,15 +7,22 @@ Released June 28, 2023
 
 With this release, the ntfy web app now contains a **[progressive web app](https://docs.ntfy.sh/subscribe/pwa/) (PWA)
 with Web Push support**, which means you'll be able to **install the ntfy web app on your desktop or phone** similar 
-to a native app (__even on iOS!__ 🥳), and get basic push notification support (without any battery drain).
+to a native app (__even on iOS!__ 🥳). Installing the PWA gives ntfy web its own launcher, a standalone window, 
+push notifications, and an app badge with the unread notification count.
 
-Installing the PWA gives ntfy web its own launcher (e.g. shortcut on Windows, app on macOS, launcher shortcut on Linux,
-home screen icon on iOS, and launcher icon on Android), a standalone window, push notifications, and an app badge with 
-the unread notification count.
+On top of that, this release also brings **dark mode** 🧛🌙 to the web app.
+
+🙏 A huge thanks for this release goes to [@nimbleghost](https://github.com/nimbleghost), for basically implementing the 
+Web Push / PWA and dark mode feature by himself. I'm really grateful for your contributions.
+
+❤️ If you like ntfy, **please consider sponsoring us** via [GitHub Sponsors](https://github.com/sponsors/binwiederhier)
+and [Liberapay](https://en.liberapay.com/ntfy/), or buying a [paid plan via the web app](https://ntfy.sh/app) (20% off
+if you use promo code `MYTOPIC`). ntfy will always remain open source.
 
 **Features:**
 
 * The web app now supports Web Push, and is installable as a [progressive web app (PWA)](https://docs.ntfy.sh/subscribe/pwa/) on Chrome, Edge, Android, and iOS ([#751](https://github.com/binwiederhier/ntfy/pull/751), thanks to [@nimbleghost](https://github.com/nimbleghost))
+* Support for dark mode in the web app ([#206](https://github.com/binwiederhier/ntfy/issues/206), thanks to [@nimbleghost](https://github.com/nimbleghost))
 
 **Bug fixes:**
 
diff --git a/web/public/static/langs/en.json b/web/public/static/langs/en.json
index fa0a3b0f..0b74c6e2 100644
--- a/web/public/static/langs/en.json
+++ b/web/public/static/langs/en.json
@@ -338,10 +338,6 @@
   "prefs_notifications_web_push_disabled_description": "Notification are received when the web app is running (via WebSocket)",
   "prefs_notifications_web_push_enabled": "Enabled for {{server}}",
   "prefs_notifications_web_push_disabled": "Disabled",
-  "prefs_ui_mode_title": "UI Mode",
-  "prefs_ui_mode_system": "System (default)",
-  "prefs_ui_mode_dark": "Dark",
-  "prefs_ui_mode_light": "Light",
   "prefs_users_title": "Manage users",
   "prefs_users_description": "Add/remove users for your protected topics here. Please note that username and password are stored in the browser's local storage.",
   "prefs_users_description_no_sync": "Users and passwords are not synchronized to your account.",
@@ -359,6 +355,10 @@
   "prefs_users_dialog_password_label": "Password",
   "prefs_appearance_title": "Appearance",
   "prefs_appearance_language_title": "Language",
+  "prefs_appearance_theme_title": "Theme",
+  "prefs_appearance_theme_system": "System (default)",
+  "prefs_appearance_theme_dark": "Dark mode",
+  "prefs_appearance_theme_light": "Light mode",
   "prefs_reservations_title": "Reserved topics",
   "prefs_reservations_description": "You can reserve topic names for personal use here. Reserving a topic gives you ownership over the topic, and allows you to define access permissions for other users over the topic.",
   "prefs_reservations_limit_reached": "You reached your reserved topics limit.",
diff --git a/web/src/app/Prefs.js b/web/src/app/Prefs.js
index 006e8bf5..4f28f87e 100644
--- a/web/src/app/Prefs.js
+++ b/web/src/app/Prefs.js
@@ -1,6 +1,6 @@
 import db from "./db";
 
-export const UI_MODE = {
+export const THEME = {
   DARK: "dark",
   LIGHT: "light",
   SYSTEM: "system",
@@ -47,13 +47,13 @@ class Prefs {
     await this.db.prefs.put({ key: "webPushEnabled", value: enabled });
   }
 
-  async uiMode() {
-    const uiMode = await this.db.prefs.get("uiMode");
-    return uiMode?.value ?? UI_MODE.SYSTEM;
+  async theme() {
+    const theme = await this.db.prefs.get("theme");
+    return theme?.value ?? THEME.SYSTEM;
   }
 
-  async setUIMode(mode) {
-    await this.db.prefs.put({ key: "uiMode", value: mode });
+  async setTheme(mode) {
+    await this.db.prefs.put({ key: "theme", value: mode });
   }
 }
 
diff --git a/web/src/components/App.jsx b/web/src/components/App.jsx
index 38343785..6c4761f1 100644
--- a/web/src/components/App.jsx
+++ b/web/src/components/App.jsx
@@ -22,19 +22,19 @@ import Login from "./Login";
 import Signup from "./Signup";
 import Account from "./Account";
 import "../app/i18n"; // Translations!
-import prefs, { UI_MODE } from "../app/Prefs";
+import prefs, { THEME } from "../app/Prefs";
 
 export const AccountContext = createContext(null);
 
-const darkModeEnabled = (prefersDarkMode, uiModePreference) => {
-  switch (uiModePreference) {
-    case UI_MODE.DARK:
+const darkModeEnabled = (prefersDarkMode, themePreference) => {
+  switch (themePreference) {
+    case THEME.DARK:
       return true;
 
-    case UI_MODE.LIGHT:
+    case THEME.LIGHT:
       return false;
 
-    case UI_MODE.SYSTEM:
+    case THEME.SYSTEM:
     default:
       return prefersDarkMode;
   }
@@ -43,20 +43,17 @@ const darkModeEnabled = (prefersDarkMode, uiModePreference) => {
 const App = () => {
   const [account, setAccount] = useState(null);
   const accountMemo = useMemo(() => ({ account, setAccount }), [account, setAccount]);
-
   const prefersDarkMode = useMediaQuery("(prefers-color-scheme: dark)");
-
-  const uiModePreference = useLiveQuery(() => prefs.uiMode());
-
+  const themePreference = useLiveQuery(() => prefs.theme());
   const theme = React.useMemo(
     () =>
       createTheme({
         ...themeOptions,
         palette: {
-          ...(darkModeEnabled(prefersDarkMode, uiModePreference) ? darkPalette : lightPalette),
+          ...(darkModeEnabled(prefersDarkMode, themePreference) ? darkPalette : lightPalette),
         },
       }),
-    [prefersDarkMode, uiModePreference]
+    [prefersDarkMode, themePreference]
   );
 
   return (
diff --git a/web/src/components/Preferences.jsx b/web/src/components/Preferences.jsx
index 42b04b4d..add9b8c0 100644
--- a/web/src/components/Preferences.jsx
+++ b/web/src/components/Preferences.jsx
@@ -43,7 +43,7 @@ import accountApi, { Permission, Role } from "../app/AccountApi";
 import { Pref, PrefGroup } from "./Pref";
 import { AccountContext } from "./App";
 import { Paragraph } from "./styles";
-import prefs, { UI_MODE } from "../app/Prefs";
+import prefs, { THEME } from "../app/Prefs";
 import { PermissionDenyAll, PermissionRead, PermissionReadWrite, PermissionWrite } from "./ReserveIcons";
 import { ReserveAddDialog, ReserveDeleteDialog, ReserveEditDialog } from "./ReserveDialogs";
 import { UnauthorizedError } from "../app/errors";
@@ -86,7 +86,6 @@ const Notifications = () => {
         {t("prefs_notifications_title")}
       </Typography>
       <PrefGroup>
-        <UIMode />
         <Sound />
         <MinPriority />
         <DeleteAfter />
@@ -238,21 +237,21 @@ const DeleteAfter = () => {
   );
 };
 
-const UIMode = () => {
+const Theme = () => {
   const { t } = useTranslation();
-  const labelId = "prefUIMode";
-  const enabled = useLiveQuery(async () => prefs.uiMode());
+  const labelId = "prefTheme";
+  const enabled = useLiveQuery(async () => prefs.theme());
   const handleChange = async (ev) => {
-    await prefs.setUIMode(ev.target.value);
+    await prefs.setTheme(ev.target.value);
   };
 
   return (
-    <Pref labelId={labelId} title={t("prefs_ui_mode_title")}>
+    <Pref labelId={labelId} title={t("prefs_appearance_theme_title")}>
       <FormControl fullWidth variant="standard" sx={{ m: 1 }}>
         <Select value={enabled ?? false} onChange={handleChange} aria-labelledby={labelId}>
-          <MenuItem value={UI_MODE.SYSTEM}>{t("prefs_ui_mode_system")}</MenuItem>
-          <MenuItem value={UI_MODE.DARK}>{t("prefs_ui_mode_dark")}</MenuItem>
-          <MenuItem value={UI_MODE.LIGHT}>{t("prefs_ui_mode_light")}</MenuItem>
+          <MenuItem value={THEME.SYSTEM}>{t("prefs_appearance_theme_system")}</MenuItem>
+          <MenuItem value={THEME.DARK}>{t("prefs_appearance_theme_dark")}</MenuItem>
+          <MenuItem value={THEME.LIGHT}>{t("prefs_appearance_theme_light")}</MenuItem>
         </Select>
       </FormControl>
     </Pref>
@@ -511,6 +510,7 @@ const Appearance = () => {
         {t("prefs_appearance_title")}
       </Typography>
       <PrefGroup>
+        <Theme />
         <Language />
       </PrefGroup>
     </Card>