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