Supporting Dark Mode In WKWebView

If you’re supporting dark mode in your apps you probably already know about using dynamic colors and creating variations of your images for dark mode. That’s great but how do you do that when your content is in a web view (and you’re not a web developer)?

Using WKWebView

When you use a WKWebView to show HTML content in your App you’re typically loading it from a remote web server. Unless you’re also responsible for the remote content there’s not much you can do to make it support dark mode.

There’s nothing stopping you from using a WKWebView to show HTML content that’s bundled with your App. I’ve sometimes used this to show rich text content like a user guide. If your App supports dark mode it can look odd if the web view doesn’t. Unlike the remote content, this is something we can control.

Since it’s easier to show an example, here’s the web content I’m starting with:

Web content in default light mode

You can see the setup I’m using to create and configure the WKWebView to show HTML content bundled with the app in this earlier post on Using Dynamic Type With Web Views.

My HTML content looks like this:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1">
<link rel="stylesheet" href="stylesheet.css">
<title>User Guide</title>
</head>
<body>
<h1>Dynamic Type With WebKit</h1>
<h2>Getting Started</h2>
<p>An example of using <mark>dynamic type</mark> fonts with HTML content displayed in a WKWebView. Changing the text size in Settings should also change the text in this web view.</p>
<h2>Handling Images</h2>
<p>An example of an image with a dark mode variation</p>
<img src="001.png" alt="Xcode source editor">
<footer>For more details see <a href="https://useyourloaf.com/blog/using-dynamic-type-with-web-views/">Using Dynamic Type With Web Views</a></footer>
</body>
</html>

The CSS file included in the App bundle looks like this:

/* stylesheet.css */
body {
    font: -apple-system-body;
}

h1 {
    font: -apple-system-headline;
    color: red;
}

h2 {
    font: -apple-system-subheadline;
    color: green;
}

footer {
    font: -apple-system-footnote;
}

a {
    color: blue;
}

img {
    max-width: 100%;
}

Supporting Dark Mode

Apple added support for dark mode to Safari 12.1 on the desktop (macOS 10.14.4) and WKWebKit on iOS 13. Support is not automatic, you need to opt-in and then customize your CSS styles. Let’s look at the steps in detail:

Dark Mode Color Scheme

By default WebKit does NOT automatically darken web content. You opt-in using the color-scheme property on the root element in your CSS stylesheet:

:root {
  color-scheme: light dark;
}

If you are using the default text and background colors that might be all you have to do. For example, with just this one change in my stylesheet.css my text switches from black on a white background to white on black:

Content with white text on black background

The other colors don’t look so good and I have a white image but not a bad first step.

Note: See Xcode 11 Environmental Overrides for details on switching the simulator between light and dark modes in the debugger.

Using CSS Variables

The next step is to replace all hard-coded colors in the CSS file with CSS variables. This will make it easier to override the colors for the dark color scheme. I have three colors in my stylesheet for titles, subheadings and links. I’ve created variables for these colors in the root element:

:root {
  color-scheme: light dark;
  --title-color: red;
  --subhead-color: green;
  --link-color: blue;
}

I can then replace the hard-coded colors with the variables:

h1 {
    font: -apple-system-headline;
    color: var(--title-color);
}

h2 {
    font: -apple-system-subheadline;
    color: var(--subhead-color);
}

a {
    color: var(--link-color);
}

Override Colors for Dark Mode

Using a media query we can now override the color variables for the dark color scheme:

@media screen and (prefers-color-scheme: dark) {
  :root {
    --title-color: #ff8080;
    --subhead-color: #80ff80;
    --link-color: #93d5ff;
  }
}

This is the result:

Content with custom colors for dark mode

Images

What about the image? We can improve things with a dark mode variation of the image. Instead of modifying the CSS we use a picture element with a media query in the HTML:

<picture>
<source srcset="001-dark.png" media="(prefers-color-scheme: dark)">
<img src="001.png" alt="Xcode source editor">
</picture>

If the dark color scheme is active we show “001-dark.png” otherwise we fallback to “001.png”. This is how the final version of my content looks:

Final version with dark version of image

Sample Code

You can find the code for this post and the earlier post on supporting dynamic type in my GitHub repository:

Read More

The WWDC 2019 video on supporting dark mode in web content is short and to the point:

The WebKit blog also has a good introduction: