DEV Community

HarmonyOS
HarmonyOS

Posted on

๐ŸŒ’ HarmonyOS Next: Supporting Light and Dark Color Modes in Your App

Read the original article๏ผš๐ŸŒ’ HarmonyOS Next: Supporting Light and Dark Color Modes in Your App

Introduction

There are two main types of themes: custom app themes and built-in dark and light themes. Custom app themes can be created by developers, while dark and light themes are integrated into the operating system. In this article, we will focus on built-in dark and light themes.

  • ๐ŸŒ™โ˜€๏ธ The phone's built-in systems support both Classic Dark Mode and Light Mode.
  • ๐ŸŽจ Custom app skinning theme, which can be defined by a developer.

๐ŸŒ™โ˜€๏ธ Dark and Light Mode Implementation

Let's initiate a project to implement both dark ๐ŸŒ‘ and light ๐ŸŒž color modes.

Scenarios based on Dark & Light mode settings:

  • The color mode of the app is in sync with the system color mode (dark/light) by default.
  • We can disable sync between the system color mode and the app manually.
  • We can select light or dark mode manually.
  • We can use components that have defined colors statically that wonโ€™t be affected by any color mode setting (system/app).
  • We can use components that have defined colors dynamically, which will be affected by the user's will.

๐Ÿ‘จโ€๐Ÿ’ป Implementing All Scenarios of Dark & Light Mode

First, check the app's current colorMode. It returns an integer or undefined: 1 for light mode โ˜€๏ธ and 0 for dark mode ๐ŸŒ™.

Important! โš ๏ธ This will set the appโ€™s color mode ๐ŸŽจ, not the systemโ€™s. By default, they are synced ๐Ÿ”„, but if changed manually, they may differ.
Button('Get Settings').onClick(() => {
  let context = this.getUIContext().getHostContext() as common.UIAbilityContext;
  this.colorMode = context.config.colorMode === 1 ? 'Light' : 'Dark';
})
  .width('40%')
Enter fullscreen mode Exit fullscreen mode

To set the color mode, you have two options: Light and Dark mode. Once you manually set the app's color mode, it will remain that way even if the system's color mode changes.

Button('Set Dark').onClick(() => {
  let context = this.getUIContext().getHostContext() as common.UIAbilityContext;
  context.getApplicationContext().setColorMode(ConfigurationConstant.ColorMode.COLOR_MODE_DARK);
  this.colorMode = 'Dark';
})
  .width('40%')


Button('Set Light').onClick(() => {
  let context = this.getUIContext().getHostContext() as common.UIAbilityContext;
  context.getApplicationContext().setColorMode(ConfigurationConstant.ColorMode.COLOR_MODE_LIGHT);
  this.colorMode = 'Light';
})
  .width('40%')
Enter fullscreen mode Exit fullscreen mode

To assign colors that will be used during color mode changes, you need to make the color properties of your components dynamic. Start by defining different colors for each color mode. After creating the project, you will find two files in the entry/src/main/resources folder: one named "Base" and the other named "Dark."

In base/element/color.json you define colors for light color mode, and in dark/element/color.json you define colors for dark color mode.

After defining and setting component colors through the files listed below, they will automatically change according to dark or light mode.

Please ensure that the names are accurate and consistent in both files.
{
  "color": [
    {
      "name": "start_window_background",
      "value": "#000000"
    },
    {
      "name": "index_row_background_color",
      "value": "#FF393939"
    },
    {
      "name": "index_text_color",
      "value": "#FFDBDBDB"
    }
  ]
}

{
  "color": [
    {
      "name": "start_window_background",
      "value": "#FFFFFF"
    },
    {
      "name": "index_text_color",
      "value": "#FF393939"
    },
    {
      "name": "index_row_background_color",
      "value": "#FFDBDBDB"
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

After setting the dark and light colors in color.json during a color mode change, your component's color will be updated according to the theme.

Row() {
  Text('This will change since its dynamically set.')
    .width('100%')
    .textAlign(TextAlign.Center)
    .fontColor($r('app.color.index_text_color'))  // this is the magic part ๐Ÿช„
    .padding(10)
}
.width('50%')
.height('20%')
.backgroundColor($r('app.color.index_row_background_color'))  // this is the magic part ๐Ÿช„
Enter fullscreen mode Exit fullscreen mode

If you want to make it static and unaffected by mode changes, you can choose colors as desired. Here is an example:

Row() {
  Text('This won\'t change since its statically set.')
    .width('100%')
    .textAlign(TextAlign.Center)
    .fontColor(Color.Black) // this won't change in any case ๐Ÿšซ
    .padding(10)
}
.width('50%')
.height('20%')
.backgroundColor(Color.White) // this won't change in any case ๐Ÿšซ
Enter fullscreen mode Exit fullscreen mode

๐Ÿ“ขโ—๐Ÿšจ Important Notes

It's essential to specify a color when using a built-in component; otherwise, it will default to the standard theme, even if no colors are defined in the JSON files.


Here, you can view an official document explaining how judgment works. You can find the default theme colors for both dark and light modes here.

๐Ÿค”If you want to change the start window background, it is set to default in the base and dark color.json files, named start_window_background. This is implemented in the module.json5 as shown here:

"startWindowBackground": "$color:start_window_background",

Enter fullscreen mode Exit fullscreen mode

If you want to sync with the system color mode, set it like this to revert to the default.

Button('Set Auto').onClick(() => {
  let context = this.getUIContext().getHostContext() as common.UIAbilityContext;
  context.getApplicationContext().setColorMode(ConfigurationConstant.ColorMode.COLOR_MODE_NOT_SET);
  this.colorMode = context.config.colorMode === 1 ? 'Light' : 'Dark';
})
  .width('40%')
Enter fullscreen mode Exit fullscreen mode

You can access the full code here: ๐Ÿ“œ๐Ÿ”—

import { common, ConfigurationConstant } from '@kit.AbilityKit';

@Entry
@Component
struct Index {
  @State colorMode: string = '';

  build() {
    Row() {
      Column({ space: 20 }) {
        Text('Color Mode\n' + this.colorMode)
          .fontSize(30)
          .fontWeight(FontWeight.Bold)
          .width('100%')
          .textAlign(TextAlign.Center)

        Row() {
          Text('This won\'t change since its statically set.')
            .width('100%')
            .textAlign(TextAlign.Center)
            .fontColor(Color.Black)
            .padding(10)
        }
        .width('50%')
        .height('20%')
        .backgroundColor(Color.White)

        Row() {
          Text('This will change since its dynamically set.')
            .width('100%')
            .textAlign(TextAlign.Center)
            .fontColor($r('app.color.index_text_color'))
            .padding(10)
        }
        .width('50%')
        .height('20%')
        .backgroundColor($r('app.color.index_row_background_color'))

        Button('Get Settings').onClick(() => {
          let context = this.getUIContext().getHostContext() as common.UIAbilityContext;
          this.colorMode = context.config.colorMode === 1 ? 'Light' : 'Dark';
        })
          .width('40%')

        Button('Set Auto').onClick(() => {
          let context = this.getUIContext().getHostContext() as common.UIAbilityContext;
          context.getApplicationContext().setColorMode(ConfigurationConstant.ColorMode.COLOR_MODE_NOT_SET);
          this.colorMode = context.config.colorMode === 1 ? 'Light' : 'Dark';
        })
          .width('40%')


        Button('Set Dark').onClick(() => {
          let context = this.getUIContext().getHostContext() as common.UIAbilityContext;
          context.getApplicationContext().setColorMode(ConfigurationConstant.ColorMode.COLOR_MODE_DARK);
          this.colorMode = 'Dark';
        })
          .width('40%')


        Button('Set Light').onClick(() => {
          let context = this.getUIContext().getHostContext() as common.UIAbilityContext;
          context.getApplicationContext().setColorMode(ConfigurationConstant.ColorMode.COLOR_MODE_LIGHT);
          this.colorMode = 'Light';
        })
          .width('40%')

      }
      .width('100%')
    }
    .height('100%')
  }
}
Enter fullscreen mode Exit fullscreen mode

๐ŸConclusion

Huawei HarmonyOS, along with ArkTS, simplifies development by making it more modular. ๐ŸŒŸ As demonstrated in this example, it automatically manages color mode changes in applications ๐ŸŽจ while also enabling developers to customize UI components as they wish. This approach to handling color modes allows for easier management of design elements. โœจ

๐Ÿ”— References

Implementing Dark and Light Mode Adaptation-Setting the Theme-UI Development (ArkTS-based Declarative Development Paradigm)-ArkUI-Application Framework - HUAWEI Developers

Written by Muaz Kartal

Top comments (0)