Dark Mode in a WebView App: How It Actually Works on iOS & Android

Dark Mode in a WebView App: How It Actually Works on iOS & Android

A user with dark mode switched on opens your app and gets a wall of white. Or worse: the operating system tries to help, inverts your page automatically, and your brand colours come out muddy with unreadable text over them. Dark mode in a WebView app is decided by your website's CSS, and the WebView only guesses when your CSS stays silent.

The short version: declare that your site supports dark mode and style it yourself, and both platforms will use your version. Say nothing, and Android will invert your page for you — with results you did not design.

Does Dark Mode Work in a WebView App?

Yes, and it works the same way it works in a mobile browser: your site reads the system setting through the prefers-color-scheme media query and serves its own dark styles. No native code is required for the common case.

Your siteWhat the WebView doesResult
Has real dark CSS and declares color-schemeUses your stylesCorrect, designed dark mode
Has dark CSS but no color-scheme declarationMay still auto-darken on AndroidDouble-darkened, muddy
Has no dark CSS at allAndroid auto-inverts; iOS leaves it lightInconsistent across platforms
Declares light onlyBoth platforms render lightDeliberate, and acceptable

That second row is the one that produces the strangest bug reports. A site that does have a dark theme can still get darkened a second time by the operating system, because it never told the WebView it had one. The fix is a single declaration, covered below. AppOfWeb includes dark mode in every paid tier, but the quality of it still depends on your site's own CSS — the shell can respect a dark theme, it cannot invent one for you. If you are new to how the wrapper works, start with what a WebView app actually is.

How Does Dark Mode Reach Your Website?

Through one CSS media query: prefers-color-scheme, which reports whether the operating system is set to light or dark. The WebView passes the device setting straight through, exactly as a browser does.

/* Default: light theme */
:root {
  --bg: #ffffff;
  --text: #111827;
}

/* Dark theme, when the OS asks for it */
@media (prefers-color-scheme: dark) {
  :root {
    --bg: #0f172a;
    --text: #e5e7eb;
  }
}

body { background: var(--bg); color: var(--text); }

Driving the whole theme from a handful of custom properties is what makes this manageable. If every component hard-codes #ffffff and #333333, a dark theme means touching every rule in your stylesheet. If colours come from variables, dark mode is one media query.

If your site also offers its own in-page light/dark toggle, keep the two in sync: the OS setting should be the default, and the user's explicit choice should override it and persist. That preference is usually stored in a cookie or localStorage, which brings the same restart-persistence rules covered in our guide to session and cookie persistence in WebView apps — a theme choice that resets on every launch is its own small annoyance.

What Is the color-scheme Declaration, and Why Does It Matter?

color-scheme is how your page tells the browser or WebView which themes it genuinely supports — and declaring it is what stops the operating system from darkening your page on your behalf. It is one line, and it is the single most-skipped step here.

Declare it in CSS, in the meta tag, or both:

/* CSS */
:root { color-scheme: light dark; }
<!-- or in the head -->
<meta name="color-scheme" content="light dark">

This does two useful things at once. First, it signals "I have handled both themes, leave my colours alone" — which is what prevents the automatic darkening described in the Android section below. Second, it makes the browser render its own built-in UI to match: form controls, text inputs, checkboxes, scrollbars and the default text caret all switch to their dark variants automatically.

That second effect is worth having on its own. Without color-scheme, a carefully built dark page frequently still shows glaring white input boxes, because those controls are drawn by the browser rather than by your CSS.

Quick diagnostic: if your dark theme looks right except for the form fields, which are still white, you are missing the color-scheme declaration.

Android: What Is Algorithmic Darkening?

Algorithmic darkening is Android WebView automatically inverting a light web page when the system is in dark mode and the page has not said it supports dark mode itself. It is a fallback for sites that were never designed for dark mode, not a feature you should rely on.

It is enabled from the native shell through the AndroidX WebKit library:

WebSettingsCompat.setAlgorithmicDarkeningAllowed(
    webView.getSettings(), true);

The older setForceDark API and its FORCE_DARK_ON / FORCE_DARK_OFF / FORCE_DARK_AUTO constants are deprecated, and modern apps should use the algorithmic darkening call instead.

The important behaviour is what it does when your page already supports dark mode: it steps aside. Once the page declares color-scheme or provides prefers-color-scheme styles, the WebView uses your styles rather than inverting anything. That is exactly the outcome you want, and it is why the declaration matters more than the native setting.

Results from automatic inversion are mediocre by nature. It has no idea which of your colours are brand colours, which greys are deliberate, or which images have transparent backgrounds. Text contrast frequently lands somewhere between poor and unreadable. Treat it as a safety net for pages you have not themed, and theme the pages that matter. Our Android Studio WebView build guide covers where settings like this sit in a real project.

iOS: How Does WKWebView Handle Dark Mode?

WKWebView follows the system appearance automatically and reports it to your page through prefers-color-scheme — there is no iOS equivalent of Android's automatic page inversion. On iOS, if your site has no dark styles, it simply stays light.

That difference is the reason the same app can look themed on Android and untouched on iOS. It is not an iOS bug; it is Android doing something extra. Build real dark CSS and both platforms land in the same place.

Two iOS-specific details are worth knowing:

  • You can force an appearance for the WebView. Setting overrideUserInterfaceStyle on the web view pins it to light or dark regardless of the system setting. This is occasionally useful for a brand that only ships one theme, but it overrides a user's explicit device preference, so use it deliberately rather than as a shortcut.
  • The native container has its own appearance. The view behind and around your web content is native, so if the shell stays light while your page goes dark, you get a bright border or a flash of white during scroll bounce. The shell's background colour has to be themed too.

The status bar is part of that same picture. Its icons only come in light or dark, so a theme switch that leaves them unchanged can make the clock and battery invisible — the case covered alongside the other native details in our guide to pull-to-refresh, status bars and safe areas.

Why Does My App Flash White on Launch?

Because the WebView's own background is white by default, and it is visible for the moment between the app opening and your CSS painting. For a dark-mode user, that is a bright flash on every single launch, and it is the most-reported dark mode complaint of all.

There are three surfaces involved, and all three have to be themed or the flash just moves:

SurfaceFix
The WebView's own backgroundSet the view's background colour in the native shell to your dark background
The native view behind itTheme the container view to match
The launch / splash screenProvide a dark variant that matches the theme

Fixing only the first still leaves a flash from the splash screen; fixing only the splash screen leaves one from the WebView. Since the splash screen is guaranteed to appear on every cold launch, its dark variant is worth getting right — our guide to splash screen best practices covers the asset side of it.

Test this from a cold start, not from the app switcher. Resuming a warm app skips the paint entirely, so the flash never appears and the bug looks fixed when it is not.

What About Logos, Images and Form Controls?

CSS handles your colours, but images with baked-in white backgrounds and dark-on-transparent logos do not theme themselves. These are what make an otherwise-correct dark mode still look unfinished.

Three cases cover almost everything:

  • Logos and icons with transparent backgrounds. A dark navy logo on a dark navy page disappears. Ship a light variant and swap it with a <picture> element using media="(prefers-color-scheme: dark)", so the browser picks the right file with no JavaScript.
  • Photographs and product images. Leave these alone. Inverting a photo is always wrong, and blanket CSS filters are what produce the muddy results people associate with bad dark modes. If a photo is too bright against a dark page, reduce its brightness slightly rather than inverting it.
  • Form controls, scrollbars and the text caret. These are drawn by the browser, and the color-scheme declaration is what themes them. This is the fix for the white-input-box problem described earlier.

One more that gets missed: any image of a document, screenshot or chart with a hard white background will glow against a dark page. Giving those a subtle border or a light-grey container reads as intentional, where a raw white rectangle reads as broken.

What Changed in 2025–2026

Dark mode moved from a nice-to-have to an expectation, and the tooling settled: color-scheme is the standard way to declare support, and Android's older force-dark API has been superseded. Nothing here is volatile, which is unusual and welcome.

Three things worth knowing as of September 2026:

  • Android's setForceDark API is deprecated in favour of setAlgorithmicDarkeningAllowed, and the newer call defers to the page's own dark styles whenever the page declares support. Code copied from older tutorials will still compile but is the wrong pattern to start from now.
  • color-scheme is broadly supported across current browsers and WebViews on both platforms, so there is no longer a reason to skip it for compatibility.
  • Users increasingly treat a missing dark mode as a defect rather than a missing nicety, particularly on a paid or frequently-opened app. It shows up in reviews in a way it did not a few years ago.

The practical takeaway has not changed: build the dark theme in your own CSS, declare it, and let both platforms use it.

Step-by-Step: Getting Dark Mode Right

Every step except the last two happens on your website, which means you can verify most of this in a desktop browser before an app build exists. Six steps.

1

Move your colours into CSS custom properties

Define background, text, border and surface colours as variables on :root. Without this, a dark theme means editing every rule in your stylesheet instead of one block.

2

Add a prefers-color-scheme dark block

Override those variables inside @media (prefers-color-scheme: dark). Check contrast rather than simply inverting — mid-greys that read well on white are often too dim on a dark background.

3

Declare color-scheme: light dark

This stops Android from auto-darkening a page you have already themed, and it themes the browser's own form controls, scrollbars and caret.

4

Provide dark variants for logos and flat graphics

Swap them with a <picture> element keyed to prefers-color-scheme. Leave photographs alone rather than filtering them.

5

Theme the native shell to match

Set the WebView's background colour, the container view behind it, and a dark splash screen variant — all three, or the white flash simply moves from one surface to another.

6

Flip the status bar icons with the theme

Status bar icons are only light or dark. A dark theme with unchanged icons can leave the clock and battery unreadable against your header.

How Do You Test Dark Mode Properly?

Toggle the system setting while the app is open, and separately do a cold start in each theme — the two reveal different bugs. Most dark mode defects survive testing because only one of those is ever tried.

  1. Switch the OS theme with the app already open. The page should re-theme immediately. If it only changes after a reload, your styles are being applied at load time rather than by the media query.
  2. Force-quit and cold start in dark mode. This is the only way to see the white flash, and it is the most common complaint.
  3. Check every form on the site. White input boxes on a dark page are the signature of a missing color-scheme declaration.
  4. Look at every logo and flat graphic. Dark-on-transparent artwork disappears entirely against a dark background.
  5. Read the status bar. Confirm the clock and battery are legible against your header in both themes.
  6. Test both platforms. Android may be auto-darkening where iOS is not, so a page that looks themed on one can be untouched on the other.

Test on real hardware rather than a simulator where you can, for the same reason that applies to file downloads in a WebView app — launch timing and system integration behave differently on a real device.

Common Mistakes

Having dark CSS but never declaring color-scheme

Android can darken a page you have already darkened, producing washed-out, muddy colours. One line prevents it, and it is the most common version of "dark mode looks wrong in the app."

Relying on automatic darkening as your dark mode

Algorithmic inversion has no idea which colours are your brand and which greys are deliberate. It is a fallback for unthemed pages, not a substitute for a designed theme.

Applying a CSS invert filter to everything

It wrecks photographs and product images, and it is the technique behind most genuinely bad dark modes. Theme with variables instead.

Theming the page but not the native shell

A dark page inside a light container flashes white on launch and shows bright edges during scroll bounce. The WebView background, the container and the splash screen all need the dark treatment.

Reusing light-theme greys unchanged

A mid-grey that reads comfortably on white is often too dim on a dark background. Check contrast in the dark theme rather than assuming the palette transfers.

Frequently Asked Questions

Does dark mode work in a WebView app?

Yes, and it works the same way it does in a mobile browser. The WebView passes the device's light or dark setting through to your website, where the prefers-color-scheme CSS media query picks it up and applies your dark styles. No native code is needed for the normal case. What you do need is real dark CSS on your site, because the app shell can respect a dark theme but cannot invent a good one for you.

Why does my dark theme look washed out or muddy inside the app?

Almost certainly because Android is darkening a page you have already darkened. When a page does not declare that it supports dark mode, Android WebView applies algorithmic darkening and inverts it automatically, and doing that to an already-dark theme produces flat, muddy colours. Adding color-scheme: light dark to your CSS tells the WebView to use your styles instead, and the problem disappears.

What is algorithmic darkening in Android WebView?

It is Android automatically inverting a light web page when the system is in dark mode and the page has not indicated that it handles dark mode itself. It is enabled from the native shell with setAlgorithmicDarkeningAllowed, which replaces the deprecated setForceDark API. It is designed as a fallback for sites that were never themed, and it steps aside as soon as your page declares dark mode support, so it should not be treated as a substitute for real dark styles.

Why does my app flash white when it opens in dark mode?

Because the WebView's own background colour is white by default, and it shows for the instant between the app launching and your CSS painting. Three surfaces have to be themed to remove it completely: the WebView's background colour, the native container view behind it, and the launch or splash screen. Fixing only one moves the flash rather than removing it, and it only reproduces on a genuine cold start, not when resuming from the app switcher.

Why are my form inputs still white in dark mode?

Because text inputs, checkboxes, scrollbars and the text caret are drawn by the browser rather than styled by your CSS, and they only switch to their dark variants when the page declares color-scheme. Adding color-scheme: light dark to your root CSS rule, or the equivalent meta tag in the head, themes all of them at once. A dark page with glaring white input boxes is the signature symptom of this being missing.

Does dark mode work differently on iOS than on Android?

Yes, in one specific way. Both platforms report the system setting to your site through prefers-color-scheme, but only Android will automatically invert a page that has no dark styles of its own. On iOS a site without dark CSS simply stays light. That is why the same app can look themed on Android and untouched on iOS, and building real dark CSS makes both platforms behave identically.

Should I add my own dark mode toggle as well as following the system setting?

It is a reasonable addition as long as the system setting remains the default. Follow prefers-color-scheme when the user has expressed no preference, let an explicit in-page choice override it, and store that choice so it survives an app restart. A toggle that resets to the system default on every launch is more irritating than having no toggle at all, so the persistence side matters as much as the switch itself.

Key Takeaways

  • Dark mode is your website's CSS, not a native featureprefers-color-scheme does the work on both platforms.
  • Declaring color-scheme: light dark is the step everyone skips, and it prevents Android double-darkening an already-dark page.
  • It also themes form controls and scrollbars, which is the fix for white input boxes on a dark page.
  • Android auto-inverts unthemed pages; iOS does not — that difference explains most cross-platform inconsistency.
  • The white launch flash needs all three surfaces themed: WebView background, container view, and splash screen.
  • Swap logos, leave photos alone, and check greys for contrast rather than assuming the light palette transfers.

Dark mode belongs to the same set of details as pull-to-refresh and safe areas — individually small, collectively the difference between an app that feels built and one that feels wrapped. It also feeds the impression a reviewer forms, which matters for Apple's Guideline 4.2, and it is one of the gaps our WebView versus native comparison weighs up.

Want an App That Respects Your Users' Theme?

AppOfWeb builds your website into native Android and iOS apps for a one-time fee — with dark mode included in every paid plan.

Get Your Free Demo →