ArkUI 主题令牌:颜色要有职责,不要随手取值
【摘要】 主题不是给页面换一套好看的颜色。主题令牌要回答颜色的职责:哪个是品牌主色,哪个是警示色,哪个代表确认结果,哪个承担主要正文。 本篇边界 这篇只做 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)