DirectSurface UIDirectSurface UI
开始使用
文档/API 参考

Theme API 参考

Theme API 负责语义颜色、字体、尺寸、动效、高程和主题切换。业务系统应通过主题定义调整整体视觉,通过组件公开参数表达局部差异,不应复制框架内部的组件状态配方。

主题编译模型

DirectSurface UI 将“主题作者输入”和“运行时消费对象”明确分开:

ThemeDefinition
  → compileTheme() / createTheme()
  → ResolvedTheme
  → 组件内部样式配方
  → 绘制值
  • ThemeDefinition:主题作者输入。根 token 和六个嵌套层都可以只覆盖需要修改的字段。
  • ResolvedTheme:完整、深冻结且只读的运行时主题。组件和 runtime 只消费该类型,不在绘制或交互期间补默认值。
  • compileTheme(base, definition):在一个完整基础主题上编译主题定义,复制输入并返回新的 ResolvedTheme
  • createTheme(base, definition):面向应用代码的等价便捷入口。
  • freezeTheme(theme):冻结已经完整解析的主题。通常无需直接调用。

ThemeDataThemeOverrides 已从公开 API 删除。迁移旧代码时,分别改用 ResolvedThemeThemeDefinition

框架内置完整主题:

  • ImGuiLightTheme
  • ImGuiDarkTheme
  • MedicalCompactLightTheme
  • ModernCompactLightTheme

对比度模式

ResolvedTheme.contrastMode 是主题的渲染策略,不是根据颜色亮暗推断出来的状态:

  • 'normal':默认模式。组件保留业务数据或文档明确配置的颜色,并在必要时修正屏幕文字的可读性。
  • 'high':强制高对比度模式。支持该策略的文档型组件可以用主题语义色覆盖文档颜色,但不会修改业务模型、持久化数据或导出内容。

内置主题均使用 'normal'。创建高对比度主题时应显式声明:

const HighContrastTheme = createTheme(ImGuiLightTheme, {  contrastMode: 'high',  surfaceDataBody: rgba(0, 0, 0),  textPrimary: rgba(255, 255, 255),  borderData: rgba(255, 255, 255),})

包入口导出 ThemeContrastMode 类型。不要通过判断 surfaceCanvas 是否为黑色等颜色特征来识别高对比度主题;这种判断会把普通深色主题误当成强制高对比度模式。

创建业务主题

推荐基于最接近业务场景的完整主题提供一个 ThemeDefinition

import {  ModernCompactLightTheme,  createTheme,  rgba,  type ResolvedTheme,  type ThemeDefinition,} from 'ds-ui' const CompanyThemeDefinition: ThemeDefinition = {  accentPrimary: rgba(18, 104, 180),  focusBorder: rgba(18, 104, 180),  typography: {    titleFontWeight: 600,  },  elevation: {    popupShadowBlur: 18,    popupShadowOffsetY: 6,  },  chart: {    negative: rgba(185, 28, 28),  },} export const CompanyTheme: ResolvedTheme = createTheme(  ModernCompactLightTheme,  CompanyThemeDefinition,)

编译过程会:

  • 合并根 token。
  • 分别深度合并六个嵌套层。
  • 复制颜色对象、数组和嵌套对象,不修改 basedefinition
  • 补全完整运行时契约。
  • 深度冻结最终主题。

不要把 ThemeDefinition 直接传给组件,也不要在 paint、layout 或 pointer 事件中重复编译主题。

Surface 层级

Surface token 表达区域层级,不表达具体颜色:

Token 语义
surfaceCanvas 应用 Canvas 的最外层底色和浮动窗口之后的工作区。
surfaceContent 页面、文档和窗口客户区的主要内容底色。
surfaceWindowTitle 非活动窗口标题栏。
surfaceWindowTitleActive 当前活动窗口标题栏。
surfacePanel 卡片、工具面板和页面分区。
surfacePopup Dropdown、Menu、Tooltip 等脱离布局流的临时浮层。
surfaceControl 普通输入框、按钮和选择器。
surfaceControlHover 普通控件的 hover 表面。
surfaceControlActive 普通控件的 pressed 表面。
surfaceNav 导航栏、tab strip、toolbar chrome 和 dock rail。
surfaceDataHeader Grid/Table/Tree 的表头、分组头和数据 chrome。
surfaceDataBody Grid/Table/Tree 的数据正文区域。

surfaceWindow 已被删除。旧代码应根据真实语义改用 surfaceCanvassurfaceContentsurfaceDataBody,不要把三种用途机械映射到同一个 token。

交互与编辑 token

stateHoverOverlaystatePressedOverlay 是可叠加在当前基础 surface 或 selection 上的交互覆盖色。它们不是独立页面背景;组件内部配方负责合成透明度和组合状态。

字段编辑使用:

  • fieldBg:输入或表格编辑字段背景。
  • fieldFocusBorder:字段编辑焦点边框。
  • fieldSelectionBg:字段内文本选择背景。

editor.selectionBg / editor.selectionText 保留给 PlainTextEditor、CodeEditor、Markdown 等文档编辑器。字段选择和文档编辑器选择是不同语义,不应互相替代。

Popup 阴影全部位于 elevation 层。根级 popupShadow* 已删除。accentSuccessActiveaccentWarningActive 也已删除;成功、警告通常是状态语义,不应模拟持续的 pressed 主操作。

必需设计系统层

每个 ResolvedTheme 都必须包含以下六个完整层:

内容 典型消费者
typography body/title/secondary 的 size、lineHeight、weight,以及等宽字体。 Text、PageHeader、Section、数据摘要。
motion fast/normal/slow 过渡时长。 Button、Tabs、Modal、Drawer、Tooltip、Notification。
elevation Modal/Drawer 遮罩,Popup/Window/Card 阴影和 Card 高光。 所有浮层和高程容器。
editor 文档编辑选择、搜索命中和当前命中。 PlainTextEditor、CodeEditor、Markdown。
chart 图表序列色、负值色和性能时间线辅助色。 Charts、PerformanceTimeline。
syntax string/number/boolean/function/type/meta 语法色。 编辑器和结构化 token。

根级 fontFamily 是通用界面字体,根级 fontSize 是普通控件默认字号。typography 负责正文、标题和辅助文字的角色化 size、lineHeight 与 weight;monoFontFamily 只用于代码、日志和等宽数据。包入口同时导出 DefaultFontFamilyDefaultMonoFontFamily 作为内置主题的默认字体常量。

颜色工具与主题校验

公开工具:

  • rgba()
  • lerpColor()
  • colorToCSS()
  • contrastRatio()
  • resolveContrastText()
  • resolveSelectionText()
  • validateTheme()
  • haveSameThemeMetrics()

主题编译器会在注册前拒绝 NaN、无穷数、稀疏/空图表色板,以及缺少 r/g/b/a 数字通道的颜色。 validateTheme(theme) 继续检查关键前景/背景对比度、颜色通道范围、状态区分、字体和密度契约, 用于报告结构正确但视觉质量不合格的主题。自定义主题应在测试或构建检查中验证:

import { ModernCompactLightTheme, validateTheme } from 'ds-ui' const issues = validateTheme(ModernCompactLightTheme)if (issues.some(issue => issue.severity === 'error')) {  throw new Error(JSON.stringify(issues, null, 2))}

通用选中态使用 selectionBg / selectionStrongtextOnSelection。当实际背景经过组合时,使用 resolveSelectionText(theme, actualBackground) 计算可读前景,不要默认把浅色选择背景搭配 textOnAccent

组件样式配方边界

组件样式配方负责把语义主题转换为 Button、Grid、Dock、Popup 等组件的最终状态值。它们是框架内部实现,会被缓存并可随组件实现调整。

业务代码不应:

  • src/theme/component_styles 或构建产物内部路径导入。
  • 依赖 derive*Style() 或内部 *StyleTokens 的对象形状。
  • 在页面代码中复制 hover、selected、focused、disabled 的合成逻辑。

组件文档中出现的 deriveButtonStyle()deriveGridCellStyle() 等名称仅用于说明内部视觉来源,不代表包入口的稳定公共 API。公开边界是主题定义、主题校验工具、组件 options 和明确列入组件文档的样式覆盖项。

ModernCompactLight v2

ModernCompactLightTheme 面向高密度桌面业务界面。v2 遵循以下层级原则:

  • Canvas、内容区、面板、数据正文和浮层有明确层级,不用同一层浅蓝铺满页面。
  • 数据正文保持中性和高可读,导航与数据 chrome 可以使用更冷的色调建立结构。
  • Popup、Modal、Window 通过 surface、边框与 elevation 共同从内容区抬升。
  • hover 是轻反馈,selected 是持续状态,pressed 更明确,focus ring 独立叠加。
  • 13px 基础字号、28px 普通控件、29px 数据行和小圆角保持紧凑;视觉现代化不以放大控件或增加无效留白为代价。

Theme Lab

Theme Lab 使用同一组固定组件样本对比内置主题,覆盖 surface 层级、角色化排版、控件组合状态、DataGrid 密度、Tree/Table、Dock、Popup、Window 和 Notification。修改主题 token 或组件样式配方后,应先在这里做横向视觉复核,避免只优化单个页面。

运行 npm run dev:showcase 后访问:

http://127.0.0.1:4175/showcase/?example=theme-lab

生产站点对应 /showcase/index.html?example=theme-lab。Website 的组件示例目录也提供“主题视觉实验室”入口和真实 TypeScript 源码。

运行时切换

通过 Application.run(..., { theme }) 传入的主题和 host.setTheme(theme) 都要求完整 ResolvedTheme

import { ImGuiLightTheme } from 'ds-ui' host.setTheme(ImGuiLightTheme)host.useMainWindowTheme()

setTheme() 设置显式运行时主题;useMainWindowTheme() 恢复由主窗口提供的主题。主题值相同的新对象不会重复触发布局;颜色变化只需重绘,字体、lineHeight 和几何尺寸变化才需要重新布局。

Canvas 文本绘制依赖字体就绪。loadDirectSurfaceFonts() 会等待当前 document.fonts.ready 并清理文本测量缓存;自定义字体资源仍由宿主应用声明和加载。