跳转至

OpenBot UI 基础

OpenBot 采用紧凑的控件尺寸体系:24 px 用于辅助元素,28 px 用于工具栏,32 px 为标准尺寸,36 px 用于重要操作。所有全局 --openbot-* token 都声明在 packages/brand/src/tokens.css 中——web 端和移动端也都会导入这一个文件——并包含其暗色值;src/renderer/src/styles.css 仅导入它,然后导入 tokens-light.css,并添加渲染器的动画变量。

暗色为默认主题。packages/brand/src/tokens-light.css 中的亮色调色板只修改 token 的值,从不声明新的 token;当根元素带有 data-openbot-color-scheme="light" 时生效。在桌面上,主进程保留“设置”中的选择(System、Light 或 Dark),每个窗口将其应用到该属性,并从 prefers-color-scheme(src/renderer/src/color-scheme.ts)解析 System 选项;Dynamic Island 始终保持暗色。主进程将 nativeTheme.themeSource 保留为 system,因此访问的网站和原生菜单会跟随计算机设置。在 macOS 上,“设置”中还有 Transparent window(透明窗口)选项:主进程为主窗口赋予 under-window vibrancy 效果和透明背景,窗口则在根元素上设置 data-openbot-window-glass。随后 src/renderer/src/styles/window-glass.css 只对窗口框架、内容卡片以及填满卡片的页面进行着色;surface token 保持不透明,所以对话框、菜单、toast 和卡片不会改变。模糊效果从 themeSource 获取亮色或暗色色调,因此在窗口透明期间,主进程会将 themeSource 设置为对应主题,网站和原生菜单也会跟随该主题。位于 /app 的 web 客户端将选择保存在浏览器存储中。公共网站从不设置该属性。无法在 CSS 中使用 token 的代码(如 Mermaid 或沙箱化的聊天视觉组件)通过 @openbot/ui/document-color-scheme 读取配色方案。Storybook 有一个 Color scheme(配色方案)工具栏。reset 和基础角色位于 styles/base.css,primitives 位于 packages/ui/src/styles/primitives.css,屏幕的规则位于各自的 feature 样式表中——features/<domain>/<domain>.css,按级联顺序从 styles.css 导入。例外是 styles/app-shell.css:其尾部是一个由所有 domain 共享的主题层,因此保持完整(原因记录在文件头部注释中)。这些文件都不定义自己的调色板。

公共 API

Feature——即 src/renderer/src/features/<domain>/ 目录——只能从 @openbot/ui 导入 primitives,并从明确的子路径(如 @openbot/ui/features/sidebar/Sidebar)导入共享的 feature 组件。禁止直接从 Kobalte 和 Lucide 导入。Kobalte 是一个行为引擎,不是应用程序的公共 API;正因如此,升级它时无需改动任何 feature。

  • Text 和 Heading——界面文本;通过 as 选择语义,通过 variant 或 size 决定外观。
  • Button——带标签的操作。default 用于某个上下文中唯一最重要的操作,secondary 用于标准操作,ghost 用于工具栏,destructive 用于破坏性操作,link 用于嵌入文本中的操作。
  • IconButton——独立图标;始终传入 label。tooltip 可以添加快捷键提示,但不能替代无障碍标签。
  • Badge——简短的、不可交互的状态或类别。不要将其用作按钮。
  • Input、Textarea、NativeSelect——统一结构中的原生控件。将它们包裹在 Field 中,自动关联 label、description、error、required 和 aria-describedby。
  • Switch——对二元设置进行即时更改。如果要做的决定需要提交表单,请使用 checkbox 或 RadioGroup。
  • Card、Separator、Spinner、Skeleton、Kbd——表面、结构和反馈元素。
  • Dialog、AlertDialog、DropdownMenu、Popover、Tooltip、Tabs、RadioGroup、Select、Combobox 和 Listbox——为复杂交互导出的 Kobalte 适配器。

实现规则

颜色必须来自语义化的 --openbot-* 变量。文本尺寸、圆角和动画时长使用 token。hover 效果仅在 @media (hover: hover) and (pointer: fine) 中生效,可按压控件使用 scale(0.97),动画时长保持在 300 ms 以下,并遵循 prefers-reduced-motion。

bun run check:ui 会阻止原生 button 和 switch、手写的 dialog/menu/tab/listbox、在 UI 层之外直接导入 Kobalte/Lucide、颜色字面量以及未 token 化的文本尺寸、圆角和过渡时长。该检查覆盖所有渲染器样式表以及 TSX 中的内联声明;唯一允许颜色字面量的地方是共享调色板 packages/brand/src/tokens.css。它还会检查复杂命名空间是否回退为直接使用 Kobalte 别名。所有迁移预算均为零,包括 data-testid 预算。

验证

独立组件在 Storybook 的 Foundations 分组下检查。完整流程、对话框、菜单、composer 和对话在开发应用中检查。每个组件都应具备 default、hover、focus-visible、active、disabled 和 loading/empty 状态(在适用处)、键盘测试和 a11y story。a11y 是 Storybook 的全局门禁(test: "error")。

如何选择组件

  • 即时操作:Button 或 IconButton;嵌入文本中的导航:link 变体。
  • 状态或元数据:Badge;对进行中工作的持续反馈:Spinner 或 Skeleton。
  • 立即生效的二元值:Switch;从短列表中选一项:RadioGroup;从较长列表中选一项:Select;需要搜索或自定义值的列表:Combobox。
  • 多个对等视图:Tabs;触发器旁边的操作列表:DropdownMenu;右键上下文菜单:ContextMenu。
  • 锚定到某个元素的轻量信息:Tooltip 或 Popover;需要聚焦的任务:Dialog;不可逆操作的确认:AlertDialog。

依赖边界

@kobalte/core@2.0.0-alpha.0 被精确锁定,并且只能通过 @openbot/ui 访问。packages/ui/src/complex.tsx 中精心挑选的适配器无论上游结构如何变化都保持其公共名称,并弥补了该 alpha 版本在对话框、菜单和 popover 关闭后会丢失焦点的问题。Bun 在 patches/ 中维护了一个本地的 Solid 2 RC 兼容性修复;一旦 Kobalte 迁移到稳定 API,先移除该补丁,并在保持 feature 导入不变的前提下确认包装器行为正常。Lucide 图标也通过共享的 packages/ui/src/icons.ts 导出;logo、头像和产品美术资源仍然是 OpenBot 自己的资产。

本页原文 Markdown:在 AtomGit 查看·内容源自开源项目 openbo/openbot