✍️ Issue Description
On Android 13+ (API 33+), in-app HTML messages always render the @media (prefers-color-scheme: dark) branch regardless of the actual system theme. A campaign whose HTML defines distinct light and dark styles always shows the dark variant, even when the host app and device are in light mode.
The same campaign HTML renders correctly on the iOS Iterable Swift SDK — the variant switches with the device theme as expected — so this is Android-specific.
The host app in our case is built with Jetpack Compose (Material 3, DayNight-aware theme at the application level). The activity theme correctly tracks the system theme; only the in-app message dialog does not, because the SDK overrides the theme on its DialogFragment and constructs the WebView under the host activity context.
We also verified the bug is still present on the SDK-100-compose-support branch (PR #1015) — the Compose support work does not address it.
Root cause is in IterableInAppFragmentHTMLNotification:
-
Line 122 — the DialogFragment style is hard-coded to a dark (non-DayNight) theme:
this.setStyle(DialogFragment.STYLE_NO_FRAME,
androidx.appcompat.R.style.Theme_AppCompat_NoActionBar);
Theme.AppCompat.NoActionBar extends Theme.AppCompat, which is the dark variant of AppCompat (the light one is Theme.AppCompat.Light, the system-following one is Theme.AppCompat.DayNight). Its android:isLightTheme is always false.
-
Line 204 — the WebView is constructed with the host activity's context, not the dialog's themed context:
webView = createWebViewSafely(getContext());
Fragment.getContext() returns the host activity context, so the DayNight fix on the dialog alone is not visible to the WebView.
On API 33+ (WebView 105+), WebView derives the CSS prefers-color-scheme value from the host theme's isLightTheme attribute (per the AndroidX WebView documentation). With (1) and (2) combined, the WebView always sees isLightTheme=false and reports prefers-color-scheme: dark to the loaded HTML, regardless of the system theme.
WebSettingsCompat.setAlgorithmicDarkeningAllowed(true) is not required to fix this — the theme + context change alone is sufficient. Algorithmic darkening is a separate feature that synthesizes dark styles for pages that do not declare their own; campaigns that already provide a prefers-color-scheme: dark block do not need it.
Proposed fix
- this.setStyle(DialogFragment.STYLE_NO_FRAME, androidx.appcompat.R.style.Theme_AppCompat_NoActionBar);
+ this.setStyle(DialogFragment.STYLE_NO_FRAME, androidx.appcompat.R.style.Theme_AppCompat_DayNight_NoActionBar);
- webView = createWebViewSafely(getContext());
+ webView = createWebViewSafely(getDialog().getContext());
getDialog() is already non-null at this code path (it is dereferenced earlier in the same method).
📋 Steps to Reproduce
- Create an in-app campaign whose HTML template includes a
@media (prefers-color-scheme: dark) block with visibly different colors from the light branch (minimal repro template below).
- Run the host app on an Android 13+ device or emulator with the system theme set to Light.
- Trigger the in-app message.
- Observe: the dark variant is rendered instead of the light one. Toggling the system theme to Dark and re-triggering produces the same dark rendering.
Expected: the light variant renders when the system is in light mode, the dark variant renders when the system is in dark mode.
Minimal reproducible HTML template
<!DOCTYPE html>
<html lang="en">
<head>
<meta name="viewport" content="width=device-width,initial-scale=1">
<style>
body { margin: 0; padding: 24px; font-family: sans-serif; }
.box {
padding: 24px;
border-radius: 12px;
background: #ffffff;
color: #1e293b;
}
@media (prefers-color-scheme: dark) {
.box {
background: #1f2937;
color: #f9fafb;
}
}
</style>
</head>
<body>
<div class="box">
<h1>Theme test</h1>
<p>Light mode should show a white card with dark text.</p>
<p>Dark mode should show a dark card with light text.</p>
<a href="iterable://dismiss">Dismiss</a>
</div>
</body>
</html>
With an unpatched SDK on Android 13+, the dark card always renders. With the two-line patch above, the card matches the system theme.
📦 Iterable SDK version: 3.7.0
📲 Android OS version: Android 13+ (API 33+)
✍️ Issue Description
On Android 13+ (API 33+), in-app HTML messages always render the
@media (prefers-color-scheme: dark)branch regardless of the actual system theme. A campaign whose HTML defines distinct light and dark styles always shows the dark variant, even when the host app and device are in light mode.The same campaign HTML renders correctly on the iOS Iterable Swift SDK — the variant switches with the device theme as expected — so this is Android-specific.
The host app in our case is built with Jetpack Compose (Material 3, DayNight-aware theme at the application level). The activity theme correctly tracks the system theme; only the in-app message dialog does not, because the SDK overrides the theme on its
DialogFragmentand constructs the WebView under the host activity context.We also verified the bug is still present on the
SDK-100-compose-supportbranch (PR #1015) — the Compose support work does not address it.Root cause is in
IterableInAppFragmentHTMLNotification:Line 122 — the
DialogFragmentstyle is hard-coded to a dark (non-DayNight) theme:Theme.AppCompat.NoActionBarextendsTheme.AppCompat, which is the dark variant of AppCompat (the light one isTheme.AppCompat.Light, the system-following one isTheme.AppCompat.DayNight). Itsandroid:isLightThemeis alwaysfalse.Line 204 — the
WebViewis constructed with the host activity's context, not the dialog's themed context:Fragment.getContext()returns the host activity context, so the DayNight fix on the dialog alone is not visible to the WebView.On API 33+ (WebView 105+),
WebViewderives the CSSprefers-color-schemevalue from the host theme'sisLightThemeattribute (per the AndroidX WebView documentation). With (1) and (2) combined, the WebView always seesisLightTheme=falseand reportsprefers-color-scheme: darkto the loaded HTML, regardless of the system theme.WebSettingsCompat.setAlgorithmicDarkeningAllowed(true)is not required to fix this — the theme + context change alone is sufficient. Algorithmic darkening is a separate feature that synthesizes dark styles for pages that do not declare their own; campaigns that already provide aprefers-color-scheme: darkblock do not need it.Proposed fix
getDialog()is already non-null at this code path (it is dereferenced earlier in the same method).📋 Steps to Reproduce
@media (prefers-color-scheme: dark)block with visibly different colors from the light branch (minimal repro template below).Expected: the light variant renders when the system is in light mode, the dark variant renders when the system is in dark mode.
Minimal reproducible HTML template
With an unpatched SDK on Android 13+, the dark card always renders. With the two-line patch above, the card matches the system theme.
📦 Iterable SDK version: 3.7.0
📲 Android OS version: Android 13+ (API 33+)