Files
classhelper-island-refactor…/parts/03-design-tokens.md
T
ClassIsland Prompt Kit 22c1784bab feat: ClassIsland / ClassHelper 灵动岛提示词套件
- 重构版主提示词(14 parts,面向 ClassHelper 灵动岛重构)
- ClassIsland 全量规格提示词(20 parts,含分卷与逐章版本)
- 投喂指南、分卷、逐节文件
- 调研记录、一致性基线、生成与质检脚本
2026-11-12 21:59:58 +08:00

357 lines
36 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
## 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`):
1. `ColorSource == 0`(自定义)→ 使用 `PrimaryColor`;
2. `ColorSource == 1`(壁纸主题色)或 `3`(屏幕主题色)→ 使用 `SelectedPlatte`(壁纸调色板第 `SelectedPlatteIndex` 项);
3. `ColorSource == 2`(跟随系统)→ 传 `null`,交给系统强调色;
4. 其余 → 兜底 `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`【推断:依据 ClassHelper `theme.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`)。**默认实现必须用 CSS `border-radius`**(与 Avalonia `CornerRadius` 的直角圆弧一致,保证与 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 落地顺序与常见错误
实现顺序必须严格如下,不得跳步:
1. 先在 `packages/shared/src/constants.ts` 建立常量对象(颜色、字号、圆角、间距、层级、DPI 基准),字段名与本节 token 名一一对应,导出为只读(`as const`)。
2. 在 `packages/ui/src/tokens.css` 的 `:root` 写入亮色值,在 `html.dark` 写入深色值;两处必须保持键集合完全一致,缺一个键都算不合格。
3. 在 `packages/desktop-client/src/renderer/styles/index.css` 引入 `--ci-*` 的桌面端覆盖(通知特效窗口、闪屏窗口等独立渲染入口要单独引入同一份 token)。
4. 在 `src/island/classic-island.css` 与 `custom-island.css` 里把 `--island-*` 映射到 `--ci-*`,禁止在岛样式里重新定义颜色字面量。
5. 最后才写组件;组件只允许消费 token。
常见错误(必须在实现时主动规避):
- 把「行级字号覆盖」写成给整个岛设置字号:这样改一行会连带改所有行,与源码作用域不符(源码把资源写在行控件自身,见 `MainWindowCustomizableNodeHelper.cs:20-26`)。
- 把 5 级前景色实现成「同一颜色的不同 `opacity` 属性」:`opacity` 会连带影响子元素与阴影,源码用的是颜色 alpha 通道(`ControlColorHelper.cs:24-33`),必须写进颜色值本身。
- 用 `filter: drop-shadow()` 代替 `box-shadow` 实现岛投影:`filter` 会新建渲染表面,开合时出现闪帧(ClassHelper `classic-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 验收标准
实现者必须逐条自查,全部通过才算完成本节:
1. **颜色**:全仓库(渲染进程 + 主进程 + shared)搜索,除 token 定义文件外不得出现 `#` 开头的颜色字面量;`grep -r "#[0-9a-fA-F]\{6\}"` 的命中只允许出现在 token 文件与主题包预览样式中。
2. **强调色**:修改 `--ci-accent` 一处,岛背景描边、提醒遮罩、进度条、按钮强调态、天气图标强调色**全部**同步变化,无残留默认蓝。
3. **5 级前景**:开启自定义前景色后,5 个 alpha 分别为 100%/85.5%/77.3%/52.9%/36.5%;关闭后 5 个变量被移除并回落。
4. **字号**:把 4 档字号分别改成 10/20/30/40,岛内所有文字按对应档位变化且不溢出 40px 行高外的容器;行级覆盖可只影响单行。
5. **圆角**:把 `--ci-radius-island` 从 8 改到 20,背景层与裁剪层同时变化,内容不溢出四角;其他卡片圆角不受影响。
6. **间距**:改 `--ci-island-pad-x` 与 `--ci-component-gap`,内容宽度变化后背景宽度动画仍正常(见 part 14)。
7. **透明**:非编辑态岛卡片外区域 100% 透明(用取色器/截图验证 alpha=0);编辑态出现亚克力底;全程不允许整窗材质导致卡片外出现色块。
8. **层级**:编辑态拖动项浮在其他项之上;提醒遮罩覆盖内容但低于右上角操作按钮(若有);`33550336` 哨兵元素永远最前。
9. **DPI**:在 100%/125%/150%/200% 四档缩放下,岛高度逻辑值恒为 40、圆角逻辑值恒为 8、文字不模糊;跨显示器拖动后重新计算,无半个像素的缝。
10. **深浅色**:Light/Dark 各截图对照,两类主题的 AQI 色、雨天色、说明文字 alpha 与上表一致;深色下阴影仍为黑色。
11. **CSS 变量唯一来源**:`--ci-*` 只在两处定义(`:root` 与 `html.dark`),组件内只引用不重定义(行级作用域覆盖除外)。
12. **字符集**:所有 token 名使用 kebab-case,前缀统一为 `ci-`;禁止出现 `--ci-primaryColor` 这类混合命名。