# How to Develop With Dynamic Colors ## Background [Dynamic Color](https://m3.material.io/styles/color/dynamic-color) is a new feature introduced as part of Android S (aka Android 12/API 31) and Material You in which the applications can be themed using a custom color palette extracted from the user’s wallpaper. ## How does it work? The Material 3 theme contains color attributes that correspond to the material “color roles.” When we call [`DynamicColors#applyIfAvailable()`](https://github.com/material-components/material-components-android/blob/b70bbc2942bdbd1ea763e72a6b1e561e4813f10c/lib/java/com/google/android/material/color/DynamicColors.java#L211) in [`ChromeBaseAppCompatActivity#onCreate`](https://source.chromium.org/chromium/chromium/src/+/main:chrome/android/java/src/org/chromium/chrome/browser/ChromeBaseAppCompatActivity.java;drc=4d2b5adb556128aab9313fc3851f192c254e09cb;l=218) (or any other activity’s `#onCreate` method), the color role attributes are overridden using colors that are extracted by the system from the user’s wallpaper. Finally, any UI surface that references the color role attributes gets dynamically colored. The color role attributes can be found [here](https://m3.material.io/libraries/mdc-android/color-theming). Using one of the color attributes, `colorPrimary`, as an example: ``` ?attr/colorPrimary -> @color/baseline_primary_600 -> #0B57D0 ``` becomes ``` ?attr/colorPrimary -> @android:color/system_accent1_600 -> #616200 ``` once the dynamic colors are applied. ## How to… Basically, the app UI surfaces need to directly or indirectly reference the color role attributes rather than color resources. There are different ways to achieve this depending on the situation. ### Semantic names Semantic names are used to color the UI components that share the same meaning or role consistently throughout the application. For example, `default_icon_color` can be used almost anywhere to tint a primary icon. A list of common semantic names can be found in [semantic_colors_dynamic.xml](https://source.chromium.org/chromium/chromium/src/+/main:components/browser_ui/styles/android/java/res/values/semantic_colors_dynamic.xml;drc=c83636b34a3e3751c28b3e43af616226f5ea111c). Before we needed to support dynamic colors, semantic names were defined as `@color` resources that could reference other colors. Now that the colors need to reference attributes, it is not possible to continue using `@color`s because Android does not support referencing an `?attr` from a `@color`, so now we use `@macro`s or color state lists. #### Macros In xml, semantic names for dynamic colors are defined using `` tags. A macro is replaced with the value it holds at the build time, similar to the C++ macros. Googlers can learn more about macros [here](http://go/aapt2-macro). Macros can be used in xml to color views and drawables similarly to the color resources (`@color/`) or theme attributes (`?attr/`). Unlike colors, macros are not resources. So, macros cannot be declared in non-default configurations, e.g. `values-night`. ##### Utility methods At the time of writing, there is no support for macros in Java code. So, we have created utility classes with static methods to access the semantic names in code. For example, if a semantic name is defined as [`@macro/default_bg_color`](https://source.chromium.org/chromium/chromium/src/+/main:components/browser_ui/styles/android/java/res/values/semantic_colors_dynamic.xml;drc=efb53ff2cb5ea3db8643840d7a9bde4ecdab1741;l=7) in xml, it would also have a utility method [`SemanticColorUtils#getDefaultBgColor()`](https://source.chromium.org/chromium/chromium/src/+/main:components/browser_ui/styles/android/java/src/org/chromium/components/browser_ui/styles/SemanticColorUtils.java;drc=fefb943cdc56d80fda8a2e13fc9327e91567e5bc;l=45) in Java. ##### Location While the most common semantic names are defined in [`semantic_colors_dynamic.xml`](https://source.chromium.org/chromium/chromium/src/+/main:components/browser_ui/styles/android/java/res/values/semantic_colors_dynamic.xml;drc=efb53ff2cb5ea3db8643840d7a9bde4ecdab1741), feature or surface specific semantic names can be defined in their relative modules or directories. Then, they can reference other macros or directly reference theme attributes. For example, [`suggestion_url_color`](https://source.chromium.org/chromium/chromium/src/+/main:chrome/browser/ui/android/omnibox/java/res/values/colors.xml;drc=efb53ff2cb5ea3db8643840d7a9bde4ecdab1741;l=10) is defined in `chrome/browser/ui/android/omnibox/` and it references `@macro/default_text_color_link`, a common semantic name. If this semantic color needs to be accessed from Java code, a utility method can be added to the utility class for that directory, e.g. [ChromeSemanticColorUtils](https://source.chromium.org/chromium/chromium/src/+/main:chrome/android/java/src/org/chromium/chrome/browser/ChromeSemanticColorUtils.java;drc=a7ae6c6cf46bfc855c55de34ce319c9b9cab10e9). #### Color state lists Color state lists are defined using the `` tag and are usually used to update the visual representation of the views based on their state, e.g. enabled vs disabled. Unlike the regular color resources, color state lists can reference attributes (and macros). Color state lists can be used to color surfaces as long as they point to dynamic colors, e.g. [`default_text_color_accent1_tint_list`](https://source.chromium.org/chromium/chromium/src/+/main:components/browser_ui/styles/android/java/res/color/default_text_color_accent1_tint_list.xml). Keep in mind that there may be limitations to the color state list usage. For example, color state lists cannot be used as `android:background` below API 29. ### Surface colors [Surface colors](https://m3.material.io/styles/color/the-color-system/color-roles#c0cdc1ba-7e67-4d6a-b294-218f659ff648) represent `?attr/colorSurface` at different surface levels or elevation values. These colors are calculated at runtime, so there is no macro or attribute that can be used to retrieve surface colors. For this reason, there are currently 2 ways to calculate surface colors. #### ChromeColors#getSurfaceColor() [`ChromeColors#getSurfaceColor()`](https://source.chromium.org/chromium/chromium/src/+/main:components/browser_ui/styles/android/java/src/org/chromium/components/browser_ui/styles/ChromeColors.java;l=153;drc=8989e41e6a3db288b26ff624819d71193554b06a) calculates a surface color using the required attributes from the provided `Context` and the elevation resource. The elevation resource should be one of the [predefined elevation levels](https://source.chromium.org/chromium/chromium/src/+/main:ui/android/java/res/values/dimens.xml;drc=0836f570d8e966dc6836120efa7998ac87a5d99b;l=76), or a resource that points to one of these. #### SurfaceColorDrawable [`SurfaceColorDrawable`](https://source.chromium.org/chromium/chromium/src/+/main:components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/SurfaceColorDrawable.java;drc=9636025627ac8986e82cfaeb5a633c2f7d53238f;l=41) is a custom drawable that automatically calculates its surface color based on the provided `app:surfaceElevation` attribute. This can be used in xml to define a drawable similarly to ``, e.g. [`oval_surface_1`](https://source.chromium.org/chromium/chromium/src/+/main:components/browser_ui/widget/android/java/res/drawable-v31/oval_surface_1.xml;drc=1eeb153bf06ca256b6a132d9e17fd6a83e702bc4). Similar to the `ChromeColors#getSurfaceColor()` method, the provided `app:surfaceElevation` should be one of the predefined elevation levels (mentioned above). The only limitation with `SurfaceColorDrawable` is that custom drawables are only supported API 24 and above. So, until the minimum API level for Chrome is increased from 23 to 24, this method will require keeping 2 versions of the same drawable: one for `drawable-v23` and one for `drawable-v24+`. ### Illustrations The guidance for illustrations is still a work in progress. Until it is finalized, the recommended approach is continuing the use of non-dynamic colors (see the non-dynamic colors section below) but making the illustration’s background transparent. In the future, we may recommend using color resources that point to system color resources. ### Launcher widgets The guidance for widgets is not finalized, so the instructions on the [Enhance your widget developer page](https://developer.android.com/guide/topics/appwidgets/enhance#dynamic-colors) can be followed. However, the mentioned “device theming” approach is extremely limited. At some point, we may recommend using system color resources similarly to the illustrations. ## Non-dynamic colors Not all colors can or should be dynamic. Some examples are migration-to-dynamic-colors-is-work-in-progress surfaces, incognito surfaces, and WebView. Non-dynamic colors still follow the semantic name pattern to keep colors consistent throughout Chrome. They are typically defined in [semantic_colors_adaptive.xml](https://source.chromium.org/chromium/chromium/src/+/main:ui/android/java/res/values/semantic_colors_adaptive.xml;drc=0836f570d8e966dc6836120efa7998ac87a5d99b), which contains colors that adapt to day and night modes and are suffixed with _baseline to indicate that they are not dynamic, or [semantic_colors_non_adaptive.xml](https://source.chromium.org/chromium/chromium/src/+/main:ui/android/java/res/values/semantic_colors_non_adaptive.xml;drc=0836f570d8e966dc6836120efa7998ac87a5d99b) which do not adapt for day night mode and may be used for e.g. incognito coloring. ### Migration-to-dynamic-colors-is-work-in-progress You may notice that there are still semantic colors that are defined as colors and not macros or some random colors being used on some surfaces. If you notice anything like this on a surface you own or maintain, please look at migrating those colors to dynamic colors. Otherwise, you can file a bug using [this link](https://bugs.chromium.org/p/chromium/issues/entry?summary=Issue+Summary&comment=Application+Version+%28from+%22Chrome+Settings+%3E+About+Chrome%22%29%3A+%0DAndroid+Build+Number+%28from+%22Android+Settings+%3E+About+Phone%2FTablet%22%29%3A+%0DDevice%3A+%0D%0DSteps+to+reproduce%3A+%0D%0DObserved+behavior%3A+%0D%0DExpected+behavior%3A+%0D%0DFrequency%3A+%0D%3Cnumber+of+times+you+were+able+to+reproduce%3E+%0D%0DAdditional+comments%3A+%0D&labels=Restrict-View-Google%2COS-Android%2CPri-2%2CHotlist-MaterialNext&cc=skym%40chromium.org). ### Incognito surfaces Incognito surfaces should not be dynamically colored. Instead, they should be colored using the night mode baseline colors. This means using colors such as `default_icon_color_light` instead of macros or attributes. ### Colors used by WebView Dynamic colors depend on the theme attributes defined in a `Context`’s `Theme`. In the case of WebView, the Context is not controlled by Chrome but by the embedder application. We cannot be sure that the embedder application is going to provide all attributes required by our dynamic color implementation. For this reason, any UI surface that can be depended on by WebView should avoid using dynamic colors. For shared widgets like [ButtonCompat](https://source.chromium.org/chromium/chromium/src/+/main:ui/android/java/src/org/chromium/ui/widget/ButtonCompat.java;l=43), the colors can be injected through the constructor, or a custom styleable attribute can be used to manipulate the colors when the widget is used within Chrome. Colors and widgets that are shared between WebView and Chrome are typically defined in the `ui/android` directory. If this is not the case for a color or widget, they can be moved to `components/browser_ui`. ## Getting colors and drawables The method used to retrieve colors or drawables needs to have access to the theme, or Context, to be able to resolve the `?attrs`. These are the commonly used methods in Chrome: * `Context#getColor(int)`: Used to get non-dynamic colors or the default color for color state lists. * `AppCompatResources#getColorStateList(Context, int)`: Used to get color state lists. * `AppCompatResources#getDrawable(Context, int)`: Used to get drawables.