Android 10 — How to officially support Dark Mode in your apps?

Duy Pham
8 min readOct 26, 2019

--

Image from 9to5google

Everyone out there are talking about this trending topic these days when android 10 and iOS 13 has just been released. There are many apps have already had Dark theme options before that. But now it comes to be standard feature of the system, not only the app itself anymore.

The questions immediately appear for us now are “what is dark mode”, “is it mandatory to support dark mode in our apps”, “what do we need to do and how long it take to adapt that changes”, “which people in the team need to be involved”…

In our company, we held the whole day with all android developers just to make that preparation prior to supporting dark mode in our apps, we tried to figure out as clear as possible those concerns.

Dark mode is the trend, users love it

Apart from dark colors which are favorite style of many people, it provides exciting battery optimization and eye-friendly visuals, according this.

From my point of view, as a user, I would be excited if the app I’m using delivers an update with the change log includes a line “dark mode supports”. Wow! they’re so professional with impressive adaptability. They would get more loyal users, possibly.

But there shouldn’t be a mistake if your app doesn’t support dark theme at the moment or in near future, just a little bit inferior for professional companies :D.

Dark theme is not required. In device setting there are no way for users to force your app into the dark without your allowance done by some configurations in the project codes. But perhaps the user is using night mode, for example in low light condition, then he gets a push message and opens your app: wtf, too bright suddenly, my eyes would be hurt!!!

The way to go

It’s not only your responsibility to bring dark theme into the project but the whole team, especially UI/UX designers. Both developers and designers should have common understanding prior to start implementing.

The developer documentation is pretty clear in android developer site, therefore I’m not going to rewrite them here but showing a big picture to analyze the needs for development team to be ready for it.

Force dark — the easiest option but risky

From above documentation you can find there is an option allowing the system to auto converts your theme into dark theme:

Force Dark analyzes each view of your light-themed app, and applies a dark theme automatically before it is drawn to the screen

But it’s really risky since the colors might not be set correctly for all elements, as well as it doesn’t know what are exactly your UX’s desires :D.

Another option is mixing manually supports for some screens but force dark for the rest to speedup progress but they would stay inconsistently. Therefore it’s not the case for really serious products.

Following are steps we need to do:

#1st step: redefine color system

Theming is mostly based on colors — the first thing we need to pay much attention to.

Perhaps we have some colors:

<resources>
<color name="white">#FFFFFF</color>
<color name="white">#FFFFFF</color>
<color name="yellow">#FFFF00</color>
<color name="fuchsia">#FF00FF</color>
<color name="red">#FF0000</color>
<color name="silver">#C0C0C0</color>
 ...
</resources>

And many of us are doing like this, using hard-coded color in styles and layout files:

<style name="TextBody" parent="TextAppearance.AppCompat.Body2">
<item name="android:textSize">@dimen/text_small</item>
<item name="android:textColor">@color/black</item>
</style>

Imagine that if dark mode is turn on but your texts are still black as hard-coded color is set in above style. It maybe the same situation for UI/UX designers when they define element style guide in Sketch/Zeplin. It’s very hard to support multiple themes this way.

It’s ok to override red , yellow or whatever color in dark mode from default light values, but red would no longer be red to be visible with high enough contrast, thus this is not the most right way to override literal color values.

Use color attributes instead

As each UI element’s colors will be different depends on current theme, so the correct approach is using attributes.

The material design site defines clearly the color theming system based on attributes such as colorPrimary, colorSecondary, colorBackground, colorError, colorOnError , textColorPrimary , textColorSecondary

Back to above example, the textColor should be set with textColorPrimary attribute instead of always black. This attribute will be override in specific theme base on user selection:

<style name="TextBody" parent="TextAppearance.AppCompat.Body2">
<item name="android:textSize">@dimen/text_small</item>
<item name="android:textColor">?attr/textColorPrimary</item>
</style>

If you are using Theme.AppCompat.DayNight these attributes are already implemented for both dark and light theme with its default colors. We are still be able to override these colors which will be described in next section.

If the default attributes are not enough, or at least their names are not clear enough for us to understand and match our style guide, we need to add custom color attributes.

Defining how many colors needed is difficult part. We should follow project style guide (if existed) and collaborate closely with UI/UX designers to adapt new color system for current designs.

#2nd step: link color attributes to styles and layouts

After having new color system, lets go through all styles and remove hard-coded colors by those attributes such as above example.

Another point is that we may have layout files with ui components are not styled but use screen-specific colors, or even the colors are set programmatically in the code. All of them need to be replaced as well. Then you would find more and more cases need to be discussed with UX. Everything should be standardized in this step no matter how bad it was.

#3rd step: check all icons and illustrations

Icons and illustrations are also super important for the app’s appearance. Basically for each theme we have to show the icon with different color accordingly.

For single color icons (same color for all visible pixels), it’s possible for us to need only 1 icon file each and apply color attributes by using vector icon or tint mode.

For multi-color icons, such as illustrations, we should have 2 versions (Dark, Light) for each, and place each one in the correct specific configuration of corresponding theme (described in next section).

Again, check all of them if anyone is using hard-coded colors and replace with attributes.

#4 step: manual testing over places

Eventually we need dedicated people to verify that every single screen, single element plays well on both Dark and Light mode.

Please also check the notification icons, messages and widgets as well as launcher screen, if has.

Finally ship the app with impressive release notes :D

Don’t forget to update store listing screenshots.

Implementation tips

Defining colors is work of UI/UX designers and we are developers being responsible for implementing them into project.

Default themes

Theme.AppCompat.DayNight or Theme.MaterialComponents.DayNight are default themes which already implemented so many attributes on both Day or Night mode. Implementing either of them is the easiest way to support Dark Theme in your app.

<style name="AppTheme" parent="Theme.AppCompat.DayNight">

But in the fact that most of apps has their own style guide with different colors than default values implemented by those. So you have to do customizations more or less for sure. Please note that using those themes is optional, it’s still possible to have Night mode of your own style without inheriting those, just to make sure necessary attribute implementation for each mode, based on following instructions.

New resource configuration

You may already known that Android SDK supports us with multiple resource configurations such as different screen sizes (ex. values-xxhpdi), api levels (ex. values-21) or languages, etc… We can override resources in those folders for different device configurations.

And values-night is the new configuration name for resources under Night theme (dark) applied for all attributes, colors, drawables… Simply create a folder named values-night in resource directory of the module then put necessary values there.

resources under night theme

Override theme attributes

In case of you want to use your own colors, instead of default one in the theme, just override desire attributes:

styles.xml in default resource directory values :

<style name="AppTheme" parent="Base.AppTheme">
<item name="colorPrimary">@color/white</item>
</style>

<style name="BaseAppTheme" parent="Theme.AppCompat.DayNight">
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
</style>

styles.xml in values-night :

<style name="AppTheme" parent="Base.AppTheme">
<item name="colorPrimary">@color/black</item>
</style>

Then ?attr/colorPrimary on default theme (Light) is white, switching to Night theme it is black.

Colors

Perhaps we have some text colors: textColorTitle and textColorDescription need to be different on Dark and Light mode. There are 2 approaches:

#approach 1: defining colors in colors.xml in default resource folder (Light theme)

<resources>
...
<color name="textColorTitle">#000000</color>
<color name="textColorDescription">#0000FF</color>
...
</resources>

and override colors.xml in values-night folder for Dark theme:

<resources>
<color name="textColorTitle">#FFFFFF</color>
<color name="textColorDescription">#FFFF00</color>
</resources>

And use it:

<style name="TextBody" parent="TextAppearance.AppCompat.Body2">
<item name="android:textSize">@dimen/text_small</item>
<item name="android:textColor">color/textColorTitle</item>
</style>

#approach 2: use theme attributes (recommended)

Defines custom attributes in attrs.xml:

<resources> 
<attr name="textColorTitle" format="color" />
<attr name="textColorDescription" format="color" />
</resources>

Set attributes colors for default AppTheme in styles.xml:

<style name="AppTheme" parent="BaseAppTheme">
<item name="textColorTitle">@color/black</item>
<item name="textColorDescription">@color/gray</item>
</style>

Create styles.xml file in folder values-night and override attributes colors for the AppTheme:

<style name="AppTheme" parent="BaseAppTheme">
<item name="textColorTitle">@color/white</item>
<item name="textColorDescription">@color/silver</item>
</style>

Then use attribute reference:

<style name="TextBody" parent="TextAppearance.AppCompat.Body2">
<item name="android:textSize">@dimen/text_small</item>
<item name="android:textColor">?textColorTitle</item>
</style>

Obviously this approach is much cleaner than the approach#1. Everyone looking at the theme items would know exactly how the attributes are set for each theme. We also don’t need to override colors which is pretty confuse, but we keep single colors.xml just holding all literal values black, white, red, green, gray…

Custom attributes is the right way to deal with multiple themes, not only for just Dark and Light, such as a music player app with 5 different material themes to satisfy as many as users, we can create 5 themes and override attribute values on each one.

Remember that values-night resource configuration is just a support from android SDK for the dark mode switching in android system. In the past (below Android 10) without it we were still able to create Dark Theme using custom attributes.

There maybe some places that you set colors programmatically in code instead of declaratively in xml files, by whatever reason, you would need to resolve theme attributes following this.

Icons and illustrations

For vector icons, it’s pretty easy to change color attribute in its xml file:

android:fillColor="?iconColor"

But for better usage in different situations, vector icons with single color are recommended to be set with black as always and use tint attribute at specific place, see this.

For png icons, it’s impossible to change the color dynamically excepting using tint.

For illustrations with multiple colors, tint would not working, changing color for certain parts of the vector icon is crazy. Example this:

image from Argil DX

The solution would be design another one for Dark theme with suitable colors, then place that one in drawable-night :

Conclusions

Being early adopter for new technologies demonstrates your development team is professional as well as presenting the user-centric direction of the product.

Supporting dark mode is definitely a good chance for you to refine color system and standardize style guide, be ready for any further changes.

Thanks for reading and looking forwards your responses 👌👍

--

--