- 重构版主提示词(14 parts,面向 ClassHelper 灵动岛重构) - ClassIsland 全量规格提示词(20 parts,含分卷与逐章版本) - 投喂指南、分卷、逐节文件 - 调研记录、一致性基线、生成与质检脚本
36 KiB
3. 设计系统 Token 总表(颜色 / 字体 / 圆角 / 间距 / 阴影 / 层级 / DPI / 深浅色)
本节是整份提示词的「地基」。实现者必须先落地本节的 token(CSS 自定义属性 + TypeScript 常量),再开始写任何界面。任何界面代码不得出现裸色值、裸字号、裸圆角,一律引用本节 token。
实现技术栈为 ClassHelper 的 Electron + Vue 3 栈,宿主已有 packages/ui/src/tokens.css 的 --ch-ui-* 与桌面端 renderer/styles/index.css 的 --ch-*。保留并复用这些既有 token 作为「应用外壳层」,新增 --ci-* 命名空间承载 ClassIsland 主界面(岛)层 token。两层必须分开:--ch-* 管设置中心/管理页,--ci-* 管岛与课程信息。
3.1 颜色 Token
3.1.1 强调色与派生色
| Token 名 | 默认值 | 用途 | 建议 CSS 变量 | 源码证据 |
|---|---|---|---|---|
PrimaryColor(用户强调色) |
#00BFFF(Colors.DeepSkyBlue) |
设置项「强调色」的默认值;ColorSource=0 时作为全站 accent |
--ci-accent |
ClassIsland/Models/Settings.cs:35、Settings.cs:992 |
SecondaryColor |
#7FFFD4(Colors.Aquamarine) |
备用强调色(组件/图表次级色) | --ci-accent-2 |
Settings.cs:36、Settings.cs:1003 |
| FluentAvalonia 初始 accent | DodgerBlue=#1E90FF |
主题服务尚未介入时的启动色 | --ci-accent-fallback |
ClassIsland/App.axaml:17 |
SelectedPlatte 兜底 |
DodgerBlue=#1E90FF |
壁纸调色板取不到颜色时的兜底 | --ci-accent-palette-fallback |
Settings.cs:1036-1039、Settings.cs:80 |
AccentFillColorDefaultBrush |
由 accent 派生(Fluent 资源) | 提醒遮罩填充、教程高亮框、进度条强调 | --ci-accent-fill |
ClassIsland.Core/StylesBase.axaml:90-93、XamlThemes/FluentTheme/Styles.axaml:180 |
AccentFillColorTertiaryBrush |
accent 低透明度变体 | 课程列表展开态底色 | --ci-accent-fill-tertiary |
ClassIsland.Core/Controls/LessonsControls/LessonControlExpanded.axaml:182 |
AccentTextFillColorPrimaryBrush |
accent 文字色 | 拖拽高亮描边、TeachingTip 指示箭头 | --ci-accent-text |
EditableComponentsListBox.axaml:310、MyTeachingTipStyles.axaml:213 |
TextOnAccentFillColorPrimaryBrush |
白/黑(随 accent 对比度) | 遮罩上的文字 | --ci-text-on-accent |
FluentTheme/Styles.axaml:162,182;ClassicTheme/Styles.axaml:142 |
CustomizedAccentBarBackground1Brush |
亮 #FFDDE6F0 / 暗 #26374C;运行时被覆盖为 accent 的 10% 透明 |
「强调按钮」淡底、Button.foreground-accent |
--ci-accent-soft |
Themes/LightTheme.axaml:18、Themes/DarkTheme.axaml:18、Services/ThemeService.cs:66-74 |
运行时强调色决策必须严格按以下分支实现(源码见 ClassIsland/MainWindow.axaml.cs:743-764):
ColorSource == 0(自定义)→ 使用PrimaryColor;ColorSource == 1(壁纸主题色)或3(屏幕主题色)→ 使用SelectedPlatte(壁纸调色板第SelectedPlatteIndex项);ColorSource == 2(跟随系统)→ 传null,交给系统强调色;- 其余 → 兜底
DodgerBlue。
--ci-accent-soft 必须以 color-mix(in srgb, var(--ci-accent) 10%, transparent) 实时推导,不得写死。ClassHelper 已有等价写法可复用:packages/ui/src/theme.css:4-9 用 color-mix() 派生 Element Plus 的 --el-color-primary-light-*。
3.1.2 前景文字阶梯(前景色 5 级)
主界面自定义前景色时,前景色不是 1 个值,而是由同一个基色派生出 5 个 alpha 版本,必须按下表实现(源码:ClassIsland.Core/Helpers/UI/ControlColorHelper.cs:20-52):
| 层级 | 资源键 | alpha | 用途 | 建议 CSS 变量 |
|---|---|---|---|---|
| 1 | TextFillColorPrimaryBrush |
255(100%) | 主要文字、标题 | --ci-text-1 |
| 2 | TextFillColorInformativeSecondaryBrush |
218(85.5%) | 次要说明(本项目覆盖值) | --ci-text-2 |
| 3 | TextFillColorSecondaryBrush |
197(77.3%) | 辅助文字 | --ci-text-3 |
| 4 | TextFillColorTertiaryBrush |
135(52.9%) | 装饰/弱提示 | --ci-text-4 |
| 5 | TextFillColorDisabledBrush |
93(36.5%) | 禁用态 | --ci-text-5 |
实现规则:
- 未启用自定义前景色时,上述 5 个 token 使用 Fluent 默认值:亮色
#E4000000/#9E000000/#72000000/#5C000000,深色#FFFFFFFF及对应 alpha 版本【推断:FluentAvalonia 资源值不在本仓库,按 Fluent 2 规范给出】。 - 启用自定义前景色时,5 个 token 全部按上表 alpha 从
CustomForegroundColor派生,且必须同时重设TemplatedControl.Foreground与TextElement.Foreground,否则模板内文字会漏改(ControlColorHelper.cs:34-40)。 - 关闭自定义前景色时,必须
Remove这 5 个局部资源并SetValue(..., AvaloniaProperty.UnsetValue)回落(ControlColorHelper.cs:44-50)——映射到 CSS 即删除元素上的内联--ci-text-*,回落:root。 - 两个默认覆盖值:亮色
TextFillColorInformativeSecondaryBrush = #c0000000(75% 黑),深色#daffffff(85.5% 白)(Themes/LightTheme.axaml:20、Themes/DarkTheme.axaml:20)。 - 语义层级类必须映射为 CSS 工具类:
.l-primary|.l1 → --ci-text-1、.l-secondary|.l2 → --ci-text-2、.l-instructive|.l3 → --ci-text-3、.l-decoration|.l4 → --ci-text-4、.l-inactive|.l5 → --ci-text-5(ClassIsland.Core/Themes/Layers.axaml:10-43)。
3.1.3 背景与表面色
| Token 名 | 亮色 | 深色 | 用途 | 建议 CSS 变量 | 源码证据 |
|---|---|---|---|---|---|
SolidBackgroundFillColorBaseBrush |
#F3F3F3【推断】 |
#202020【推断】 |
经典主题岛背景、编辑模式窗口底、抽屉内容底、主题卡渐变底 | --ci-bg-base |
ClassicTheme/Styles.axaml:214;DrawerHost.axaml:29;MainWindow.axaml:188 |
SolidBackgroundFillColorSecondaryBrush |
#EEEEEE【推断】 |
#1C1C1C【推断】 |
Fluent 主题岛背景 | --ci-bg-secondary |
FluentTheme/Styles.axaml:45 |
SolidBackgroundFillColorTertiaryBrush |
#F9F9F9【推断】 |
#282828【推断】 |
弹出层底(.popup-bg) |
--ci-bg-tertiary |
StylesBase.axaml:291 |
ControlFillColorSecondaryBrush |
半透明白/黑 | 同左 | 主题卡底色 | --ci-fill-secondary |
ThemesSettingsPage.axaml:149 |
SmokeFillColorDefaultBrush |
半透明遮罩 | 同左 | 编辑模式窗口背景、抽屉忽略层 | --ci-smoke |
MainWindow.axaml:178;DrawerHost.axaml:20 |
Layer 应用底色 |
#F3F3F3 |
#161719 |
设置中心/管理页页面底(沿用 ClassHelper) | --ch-bg |
ClassHelper/renderer/styles/index.css:27,114 |
Layer 卡片底 |
rgba(255,255,255,.7) |
rgba(32,33,36,.72) |
应用外壳卡片 | --ch-layer |
同上 :28,115 |
CustomBackgroundColor |
#000000(默认) |
同左 | 岛自定义背景色默认值 | --ci-island-bg-custom |
Settings.cs:34、Settings.cs:1129 |
主界面行的背景最终呈现必须叠加两个不透明度:
- 自定义背景色本身的不透明度:
--ci-island-bg-opacity,默认0.5(Settings.cs:64、:1141;Fluent 用MainWindowStylesAssist.BackgroundOpacity绑定到Border.Opacity,见FluentTheme/Styles.axaml:44); - 整行不透明度:
MainWindowLineSettings.Opacity,默认1.0(ClassIsland.Core/Models/Components/MainWindowLineSettings.cs:42;Controls/MainWindowLine.axaml:51)。 - 鼠标移出淡化态:整行
opacity = 0.05(Controls/MainWindowLine.axaml:73-75)。
3.1.4 描边与分隔线
| Token 名 | 值 | 用途 | 建议 CSS 变量 | 源码证据 |
|---|---|---|---|---|
| 岛描边 | 1px ControlElevationBorderBrush |
经典/Fluent 行背景描边 | --ci-stroke-island / 宽度 --ci-stroke-island-w:1px |
FluentTheme/Styles.axaml:46-47;ClassicTheme/Styles.axaml:61-65 |
ControlStrokeColorDefaultBrush |
亮色半透明黑 | 编辑模式选中框描边 | --ci-stroke-control |
MainWindow.axaml:357 |
DividerStrokeColorDefaultBrush |
亮色半透明黑 | 操作条分隔线(1px,长 25px) | --ci-stroke-divider |
MainWindow.axaml:610-612 |
SurfaceStrokeColorFlyoutBrush |
半透明 | 弹窗描边 | --ci-stroke-flyout |
StylesBase.axaml:292 |
ControlStrongFillColorDefaultBrush |
中灰 | OOBE 方块、Workflow 进度条底、Shimmer 底 | --ci-fill-strong |
OobeIntroAnimationControl.axaml:34;WorkflowProgressIndicatorItemControl.axaml:26;Shimmer.axaml:11 |
| 主题卡描边 | 1px ControlElevationBorderBrush |
主题网格卡片 | --ci-stroke-card |
ThemesSettingsPage.axaml:150-151 |
| 拖拽高亮描边 | 1px accent | 组件拖拽占位 | --ci-stroke-drag |
EditableComponentsListBox.axaml:310-311 |
| 教程高亮描边 | 2.5px(外框)/ 3px(四角) | 教程 spotlight | --ci-stroke-tutorial |
TutorialHighlightControl.axaml:96-118 |
3.1.5 状态色
天气与空气质量状态色(浅色/深色两套,必须都实现):
| 语义 | 亮色 | 深色 | 建议 CSS 变量 | 源码证据 |
|---|---|---|---|---|
| AQI 1 优 | ForestGreen=#228B22 |
MediumSeaGreen=#3CB371 |
--ci-aqi-1 |
LightTheme.axaml:10;DarkTheme.axaml:10 |
| AQI 2 良 | #CCCC00 |
Yellow=#FFFF00 |
--ci-aqi-2 |
:11 |
| AQI 3 轻度 | DarkOrange=#FF8C00 |
Orange=#FFA500 |
--ci-aqi-3 |
:12 |
| AQI 4 中度 | IndianRed=#CD5C5C |
同亮色 | --ci-aqi-4 |
:13 |
| AQI 5 重度 | DarkRed=#8B0000 |
同亮色 | --ci-aqi-5 |
:14 |
| AQI 6 严重 | Purple=#800080 |
MediumPurple=#9370DB |
--ci-aqi-6 |
:15 |
| 雨天图标色 | Blue=#0000FF |
DeepSkyBlue=#00BFFF |
--ci-weather-rain |
LightTheme.axaml:3;DarkTheme.axaml:3 |
| 上课发光 | DropShadowEffect blur 6(亮)/2(暗),颜色 #FFFF00 |
同左 | --ci-glow-class |
LightTheme.axaml:4-7;DarkTheme.axaml:4-7 |
| 危险/删除按钮 | IndianRed=#CD5C5C |
同左 | --ci-danger |
Themes/Generic.xaml:225-226,275-276;MainWindow.axaml:435 |
| 警告/错误图标 | SystemFillColorCriticalBrush / SystemFillColorCautionBackgroundBrush |
同左 | --ci-warning-* |
StylesBase.axaml:209,217;ThemesSettingsPage.axaml:343 |
3.1.6 阴影与遮罩色(必须逐值实现)
| 用途 | 值 | 建议 CSS 变量 | 源码证据 |
|---|---|---|---|
| Fluent 岛投影 | 0 4 8 2 #48000000 |
--ci-shadow-island: 0 4px 8px 2px #48000000 |
FluentTheme/Styles.axaml:49 |
| 提醒遮罩时岛投影 | 0 8 32 0 #66000000 |
--ci-shadow-mask |
FluentTheme/Styles.axaml:324,327 |
| 竖抽屉/遮罩卡投影 | 0 8 32 0 #66000000 |
--ci-shadow-drawer |
VerticalDrawer.axaml:45 |
| 横抽屉内浮层投影 | 0 0 8 #33000000 |
--ci-shadow-drawer-x |
DrawerHost.axaml:33 |
| 经典主题提醒时行投影 | DropShadowEffect BlurRadius=8, Opacity=1, Offset=0 |
--ci-shadow-classic-mask |
ClassicTheme/Styles.axaml:283-287 |
| 时间线装饰器阴影 | DropShadowEffect opacity 0.4, blur 8 |
--ci-shadow-adorn |
Themes/Generic.xaml:215-219 |
| 弹窗底渐变遮罩 | #00ffffff→(0.65)#00ffffff→(1)#ffffffff |
--ci-mask-fade |
ThemesSettingsPage.axaml:162-169 |
| 教程聚光遮罩 | #99000000(60% 黑) |
--ci-scrim-tutorial |
TutorialSpotlightControl.axaml:45-61 |
| 编辑模式调试叠加 | #77222222 |
--ci-debug-bg |
MainWindow.axaml:645 |
| 启动闪屏底 | #88111111 + 1px ControlStrokeColorOnAccentDefaultBrush |
--ci-splash-bg |
SplashWindow.axaml:24-28 |
| 主题卡渐变底 | 径向 #ff0c0e1f→#ff0d1111(关于页) |
--ci-brand-gradient-a/b |
AboutSettingsPage.axaml:153-154 |
| 关于页正文底 | #0d1111 |
--ci-brand-dark |
AboutSettingsPage.axaml:131 |
| 通知涟漪默认填充 | AccentFillColorDefaultBrush(可被 NotificationContent.Color 覆盖) |
--ci-ripple |
RippleEffect.axaml.cs:61;MainWindowLine.cs:750 |
| 通知涟漪描边宽 | 200(Ellipse StrokeThickness) | --ci-ripple-stroke:200 |
RippleEffect.axaml:15 |
3.2 字体 Token
3.2.1 字体族
| Token | 值 | 用途 | 建议 CSS 变量/写法 |
|---|---|---|---|
AppFont(全局默认) |
HarmonyOS Sans SC, Microsoft YaHei UI, Noto Sans CJK SC, Noto Sans SC, PingFang SC, Source Han Sans SC, SimHei, WenQuanYi Micro Hei, sans-serif, FluentSystemIcons-Resizable |
全应用默认字体 | --ci-font: 'HarmonyOS Sans SC', 'Microsoft YaHei UI', 'Noto Sans CJK SC', 'Noto Sans SC', 'PingFang SC', 'Source Han Sans SC', SimHei, sans-serif |
MainWindowFont(岛字体,用户可改) |
默认等于 DefaultFontFamilyKey:avares://ClassIsland/Assets/Fonts/#HarmonyOS Sans SC, ...FluentSystemIcons-Resizable |
只作用于岛的 ZoomBorder 文本 |
--ci-font-island(元素级覆盖) |
HarmonyOsSans(旧键) |
HarmonyOS Sans SC, Microsoft YaHei UI, SimHei |
旧样式/右键菜单 | 兼容别名 |
BackendFontFamily |
Microsoft YaHei UI |
后端渲染/图表兜底 | --ci-font-backend |
| 图标字体 1 | FluentSystemIcons-Resizable(avares://ClassIsland.Core/Assets/Fonts/#FluentSystemIcons-Resizable) |
全局 Fluent 图标 | --ci-font-icon-fluent |
| 图标字体 2 | lucide(#lucide) |
Lucide 天气图标等 | --ci-font-icon-lucide |
| 图标字体 3 | SF-Symbols(#SF-Symbols) |
SFSymbols 天气图标 | --ci-font-icon-sf |
证据:ClassIsland/Assets/Fonts/Fonts.axaml:3、Assets/Fonts/Fonts.xaml:3、MainWindow.axaml.cs:185-189、App.axaml:48、ClassIsland.Core/AppBase.cs:142-152、ClassIsland.Core/ThemeBase.axaml:46-47。
实现要求:
- 字体栈必须按上表顺序声明,任何一环缺失即回落下一位;不得只写
HarmonyOS Sans SC。 - 字体文件必须以本地资源打包,禁止运行时从网络加载字体(离线教室环境)。
- 图标字体与正文字体必须拆成两个 token,图标组件禁止继承正文字体族。
3.2.2 字重
字重设置以整数保存,映射表必须逐值实现(ClassIsland.Core/Converters/IntToFontWeightConverter.cs:16-28):
| 数值 | 字重名 | CSS |
|---|---|---|
| 100 | Thin | 100 |
| 200 | ExtraLight | 200 |
| 300 | Light | 300 |
| 400 | Normal | 400 |
| 500 | Medium(默认) | 500 |
| 600 | SemiBold | 600 |
| 700 | Bold | 700 |
| 800 | ExtraBold | 800 |
| 900 | Black | 900 |
| 其他 | Normal | 400 |
默认字重来自 Settings.cs:101((int)FontWeight.Medium = 500)。岛内绝大多数文字必须用 500(Medium),标题可用 700(Bold),说明文字用 400(Regular)。
4 档核心字号在设置页的输入范围均为 1–40 px(AppearanceSettingsPage.axaml:482-533,NumberBox 的 Minimum=1、Maximum=40)。实现时必须允许 1–40 的整数/小数输入,并在预览区用四行示例文字实时展示 4 档效果(源码「字号预览」tab:AppearanceSettingsPage.axaml:570-591)。
3.2.3 字号阶梯(核心 4 档 + 扩展档)
核心 4 档是用户可调的「主界面字号」,默认值与用途如下(Settings.cs:197-200;ClassIsland.Core/ThemeBase.axaml:32-35;Settings.cs:1209-1251):
| Token | 默认 px | 用途 | 建议 CSS 变量 | 证据 |
|---|---|---|---|---|
MainWindowSecondaryFontSize |
14 | 次要信息:天气副标题、日期星期、倒计时标签 | --ci-fs-secondary |
ThemeBase.axaml:32;WeatherComponent.axaml:261,294,296,305 |
MainWindowBodyFontSize |
16 | 正文:课程名、时间、温度 | --ci-fs-body |
ThemeBase.axaml:33;MainWindow.axaml:313;CountDownComponent.axaml:65 |
MainWindowEmphasizedFontSize |
18 | 强调:时钟、提醒主文案 | --ci-fs-emphasized |
ThemeBase.axaml:34;ClockComponent.axaml:14;TwoIconsMaskTemplate.axaml:14 |
MainWindowLargeFontSize |
20 | 大号标题 | --ci-fs-large |
ThemeBase.axaml:35 |
扩展固定档(非用户设置,必须写死为 token 并说明来源):
| 字号 | 用途 | 建议 CSS 变量 | 证据 |
|---|---|---|---|
| 9 | 启动闪屏版本号(0.5 不透明度) | --ci-fs-version |
SplashWindow.axaml:86-87 |
| 12 | 主题卡作者名 | --ci-fs-caption |
ThemesSettingsPage.axaml:191 |
| 13 | 组件设置小字、ClassHelper 控件基准 | --ci-fs-control-base |
CountDownComponentSettingsControl.axaml:185-201;ClassHelper/renderer/styles/index.css:97 |
| 14 | 通用控件/次级标签 | --ci-fs-label |
MainWindow.axaml:406,452,497 |
| 16 | 主题卡名称、闪屏状态文字 | --ci-fs-title |
ThemesSettingsPage.axaml:188;SplashWindow.axaml:80 |
| 17 | 滚动提醒文字 | --ci-fs-rolling |
RollingTextTemplate.axaml:26 |
| 18 | OOBE 说明文字 | --ci-fs-oobe-body |
WelcomeWindowIntroControl.xaml:166 |
| 20 | 启动 logo 文字、导航标签 | --ci-fs-brand |
SplashWindow.axaml:56-58;StylesBase.axaml:118-123 |
| 22 | 天气温度/图标文字 | --ci-fs-weather |
WeatherComponent.axaml:86-175 |
| 32 | OOBE 大标题 | --ci-fs-oobe-title |
OobeIntroAnimationControl.axaml:69,87;WelcomePage.axaml:38 |
实现硬约束:
- 4 档核心字号必须由用户设置驱动,且必须能作用到「组件自身」与「单个主界面行」两个作用域:行级覆盖时把 4 个变量写到行根元素的
style上;关闭行级覆盖时移除内联变量,回落:root(MainWindowCustomizableNodeHelper.cs:20-37)。 - 缩放:所有字号乘以界面缩放
--ci-scale(默认 1.0),但不得用浏览器 zoom,必须用布局缩放以保持像素对齐(见 3.8)。 - 行高:源码未给出显式
LineHeight,建议正文line-height: 1.25、说明文字1.6【推断:依据 ClassHelpertheme.css:60-63的line-height:1.6与 ClassIsland 主界面 40px 行高/16px 字号的比值】。
3.3 圆角 Token
| Token | 默认值 | 用途 | 建议 CSS 变量 | 证据 |
|---|---|---|---|---|
RadiusX / RadiusY(岛圆角) |
8(UI 范围 0–20,步进 0.5,提示文案:调至最大时左右边缘为半圆) |
主界面行背景圆角;SizeDoubleToCornerRadiusConverter 以 ConverterParameter=1.0 直接取值(除以 1 即原值) |
--ci-radius-island: 8px |
Settings.cs:183-184、:1187-1207;AppearanceSettingsPage.axaml:75-87;FluentTheme/Styles.axaml:48,91,124,160-168,252;SizeDoubleToCornerRadiusConverter.cs:12-18 |
| 行级自定义圆角 | 0(关闭) |
单行覆盖圆角 | --ci-radius-island-local |
MainWindowLineSettings.cs:40-41 |
| 岛内容高度 | 40 |
所有行的容器高度(Fluent/Classic 同值) | --ci-island-height: 40px |
FluentTheme/Styles.axaml:65;ClassicTheme/Styles.axaml:16;MainWindowLine.axaml:16 |
| 弹窗圆角 | 6 |
.popup-bg、编辑模式操作条 |
--ci-radius-popup: 6px |
StylesBase.axaml:295;MainWindow.axaml:488,599 |
| 卡片圆角 | 4 |
设置项卡片、主题卡、Shimmer、时间线装饰器、OOBE 提示 | --ci-radius-card: 4px |
StylesBase.axaml:257-260;ThemesSettingsPage.axaml:152,161;Themes/Generic.xaml:214 |
| 控件圆角 | 4 |
按钮/输入框等 | --ci-radius-control: 4px |
ClassHelper index.css:52;theme.css:10 |
| 抽屉圆角 | 8 8 0 0 |
底部竖抽屉 | --ci-radius-drawer: 8px 8px 0 0 |
VerticalDrawer.axaml:9 |
| 胶囊圆角 | 999 |
徽标、胶囊按钮 | --ci-radius-pill: 999px |
classic-island.css:308,454,541,694 |
| 圆形按钮 | 14(28×28) |
主题卡安装按钮 | --ci-radius-icon-btn: 14px |
ThemesSettingsPage.axaml:246-249 |
| OOBE 大圆钮 | 24(48×48) |
欢迎页「下一步」 | --ci-radius-oobe-btn: 24px |
WelcomePage.axaml:61-65 |
| 岛内小元素 | 0.5 × --ci-island-font / 0.36 × --ci-island-font |
岛内图标块 | 用 calc 派生 | classic-island.css:388,403,476,502 |
实现硬约束:
- 岛圆角必须四角同值(源码把
RadiusX同时传给宽高两个方向:FluentTheme/Styles.axaml:31-34传两次RadiusX),不得实现「椭圆圆角」。 - 圆角必须作用于「背景层」与「内容裁剪层」两层,且两层同值,否则内容会溢出圆角外(
FluentTheme/Styles.axaml:48,91,124,160-168)。 - 若引入 ClassHelper 的连续圆角(squircle),必须保持
SUPERELLIPSE_EXPONENT = 4.2、每角CORNER_SEGMENTS = 14、展开卡半径min(48*scale, w/2, h/2)不变(ClassHelper/packages/desktop-client/src/island/squircle.ts:21,24,79-81)。默认实现必须用 CSSborder-radius(与 AvaloniaCornerRadius的直角圆弧一致,保证与 ClassIsland 逐像素相同);连续圆角只能由主题包显式启用(P2),不得作为默认岛外形。
3.4 间距 Token
| Token | 值 | 用途 | 建议 CSS 变量 | 证据 |
|---|---|---|---|---|
| 行内容左右内边距 | 12 0 |
岛内容区 GridContentRoot.Margin |
--ci-island-pad-x: 12px |
FluentTheme/Styles.axaml:110;ClassicTheme/Styles.axaml:100 |
| 行内容最小宽度 | 20 |
MinWidth |
--ci-island-min-w: 20px |
FluentTheme/Styles.axaml:109 |
| 组件间距 | 6 0 |
每个 ComponentPresenter.Margin;token 值取相邻间距 12px(每个组件左右各 6px),供 calc(var(--ci-component-gap) / 2) 反推单侧 margin |
--ci-component-gap: 12px |
MainWindow.axaml:396,546;与 part 04 §4.4 对齐 |
| 行间距 | 5 |
多行堆叠 StackPanel.Spacing(用户设置) |
--ci-line-gap: 5px |
Settings.cs:219;MainWindow.axaml:344 |
| 设置页容器 | margin 12 12 18 12;max-width 960;spacing 4 |
设置页正文 | --ci-settings-pad / --ci-settings-max-w: 960px |
StylesBase.axaml:67-71;ThemeBase.axaml:37 |
| 组件设置容器 | margin 4 0 4 12;max-width 960;spacing 4 |
子组件设置 | --ci-component-settings-pad |
StylesBase.axaml:73-77 |
| 弹窗内边距 | 12 |
.popup-bg |
--ci-popup-pad: 12px |
StylesBase.axaml:294 |
| 主题卡网格 | 单元高 150;目标宽 328;单元 margin 2;卡内 padding 8 |
主题市场网格 | --ci-theme-card-h: 150px、--ci-theme-card-w: 328px |
ThemesSettingsPage.axaml:139-142,172 |
| 编辑模式操作条 | 底距 64;padding 4;按钮间距 6 |
编辑态底部工具条 | --ci-editbar-bottom: 64px |
MainWindow.axaml:595-604 |
| 闪屏 | 尺寸 400×100;padding 17;logo 高 24;行距 8;底部进度条高 4 |
启动闪屏 | --ci-splash-w: 400px、--ci-splash-h: 100px |
SplashWindow.axaml:16-17,24,29,42,92-96 |
| 岛内水平内边距(ClassHelper 风格) | 0.78 × 字号 |
岛胶囊左右内边距 | calc 派生 | classic-island.css:215 |
| 进度条高度 | 3px |
岛内倒计时/提醒进度条统一高度 | --ci-progress-height: 3px |
与 part 04 §4.4 对齐;源码进度条未显式设高(FluentTheme/Styles.axaml:126-146),取 FluentAvalonia ProgressBar 默认值【推断】 |
| 岛内元素间距(ClassHelper) | 0.62 × 字号 |
岛内行间距 | calc 派生 | classic-island.css:212 |
3.5 透明与模糊 Token
| Token | 值 | 用途 | 建议实现 | 证据 |
|---|---|---|---|---|
| 主窗口透明级别 | Transparent |
非编辑态的岛窗口必须逐像素透明 | Electron transparent: true + backgroundColor: '#00000000' |
MainWindow.axaml:168-176 |
| 编辑模式透明级别 | AcrylicBlur, Transparent(回退 Transparent) |
编辑态窗口使用亚克力 | 只能用 CSS backdrop-filter 在自绘层模拟,不得用 setBackgroundMaterial(会破坏逐像素透明) |
MainWindow.axaml:177-181;ClassHelper island.ts:1726-1734 明确禁止 |
| 普通窗口模式 | 若禁用透明则 TransparencyLevelHint=None |
窗口模式 | transparent:false |
MainWindow.axaml:187-190 |
| 闪屏透明 | AcrylicBlur, Transparent |
启动闪屏 | backdrop-filter: blur(...) + 半透明底 |
SplashWindow.axaml:21 |
| 岛背景不透明度 | 0.5(默认范围 0–1,步进 0.05,滑块带数值气泡) |
背景层 opacity | --ci-island-opacity(主名;兼容别名 --ci-island-bg-opacity) |
Settings.cs:64,1141;AppearanceSettingsPage.axaml:61-72;FluentTheme/Styles.axaml:44 |
| 行整体不透明度 | 1.0(行级设置,可被单行覆盖) |
行级 opacity | --ci-line-opacity |
MainWindowLineSettings.cs:42 |
| 鼠标移出淡化 | 0.05 |
悬停淡出模式 | opacity:.05 |
MainWindowLine.axaml:73-75 |
| 提醒特效渲染缩放 | 1.0;屏高 ≥1400 → 0.75;≥2000 → 0.5(自动时) |
强提醒涟漪窗口缩放 | --ci-effect-scale |
Settings.cs:150;MainWindow.axaml.cs:377-392 |
| 遮罩动画层级不透明度 | 0 → 1(0.1s) | 遮罩出现时附加投影 | box-shadow transition |
FluentTheme/Styles.axaml:355-367 |
硬规则:
- 不得给岛卡片加
backdrop-filter(会把整块矩形模糊掉);模糊只允许出现在「编辑模式窗口背景」与「闪屏底」这两个整窗层。 - 岛必须是「透明窗口 + 自绘圆角卡片」,卡片外区域完全透明、可点击穿透(与 ClassHelper 一致,见
island.ts:250-257)。
3.6 层级(z-index)Token
| 层 | 数值 | 用途 | 建议 CSS 变量 | 证据 |
|---|---|---|---|---|
| 窗口层级-底层 | WindowLayer=0 |
岛沉到桌面层(Bottommost),仅提醒/编辑时置顶 |
--ci-z-window:0 |
Settings.cs:52;MainWindow.axaml.cs:843-874 |
| 窗口层级-置顶 | WindowLayer=1(默认) |
岛始终置顶 | --ci-z-window-top:1 |
同上;默认值 Settings.cs:52 |
| 岛内容层 | 0 | 正常内容 | — | — |
| 遮罩/覆盖层 | 1 | 内容之上、背景之下 | --ci-z-mask:1 |
FluentTheme/Styles.axaml:155-197 |
| 拖拽占位 | 1(且 opacity .5) |
编辑态拖动项 | --ci-z-drag:1 |
MainWindow.axaml:478-481 |
| 时间线装饰器 | 9999 |
分割线/行动装饰器必须压住时间块 | --ci-z-timeline-adorn:9999 |
Themes/Generic.xaml:193,243 |
| 「无穷大」哨兵 | 33550336 |
必须永远最前的元素(OOBE 方块、时间线提示、拖拽阈值) | --ci-z-sentinel:33550336 |
OobeIntroAnimationControl.axaml:45;TimeLineListControl.axaml:309;MainWindow.axaml:334 |
| 提醒特效窗口 | 独立 Topmost 窗口 | 全屏涟漪,高于一切 | 独立 BrowserWindow alwaysOnTop |
Views/TopmostEffectWindow.axaml.cs:120-128 |
| 教程聚光灯 | 独立置顶窗口 | 遮罩挖洞 | 独立窗口 | Controls/Tutorial/TutorialSpotlightControl.axaml:41 |
| 抽屉遮罩 | 覆盖内容 | 横/竖抽屉 | --ci-z-drawer:10【推断】 |
DrawerHost.axaml、VerticalDrawer.axaml |
| 应用外壳引导层 | 10000 |
全屏引导 | --ch-z-guide:10000 |
ClassHelper/packages/ui/src/theme.css:155 |
| 主题切换快照层 | 旧 1 / 新 2 |
View Transition 两帧 | --ch-z-view-old/new |
ClassHelper/renderer/styles/index.css:948-954 |
实现必须满足:
- 岛内容、遮罩、覆盖层在同一「行」内按上表叠放;多行之间不重叠(
MainWindowLine的ClipToBounds=False,行本身不裁剪)。 33550336是源码里反复使用的「无穷大」哨兵(同时被用作拖拽阈值HorizontalDragThreshold="33550336"表达「禁止横向拖动」,见MainWindow.axaml:334)。实现时可直接用z-index: 2147483647代替,但必须在常量注释里保留原值语义。- 编辑器/设置中心内任何弹窗、Tooltip 都必须高于主内容;不得用负 z-index 实现遮罩。
3.7 DPI 与缩放规则
| 规则 | 数值/算法 | 证据 |
|---|---|---|
| 界面缩放用户设置 | Scale,默认 1.0,UI 范围 0.75–2.5,步进 0.05(滑块带数值气泡,格式 F2) |
Settings.cs:86,1153;Views/SettingPages/AppearanceSettingsPage.axaml:27-39 |
| 缩放实现 | 用 LayoutTransform(布局缩放)+ ScaleTransform,同时作用于所有行;不得用 transform: scale() 直接缩放窗口内容(会模糊) |
MainWindow.axaml:250-265 |
| 编码缩放补偿 | GridRootLeft = Width/10*(Scale-1)、GridRootTop = Height/10*(Scale-1) |
MainWindow.axaml.cs:881-882,938-939 |
| 编辑模式缩放边界 | MinZoom 0.5 / MaxZoom 3 / ZoomSpeed 1.2,EnableConstrains=True,显示缩放指示 |
MainWindow.axaml:199-209 |
| 窗口尺寸换算 | 逻辑宽 = 物理宽 / dpiX;逻辑高 = (物理高 − 1) / dpiY(−1 防被误判全屏) |
MainWindow.axaml.cs:969-971 |
| 窗口定位 | 使用物理像素 PixelPoint,不做 dpi 换算 |
MainWindow.axaml.cs:905-909 |
| 安全区(主题声明) | 竖直安全区 = 当前启用主题 VerticalSafeAreaPx 的最大值;Fluent 主题为 20 |
Services/XamlThemeService.cs:111-135;App.Services.xaml.cs:314 |
| 教程运行时的安全区 | 主题安全区与 150 取大者 |
MainWindow.axaml.cs:898-902 |
| 通知特效窗口缩放 | 1 / dpiX |
MainWindow.axaml.cs:929 |
| 鼠标命中判定 | 屏幕坐标与 Bounds * dpi * Scale 比较 |
Controls/MainWindowLine.cs:625-636 |
| 字体缩放(关怀模式,应用外壳) | --ch-font-scale: 1;关怀模式 1.15,只放大字号不动几何 |
ClassHelper/renderer/styles/index.css:103,973-979 |
| 岛内字号缩放(ClassHelper 模型) | 所有岛内尺寸由 --island-font 用 calc() 派生 |
classic-island.css:2-7,57 |
实现要求:
- 所有视觉常量必须定义在 96 DPI 的 CSS px 基准上,再由
--ci-scale统一放大;不得为不同 DPI 写两套值。 - 多显示器:每行/每窗口使用「所在显示器」的 DPI 与工作区;切换显示器必须重新计算窗口尺寸与位置(
GetSelectedScreenSafe,MainWindow.axaml.cs:806-812)。 Scale改变时必须重算窗口宽高与安全区,不能只改 CSS transform。
3.8 深浅色规则
| 规则 | 要求 | 证据 |
|---|---|---|
| 主题模式枚举 | 0=跟随系统、1=明亮、2=黑暗;默认 2(黑暗) |
Settings.cs:32,979-989;Services/ThemeService.cs:53-59 |
| 跟随系统实现 | themeVariant=Default + preferSystemTheme=true;系统切换要实时响应 |
ThemeService.cs:53-63 |
| 资源覆盖机制 | Light/Dark 两套 ThemeDictionary,同名 key 覆盖:RainyColorBrush、LuminousClass、6 个 AQI、CustomizedAccentBarBackground1Brush、TextFillColorInformativeSecondaryBrush |
App.axaml:35-38;Themes/LightTheme.axaml、Themes/DarkTheme.axaml |
| 自定义前景色优先级 | 用户自定义前景色 > 主题前景色;关闭时回落主题 | ControlColorHelper.cs:20-52 |
| 自定义背景色优先级 | 用户自定义背景色 > 主题背景;关闭时回落 SolidBackgroundFillColor* |
FluentTheme/Styles.axaml:51-56;ClassicTheme/Styles.axaml:216-218 |
| 深色下的阴影 | 阴影颜色不变(黑),只调不透明度;不得在深色下把投影改成白色 | Fluent #48000000/#66000000/#33000000 与 VerticalDrawer.axaml:47 注释「Even in WinUI, shadow is always black regardless of light/dark mode」 |
| ClassHelper 深色底色 | 应用壳亮 #F3F3F3/暗 #161719;卡片 rgba(255,255,255,.7) / rgba(32,33,36,.72) |
renderer/styles/index.css:27-48,114-137 |
| 主题切换过渡 | 窗口 Background 用 200ms 过渡;整页换肤用圆形扩散 View Transition(320×1.5=480ms) |
MainWindow.axaml:40-44;ClassHelper/renderer/composables/motion.ts:226-235 |
实现禁止项:
- 禁止在组件里写死
#fff/#000;必须走--ci-text-*。 - 禁止只做深色、不做亮色(两套必须同步验收)。
- 禁止在深色模式使用纯黑
#000000作为岛背景(默认自定义背景色是黑,但必须靠--ci-island-bg-opacity降为 50% 并叠加主题描边,否则与桌面融为一体)。
3.9 ClassHelper 侧落点(实现映射表)
| 内容 | 落点文件 | 要求 |
|---|---|---|
| 应用外壳 token | packages/ui/src/tokens.css |
保留 --ch-ui-*,新增 --ci-* 只读引用 |
| 应用外壳主题 | packages/ui/src/theme.css |
把 --ci-accent 注入 --el-color-primary 系列,color-mix 派生 |
| 桌面端全局样式 | packages/desktop-client/src/renderer/styles/index.css |
在 :root 与 html.dark 中定义 --ci-* 的亮/暗值 |
| 岛渲染进程样式 | src/island/classic-island.css、custom-island.css |
定义 --island-* 与 --wn-*(classic-island.css:60-82) |
| 动效常量 | packages/shared/src/motion.ts + 桌面端/管理端 composables/motion.ts |
新增 token 只加在 shared;不得两端各抄一份(motion.ts:14-16 明确约束) |
| 设计常量的 TS 形态 | packages/shared/src/constants.ts |
输出 CLASSISLAND_TOKENS 常量对象,供窗口几何计算复用 |
3.10 落地顺序与常见错误
实现顺序必须严格如下,不得跳步:
- 先在
packages/shared/src/constants.ts建立常量对象(颜色、字号、圆角、间距、层级、DPI 基准),字段名与本节 token 名一一对应,导出为只读(as const)。 - 在
packages/ui/src/tokens.css的:root写入亮色值,在html.dark写入深色值;两处必须保持键集合完全一致,缺一个键都算不合格。 - 在
packages/desktop-client/src/renderer/styles/index.css引入--ci-*的桌面端覆盖(通知特效窗口、闪屏窗口等独立渲染入口要单独引入同一份 token)。 - 在
src/island/classic-island.css与custom-island.css里把--island-*映射到--ci-*,禁止在岛样式里重新定义颜色字面量。 - 最后才写组件;组件只允许消费 token。
常见错误(必须在实现时主动规避):
- 把「行级字号覆盖」写成给整个岛设置字号:这样改一行会连带改所有行,与源码作用域不符(源码把资源写在行控件自身,见
MainWindowCustomizableNodeHelper.cs:20-26)。 - 把 5 级前景色实现成「同一颜色的不同
opacity属性」:opacity会连带影响子元素与阴影,源码用的是颜色 alpha 通道(ControlColorHelper.cs:24-33),必须写进颜色值本身。 - 用
filter: drop-shadow()代替box-shadow实现岛投影:filter会新建渲染表面,开合时出现闪帧(ClassHelperclassic-island.css:114-120已记录该问题)。 - 在深色主题下把阴影改成
rgba(0,0,0,.6)以外的颜色,或直接删除阴影:源码在两种主题下都用黑色阴影,只改透明度。 - 忘记「安全区」:Fluent 主题声明了
20px竖直安全区,岛贴边时会空出这段距离(App.Services.xaml.cs:314、XamlThemeService.cs:111-135),漏实现会导致岛压住任务栏或屏幕边缘。 - 忘记
Scale变化时重算窗口:只改 CSS 会导致点击热区与可见区域错位(源码在MainWindow.axaml.cs:876-931每次都重算Bounds与ActualClientBound)。 - 把「编辑模式操作条」的层级设在岛内容之下:它必须浮在行内容之上,且底距 64px(
MainWindow.axaml:595)。
3.11 验收标准
实现者必须逐条自查,全部通过才算完成本节:
- 颜色:全仓库(渲染进程 + 主进程 + shared)搜索,除 token 定义文件外不得出现
#开头的颜色字面量;grep -r "#[0-9a-fA-F]\{6\}"的命中只允许出现在 token 文件与主题包预览样式中。 - 强调色:修改
--ci-accent一处,岛背景描边、提醒遮罩、进度条、按钮强调态、天气图标强调色全部同步变化,无残留默认蓝。 - 5 级前景:开启自定义前景色后,5 个 alpha 分别为 100%/85.5%/77.3%/52.9%/36.5%;关闭后 5 个变量被移除并回落。
- 字号:把 4 档字号分别改成 10/20/30/40,岛内所有文字按对应档位变化且不溢出 40px 行高外的容器;行级覆盖可只影响单行。
- 圆角:把
--ci-radius-island从 8 改到 20,背景层与裁剪层同时变化,内容不溢出四角;其他卡片圆角不受影响。 - 间距:改
--ci-island-pad-x与--ci-component-gap,内容宽度变化后背景宽度动画仍正常(见 part 14)。 - 透明:非编辑态岛卡片外区域 100% 透明(用取色器/截图验证 alpha=0);编辑态出现亚克力底;全程不允许整窗材质导致卡片外出现色块。
- 层级:编辑态拖动项浮在其他项之上;提醒遮罩覆盖内容但低于右上角操作按钮(若有);
33550336哨兵元素永远最前。 - DPI:在 100%/125%/150%/200% 四档缩放下,岛高度逻辑值恒为 40、圆角逻辑值恒为 8、文字不模糊;跨显示器拖动后重新计算,无半个像素的缝。
- 深浅色:Light/Dark 各截图对照,两类主题的 AQI 色、雨天色、说明文字 alpha 与上表一致;深色下阴影仍为黑色。
- CSS 变量唯一来源:
--ci-*只在两处定义(:root与html.dark),组件内只引用不重定义(行级作用域覆盖除外)。 - 字符集:所有 token 名使用 kebab-case,前缀统一为
ci-;禁止出现--ci-primaryColor这类混合命名。