diff --git a/.changeset/calm-themes-follow.md b/.changeset/calm-themes-follow.md
new file mode 100644
index 0000000000..0293c32e81
--- /dev/null
+++ b/.changeset/calm-themes-follow.md
@@ -0,0 +1,5 @@
+---
+"@emdash-cms/admin": patch
+---
+
+Fixes the admin appearance toggle so every click changes the visible color scheme. The admin follows the system preference whenever the selected appearance matches it.
diff --git a/e2e/tests/visual-regression.spec.ts-snapshots/content-editor-ltr-chromium-linux.png b/e2e/tests/visual-regression.spec.ts-snapshots/content-editor-ltr-chromium-linux.png
index d4705349c7..ed1379a5e2 100644
Binary files a/e2e/tests/visual-regression.spec.ts-snapshots/content-editor-ltr-chromium-linux.png and b/e2e/tests/visual-regression.spec.ts-snapshots/content-editor-ltr-chromium-linux.png differ
diff --git a/e2e/tests/visual-regression.spec.ts-snapshots/content-editor-rtl-chromium-linux.png b/e2e/tests/visual-regression.spec.ts-snapshots/content-editor-rtl-chromium-linux.png
index 545a062ace..491f2d3ef9 100644
Binary files a/e2e/tests/visual-regression.spec.ts-snapshots/content-editor-rtl-chromium-linux.png and b/e2e/tests/visual-regression.spec.ts-snapshots/content-editor-rtl-chromium-linux.png differ
diff --git a/e2e/tests/visual-regression.spec.ts-snapshots/content-list-byline-filter-ltr-chromium-linux.png b/e2e/tests/visual-regression.spec.ts-snapshots/content-list-byline-filter-ltr-chromium-linux.png
index 92ad6a0663..1ac09eecf5 100644
Binary files a/e2e/tests/visual-regression.spec.ts-snapshots/content-list-byline-filter-ltr-chromium-linux.png and b/e2e/tests/visual-regression.spec.ts-snapshots/content-list-byline-filter-ltr-chromium-linux.png differ
diff --git a/e2e/tests/visual-regression.spec.ts-snapshots/content-list-byline-filter-rtl-chromium-linux.png b/e2e/tests/visual-regression.spec.ts-snapshots/content-list-byline-filter-rtl-chromium-linux.png
index 3a747fb80a..6334016894 100644
Binary files a/e2e/tests/visual-regression.spec.ts-snapshots/content-list-byline-filter-rtl-chromium-linux.png and b/e2e/tests/visual-regression.spec.ts-snapshots/content-list-byline-filter-rtl-chromium-linux.png differ
diff --git a/e2e/tests/visual-regression.spec.ts-snapshots/content-list-date-field-filter-ltr-chromium-linux.png b/e2e/tests/visual-regression.spec.ts-snapshots/content-list-date-field-filter-ltr-chromium-linux.png
index c5400181d2..37444bc723 100644
Binary files a/e2e/tests/visual-regression.spec.ts-snapshots/content-list-date-field-filter-ltr-chromium-linux.png and b/e2e/tests/visual-regression.spec.ts-snapshots/content-list-date-field-filter-ltr-chromium-linux.png differ
diff --git a/e2e/tests/visual-regression.spec.ts-snapshots/content-list-date-field-filter-rtl-chromium-linux.png b/e2e/tests/visual-regression.spec.ts-snapshots/content-list-date-field-filter-rtl-chromium-linux.png
index 3351c7fac0..9aecc41f92 100644
Binary files a/e2e/tests/visual-regression.spec.ts-snapshots/content-list-date-field-filter-rtl-chromium-linux.png and b/e2e/tests/visual-regression.spec.ts-snapshots/content-list-date-field-filter-rtl-chromium-linux.png differ
diff --git a/e2e/tests/visual-regression.spec.ts-snapshots/content-list-date-range-filter-ltr-chromium-linux.png b/e2e/tests/visual-regression.spec.ts-snapshots/content-list-date-range-filter-ltr-chromium-linux.png
index 5fa5243521..12483c800f 100644
Binary files a/e2e/tests/visual-regression.spec.ts-snapshots/content-list-date-range-filter-ltr-chromium-linux.png and b/e2e/tests/visual-regression.spec.ts-snapshots/content-list-date-range-filter-ltr-chromium-linux.png differ
diff --git a/e2e/tests/visual-regression.spec.ts-snapshots/content-list-date-range-filter-rtl-chromium-linux.png b/e2e/tests/visual-regression.spec.ts-snapshots/content-list-date-range-filter-rtl-chromium-linux.png
index 6ceac5b737..a7f5f2d6a2 100644
Binary files a/e2e/tests/visual-regression.spec.ts-snapshots/content-list-date-range-filter-rtl-chromium-linux.png and b/e2e/tests/visual-regression.spec.ts-snapshots/content-list-date-range-filter-rtl-chromium-linux.png differ
diff --git a/e2e/tests/visual-regression.spec.ts-snapshots/content-list-ltr-chromium-linux.png b/e2e/tests/visual-regression.spec.ts-snapshots/content-list-ltr-chromium-linux.png
index 60b6f6ddb6..e7c14303e9 100644
Binary files a/e2e/tests/visual-regression.spec.ts-snapshots/content-list-ltr-chromium-linux.png and b/e2e/tests/visual-regression.spec.ts-snapshots/content-list-ltr-chromium-linux.png differ
diff --git a/e2e/tests/visual-regression.spec.ts-snapshots/content-list-rtl-chromium-linux.png b/e2e/tests/visual-regression.spec.ts-snapshots/content-list-rtl-chromium-linux.png
index a5da628a60..18e9761fe7 100644
Binary files a/e2e/tests/visual-regression.spec.ts-snapshots/content-list-rtl-chromium-linux.png and b/e2e/tests/visual-regression.spec.ts-snapshots/content-list-rtl-chromium-linux.png differ
diff --git a/e2e/tests/visual-regression.spec.ts-snapshots/content-list-status-filter-ltr-chromium-linux.png b/e2e/tests/visual-regression.spec.ts-snapshots/content-list-status-filter-ltr-chromium-linux.png
index 54880994fd..1efc8a6ab2 100644
Binary files a/e2e/tests/visual-regression.spec.ts-snapshots/content-list-status-filter-ltr-chromium-linux.png and b/e2e/tests/visual-regression.spec.ts-snapshots/content-list-status-filter-ltr-chromium-linux.png differ
diff --git a/e2e/tests/visual-regression.spec.ts-snapshots/content-list-status-filter-rtl-chromium-linux.png b/e2e/tests/visual-regression.spec.ts-snapshots/content-list-status-filter-rtl-chromium-linux.png
index 7a6c0f8a38..ddc60df239 100644
Binary files a/e2e/tests/visual-regression.spec.ts-snapshots/content-list-status-filter-rtl-chromium-linux.png and b/e2e/tests/visual-regression.spec.ts-snapshots/content-list-status-filter-rtl-chromium-linux.png differ
diff --git a/e2e/tests/visual-regression.spec.ts-snapshots/content-new-ltr-chromium-linux.png b/e2e/tests/visual-regression.spec.ts-snapshots/content-new-ltr-chromium-linux.png
index a75b5bb96d..4aa5978740 100644
Binary files a/e2e/tests/visual-regression.spec.ts-snapshots/content-new-ltr-chromium-linux.png and b/e2e/tests/visual-regression.spec.ts-snapshots/content-new-ltr-chromium-linux.png differ
diff --git a/e2e/tests/visual-regression.spec.ts-snapshots/content-new-rtl-chromium-linux.png b/e2e/tests/visual-regression.spec.ts-snapshots/content-new-rtl-chromium-linux.png
index 56ea358dcb..31cf633e74 100644
Binary files a/e2e/tests/visual-regression.spec.ts-snapshots/content-new-rtl-chromium-linux.png and b/e2e/tests/visual-regression.spec.ts-snapshots/content-new-rtl-chromium-linux.png differ
diff --git a/e2e/tests/visual-regression.spec.ts-snapshots/dashboard-ltr-chromium-linux.png b/e2e/tests/visual-regression.spec.ts-snapshots/dashboard-ltr-chromium-linux.png
index da352b3a39..6641d06742 100644
Binary files a/e2e/tests/visual-regression.spec.ts-snapshots/dashboard-ltr-chromium-linux.png and b/e2e/tests/visual-regression.spec.ts-snapshots/dashboard-ltr-chromium-linux.png differ
diff --git a/e2e/tests/visual-regression.spec.ts-snapshots/dashboard-rtl-chromium-linux.png b/e2e/tests/visual-regression.spec.ts-snapshots/dashboard-rtl-chromium-linux.png
index 9362c4bea1..6d3f148caa 100644
Binary files a/e2e/tests/visual-regression.spec.ts-snapshots/dashboard-rtl-chromium-linux.png and b/e2e/tests/visual-regression.spec.ts-snapshots/dashboard-rtl-chromium-linux.png differ
diff --git a/e2e/tests/visual-regression.spec.ts-snapshots/media-ltr-chromium-linux.png b/e2e/tests/visual-regression.spec.ts-snapshots/media-ltr-chromium-linux.png
index 6a1565c96d..c611092535 100644
Binary files a/e2e/tests/visual-regression.spec.ts-snapshots/media-ltr-chromium-linux.png and b/e2e/tests/visual-regression.spec.ts-snapshots/media-ltr-chromium-linux.png differ
diff --git a/e2e/tests/visual-regression.spec.ts-snapshots/media-mobile-ltr-chromium-linux.png b/e2e/tests/visual-regression.spec.ts-snapshots/media-mobile-ltr-chromium-linux.png
index 9ffb29d096..3c81878b06 100644
Binary files a/e2e/tests/visual-regression.spec.ts-snapshots/media-mobile-ltr-chromium-linux.png and b/e2e/tests/visual-regression.spec.ts-snapshots/media-mobile-ltr-chromium-linux.png differ
diff --git a/e2e/tests/visual-regression.spec.ts-snapshots/media-mobile-rtl-chromium-linux.png b/e2e/tests/visual-regression.spec.ts-snapshots/media-mobile-rtl-chromium-linux.png
index 4bdaec5de6..e31c870026 100644
Binary files a/e2e/tests/visual-regression.spec.ts-snapshots/media-mobile-rtl-chromium-linux.png and b/e2e/tests/visual-regression.spec.ts-snapshots/media-mobile-rtl-chromium-linux.png differ
diff --git a/e2e/tests/visual-regression.spec.ts-snapshots/media-rtl-chromium-linux.png b/e2e/tests/visual-regression.spec.ts-snapshots/media-rtl-chromium-linux.png
index cd7d15cf10..cc125eaf19 100644
Binary files a/e2e/tests/visual-regression.spec.ts-snapshots/media-rtl-chromium-linux.png and b/e2e/tests/visual-regression.spec.ts-snapshots/media-rtl-chromium-linux.png differ
diff --git a/e2e/tests/visual-regression.spec.ts-snapshots/menus-ltr-chromium-linux.png b/e2e/tests/visual-regression.spec.ts-snapshots/menus-ltr-chromium-linux.png
index e2a1cac322..cf21b8c4ea 100644
Binary files a/e2e/tests/visual-regression.spec.ts-snapshots/menus-ltr-chromium-linux.png and b/e2e/tests/visual-regression.spec.ts-snapshots/menus-ltr-chromium-linux.png differ
diff --git a/e2e/tests/visual-regression.spec.ts-snapshots/menus-rtl-chromium-linux.png b/e2e/tests/visual-regression.spec.ts-snapshots/menus-rtl-chromium-linux.png
index 07322f7455..21252403b4 100644
Binary files a/e2e/tests/visual-regression.spec.ts-snapshots/menus-rtl-chromium-linux.png and b/e2e/tests/visual-regression.spec.ts-snapshots/menus-rtl-chromium-linux.png differ
diff --git a/e2e/tests/visual-regression.spec.ts-snapshots/settings-ltr-chromium-linux.png b/e2e/tests/visual-regression.spec.ts-snapshots/settings-ltr-chromium-linux.png
index 509e755206..29b875bc13 100644
Binary files a/e2e/tests/visual-regression.spec.ts-snapshots/settings-ltr-chromium-linux.png and b/e2e/tests/visual-regression.spec.ts-snapshots/settings-ltr-chromium-linux.png differ
diff --git a/e2e/tests/visual-regression.spec.ts-snapshots/settings-rtl-chromium-linux.png b/e2e/tests/visual-regression.spec.ts-snapshots/settings-rtl-chromium-linux.png
index 3407207dde..12150afd4b 100644
Binary files a/e2e/tests/visual-regression.spec.ts-snapshots/settings-rtl-chromium-linux.png and b/e2e/tests/visual-regression.spec.ts-snapshots/settings-rtl-chromium-linux.png differ
diff --git a/packages/admin/src/components/ThemeProvider.tsx b/packages/admin/src/components/ThemeProvider.tsx
index 3e6a1a3cfb..277bfe9c4a 100644
--- a/packages/admin/src/components/ThemeProvider.tsx
+++ b/packages/admin/src/components/ThemeProvider.tsx
@@ -79,7 +79,11 @@ export function ThemeProvider({ children, defaultTheme = "system" }: ThemeProvid
const setTheme = React.useCallback((newTheme: Theme) => {
setThemeState(newTheme);
- localStorage.setItem(STORAGE_KEY, newTheme);
+ if (newTheme === "system") {
+ localStorage.removeItem(STORAGE_KEY);
+ } else {
+ localStorage.setItem(STORAGE_KEY, newTheme);
+ }
}, []);
const value = React.useMemo(
diff --git a/packages/admin/src/components/ThemeToggle.tsx b/packages/admin/src/components/ThemeToggle.tsx
index fc084718ad..fcb0dc86c6 100644
--- a/packages/admin/src/components/ThemeToggle.tsx
+++ b/packages/admin/src/components/ThemeToggle.tsx
@@ -1,45 +1,34 @@
import { Button } from "@cloudflare/kumo";
import { useLingui } from "@lingui/react/macro";
-import { Sun, Moon, Monitor } from "@phosphor-icons/react";
-import * as React from "react";
+import { Sun, Moon } from "@phosphor-icons/react";
import { useTheme } from "./ThemeProvider";
-/**
- * Theme toggle button that cycles through: system -> light -> dark
- */
export function ThemeToggle() {
const { t } = useLingui();
- const { theme, setTheme, resolvedTheme } = useTheme();
+ const { setTheme, resolvedTheme } = useTheme();
- const cycleTheme = () => {
- const order: ["system", "light", "dark"] = ["system", "light", "dark"];
- const currentIndex = order.indexOf(theme);
- const nextIndex = (currentIndex + 1) % order.length;
- setTheme(order[nextIndex]!);
+ const toggleTheme = () => {
+ const nextTheme = resolvedTheme === "light" ? "dark" : "light";
+ const systemTheme = window.matchMedia("(prefers-color-scheme: dark)").matches
+ ? "dark"
+ : "light";
+ setTheme(nextTheme === systemTheme ? "system" : nextTheme);
};
- const resolvedLabel = resolvedTheme === "light" ? t`light` : t`dark`;
- const label =
- theme === "system" ? t`System (${resolvedLabel})` : theme === "light" ? t`Light` : t`Dark`;
+ const label = resolvedTheme === "light" ? t`Switch to dark` : t`Switch to light`;
return (
);
}
diff --git a/packages/admin/tests/components/Header.test.tsx b/packages/admin/tests/components/Header.test.tsx
index 7ecdb9e637..36f18fd80f 100644
--- a/packages/admin/tests/components/Header.test.tsx
+++ b/packages/admin/tests/components/Header.test.tsx
@@ -53,7 +53,7 @@ const { Header } = await import("../../src/components/Header");
// Constants
// ---------------------------------------------------------------------------
-const THEME_BUTTON_REGEX = /Toggle theme/;
+const THEME_BUTTON_REGEX = /Switch to (light|dark)/;
function TestWrapper({ children }: { children: React.ReactNode }) {
const qc = new QueryClient({
@@ -80,7 +80,7 @@ describe("Header", () => {
,
);
- // ThemeToggle renders a button with aria-label "Toggle theme (current: …)"
+ // ThemeToggle exposes its next action in the aria-label.
// (Kumo 2.x wraps `