• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Setting Theme Skinning
2
3## Overview
4
5Applications developed using the ArkTS-based declarative development paradigm can implement component theming, including local light/dark modes and dynamic skin changes. This feature does not support the C-API and Node-API, and it does not support theme settings for abilities and windows.
6This document covers the following topics:
7- [Customizing Brand Colors](#customizing-brand-colors)
8- [Setting Custom Brand Colors for Application Components](#setting-custom-brand-colors-for-application-components)
9- [Setting a Custom Theme Style for Specific Application Pages](#setting-a-custom-theme-style-for-specific-application-pages)
10- [Setting Local Light and Dark Modes for Application Pages](#setting-local-light-and-dark-modes-for-application-pages)
11
12
13## Customizing Brand Colors
14Use [CustomTheme](../reference/apis-arkui/js-apis-arkui-theme.md#customtheme) to tailor themes, by selectively overriding the properties you wish to modify; the unmodified properties will inherit from the system default settings, as described in [System Default Token Color Values](#system-default-token-color-values). The following is an example.
15
16  ```ts
17    import { CustomColors, CustomTheme } from '@kit.ArkUI'
18
19    export class AppColors implements CustomColors {
20      // Custom brand color
21      brand: ResourceColor = '#FF75D9';
22    }
23
24    export class AppTheme implements CustomTheme {
25      public colors: AppColors = new AppColors()
26    }
27
28    export let gAppTheme: CustomTheme = new AppTheme()
29  ```
30
31## Setting Custom Brand Colors for Application Components
32- Set the custom brand colors at the page entry point by executing [ThemeControl](../reference/apis-arkui/js-apis-arkui-theme.md#themecontrol) before the page builds.
33Use the [onWillApplyTheme](../reference/apis-arkui/arkui-ts/ts-custom-component-lifecycle.md#onwillapplytheme12) callback function to allow custom components to access the currently active **Theme** object.
34
35  ```ts
36    import { Theme, ThemeControl } from '@kit.ArkUI'
37    import { gAppTheme } from './AppTheme'
38
39    // Execute ThemeControl before the page builds.
40    ThemeControl.setDefaultTheme(gAppTheme)
41
42    @Entry
43    @Component
44    struct DisplayPage {
45      @State menuItemColor: ResourceColor = $r('sys.color.background_primary')
46
47      onWillApplyTheme(theme: Theme) {
48        this.menuItemColor = theme.colors.backgroundPrimary;
49      }
50
51      build() {
52        Column() {
53          List({ space: 10 }) {
54            ListItem() {
55              Column({ space: '5vp' }) {
56                Text('Color mode')
57                  .margin({ top: '5vp', left: '14fp' })
58                  .width('100%')
59                Row() {
60                  Column() {
61                    Text('Light')
62                      .fontSize('16fp')
63                      .textAlign(TextAlign.Start)
64                      .alignSelf(ItemAlign.Center)
65                    Radio({ group: 'light or dark', value: 'light' })
66                      .checked(true)
67                  }
68                  .width('50%')
69
70                  Column() {
71                    Text('Dark')
72                      .fontSize('16fp')
73                      .textAlign(TextAlign.Start)
74                      .alignSelf(ItemAlign.Center)
75                    Radio({ group: 'light or dark', value: 'dark' })
76                  }
77                  .width('50%')
78                }
79              }
80              .width('100%')
81              .height('90vp')
82              .borderRadius('10vp')
83              .backgroundColor(this.menuItemColor)
84            }
85
86            ListItem() {
87              Column() {
88                Text('Brightness')
89                  .width('100%')
90                  .margin({ top: '5vp', left: '14fp' })
91                Slider({ value: 40, max: 100 })
92              }
93              .width('100%')
94              .height('70vp')
95              .borderRadius('10vp')
96              .backgroundColor(this.menuItemColor)
97            }
98
99            ListItem() {
100              Column() {
101                Row() {
102                  Column({ space: '5vp' }) {
103                    Text('Touch sensitivity')
104                      .fontSize('16fp')
105                      .textAlign(TextAlign.Start)
106                      .width('100%')
107                    Text('Increase the touch sensitivity of your screen' +
108                      ' for use with screen protectors')
109                      .fontSize('12fp')
110                      .fontColor(Color.Blue)
111                      .textAlign(TextAlign.Start)
112                      .width('100%')
113                  }
114                  .alignSelf(ItemAlign.Center)
115                  .margin({ left: '14fp' })
116                  .width('75%')
117
118                  Toggle({ type: ToggleType.Switch, isOn: true })
119                    .margin({ right: '14fp' })
120                    .alignSelf(ItemAlign.Center)
121                }
122                .width('100%')
123                .height('80vp')
124              }
125              .width('100%')
126              .borderRadius('10vp')
127              .backgroundColor(this.menuItemColor)
128            }
129          }
130        }
131        .padding('10vp')
132        .backgroundColor('#dcdcdc')
133        .width('100%')
134        .height('100%')
135      }
136    }
137  ```
138
139- Within an ability, set [ThemeControl](../reference/apis-arkui/js-apis-arkui-theme.md#themecontrol) by calling [setDefaultTheme](../reference/apis-arkui/js-apis-arkui-theme.md#setdefaulttheme) in the **onWindowStageCreate()** API.
140
141  ```ts
142    import {AbilityConstant, UIAbility, Want } from '@kit.AbilityKit';
143    import { hilog } from '@kit.PerformanceAnalysisKit';
144    import { window, CustomColors, ThemeControl } from '@kit.ArkUI';
145
146    class AppColors implements CustomColors {
147      fontPrimary = 0xFFD53032
148      iconOnPrimary = 0xFFD53032
149      iconFourth = 0xFFD53032
150    }
151
152    const abilityThemeColors = new AppColors();
153
154    export default class EntryAbility extends UIAbility {
155      onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) {
156        hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onCreate');
157      }
158
159      onDestroy() {
160        hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onDestroy');
161      }
162
163      onWindowStageCreate(windowStage: window.WindowStage) {
164        // Main window is created, set main page for this ability
165        hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageCreate');
166
167        windowStage.loadContent('pages/Index', (err, data) => {
168          if (err.code) {
169            hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? '');
170            return;
171          }
172          hilog.info(0x0000, 'testTag', 'Succeeded in loading the content. Data: %{public}s', JSON.stringify(data) ?? '');
173          // Call setDefaultTheme in the onWindowStageCreate() API.
174          ThemeControl.setDefaultTheme({ colors: abilityThemeColors })
175          hilog.info(0x0000, 'testTag', '%{public}s', 'ThemeControl.setDefaultTheme done');
176        });
177      }
178
179    }
180  ```
181
182![systemTheme](figures/systemTheme.png)
183
184Note: If the parameter for **setDefaultTheme** is undefined, refer to [System Default Token Color Values](#system-default-token-color-values) for the default color values associated with the tokens.
185
186## Setting a Custom Theme Style for Specific Application Pages
187Use [WithTheme](../reference/apis-arkui/arkui-ts/ts-container-with-theme.md) to apply the color scheme of a custom theme to the default styles of components within the scope. This way, the colors of components within the **WithTheme** scope align with the theme's color scheme.
188In the example below, components within the scope are styled with a custom theme by using **WithTheme({ theme: this.myTheme })**. You can switch to a different theme style by updating **this.myTheme**.
189The [onWillApplyTheme](../reference/apis-arkui/arkui-ts/ts-custom-component-lifecycle.md#onwillapplytheme12) callback function allows custom components to access the currently active **Theme** object.
190
191  ```ts
192    import { CustomColors, CustomTheme, Theme } from '@kit.ArkUI'
193
194    class AppColors implements CustomColors {
195      fontPrimary: ResourceColor = $r('app.color.brand_purple')
196      backgroundEmphasize: ResourceColor = $r('app.color.brand_purple')
197    }
198
199    class AppColorsSec implements CustomColors {
200      fontPrimary: ResourceColor = $r('app.color.brand')
201      backgroundEmphasize: ResourceColor = $r('app.color.brand')
202    }
203
204    class AppTheme implements CustomTheme {
205      public colors: AppColors = new AppColors()
206    }
207
208    class AppThemeSec implements CustomTheme {
209      public colors: AppColors = new AppColorsSec()
210    }
211
212    @Entry
213    @Component
214    struct DisplayPage {
215      @State customTheme: CustomTheme = new AppTheme()
216      @State message: string = 'Set a custom theme style for specific pages'
217      count = 0;
218
219      build() {
220        WithTheme({ theme: this.customTheme }) {
221          Row(){
222            Column() {
223              Text('WithTheme')
224                .fontSize(30)
225                .margin({bottom: 10})
226              Text(this.message)
227                .margin({bottom: 10})
228              Button('Change Theme').onClick(() => {
229                this.count++;
230                if (this.count > 1) {
231                  this.count = 0;
232                }
233                switch (this.count) {
234                  case 0:
235                    this.customTheme = new AppTheme();
236                    break;
237                  case 1:
238                    this.customTheme = new AppThemeSec();
239                    break;
240                }
241              })
242            }
243            .width('100%')
244          }
245          .height('100%')
246          .width('100%')
247        }
248      }
249    }
250  ```
251
252![customTheme](figures/customTheme.gif)
253
254## Setting Local Light and Dark Modes for Application Pages
255Use [WithTheme](../reference/apis-arkui/arkui-ts/ts-container-with-theme.md) to set light and dark modes. [ThemeColorMode](../reference/apis-arkui/arkui-ts/ts-appendix-enums.md#themecolormode10) offers three options: **ThemeColorMode.SYSTEM** for following the system setting, **ThemeColorMode.LIGHT** for light mode, and **ThemeColorMode.DARK** for dark mode.<br>
256Within the **WithTheme** scope, component styles adapt to the specified light or dark color mode by accessing the corresponding system and application resource values, and the color schemes of components are activated according to the chosen color mode.<br>
257In the example below, components within the scope are set to dark mode using **WithTheme({ colorMode: ThemeColorMode.DARK })**.
258
259To enable light and dark modes, add a **dark.json** resource file for the modes to take effect.
260
261![resources_dark](figures/resources_dark.png)
262
263Example of the **dark.json** file content:
264  ```ts
265    {
266      "color": [
267        {
268          "name": "start_window_background",
269          "value": "#FFFFFF"
270        }
271      ]
272    }
273  ```
274
275  ```ts
276    @Entry
277    @Component
278    struct DisplayPage {
279      @State message: string = 'Hello World';
280      @State colorMode: ThemeColorMode = ThemeColorMode.DARK;
281
282      build() {
283        WithTheme({ colorMode: this.colorMode }) {
284          Row() {
285            Column() {
286              Text(this.message)
287                .fontSize(50)
288                .fontWeight(FontWeight.Bold)
289              Button('Switch ColorMode').onClick(() => {
290                if (this.colorMode === ThemeColorMode.LIGHT) {
291                  this.colorMode = ThemeColorMode.DARK;
292                } else if (this.colorMode === ThemeColorMode.DARK) {
293                  this.colorMode = ThemeColorMode.LIGHT;
294                }
295              })
296            }
297            .width('100%')
298          }
299          .backgroundColor($r('sys.color.background_primary'))
300          .height('100%')
301          .expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP, SafeAreaEdge.END, SafeAreaEdge.BOTTOM, SafeAreaEdge.START])
302        }
303      }
304    }
305  ```
306
307![lightDarkMode](figures/lightDarkMode.png)
308
309## System Default Token Color Values
310
311| Token                                      | Category| Light |           | Dark    |                                              |
312|--------------------------------------------|-----| --- |-----------| ------- | -------------------------------------------- |
313| theme.colors.brand                         | Brand color.|#ff0a59f7| ![](figures/ff0a59f7.png "#ff0a59f7") |#ff317af7|![](figures/ff317af7.png "#ff317af7")|
314| theme.colors.warning                       | Alert color.|#ffe84026| ![](figures/ffe84026.png "#ffe84026") |#ffd94838|![](figures/ffd94838.png "#ffd94838")|
315| theme.colors.alert                         | Warning color.|#ffed6f21| ![](figures/ffed6f21.png "#ffed6f21") |#ffdb6b42|![](figures/ffdb6b42.png "#ffdb6b42")|
316| theme.colors.confirm                       | Confirmation color.|#ff64bb5c| ![](figures/ff64bb5c.png "#ff64bb5c") |#ff5ba854|![](figures/ff5ba854.png "#ff5ba854")|
317| theme.colors.fontPrimary                   | Primary text color.| #e5000000 | ![](figures/e5000000.png "#e5000000") |#e5ffffff|![](figures/e5ffffff.png "#e5ffffff")|
318| theme.colors.fontSecondary                 | Secondary text color.| #99000000 | ![](figures/99000000.png "#99000000") |#99ffffff|![](figures/99ffffff.png "#99ffffff")|
319| theme.colors.fontTertiary                  | Tertiary text color.| #66000000 | ![](figures/66000000.png "#66000000") |#66ffffff|![](figures/66ffffff.png "#66ffffff")|
320| theme.colors.fontFourth                    | Quaternary text color.| #33000000 | ![](figures/33000000.png "#33000000") |#33ffffff|![](figures/33ffffff.png "#33ffffff")|
321| theme.colors.fontEmphasize                 | Highlight text color.| #ff0a59f7 | ![](figures/ff0a59f7.png "#ff0a59f7") |#ff317af7|![](figures/ff317af7.png "#ff317af7")|
322| theme.colors.fontOnPrimary                 | Primary text invert color.| #ffffffff | ![](figures/ffffffff.png "#ffffffff") |#ff000000|![](figures/ff000000.png "#ff000000")|
323| theme.colors.fontOnSecondary               | Secondary text invert color.| #99ffffff | ![](figures/99ffffff.png "#99ffffff") |#99000000|![](figures/99000000.png "#99000000")|
324| theme.colors.fontOnTertiary                | Tertiary text invert color.| #66ffffff | ![](figures/66ffffff.png "#66ffffff") |#66000000|![](figures/66000000.png "#66000000")|
325| theme.colors.fontOnFourth                  | Quaternary text invert color.| #33ffffff | ![](figures/33ffffff.png "#33ffffff") |#33000000|![](figures/33000000.png "#33000000")|
326| theme.colors.iconPrimary                   | Primary icon color.| #e5000000 | ![](figures/e5000000.png "#e5000000") |#e5ffffff|![](figures/e5ffffff.png "#e5ffffff")|
327| theme.colors.iconSecondary                 | Secondary icon color.| #99000000 | ![](figures/99000000.png "#99000000") |#99ffffff|![](figures/99ffffff.png "#99ffffff")|
328| theme.colors.iconTertiary                  | Tertiary icon color.| #66000000 | ![](figures/66000000.png "#66000000") |#66ffffff|![](figures/66ffffff.png "#66ffffff")|
329| theme.colors.iconFourth                    | Quaternary icon color.| #33000000 | ![](figures/33000000.png "#33000000") |#33ffffff|![](figures/33ffffff.png "#33ffffff")|
330| theme.colors.iconEmphasize                 | Emphasis icon color.| #ff0a59f7 | ![](figures/ff0a59f7.png "#ff0a59f7") |#ff317af7|![](figures/ff317af7.png "#ff317af7")|
331| theme.colors.iconSubEmphasize              | Emphasis auxiliary icon color.| #660a59f7 | ![](figures/660a59f7.png "#660a59f7") |#66317af7|![](figures/66317af7.png "#66317af7")|
332| theme.colors.iconOnPrimary                 | Primary icon invert color.| #ffffffff | ![](figures/ffffffff.png "#ffffffff") |#ff000000|![](figures/ff000000.png "#ff000000")|
333| theme.colors.iconOnSecondary               | Secondary icon invert color.| #99ffffff | ![](figures/99ffffff.png "#99ffffff") |#99000000|![](figures/99000000.png "#99000000")|
334| theme.colors.iconOnTertiary                | Tertiary icon invert color.| #66ffffff | ![](figures/66ffffff.png "#66ffffff") |#66000000|![](figures/66000000.png "#66000000")|
335| theme.colors.iconOnFourth                  | Quaternary icon invert color.| #33ffffff | ![](figures/33ffffff.png "#33ffffff") |#33000000|![](figures/33000000.png "#33000000")|
336| theme.colors.backgroundPrimary             | Primary background color (solid, opaque).| #ffffffff | ![](figures/ffffffff.png "#ffffffff") |#ffe5e5e5|![](figures/ffe5e5e5.png "#ffe5e5e5")|
337| theme.colors.backgroundSecondary           | Secondary background color (solid, opaque).| #fff1f3f5 | ![](figures/fff1f3f5.png "#fff1f3f5") |#ff191a1c|![](figures/ff191a1c.png "#ff191a1c")|
338| theme.colors.backgroundTertiary            | Tertiary background color (solid, opaque).| #ffe5e5ea | ![](figures/ffe5e5ea.png "#ffe5e5ea") |#ff202224|![](figures/ff202224.png "#ff202224")|
339| theme.colors.backgroundFourth              | Quaternary background color (solid, opaque).| #ffd1d1d6 | ![](figures/ffd1d1d6.png "#ffd1d1d6") |#ff2e3033|![](figures/ff2e3033.png "#ff2e3033")|
340| theme.colors.backgroundEmphasize           | Emphasis background color (solid, opaque).| #ff0a59f7 | ![](figures/ff0a59f7.png "#ff0a59f7") |#ff317af7|![](figures/ff317af7.png "#ff317af7")|
341| theme.colors.compForegroundPrimary         | Foreground.| #ff000000 | ![](figures/ff000000.png "#ff000000") | #ffe5e5e5 |![](figures/ffe5e5e5.png "#ffe5e5e5")|
342| theme.colors.compBackgroundPrimary         | White background.| #ffffffff |![](figures/ffffffff.png "#ffffffff")| #ffffffff |![](figures/ffffffff.png "#ffffffff")|
343| theme.colors.compBackgroundPrimaryTran     | White transparent background.| #ffffffff |![](figures/ffffffff.png "#ffffffff")| #33ffffff |![](figures/33ffffff.png "#33ffffff")|
344| theme.colors.compBackgroundPrimaryContrary | Always-on background.| #ffffffff |![](figures/ffffffff.png "#ffffffff")| #ffe5e5e5 |![](figures/ffe5e5e5.png "#ffe5e5e5")|
345| theme.colors.compBackgroundGray            | Gray background.| #fff1f3f5 |![](figures/fff1f3f5.png "#fff1f3f5")| #ffe5e5ea |![](figures/ffe5e5ea.png "#ffe5e5ea")|
346| theme.colors.compBackgroundSecondary       | Secondary background.| #19000000 |![](figures/19000000.png "#19000000")| #19ffffff |![](figures/19ffffff.png "#19ffffff")|
347| theme.colors.compBackgroundTertiary        | Tertiary background.| #0c000000 |![](figures/0c000000.png "#0c000000")| #0cffffff |![](figures/0cffffff.png "#0cffffff")|
348| theme.colors.compBackgroundEmphasize       | Emphasis background.| #ff0a59f7 |![](figures/ff0a59f7.png "#ff0a59f7")| #ff317af7 |![](figures/ff317af7.png "#ff317af7")|
349| theme.colors.compBackgroundNeutral         | Black, neutral, emphasis background.| #ff000000 |![](figures/ff000000.png "#ff000000")| #ffffffff |![](figures/ffffffff.png "#ffffffff")|
350| theme.colors.compEmphasizeSecondary        | 20% emphasis background.| #330a59f7 |![](figures/330a59f7.png "#330a59f7")| #33317af7 |![](figures/33317af7.png "#33317af7")|
351| theme.colors.compEmphasizeTertiary         | 10% emphasis background.| #190a59f7 |![](figures/190a59f7.png "#190a59f7")| #19317af7 |![](figures/19317af7.png "#19317af7")|
352| theme.colors.compDivider                   | Divider color.| #33000000 |![](figures/33000000.png "#33000000")| #33ffffff |![](figures/33ffffff.png "#33ffffff")|
353| theme.colors.compCommonContrary            | Common invert color.| #ffffffff |![](figures/ffffffff.png "#ffffffff")| #ff000000 |![](figures/ff000000.png "#ff000000")|
354| theme.colors.compBackgroundFocus           | Background color in the focused state.| #fff1f3f5 |![](figures/fff1f3f5.png "#fff1f3f5")| #ff000000 |![](figures/fff1f3f5.png "#fff1f3f5")|
355| theme.colors.compFocusedPrimary            | Primary inverted color in the focused state.| #e5000000 |![](figures/e5000000.png "#e5000000")| #e5ffffff |![](figures/e5ffffff.png "#e5ffffff")|
356| theme.colors.compFocusedSecondary          | Secondary inverted color in the focused state.| #99000000 |![](figures/99000000.png "#99000000")| #99ffffff |![](figures/99ffffff.png "#99ffffff")|
357| theme.colors.compFocusedTertiary           | Tertiary inverted color in the focused state.| #66000000 |![](figures/66000000.png "#66000000")| #66ffffff |![](figures/66ffffff.png "#66ffffff")|
358| theme.colors.interactiveHover              | Common interactive color for the hover state.| #0c000000 |![](figures/0c000000.png "#0c000000")| #0cffffff |![](figures/0cffffff.png "#0cffffff")|
359| theme.colors.interactivePressed            | Common interactive color for the pressed state.| #19000000 |![](figures/19000000.png "#19000000")| #19ffffff |![](figures/19ffffff.png "#19ffffff")|
360| theme.colors.interactiveFocus              | Common interactive color for the focused state.| #ff0a59f7 |![](figures/ff0a59f7.png "#ff0a59f7")| #ff317af7 |![](figures/ff317af7.png "#ff317af7")|
361| theme.colors.interactiveActive             | Common interactive color for the active state.| #ff0a59f7 |![](figures/ff0a59f7.png "#ff0a59f7")| #ff317af7 |![](figures/ff317af7.png "#ff317af7")|
362| theme.colors.interactiveSelect             | Common interactive color for the selected state.| #33000000 |![](figures/33000000.png "#33000000")| #33ffffff |![](figures/33ffffff.png "#33ffffff")|
363| theme.colors.interactiveClick              | Common interactive color for the clicked state.| #19000000 |![](figures/19000000.png "#19000000")| #19ffffff |![](figures/19ffffff.png "#19ffffff")|
364