diff --git a/packages/app-shell/src/chrome/ThemeProvider.tsx b/packages/app-shell/src/chrome/ThemeProvider.tsx
index d89a82e11c..51d1936d17 100644
--- a/packages/app-shell/src/chrome/ThemeProvider.tsx
+++ b/packages/app-shell/src/chrome/ThemeProvider.tsx
@@ -1,6 +1,8 @@
import { createContext, useContext, useEffect, useState } from "react"
-type Theme = "dark" | "light" | "system"
+// `auto` is the spec's OS-following mode (ThemeModeSchema, ui/theme.zod.ts);
+// `system` is this provider's pre-spec spelling, kept for stored values.
+type Theme = "dark" | "light" | "auto" | "system"
type ThemeProviderProps = {
children: React.ReactNode
@@ -35,7 +37,10 @@ export function ThemeProvider({
root.classList.remove("light", "dark")
- if (theme === "system") {
+ // Branching on `system` alone sent the spec's `auto` into
+ // `classList.add('auto')` — a class no Tailwind variant matches, locking
+ // the light theme with the OS preference ignored (#2942).
+ if (theme === "auto" || theme === "system") {
const systemTheme = window.matchMedia("(prefers-color-scheme: dark)")
.matches
? "dark"
diff --git a/packages/components/src/__tests__/toaster-position-spec-parity.test.tsx b/packages/components/src/__tests__/toaster-position-spec-parity.test.tsx
new file mode 100644
index 0000000000..b354b2a0f3
--- /dev/null
+++ b/packages/components/src/__tests__/toaster-position-spec-parity.test.tsx
@@ -0,0 +1,75 @@
+/**
+ * ObjectUI
+ * Copyright (c) 2024-present ObjectStack Inc.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+/**
+ * Toaster position ↔ spec vocabulary parity + behavior (#2942).
+ *
+ * The `toaster` block used to mount `