Label Studio 前端设计系统完全指南:从 Design Tokens 到 @humansignal/ui 组件库的工程化实践
【免费下载链接】label-studioLabel Studio is a multi-type data labeling and annotation tool with standardized output format项目地址: https://gitcode.com/GitHub_Trending/la/label-studio
本指南以 Label Studio 仓库根目录的 DESIGN.md 为骨架,系统讲解该标注平台前端的设计体系:品牌原则、内容规范、WCAG 2.1 AA 无障碍标准、语义化 Design Tokens、@humansignal/ui共享组件库的用法与开发规范、常见交互模式与反模式清单。读完你将掌握 Label Studio 前端(web/目录下的 React 应用)从"写样式"到"写组件"的完整规范,并能在源码层面理解令牌体系与组件是如何落地实现的。
品牌与设计原则
Label Studio 的设计人格(Personality)被定义为:人性化(Human)、乐观(Optimistic)、轻松(Lighthearted)、可靠(Reliable)、适应性强(Adaptable)、精工细作(Fine-crafting)。
在此基础上,产品设计遵循五条核心原则:
- 解决人的问题(Solve human problems):一切功能围绕标注员、审核员的真实工作流展开;
- 增强人的能力(Enhance human ability):界面让标注与审核更快、更准确,而不是替人做决定;
- 可信赖(Trustworthy):数据、状态与操作结果透明可预期;
- 透明(Transparent):系统行为与数据流向对用户可见;
- 可预测(Predictive):交互符合直觉,减少意外。
这五条原则与 Label Studio 的核心业务(多类型数据标注、标准化输出格式)直接对应——标注工作强调精确与信任,因此界面规范处处体现"状态可感知、操作可撤销、层级可预期"。
内容规范(Content Guidelines)
语气与语调(Voice and Tone)
面向用户的文案应当:口语化(Conversational)、开放(Open)、清晰(Clear)、实用(Practical),整体风格非正式但积极(Informal, optimistic, positive, friendly),让用户在使用时感到被鼓励和有动力。
写作风格(Writing Style)
- 短句:目标阅读水平为七年级及以下(约等于中文通俗易懂的表述),降低认知负担;
- 命名一致性:同一个元素在整个应用中使用同一名称——例如标注业务中用了 "category",就不要再混用 "type" 或 "group";
- 多用主动语态:如写 "Reviewers review and update annotations",而不是 "Annotations are reviewed by reviewers";
- 标题、输入标签与控件使用 Sentence Case:如 "Selection details"、"Email address"、"Enable notifications";
- 按钮与导航项使用 Title Case:如 "Save Changes"、"Upload Dataset"、"View Documentation";
- 使用缩略形式让语言更口语化:如 "can't"、"don't"、"it's"。
UI 文案规则
- 错误消息(Error Messages):清晰、具体、有帮助——告诉用户发生了什么以及如何修复;
- 反馈消息(Feedback Messages):积极、鼓励、信息充分;
- 标签与按钮(Labels and Buttons):按钮用动作动词(Save、Cancel、Upload);标签保持简洁但具描述性;按钮用 Title Case,标签用 Sentence Case;
- 链接(Links):使用能指示目标位置的描述性链接文本。
词汇表
领域术语(Project、Task、Annotation 等)由terminology.mdcCursor 规则维护。需要说明的是,DESIGN.md 中引用的react.mdc、tailwind.mdc、typescript.mdc、frontend-unit-tests.mdc、terminology.mdc属于配套的编辑器规则文档,当前仓库中未包含这些文件,本文将在相关章节给出等价的可执行规范。
无障碍标准(Accessibility Standards)
Label Studio 前端要求满足WCAG 2.1 Level AA。结合 web/libs/ui/src/lib/empty-state/empty-state.tsx 等组件实现可以看到,无障碍不是口号而是落到代码里的硬性要求。
键盘导航(Keyboard Navigation)
- 所有交互元素必须键盘可达;
- Tab 顺序正确贯穿整个界面;
- 可见焦点指示器,与背景的对比度最低3:1;
- 支持 ESC、Enter、Space、方向键;
- 无焦点陷阱(模态框除外);
- 模态框/对话框关闭后,焦点回到合适的原始位置。
视觉(Visual)
- 文本对比度最低4.5:1;
- 颜色不能作为传递信息的唯一手段(必须辅以图标、文字或形状);
- 界面可放大到200%且不丢失内容;
- 所有图片必须有 alt 文本。
结构(Structure)
- 全程使用语义化 HTML;
- 必要时使用 ARIA 属性;
- 标题层级正确(H1 → H2 → H3);
- 表单标签与输入控件关联;
- 错误消息通过
aria-describedby与字段关联; - 动态内容使用 ARIA live regions。
以EmptyState组件为例,其实现会自动为标题和描述生成唯一 ID,并通过aria-labelledby引用标题、aria-describedby引用描述,同时支持传入aria-label覆盖无障碍名称——"开箱即用的无障碍"在 empty-state.tsx 中有完整体现。
Design Tokens:语义化的视觉地基
设计令牌是整个设计系统的核心。DESIGN.md 明确其位置在web/libs/ui/src/tokens/tokens.prefix.css,并强调一条铁律:
始终使用语义令牌(semantic tokens),禁止使用数值令牌(numeric tokens)。
| 类别 | ✅ 使用语义令牌 | ❌ 不要用数值令牌 |
|---|---|---|
| 间距 Spacing | p-tight、m-base、gap-wide | p-200、m-400、gap-600 |
| 排版 Typography | text-body-medium、text-label-small | text-16、text-14 |
| 颜色 Colors | bg-primary-surface、text-neutral-content | bg-grape-600、text-sand-800 |
令牌的文件层级与生成链路
从源码看,这套令牌体系有一条清晰的生成链路:
- 设计源:web/design-tokens.json(6 千余行的 JSON,携带 Figma Variable 元数据
$variable_metadata、明暗双 mode); - 构建产物:web/libs/ui/src/tokens/tokens.prefix.css(文件头注明 "Generated from design-tokens.json - DO NOT EDIT DIRECTLY");
- 运行时桥接:web/libs/ui/src/tokens/tokens.js 被 web/libs/ui/src/tailwind.config.js 通过
createRequire加载,把 colors、fontSize、lineHeight、letterSpacing、fontFamily、fontWeight、spacing、cornerRadius 全部展开为 Tailwind 的可用工具类。
因此,你在 JSX 里写的bg-primary-surface最终会解析到 tokens.prefix.css 里的--color-primary-surface,而后者又引用自基础色板(如--color-grape-700)。一旦某个组件想跳过语义层直接写bg-grape-600,暗黑模式与后续品牌调整都会失效——这正是"永远用语义令牌"的根本原因。
颜色令牌(Color Tokens)
语义颜色分为六大类:
- Primary(品牌色):grape / blue;
- Neutral(灰阶):sand;
- Positive(成功态):kale / green;
- Negative(错误态):persimmon / red;
- Warning(警告态):canteloupe / orange;
- Accent(装饰色):grape、blueberry、kale、kiwi、mango、canteloupe、persimmon、plum、fig、sand。
颜色令牌按用途结构化命名,在 tokens.prefix.css 中可看到完整定义:
/* Surface colors(交互元素的背景) */ --color-primary-surface --color-primary-surface-hover --color-primary-surface-active /* Content colors(文本) */ --color-neutral-content --color-neutral-content-subtle --color-neutral-content-subtler --color-neutral-content-subtlest /* 用于禁用文本 */ /* Background colors(页面/容器背景) */ --color-neutral-background --color-primary-background /* Border colors(边框) */ --color-neutral-border --color-primary-border-subtle /* Icon colors(图标) */ --color-primary-icon --color-negative-iconAccent 色(用于标签、图表、分类)按状态阶梯使用:
- 默认:
-bold文本 +-subtlest背景; - 悬停:
-bold文本 +-subtle背景; - 激活:
-subtlest文本 +-base背景; - 图表:
-base背景。
间距令牌(Spacing Tokens)
语义间距刻度(同时在 CSS 变量与 Tailwind 工具类两个层面生效):
--spacing-tightest/tightest:2px--spacing-tighter/tighter:4px--spacing-tight/tight:8px--spacing-base/base:16px--spacing-wide/wide:24px--spacing-wider/wider:32px--spacing-widest/widest:40px
以源码为证,tokens.prefix.css 中语义变量通过var(--spacing-*)引用基础刻度(如--spacing-tight: var(--spacing-200),而--spacing-200: 0.5rem),做到了"语义层 + 数值层"的解耦。
排版令牌(Typography Tokens)
text-body-smallest/--font-size-body-smallest:10pxtext-body-smaller/--font-size-body-smaller:12pxtext-body-small/--font-size-body-small:14pxtext-body-medium/--font-size-body-medium:16pxtext-label-small/--font-size-label-small:14pxtext-label-medium/--font-size-label-medium:16pxtext-title-small/--font-size-title-small:18pxtext-title-medium/--font-size-title-medium:20pxtext-title-large/--font-size-title-large:24px
此外 tokens.prefix.css 还定义了headline-*、display-*等更大的展示层级,以及配套的--line-height-*行高、--font-weight-*字重、--font-family-*字体族(正文使用 "Figtree",等宽使用 "IBM Plex Mono",字体文件位于 web/libs/ui/src/fonts)。
暗黑模式(Dark Mode)
暗黑模式在使用语义令牌的前提下自动生效,无需任何额外处理。源码层面,tokens.prefix.css 通过[data-color-scheme="dark"]选择器整体覆写语义变量的取值(例如--color-neutral-surface由sand-100切换为sand-850),同时 tokens.prefix.css 还内置了max-width: 767px下的移动端字号降级方案。
硬性禁令:永远不要硬编码颜色、数值令牌(如grape-600)或内联颜色样式,否则会破坏暗黑模式。
组件库:@humansignal/ui
位置与导入
Label Studio 的所有共享 UI 组件集中在@humansignal/ui包:
- 源码目录:
web/libs/ui/src/lib/ - 导入方式:
import { Button, Badge } from '@humansignal/ui';
从 web/libs/ui/src/index.ts 可以看到组件库的公开面:Button、Badge、Message、Tooltip、Modal、EmptyState、Drawer、Tabs、Toast、Toggle、Spinner、Skeleton、Typography、Dropdown、Select、Checkbox、DataTable、Pagination、AI Chat 等数十个组件均从这里统一导出;其中MultiTreeSelect特意使用具名导出而非export *,以避免与公开的TreeSelect发生命名冲突——这个注释细节(index.ts)提示了组件库对公共 API 稳定性的重视。
组件发现(Component Discovery)
- 源码:
web/libs/ui/src/lib/; - Storybook:运行
yarn nx storybook storybook(端口4400)浏览全部组件与交互文档。
关键纪律:创建新组件之前,务必先检查@humansignal/ui是否已有现成实现。规范要求:信息提示框用Message,空状态用EmptyState,按钮一律用Button而不是原生<button>。
导入模式(Import Patterns)
// UI 组件 import { Button, Badge, Message } from '@humansignal/ui'; // 图标 import { IconCheck, IconCross } from '@humansignal/icons'; // 核心工具 import { cn } from '@humansignal/core';图标资源位于web/libs/ui/src/assets/icons/(SVG 集合),由@humansignal/icons统一暴露。
shadcn/ui 集成
部分组件基于 shadcn/ui 构建,但一律通过@humansignal/ui导入,严禁从/src/shad/直接导入(底层 shad 组件位于web/libs/ui/src/shad/components/,属于内部实现细节)。
样式指南(Styling Guidelines)
Tailwind CSS
使用语义工具类,这是整套规范的最高频实践:
- ✅
p-tight、bg-primary-surface、text-body-medium - ❌
p-200、bg-grape-600、text-16
响应式设计通过sm:、md:、lg:前缀实现。以 web/libs/ui/src/tailwind.config.js 为例,其content覆盖apps/**与全部libs/*/src/**,并把 tokens.js 的配色、字号、行高、字距、字体、字重、间距、圆角全部注入主题,同时保留了若干兼容旧版 shadcn 的 HSL 变量(文件内明确注释 "DO NOT USE THESE COLORS",提醒开发者以 Figma 令牌为准)。
CSS Modules
样式文件与组件同目录放置(.module.css),并使用组件令牌(Component Tokens)模式——组件内部通过局部 CSS 变量引用全局语义令牌,再在具体变体中覆写:
.base { --background-color: var(--color-primary-surface); --text-color: var(--color-primary-surface-content); background-color: var(--background-color); color: var(--text-color); } .variant-neutral { --background-color: var(--color-neutral-surface); --text-color: var(--color-neutral-content); }这一模式在 web/libs/ui/src/lib/button/button.module.css 中有完整实现:.base定义--background-color、--text-color、--border-color、--focus-outline等局部变量,各.variant-*与.look-*只负责覆写这些变量,而.waiting、:focus、:disabled等状态逻辑全部复用同一套变量,从而把按钮从 primary 到 gradient 的所有形态压缩在约 500 行纯声明式 CSS 中。
CSS 内嵌 Tailwind 写法:@apply flex items-center gap-tight;。
Canvas 元素取色
对于无法使用 CSS 变量的 Canvas/JS 渲染场景(如图形编辑器标注层),使用getTokenColor获取颜色值:
import { getTokenColor } from '@humansignal/ui'; ctx.fillStyle = getTokenColor('--color-primary-surface');其实现位于 web/libs/ui/src/utils/getTokenColor.ts:它从 web/design-tokens.json 中按令牌名解析路径、跟随{@...}引用链解出最终色值,支持hex/rgb/rgba三种格式与可选透明度,若令牌无法解析会返回品红色(#ff00ff)以让缺失立刻暴露。配套的 web/libs/ui/src/hooks/useTokenColor.ts 基于 jotai 的themeAtom自动感知明暗主题并返回主题感知的取色函数,主题切换时自动重渲染——这正是 Canvas 图层正确跟随暗黑模式的关键。
组件开发规范(Component Development)
文件结构
@humansignal/ui组件:kebab-case命名(button.tsx、empty-state.tsx);- 应用层组件:允许 PascalCase(如
DataManager.tsx); - 同目录聚合:
.tsx、.module.css、.stories.tsx、.test.tsx放在一起; - 每个组件必须有 Storybook stories。
从web/libs/ui/src/lib/的目录结构看(badge/、button/、empty-state/、modal/、tooltip/、toast/、tabs/等),这一规范已被严格执行,且多数组件同时携带*.module.css与测试文件。
通用模式(Common Patterns)
组件变体(Variants)
- 状态 State:
primary、neutral、positive、negative、warning、gradient(源码中 Button 另有inverted变体,见 button.tsx); - 尺寸 Size:
smaller(24px)、small(32px)、medium(40px),large(48px+); - 外观 Look:
filled(实心)、outlined(描边)、string(纯文字)。
禁用状态(Disabled States)
禁用文本使用neutral-content-subtlest:
<button disabled className="text-neutral-content-subtlest">加载状态(Loading States)
使用waiting属性:
<Button waiting={isLoading}>Save</Button>button.module.css 中的.waiting状态用repeating-linear-gradient绘制斜纹并通过@keyframes button-waiting实现 1s 线性循环动画;配合waitingClickable与secondaryOnClick,可以在等待期间保留可点击的次级动作(实现见 button.tsx)。
空状态(Empty States)
一律使用EmptyState,包含图标、标题、描述与动作:
<EmptyState icon={<IconInbox />} title="No tasks yet" description="..." actions={<Button>Create</Button>} />组件支持large/medium/small三种尺寸与六种颜色变体,并根据尺寸自动匹配图标大小与排版层级(empty-state.tsx),例如 Data Manager 使用 large(40px 图标 + headline medium),侧边栏使用 small(24px 图标 + body)。
模态框模式(Modal Patterns)
- 底部动作区(Footer Actions):所有 CTA 与导航按钮放在 footer;默认右对齐,"Previous" 类返回按钮左对齐;
- 按钮视觉层级:见下文"按钮层级"一节;
- 破坏性操作(Destructive Actions):必须二次确认;高影响操作要求输入验证(如输入 "DELETE" 或实体名称);
- 模态框堆叠:避免模态框套模态框,改用多步骤模态框或抽屉(Drawer)。
// 右对齐 footer(默认) <Modal.Footer align="right"> <Button variant="neutral" look="outlined">Cancel</Button> <Button variant="primary" look="filled">Save Changes</Button> </Modal.Footer>模态框相关实现位于web/libs/ui/src/lib/modal/(Modal.tsx、ModalHeader.tsx、ModalFooter.tsx、ModalBody.tsx、ModalCloseButton.tsx等,其中 ModalFooter.tsx 目前支持bare、style、className属性)。
组件复用与最佳实践
组件选择(Component Selection)
新建组件前先查@humansignal/ui与 Storybook。必须使用现成组件:
<Button>而不是<button><Message>用于信息提示框<Tooltip>用于气泡提示<Modal>用于模态框<EmptyState>用于空状态
命名约定(Naming Conventions)
@humansignal/ui:kebab-case(button.tsx);- 应用组件:PascalCase(
DataManager.tsx); - Props 类型:
ComponentNameProps(如ButtonProps,见 button.tsx)。
值与令牌(Values & Tokens)
绝不硬编码数值,一律使用语义令牌:
className="text-primary-content p-tight text-body-medium"组件令牌(Component Tokens)模式:
.component { --component-bg: var(--color-neutral-surface); background: var(--component-bg); }尺寸优先使用rem,必要时可用px。
按钮层级(Button Hierarchy)
每个屏幕只有一个 primary/filled 按钮(单一 CTA)。
视觉层级跟随对齐方向:
- 右对齐:从右到左排,primary 在最右:
[Cancel] [Save] - 左对齐:从左到右排,primary 在最左:
[Next] [Skip]
响应式设计(Responsive Design)
布局必须自适应:
flex-col md:flex-row p-tight md:p-base text-title-medium md:text-headline-small保存设置(Saving Settings)
设置/配置页使用显式 Save 按钮(不做自动保存)。例外情况:草稿内容、偏好设置、即时生效的开关(toggle)。
反模式清单(Anti-Patterns to Avoid)
以下行为被明确禁止,是 Code Review 时的红线:
- ❌ 数值令牌(
p-200、bg-grape-600) - ❌ 硬编码颜色(
color: #4C5FA9) - ❌ 内联样式(会破坏暗黑模式)
- ❌ 已有类似组件却重复造轮子
- ❌ 使用原生
<button>而非<Button> - ❌ 直接从
/src/shad/导入 - ❌ 一个屏幕出现多个 primary/filled 按钮
- ❌ 缺失键盘导航
- ❌ 缺失焦点指示器
- ❌ 仅用颜色传递信息
- ❌ 对比度不足(< 4.5:1)
- ❌ 非语义化 HTML
- ❌ 非响应式布局
快速参考(Quick Reference)
关键文件
| 用途 | 路径/命令 |
|---|---|
| 组件源码 | web/libs/ui/src/lib/ |
| 设计令牌 | web/libs/ui/src/tokens/tokens.prefix.css |
| 令牌设计源 | web/design-tokens.json |
| 图标 | @humansignal/icons(资源在web/libs/ui/src/assets/icons/) |
| Storybook | yarn nx storybook storybook(端口 4400) |
| Tailwind 配置 | web/libs/ui/src/tailwind.config.js |
配套文档
DESIGN.md 引用了react.mdc(组件结构、Hooks、状态管理)、tailwind.mdc(工具类与响应式)、typescript.mdc(类型约定)、frontend-unit-tests.mdc(测试模式)、terminology.mdc(领域术语)五份配套规范,它们以编辑器规则(Cursor Rule)形式存在,未包含在当前仓库中;本文的样式、组件、命名与模式章节已给出对应的可执行版本。
常见导入
import { Button, Message, EmptyState } from '@humansignal/ui'; import { IconCheck } from '@humansignal/icons'; import { cn } from '@humansignal/core';令牌示例
p-tight、m-base、gap-wide、text-body-medium、bg-primary-surface、text-neutral-content、text-neutral-content-subtlest(禁用态文本)。
小结
Label Studio 的设计系统是一条从 Figma 变量(design-tokens.json)到 CSS 令牌(tokens.prefix.css)、再到 Tailwind 工具类与@humansignal/ui组件的完整工程链路。开发者日常遵守三条核心纪律即可保持体系健康:只用语义令牌、优先复用@humansignal/ui组件、把无障碍当成默认要求。这套体系既保证了多类型标注界面(图像、文本、音频、视频、时间序列等)的视觉一致性与暗黑模式可用性,也为后续迭代提供了可预测、可维护的扩展基础。
【免费下载链接】label-studioLabel Studio is a multi-type data labeling and annotation tool with standardized output format项目地址: https://gitcode.com/GitHub_Trending/la/label-studio
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考