ArkTS Best Practices: How to Build a Modular Theme Management System in 2025

arkts-best-practices:-how-to-build-a-modular-theme-management-system-in-2025

Read the original article:ArkTS Best Practices: How to Build a Modular Theme Management System in 2025

Theme management on HarmonyOS Next mobile apps
​​

A complete guide on building modular, scalable, and reactive theme management in ArkTS for HarmonyOS Next applications.

Introduction

Have you ever tried switching between light and dark modes in your ArkTS app, only to find your components visually breaking or needing manual updates? In 2025, with HarmonyOS Next gaining traction, having a centralized and modular theme system isn’t just a nice-to-have — it’s a best practice.

This article walks you through building a scalable and reusable theme management system using ArkTS. We’ll show how it mirrors best practices from frameworks like Flutter, yet is tailored to HarmonyOS constraints.

In HarmonyOS Next development, especially for wearables or multi-device applications, a consistent theme is essential. Styling inconsistencies lead to poor user experience and maintenance overhead. Unlike Flutter’s Theme.of(context) or copyWith ArkTS lacks out-of-the-box theme support, which makes it important to implement a solid custom solution.

Description

We aim to:

  • Create centralized theme definitions

  • Allow runtime switching between Light and Dark modes

  • Provide reusable components that react to theme changes

  • Reduce code duplication

We’ll cover:

  • ThemeMode enum for mode management

  • ColorScheme and TextStyle for central styling

  • AppTheme as the core context holder

  • ThemeStore for reactivity

  • ThemedText, ThemedButton, and more

Solution / Approach

1. ThemeMode Enum

export enum ThemeMode {
  LIGHT,
  DARK,
}

This helps us manage modes cleanly using strict types.

2. TextStyle & ComponentStyle Interfaces

export interface TextStyle {
  fontSize: number;
  fontWeight: number;
  color: string;
  fontFamily?: string;
}

These define base styling contracts for text and UI elements.

3. ColorScheme & IColorSchemeOptions

export interface IColorSchemeOptions {
  primary: string;
  onPrimary: string;
  background: string;
  onBackground: string;
  surface: string;
  onSurface: string;
  error: string;
  onError: string;
}

export class ColorScheme {
  constructor(options: IColorSchemeOptions) {
    // Assign properties here
  }
}

We use IColorSchemeOptions to clearly define required parameters.

4. Light & Dark Color Schemes

export const LightColorScheme = new ColorScheme({
  primary: '#1976D2',
  onPrimary: '#FFFFFF',
  background: '#FFFFFF',
  onBackground: '#000000',
  surface: '#F5F5F5',
  onSurface: '#000000',
  error: '#D32F2F',
  onError: '#FFFFFF'
});

export const DarkColorScheme = new ColorScheme({
  primary: '#90CAF9',
  onPrimary: '#000000',
  background: '#121212',
  onBackground: '#FFFFFF',
  surface: '#1E1E1E',
  onSurface: '#FFFFFF',
  error: '#CF6679',
  onError: '#000000'
});

5. AppTheme Class

export class AppTheme {
  colors: ColorScheme;
  textTheme: TextTheme;
  componentTheme: ComponentTheme;

  constructor(mode: ThemeMode) {
    this.colors = mode === ThemeMode.DARK ? DarkColorScheme : LightColorScheme;
    this.textTheme = new TextTheme(this.colors);
    this.componentTheme = new ComponentTheme(this.colors);
  }
}

AppTheme acts like the root context of all style-related definitions.

6. TextTheme and ComponentTheme

export class TextTheme {
  titleLarge: TextStyle;
  // ... other text styles
  constructor(colors: ColorScheme) {
    this.titleLarge = { fontSize: 24, fontWeight: 700, color: colors.primary };
  }
}

export class ComponentTheme {
  buttonStyle: ComponentStyle;
  constructor(colors: ColorScheme) {
    this.buttonStyle = {
      backgroundColor: colors.primary,
      fontColor: colors.onPrimary,
      borderRadius: 12,
      padding: 12
    };
  }
}

7. ThemeStore (Reactive Store)

@Observed
export class ThemeStore {
  theme: AppTheme = new AppTheme(ThemeMode.LIGHT);

  setTheme(mode: ThemeMode) {
    this.theme = new AppTheme(mode);
  }
}
export const themeStore = new ThemeStore();

Any UI using this store will automatically update on mode change.

8. Helper: applyTextStyle & copyWith

export function applyTextStyle(text: StylableText, style: TextStyle): StylableText {
  return text
    .fontSize(`${style.fontSize}fp`)
    .fontWeight(style.fontWeight)
    .fontColor(style.color)
    .fontFamily(style.fontFamily ?? 'HarmonyOS Sans');
}

export class TextStyleImpl implements TextStyle {
  // All props
  copyWith(overrides: Partial): TextStyle {
    return new TextStyleImpl({ ...this, ...overrides });
  }
}

9. Themed Components (Reusable)

@Component
export struct ThemedText {
  @Prop text: string;
  @Prop style: TextStyle;
  build() {
    Text(this.text)
      .fontSize(`${this.style.fontSize}fp`)
      .fontWeight(this.style.fontWeight)
      .fontColor(this.style.color)
  }
}

These components consume styles from themeStore.theme.

Conclusion

Building a modular theme management system in ArkTS is not just about aesthetics — it’s about creating scalable, maintainable, and consistent experiences across your entire application.

By centralizing theme logic using AppTheme, ThemeStore, and reusable components like ThemedText developers can reduce duplication, simplify updates, and enable seamless light/dark mode switching.

If you’re building for HarmonyOS Next in 2025, this approach will save you time, prevent UI inconsistencies, and elevate your app’s user experience.

🧠 What’s next? Try extending this architecture with typography scaling, spacing tokens, or even user-selectable themes via settings.

💬 How are you handling themes in your ArkTS project? Share your tips or questions in the comments!

Key Takeaways

  • ArkTS lacks built-in theme systems, so we must build them.

  • Inspired by Flutter’s Theme.of() and copyWith.

  • Centralization improves consistency, scalability, and DX.

  • Reactive store enables real-time theme switching.

🧠 Pro Tip: You can also structure your themes as external resource files using and access them via , but modular runtime control is still easier using in-code object-based theme stores.resources/color.json$r()

✍️Written by Arif Emre Ankara

Total
0
Shares
Leave a Reply

Your email address will not be published. Required fields are marked *

Previous Post
take-control:-manage-hybrid-cloud-environment-using-azure-arc

Take Control: Manage Hybrid Cloud Environment Using Azure Arc

Next Post
the-psychology-behind-clicks:-where-digital-marketing-meets-neuroscience

The psychology behind clicks: Where digital marketing meets neuroscience

Related Posts