ArkUI 主题令牌:颜色要有职责,不要随手取值

举报
蓝瘦的蜕变 发表于 2026/09/23 17:30:59 2026/09/23
【摘要】 主题不是给页面换一套好看的颜色。主题令牌要回答颜色的职责:哪个是品牌主色,哪个是警示色,哪个代表确认结果,哪个承担主要正文。 本篇边界 这篇只做 CustomTheme / ThemeControl 的最小示例,不扩展成完整设计系统。示例只列 4 个常用颜色令牌,避免一开始维护一大套无人使用的 token。 交互预期

ArkUI 主题令牌:颜色要有职责,不要随手取值

主题不是给页面换一套好看的颜色。主题令牌要回答颜色的职责:哪个是品牌主色,哪个是警示色,哪个代表确认结果,哪个承担主要正文。

本篇边界

这篇只做 CustomTheme / ThemeControl 的最小示例,不扩展成完整设计系统。示例只列 4 个常用颜色令牌,避免一开始维护一大套无人使用的 token。

交互预期

  • 点击“清爽蓝 / 松石绿”,页面会切换局部主题预览。
  • 点击“设置为当前页面默认主题”,会调用 ThemeControl.setDefaultTheme 并显示反馈。
  • 下方令牌清单会展示当前主题的 brand / warning / confirm / fontPrimary

Builder 刷新规则

本页已完成 @Builder 参数刷新检阅。分区标题不再使用 sectionTitle(title, desc) 多参数 Builder;令牌卡片不再通过 ThemeTokenItem class 对象传入 Builder,改为 tokenCard(index) 后在 Builder 内部读取当前主题令牌。

完整示例代码

import { CustomTheme, ThemeControl } from '@kit.ArkUI'

class ProductThemeItem {
  label: string
  desc: string
  brand: string
  warning: string
  confirm: string
  fontPrimary: string

  constructor(label: string, desc: string, brand: string, warning: string, confirm: string, fontPrimary: string) {
    this.label = label
    this.desc = desc
    this.brand = brand
    this.warning = warning
    this.confirm = confirm
    this.fontPrimary = fontPrimary
  }
}

@Entry
@Component
struct ArkUIThemeTokenDemo {
  @State themeIndex: number = 0
  @State feedback: string = '尚未设置默认主题,当前只显示局部主题预览。'

  private themes: ProductThemeItem[] = [
    new ProductThemeItem('清爽蓝', '适合效率工具和信息展示。', '#2563EB', '#B45309', '#0F766E', '#111827'),
    new ProductThemeItem('松石绿', '适合健康、协作和轻量运营。', '#0F766E', '#C2410C', '#15803D', '#0F172A')
  ]

  private currentThemeItem(): ProductThemeItem {
    return this.themes[this.themeIndex]
  }

  private currentTheme(): CustomTheme {
    const item = this.currentThemeItem()
    const theme: CustomTheme = {
      colors: {
        brand: item.brand,
        warning: item.warning,
        confirm: item.confirm,
        fontPrimary: item.fontPrimary
      }
    }
    return theme
  }

  private tokenName(index: number): string {
    if (index === 0) {
      return 'brand'
    }
    if (index === 1) {
      return 'warning'
    }
    if (index === 2) {
      return 'confirm'
    }
    return 'fontPrimary'
  }

  private tokenDesc(index: number): string {
    if (index === 0) {
      return '品牌主色,优先驱动主按钮和强调信息。'
    }
    if (index === 1) {
      return '警示色,用于风险提示,不要移用成普通强调。'
    }
    if (index === 2) {
      return '确认色,用于成功、完成和正向结果。'
    }
    return '一级正文色,保证主要内容可读。'
  }

  private tokenValue(index: number): string {
    const item = this.currentThemeItem()
    if (index === 0) {
      return item.brand
    }
    if (index === 1) {
      return item.warning
    }
    if (index === 2) {
      return item.confirm
    }
    return item.fontPrimary
  }

  @Builder
  previewSectionTitle() {
    Column({ space: 6 }) {
      Text('主题预览')
        .fontSize(19)
        .fontWeight(FontWeight.Bold)
        .fontColor('#111827')
      Text('本页用 CustomTheme 做局部主题,并提供一个按钮演示 ThemeControl.setDefaultTheme。')
        .fontSize(13)
        .fontColor('#6B7280')
        .lineHeight(20)
    }
    .width('100%')
    .alignItems(HorizontalAlign.Start)
  }

  @Builder
  tokenSectionTitle() {
    Column({ space: 6 }) {
      Text('令牌清单')
        .fontSize(19)
        .fontWeight(FontWeight.Bold)
        .fontColor('#111827')
      Text('示例只列最常用的 4 个颜色令牌,避免一开始写成完整设计系统。')
        .fontSize(13)
        .fontColor('#6B7280')
        .lineHeight(20)
    }
    .width('100%')
    .alignItems(HorizontalAlign.Start)
  }

  @Builder
  themeButton(index: number) {
    Button(this.themes[index].label)
      .height(42)
      .layoutWeight(1)
      .fontSize(14)
      .fontColor(this.themeIndex === index ? '#FFFFFF' : '#334155')
      .backgroundColor(this.themeIndex === index ? this.themes[index].brand : '#E5E7EB')
      .onClick(() => {
        this.themeIndex = index
        this.feedback = `已切换局部主题预览:${this.currentThemeItem().label}。`
      })
  }

  @Builder
  tokenCard(index: number) {
    Row({ space: 12 }) {
      Blank()
        .width(42)
        .height(42)
        .backgroundColor(this.tokenValue(index))
        .borderRadius(8)
      Column({ space: 4 }) {
        Text(this.tokenName(index))
          .fontSize(15)
          .fontWeight(FontWeight.Bold)
          .fontColor('#111827')
        Text(this.tokenDesc(index))
          .fontSize(13)
          .fontColor('#6B7280')
          .lineHeight(20)
        Text(this.tokenValue(index))
          .fontSize(12)
          .fontColor('#475569')
      }
      .layoutWeight(1)
      .alignItems(HorizontalAlign.Start)
    }
    .width('100%')
    .padding(14)
    .borderRadius(12)
    .backgroundColor('#FFFFFF')
    .border({ width: 1, color: '#E5E7EB' })
  }

  @Builder
  themePreview() {
    WithTheme({ theme: this.currentTheme() }) {
      Column({ space: 12 }) {
        Text(this.currentThemeItem().label)
          .fontSize(22)
          .fontWeight(FontWeight.Bold)
          .fontColor(this.currentThemeItem().fontPrimary)
        Text(this.currentThemeItem().desc)
          .fontSize(14)
          .fontColor('#475569')
          .lineHeight(22)
        Row({ space: 10 }) {
          Text('主要动作')
            .fontSize(14)
            .fontColor('#FFFFFF')
            .padding({ left: 12, right: 12, top: 8, bottom: 8 })
            .backgroundColor(this.currentThemeItem().brand)
            .borderRadius(8)
          Text('风险提示')
            .fontSize(14)
            .fontColor('#FFFFFF')
            .padding({ left: 12, right: 12, top: 8, bottom: 8 })
            .backgroundColor(this.currentThemeItem().warning)
            .borderRadius(8)
        }
      }
      .width('100%')
      .padding(16)
      .borderRadius(14)
      .backgroundColor('#FFFFFF')
      .border({ width: 1, color: '#E5E7EB' })
      .alignItems(HorizontalAlign.Start)
    }
  }

  build() {
    Scroll() {
      Column({ space: 18 }) {
        Text('ArkUI 主题令牌')
          .fontSize(26)
          .fontWeight(FontWeight.Bold)
          .fontColor('#0F172A')
          .width('100%')
        Text('主题不是随手换几个颜色。先把品牌色、警示色、确认色和正文色这类令牌分清楚,再考虑复杂设计系统。')
          .fontSize(14)
          .fontColor('#64748B')
          .lineHeight(22)
          .width('100%')

        this.previewSectionTitle()
        Row({ space: 8 }) {
          this.themeButton(0)
          this.themeButton(1)
        }
        .width('100%')

        this.themePreview()

        Button('设置为当前页面默认主题')
          .height(46)
          .width('100%')
          .fontSize(15)
          .backgroundColor(this.currentThemeItem().brand)
          .onClick(() => {
            ThemeControl.setDefaultTheme(this.currentTheme())
            this.feedback = `已调用 ThemeControl.setDefaultTheme:${this.currentThemeItem().label}。`
          })
        Text(this.feedback)
          .width('100%')
          .fontSize(14)
          .fontColor('#1F2937')
          .lineHeight(22)
          .padding(12)
          .backgroundColor('#F3F4F6')
          .borderRadius(10)

        this.tokenSectionTitle()
        this.tokenCard(0)
        this.tokenCard(1)
        this.tokenCard(2)
        this.tokenCard(3)
      }
      .width('100%')
      .padding(20)
      .alignItems(HorizontalAlign.Start)
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#F8FAFC')
  }
}
【版权声明】本文为华为云社区用户原创内容,未经允许不得转载,如需转载请自行联系原作者进行授权。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。