关于 makepad-2.0-theme
为 Makepad 2.0 应用提供 Splash 脚本可调用的设计令牌系统,涵盖颜色、字体、间距和控件状态。内置深色、浅色与骨架主题,便于统一界面样式并切换主题。
主要功能
- 01内置深色、浅色和骨架主题
- 02提供颜色与控件状态令牌
- 03提供字体样式和字号变量
- 04提供间距及尺寸预设
- 05支持调整主题全局参数
适用场景
- 01为应用统一颜色、字体与间距
- 02在深色和浅色主题间切换
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ zhanghandong/ makepad-skills --skill makepad-2.0-theme安装后在 Claude Code 中输入/makepad-2.0-theme确认已加载。
附带文件references/theme-variables.md
详细介绍
Makepad 2.0 主题系统
概述
Makepad 2.0 主题系统提供了一套全面的设计令牌,可通过 Splash 脚本中的 theme.* 变量访问。它能为整个应用的颜色、排版、间距和控件状态提供一致的样式。
提供三种内置主题:
mod.themes.dark-- 深色桌面主题(默认)mod.themes.light-- 浅色桌面主题mod.themes.skeleton-- 使用硬编码值的极简骨架主题
黄金法则:在生产环境的 UI 中,颜色、字号或间距始终使用 theme.* 变量,而不要使用硬编码值。这样可确保应用自动支持主题切换,并保持视觉一致性。
在 App::run 中设置主题
必须在加载控件之前加载主题。标准模式如下:
impl App {
fn run(vm: &mut ScriptVm) -> Self {
// Step 1: Load theme definitions (dark, light, skeleton)
crate::makepad_widgets::theme_mod(vm);
// Step 2: Select active theme (MUST come before widgets_mod)
script_eval!(vm, {
mod.theme = mod.themes.light // or mod.themes.dark
});
// Step 3: Load widget definitions (they reference mod.theme)
crate::makepad_widgets::widgets_mod(vm);
// Step 4: Load your app's script_mod
App::from_script_mod(vm, self::script_mod)
}
}
如果跳过步骤 1-2,默认主题将是深色主题(在 theme_mod 中设置)。
计数器示例使用简化的单行写法 crate::makepad_widgets::script_mod(vm),它会将步骤 1-3 与默认深色主题一并加载。
内部工作原理
theme_mod() 会执行以下操作:
- 调用
makepad_draw::script_mod(vm)加载绘图基元 - 创建
mod.themes模块 - 加载
theme_desktop_dark、theme_desktop_light和theme_desktop_skeleton - 将
mod.theme = mod.themes.dark设为默认值
随后,widgets_mod() 会创建 mod.prelude.widgets_internal,并设置 theme: mod.theme,让所有 use mod.prelude.widgets.* 的控件脚本都能使用 theme.*。
主题全局参数
每个主题都定义了可调整的全局参数,用于控制整体观感:
| 参数 | 用途 | 默认值(深色/浅色) |
|---|---|---|
color_contrast | 控制调色板范围 | 1.0 |
color_tint | 应用于背景的色调 | #0000ff |
color_tint_amount | 色调的应用程度(0-1) | 0.0 |
space_factor | 基础间距倍数 | 6.0 |
corner_radius | 基础圆角半径 | 2.5 |
beveling | 斜面强度 | 0.75 |
font_size_base | 基础字号(px) | 10.0 |
font_size_contrast | 各级字号之间的步长 | 2.5 |
主题颜色变量——主要颜色
这些是你在应用代码中最常用的颜色:
| 变量 | 用途 | 浅色外观 | 深色外观 |
|---|---|---|---|
theme.color_bg_app | 应用背景 | 浅灰色(约 #DDD) | 深灰色(约 #333) |
theme.color_fg_app | 前景层 | 稍深 | 稍浅 |
theme.color_bg_container | 卡片/容器背景 | 半透明浅色 | 半透明深色 |
theme.color_bg_even | 交替行(偶数行) | 较浅 | 较深 |
theme.color_bg_odd | 交替行(奇数行) | 较深 | 较浅 |
theme.color_bg_highlight | 高亮背景 | 类白色 #FFFFFF22 | 低不透明度的类白色 |
theme.color_bg_highlight_inline | 行内高亮 | color_d_1 | color_d_3 |
theme.color_bg_unfocussed | 未聚焦高亮 | bg_highlight 的 85% | bg_highlight 的 85% |
theme.color_app_caption_bar | 标题栏背景 | 透明 | 透明 |
theme.color_white | 纯白色 | #FFFFFF | #FFFFFF |
theme.color_makepad | Makepad 品牌色 | #FF5C39 | #FF5C39 |
主题颜色变量 -- 文本和标签
| 变量 | 用途 |
|---|---|
theme.color_label_inner | 内部元素(按钮、标签)上的主要文本 |
theme.color_label_inner_hover | 悬停时的文本 |
theme.color_label_inner_down | 按下时的文本 |
theme.color_label_inner_focus | 聚焦时的文本 |
theme.color_label_inner_active | 激活/选中时的文本 |
theme.color_label_inner_inactive | 次要/弱化文本 |
theme.color_label_inner_disabled | 禁用状态的文本 |
theme.color_label_outer | 外部元素(选项卡、标题)上的主要文本 |
theme.color_label_outer_off | 关闭状态的外部文本 |
theme.color_label_outer_disabled | 禁用状态的外部文本 |
theme.color_text | 通用文本颜色 |
theme.color_text_hover | 悬停时的文本 |
theme.color_text_focus | 聚焦时的文本 |
theme.color_text_disabled | 禁用状态的文本 |
theme.color_text_placeholder | 占位文本 |
theme.color_text_meta | 元数据文本 |
theme.color_text_cursor | 文本光标颜色 |
主题颜色变量 -- 控件状态
凸起颜色(按钮、凸起元素):
| 变量 | 用途 |
|---|---|
theme.color_outset | 默认按钮背景 |
theme.color_outset_hover | 悬停时的按钮 |
theme.color_outset_down | 按下时的按钮 |
theme.color_outset_active | 切换控件的激活状态 |
theme.color_outset_focus | 聚焦的按钮 |
theme.color_outset_disabled | 禁用的按钮 |
theme.color_outset_inactive | 未激活的按钮 |
凹陷颜色(文本输入框、复选框、单选按钮):
| 变量 | 用途 |
|---|---|
theme.color_inset | 默认输入框背景 |
theme.color_inset_hover | 悬停时的输入框 |
theme.color_inset_focus | 聚焦时的输入框 |
theme.color_inset_disabled | 禁用的输入框 |
theme.color_inset_empty | 空输入框 |
选择和高亮:
| 变量 | 用途 |
|---|---|
theme.color_selection_focus | 文本选择高亮 |
theme.color_selection_hover | 悬停时的选择区域 |
theme.color_highlight | 通用强调/高亮 |
theme.color_cursor | 光标颜色 |
theme.color_cursor_focus | 聚焦状态的光标 |
主题颜色变量 -- 语义/状态
| 变量 | 用途 | 值 |
|---|---|---|
theme.color_error | 错误状态 | 红色(#C00) |
theme.color_warning | 警告状态 | 橙色(#FA0) |
theme.color_high | 高严重性 | 红色(#C00) |
theme.color_mid | 中等严重性 | 橙色(#FA0) |
theme.color_low | 低严重性 | 黄绿色(#8A0) |
theme.color_panic | 恐慌/危急状态 | 洋红色(#f0f) |
主题颜色变量 -- 斜面系统
主题采用分层斜面系统,以实现控件的 3D 效果:
| 组 | 变量 | 用途 |
|---|---|---|
color_bevel* | _hover、_focus、_active、_down、_disabled | 平面斜面 |
color_bevel_inset_1* | 相同后缀 | 内阴影(第 1 层) |
color_bevel_inset_2* | 相同后缀 | 内高光(第 2 层) |
color_bevel_outset_1* | 相同后缀 | 外高光(第 1 层) |
color_bevel_outset_2* | 相同后缀 | 外阴影(第 2 层) |
主题颜色变量 -- 其他控件颜色
| 变量组 | 用途 |
|---|---|
theme.color_icon* | 图标颜色(默认、未激活、已激活、禁用) |
theme.color_mark* | 复选标记/单选标记颜色 |
theme.color_val* | 进度条和滑块填充颜色 |
theme.color_handle* | 滑块手柄颜色 |
theme.color_shadow* | 阴影效果 |
theme.color_drag_quad | 拖动预览叠加层 |
theme.color_dock_tab_active | 活动停靠选项卡背景 |
主题字体变量
字体大小
字体大小根据 font_size_base 和 font_size_contrast 计算:
| 变量 | 公式 | 默认值 |
|---|---|---|
theme.font_size_1 | base + 8 * contrast | 30.0(最大标题) |
theme.font_size_2 | base + 4 * contrast | 20.0(中号标题) |
theme.font_size_3 | base + 2 * contrast | 15.0(小号标题) |
theme.font_size_4 | base + 1 * contrast | 12.5(副标题) |
theme.font_size_p | base | 10.0(正文) |
theme.font_size_code | 固定值 | 9.0(等宽代码) |
字体样式(TextStyle 对象)
| 变量 | 描述 | 字体文件 |
|---|---|---|
theme.font_regular | 常规字重正文 | IBMPlexSans-Text.ttf |
theme.font_bold | 粗体/半粗体文本 | IBMPlexSans-SemiBold.ttf |
theme.font_italic | 斜体文本 | IBMPlexSans-Italic.ttf |
theme.font_bold_italic | 粗斜体文本 | IBMPlexSans-BoldItalic.ttf |
theme.font_code | 等宽代码字体 | LiberationMono-Regular.ttf |
theme.font_label | 标签文本(旧版) | IBMPlexSans-Text.ttf |
theme.font_icons | 图标字体(FontAwesome) | fa-solid-900.ttf |
每种字体样式均支持多种语言:
- 拉丁文:IBM Plex Sans 系列
- 中文:LXGW WenKai 系列
- Emoji:Noto Color Emoji
行间距常量
| 变量 | 值 | 用途 |
|---|---|---|
theme.font_wdgt_line_spacing | 1.2 | 组件文本 |
theme.font_hl_line_spacing | 1.05 | 标题文本 |
theme.font_longform_line_spacing | 1.2 | 长篇文本 |
主题间距变量
基础间距
间距根据 space_factor(默认值为 6.0)计算:
| 变量 | 公式 | 默认值 |
|---|---|---|
theme.space_1 | 0.5 * space_factor | 3.0(超小) |
theme.space_2 | 1.0 * space_factor | 6.0(小/标准) |
theme.space_3 | 1.5 * space_factor | 9.0(中) |
外边距/内边距预设(Inset 对象)
四边预设:
| 变量 | 描述 | 值 |
|---|---|---|
theme.mspace_1 | 四边 XS 内边距 | 每边 3px |
theme.mspace_2 | 四边 SM 内边距 | 每边 6px |
theme.mspace_3 | 四边 MD 内边距 | 每边 9px |
仅水平方向的预设:
| 变量 | 描述 | 值 |
|---|---|---|
theme.mspace_h_1 | XS 水平内边距 | 左/右:3px,上/下:0 |
theme.mspace_h_2 | SM 水平内边距 | 左/右:6px,上/下:0 |
theme.mspace_h_3 | MD 水平内边距 | 左/右:9px,上/下:0 |
仅垂直方向的预设:
| 变量 | 描述 | 值 |
|---|---|---|
theme.mspace_v_1 | XS 垂直内边距 | 上/下:3px,左/右:0 |
theme.mspace_v_2 | SM 垂直内边距 | 上/下:6px,左/右:0 |
theme.mspace_v_3 | MD 垂直内边距 | 上/下:9px,左/右:0 |
尺寸变量
| 变量 | 用途 | 默认值 |
|---|---|---|
theme.data_item_height | 标准数据行高度 | ~23px |
theme.data_icon_width | 标准图标宽度 | ~16px |
theme.data_icon_height | 标准图标高度 | ~22px |
theme.container_corner_radius | 容器边框圆角半径 | 5.0 |
theme.textselection_corner_radius | 文本选区圆角半径 | 1.25 |
theme.tab_height | 标签栏高度 | 36.0 |
在 Splash 中使用主题变量
颜色
// Background color
draw_bg.color: theme.color_bg_container
// Text color
draw_text.color: theme.color_label_inner
// Muted/secondary text
draw_text.color: theme.color_label_inner_inactive
// Color math -- multiply for opacity
draw_text.color: theme.color_label_inner_inactive * 0.8
// Semantic colors
draw_bg.color: theme.color_warning
draw_bg.color: theme.color_error
字体大小
// Body text size
draw_text.text_style.font_size: theme.font_size_p
// Heading sizes
draw_text.text_style.font_size: theme.font_size_1 // Largest
draw_text.text_style.font_size: theme.font_size_2 // Medium
draw_text.text_style.font_size: theme.font_size_3 // Small
draw_text.text_style.font_size: theme.font_size_4 // Sub-heading
// Code font size
draw_text.text_style.font_size: theme.font_size_code
字体样式
// Bold text with custom size (override with {} syntax)
draw_text.text_style: theme.font_bold{font_size: theme.font_size_2}
// Bold text keeping default size
draw_text.text_style: theme.font_bold{}
// Code font
draw_text.text_style: theme.font_code{}
// Override font size using +: merge syntax
draw_text +: {text_style +: {font_size: theme.font_size_3}}
间距
// Uniform padding
padding: theme.mspace_2
// Padding with overrides
padding: theme.mspace_2{left: theme.space_3, right: theme.space_3}
// Horizontal-only padding with custom values
padding: theme.mspace_h_1{left: theme.space_2, right: theme.space_2}
// Spacing between children
spacing: theme.space_2
// Computed spacing
width: Fill height: 9. * theme.space_1
padding: theme.mspace_3{left: theme.space_3 * 2, right: theme.space_3 * 2}
颜色语法参考
Splash 支持多种颜色格式(并非主题专属,但必不可少):
| 语法 | 示例 | 描述 |
|---|---|---|
#RGB | #f00 | 短十六进制(红色) |
#RRGGBB | #ff0000 | 完整十六进制 |
#RRGGBBAA | #ff000080 | 带 Alpha 通道的十六进制 |
#xRRGGBB | #x2ecc71 | 以字母 e 开头的十六进制(前缀为 #x) |
#N | #D | 灰度简写 |
vec4(r,g,b,a) | vec4(1.0, 0.0, 0.0, 1.0) | RGBA 浮点值(0.0-1.0) |
theme.* | theme.color_highlight | 主题变量 |
重要:十六进制颜色以字母 e 开头时,请使用 #x 前缀,
以避免与科学计数法混淆。例如,使用 #x2ecc71 而非 #2ecc71。
支持颜色运算:
// Multiply for opacity
theme.color_label_inner_inactive * 0.8
// Mix two colors
mix(theme.color_w, theme.color_b, 0.5)
运行时切换主题
你可以在 Rust 事件处理器中动态切换主题:
impl MatchEvent for App {
fn handle_actions(&mut self, cx: &mut Cx, actions: &Actions) {
if self.ui.button(cx, ids!(toggle_theme)).clicked(actions) {
script_eval!(cx, {
mod.theme = mod.themes.dark // or mod.themes.light
});
// All widgets using theme.* will pick up new values
// You may need to trigger a re-render
}
}
}
也可以在加载 widget 之前切换主题(在 App::run 中),以静态选择主题:
fn run(vm: &mut ScriptVm) -> Self {
crate::makepad_widgets::theme_mod(vm);
script_eval!(vm, {
mod.theme = mod.themes.light
});
crate::makepad_widgets::widgets_mod(vm);
App::from_script_mod(vm, self::script_mod)
}
完整的主题感知 UI 示例
此示例演示如何仅使用主题变量创建主题化卡片列表:
script_mod! {
use mod.prelude.widgets.*
let CardItem = RoundedView{
width: Fill height: Fit
padding: theme.mspace_2{left: theme.space_3, right: theme.space_3}
flow: Right spacing: theme.space_2
align: Align{y: 0.5}
draw_bg.color: theme.color_bg_container
draw_bg.border_radius: theme.container_corner_radius
icon := Label{
width: 24 height: 24
text: ""
}
content := View{
width: Fill height: Fit
flow: Down spacing: theme.space_1
title := Label{
text: "Title"
draw_text.color: theme.color_label_inner
draw_text.text_style: theme.font_bold{font_size: theme.font_size_4}
}
subtitle := Label{
text: "Subtitle"
draw_text.color: theme.color_label_inner_inactive
draw_text.text_style.font_size: theme.font_size_p
}
}
badge := RoundedView{
width: Fit height: Fit
padding: theme.mspace_h_1
draw_bg.color: theme.color_bg_highlight_inline
draw_bg.border_radius: 4.0
badge_label := Label{
text: "new"
draw_text.color: theme.color_highlight
draw_text.text_style.font_size: theme.font_size_code
draw_text.text_style: theme.font_bold{}
}
}
}
startup() do #(App::script_component(vm)){
ui: Root{
main_window := Window{
pass.clear_color: theme.color_bg_app
window.inner_size: vec2(400, 600)
body +: {
width: Fill height: Fill
flow: Down spacing: 0
// Header
SolidView{
width: Fill height: Fit
padding: theme.mspace_3
draw_bg.color: theme.color_app_caption_bar
Label{
text: "My Cards"
draw_text.color: theme.color_label_inner
draw_text.text_style: theme.font_bold{font_size: theme.font_size_2}
}
}
// Card list
ScrollYView{
width: Fill height: Fill
padding: theme.mspace_2
flow: Down spacing: theme.space_1
CardItem{title.text: "First Card" subtitle.text: "Description here"}
CardItem{title.text: "Second Card" subtitle.text: "Another card"}
}
// Footer
SolidView{
width: Fill height: Fit
padding: theme.mspace_2
draw_bg.color: theme.color_bg_container
Label{
text: "2 items"
draw_text.color: theme.color_label_inner_inactive
draw_text.text_style.font_size: theme.font_size_code
}
}
}
}
}
}
}
最佳实践
-
生产应用中的颜色始终使用
theme.*-- 当存在theme.color_error时,绝不要硬编码#ff0000。 这样可以启用主题切换和无障碍支持。 -
使用主题字体以保持排版一致 -- 优先使用
theme.font_bold{font_size: theme.font_size_2}, 而不是手动指定字体系列。 -
使用主题间距以保持布局一致 --
theme.mspace_2和theme.space_2可以营造和谐的节奏。避免使用padding: Inset{top: 8, ...}这样的神奇数字。 -
使用
{}语法覆盖 -- 扩展主题值,而不是替换它们:theme.font_bold{font_size: 20}保留粗体字体系列,但覆盖字号。theme.mspace_2{left: theme.space_3}保留上、右、下边距,但覆盖左边距。 -
使用合并运算符
+:进行部分覆盖 -- 只想更改某个嵌套属性时:draw_text +: {text_style +: {font_size: theme.font_size_3}} -
选择合适的文本颜色变量:
theme.color_label_inner用于主要 UI 文本(按钮、标签)theme.color_label_inner_inactive用于次要/弱化文本theme.color_text用于常规内容文本theme.color_text_placeholder用于占位文本
-
使用适配状态的颜色变体 -- 在悬停/聚焦/按下时会变化的 widget 应使用匹配的
_hover、_focus、_down后缀。 -
通过乘法调整透明度 --
theme.color_label_inner_inactive * 0.8可以生成更柔和的变体,而无需新增变量。 -
在
widgets_mod之前选择主题 -- 必须在调用theme_mod()和widgets_mod()之间设置主题,这样 widget 定义才能获取 正确的主题值。 -
对于简单应用,使用
crate::makepad_widgets::script_mod(vm),它会通过一次调用加载 所有内容,并使用默认的深色主题。
源文件
- 深色主题:
widgets/src/theme_desktop_dark.rs - 浅色主题:
widgets/src/theme_desktop_light.rs - 主题骨架:
widgets/src/theme_desktop_skeleton.rs - 主题加载器:
widgets/src/lib.rs(theme_mod和widgets_mod函数) - 用法示例:
examples/todo/src/app.rs(浅色主题,完整使用主题变量) - 用法示例:
examples/counter/src/app.rs(默认深色主题)