diff --git a/dash-apps/web-ambient/index.html b/dash-apps/web-ambient/index.html
index e7dec26..c1f93ff 100644
--- a/dash-apps/web-ambient/index.html
+++ b/dash-apps/web-ambient/index.html
@@ -613,6 +613,14 @@
function receiveMessage(car) {
lastCar = car;
+ // iOS sends darkMode in the message payload; Android sets data-theme via onCarStateUpdate.
+ // Only write on change: the MutationObserver below fires on every setAttribute call.
+ if (typeof car.darkMode === "boolean") {
+ const nextTheme = car.darkMode ? "dark" : "light";
+ if (document.documentElement.getAttribute("data-theme") !== nextTheme) {
+ document.documentElement.setAttribute("data-theme", nextTheme);
+ }
+ }
const themeKey = activeThemeKey();
const theme = THEME_STYLES[themeKey];
const speed = Math.max(0, Math.round(Number(car.egoSpeed) || 0));
diff --git a/dash-apps/web-retro/index.html b/dash-apps/web-retro/index.html
index b1cd3fe..9fc1858 100644
--- a/dash-apps/web-retro/index.html
+++ b/dash-apps/web-retro/index.html
@@ -806,6 +806,13 @@
}
function receiveMessage(car) {
+ // iOS sends darkMode in the message payload; Android sets data-theme via onCarStateUpdate.
+ if (typeof car.darkMode === 'boolean') {
+ const nextTheme = car.darkMode ? 'dark' : 'light';
+ if (document.documentElement.getAttribute('data-theme') !== nextTheme) {
+ document.documentElement.setAttribute('data-theme', nextTheme);
+ }
+ }
pendingCarState = car;
if (renderFrame) return;
renderFrame = window.requestAnimationFrame(flushCarState);