docs: add product context and current Ant Design system
douyin-release-gate / verify (push) Failing after 18m56s

This commit is contained in:
2026-10-08 22:42:04 +08:00
parent e4c0186f96
commit f238d11ef2
4 changed files with 891 additions and 378 deletions
+504
View File
@@ -0,0 +1,504 @@
{
"schemaVersion": 2,
"generatedAt": "2026-10-08T14:40:35.638Z",
"title": "Design System: CreatorHub",
"extensions": {
"colorMeta": {
"primary": {
"role": "primary",
"displayName": "清晰交互蓝",
"tonalRamp": [
"#001d66",
"#002c8c",
"#003eb3",
"#0958d9",
"#1677ff",
"#69b1ff",
"#bae0ff",
"#e6f4ff"
]
},
"primary-hover": {
"role": "primary",
"displayName": "交互蓝·悬停",
"tonalRamp": [
"#001d66",
"#002c8c",
"#003eb3",
"#0958d9",
"#1677ff",
"#69b1ff",
"#bae0ff",
"#e6f4ff"
]
},
"primary-active": {
"role": "primary",
"displayName": "交互蓝·按下",
"tonalRamp": [
"#001d66",
"#002c8c",
"#003eb3",
"#0958d9",
"#1677ff",
"#69b1ff",
"#bae0ff",
"#e6f4ff"
]
},
"primary-background": {
"role": "primary",
"displayName": "浅蓝交互底",
"tonalRamp": [
"#001d66",
"#002c8c",
"#003eb3",
"#0958d9",
"#1677ff",
"#69b1ff",
"#bae0ff",
"#e6f4ff"
]
},
"success": {
"role": "secondary",
"displayName": "成功绿",
"tonalRamp": [
"#092b00",
"#135200",
"#237804",
"#389e0d",
"#52c41a",
"#95de64",
"#d9f7be",
"#f6ffed"
]
},
"warning": {
"role": "secondary",
"displayName": "警告金",
"tonalRamp": [
"#613400",
"#874d00",
"#ad6800",
"#d48806",
"#faad14",
"#ffd666",
"#fff1b8",
"#fffbe6"
]
},
"error": {
"role": "secondary",
"displayName": "错误红",
"tonalRamp": [
"#5c0011",
"#820014",
"#a8071a",
"#cf1322",
"#f5222d",
"#ff7875",
"#ffccc7",
"#fff1f0"
]
},
"neutral-layout": {
"role": "neutral",
"displayName": "浅灰工作底",
"tonalRamp": [
"oklch(15% 0 0)",
"oklch(26% 0 0)",
"oklch(38% 0 0)",
"oklch(50% 0 0)",
"oklch(62% 0 0)",
"oklch(74% 0 0)",
"oklch(86% 0 0)",
"oklch(95% 0 0)"
]
},
"neutral-container": {
"role": "neutral",
"displayName": "白色内容面",
"tonalRamp": [
"oklch(15% 0 0)",
"oklch(26% 0 0)",
"oklch(38% 0 0)",
"oklch(50% 0 0)",
"oklch(62% 0 0)",
"oklch(74% 0 0)",
"oklch(86% 0 0)",
"oklch(95% 0 0)"
]
},
"neutral-text": {
"role": "neutral",
"displayName": "深色正文",
"tonalRamp": [
"oklch(15% 0 0)",
"oklch(26% 0 0)",
"oklch(38% 0 0)",
"oklch(50% 0 0)",
"oklch(62% 0 0)",
"oklch(74% 0 0)",
"oklch(86% 0 0)",
"oklch(95% 0 0)"
]
},
"neutral-secondary": {
"role": "neutral",
"displayName": "次级说明",
"tonalRamp": [
"oklch(15% 0 0)",
"oklch(26% 0 0)",
"oklch(38% 0 0)",
"oklch(50% 0 0)",
"oklch(62% 0 0)",
"oklch(74% 0 0)",
"oklch(86% 0 0)",
"oklch(95% 0 0)"
]
},
"neutral-disabled": {
"role": "neutral",
"displayName": "禁用文字",
"tonalRamp": [
"oklch(15% 0 0)",
"oklch(26% 0 0)",
"oklch(38% 0 0)",
"oklch(50% 0 0)",
"oklch(62% 0 0)",
"oklch(74% 0 0)",
"oklch(86% 0 0)",
"oklch(95% 0 0)"
]
},
"neutral-border": {
"role": "neutral",
"displayName": "控件边界",
"tonalRamp": [
"oklch(15% 0 0)",
"oklch(26% 0 0)",
"oklch(38% 0 0)",
"oklch(50% 0 0)",
"oklch(62% 0 0)",
"oklch(74% 0 0)",
"oklch(86% 0 0)",
"oklch(95% 0 0)"
]
},
"neutral-divider": {
"role": "neutral",
"displayName": "轻分隔",
"tonalRamp": [
"oklch(15% 0 0)",
"oklch(26% 0 0)",
"oklch(38% 0 0)",
"oklch(50% 0 0)",
"oklch(62% 0 0)",
"oklch(74% 0 0)",
"oklch(86% 0 0)",
"oklch(95% 0 0)"
]
},
"nav-selected-background": {
"role": "neutral",
"displayName": "侧栏浅灰选中底",
"tonalRamp": [
"oklch(15% 0 0)",
"oklch(26% 0 0)",
"oklch(38% 0 0)",
"oklch(50% 0 0)",
"oklch(62% 0 0)",
"oklch(74% 0 0)",
"oklch(86% 0 0)",
"oklch(95% 0 0)"
]
},
"nav-selected-text": {
"role": "neutral",
"displayName": "侧栏深色选中文字",
"tonalRamp": [
"oklch(15% 0 0)",
"oklch(26% 0 0)",
"oklch(38% 0 0)",
"oklch(50% 0 0)",
"oklch(62% 0 0)",
"oklch(74% 0 0)",
"oklch(86% 0 0)",
"oklch(95% 0 0)"
]
},
"event-like": {
"role": "secondary",
"displayName": "点赞粉色",
"tonalRamp": [
"#520339",
"#780650",
"#9e1068",
"#c41d7f",
"#eb2f96",
"#ff85c0",
"#ffd6e7",
"#fff0f6"
]
},
"event-follow": {
"role": "secondary",
"displayName": "关注紫色",
"tonalRamp": [
"#120338",
"#22075e",
"#391085",
"#531dab",
"#722ed1",
"#b37feb",
"#efdbff",
"#f9f0ff"
]
},
"event-repost": {
"role": "secondary",
"displayName": "转发橙色",
"tonalRamp": [
"#612500",
"#873800",
"#ad4e00",
"#d46b08",
"#fa8c16",
"#ffc069",
"#ffe7ba",
"#fff7e6"
]
},
"source-sync": {
"role": "secondary",
"displayName": "同步金色",
"tonalRamp": [
"#613400",
"#874d00",
"#ad6800",
"#d48806",
"#faad14",
"#ffd666",
"#fff1b8",
"#fffbe6"
]
},
"source-notice": {
"role": "secondary",
"displayName": "通知青色",
"tonalRamp": [
"#002329",
"#00474f",
"#006d75",
"#08979c",
"#13c2c2",
"#5cdbd3",
"#b5f5ec",
"#e6fffb"
]
},
"tag-like-background": {
"role": "secondary",
"displayName": "点赞标签底色",
"tonalRamp": [
"#520339",
"#780650",
"#9e1068",
"#c41d7f",
"#eb2f96",
"#ff85c0",
"#ffd6e7",
"#fff0f6"
]
},
"tag-like-text": {
"role": "secondary",
"displayName": "点赞标签文字",
"tonalRamp": [
"#520339",
"#780650",
"#9e1068",
"#c41d7f",
"#eb2f96",
"#ff85c0",
"#ffd6e7",
"#fff0f6"
]
}
},
"typographyMeta": {
"title": {
"displayName": "常规标题",
"purpose": "记录默认标题角色,不覆盖 PageContainer 的实际样式。"
},
"body": {
"displayName": "正文",
"purpose": "表格、表单、操作和说明的主要阅读角色。"
},
"label": {
"displayName": "强调标签",
"purpose": "字段或小节需要强调时沿用默认加粗。"
}
},
"shadows": [
{
"name": "antd-floating",
"value": "\n 0 6px 16px 0 rgba(0,0,0,0.08),\n 0 3px 6px -4px rgba(0,0,0,0.12),\n 0 9px 28px 8px rgba(0,0,0,0.05)\n ",
"purpose": "Ant Design 默认浮层深度,不为普通卡片追加。"
},
{
"name": "antd-floating-secondary",
"value": "\n 0 6px 16px 0 rgba(0,0,0,0.08),\n 0 3px 6px -4px rgba(0,0,0,0.12),\n 0 9px 28px 8px rgba(0,0,0,0.05)\n ",
"purpose": "较轻的默认浮层阴影。"
}
],
"motion": [
{
"name": "motionDurationFast",
"value": "0.1s",
"purpose": "原生组件默认动效时长,不新增装饰动画。"
},
{
"name": "motionDurationMid",
"value": "0.2s",
"purpose": "原生组件默认动效时长,不新增装饰动画。"
},
{
"name": "motionDurationSlow",
"value": "0.3s",
"purpose": "原生组件默认动效时长,不新增装饰动画。"
},
{
"name": "motionEaseInOut",
"value": "cubic-bezier(0.645, 0.045, 0.355, 1)",
"purpose": "原生组件默认状态过渡缓动。"
}
],
"breakpoints": [
{
"name": "xs",
"value": "480px"
},
{
"name": "sm",
"value": "576px"
},
{
"name": "md",
"value": "768px"
},
{
"name": "lg",
"value": "992px"
},
{
"name": "xl",
"value": "1200px"
},
{
"name": "xxl",
"value": "1600px"
}
]
},
"components": [
{
"name": "主要按钮",
"kind": "button",
"refersTo": "button-primary",
"description": "原生 Button 的主要样式,用于当前任务的主操作。",
"html": "<button class=\"ds-button-primary\" type=\"button\">保存</button>",
"css": ".ds-button-primary{display:inline-flex;align-items:center;justify-content:center;box-sizing:border-box;height:32px;padding:4px 15px;border:1px solid transparent;border-radius:6px;cursor:pointer;font-family:-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial,\n'Noto Sans', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol',\n'Noto Color Emoji';font-size:14px;line-height:1.5714285714285714;font-weight:400;transition:all 0.2s cubic-bezier(0.645, 0.045, 0.355, 1);background:#1677ff;color:#fff;box-shadow:0 2px 0 rgba(5,145,255,0.1);}.ds-button-primary:hover{background:#4096ff;}.ds-button-primary:active{background:#0958d9;}.ds-button-primary:focus-visible{outline:3px solid #91caff;outline-offset:1px;}"
},
{
"name": "默认按钮",
"kind": "button",
"refersTo": "button-default",
"description": "次级操作使用默认白底、轻边界,不与主操作争夺注意力。",
"html": "<button class=\"ds-button-default\" type=\"button\">取消</button>",
"css": ".ds-button-default{display:inline-flex;align-items:center;justify-content:center;box-sizing:border-box;height:32px;padding:4px 15px;border:1px solid transparent;border-radius:6px;cursor:pointer;font-family:-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial,\n'Noto Sans', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol',\n'Noto Color Emoji';font-size:14px;line-height:1.5714285714285714;font-weight:400;transition:all 0.2s cubic-bezier(0.645, 0.045, 0.355, 1);background:#ffffff;color:rgba(0,0,0,0.88);border-color:#d9d9d9;box-shadow:0 2px 0 rgba(0,0,0,0.02);}.ds-button-default:hover{color:#4096ff;border-color:#4096ff;}.ds-button-default:active{color:#0958d9;border-color:#0958d9;}.ds-button-default:focus-visible{outline:3px solid #91caff;outline-offset:1px;}"
},
{
"name": "文字按钮",
"kind": "button",
"refersTo": "button-text",
"description": "轻量辅助入口,默认不增加边界或重背景。",
"html": "<button class=\"ds-button-text\" type=\"button\">更多</button>",
"css": ".ds-button-text{display:inline-flex;align-items:center;justify-content:center;box-sizing:border-box;height:32px;padding:4px 15px;border:1px solid transparent;border-radius:6px;cursor:pointer;font-family:-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial,\n'Noto Sans', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol',\n'Noto Color Emoji';font-size:14px;line-height:1.5714285714285714;font-weight:400;transition:all 0.2s cubic-bezier(0.645, 0.045, 0.355, 1);background:transparent;color:rgba(0,0,0,0.88);}.ds-button-text:hover{background:rgba(0,0,0,0.04);}.ds-button-text:active{background:rgba(0,0,0,0.15);}.ds-button-text:focus-visible{outline:3px solid #91caff;outline-offset:1px;}"
},
{
"name": "文字输入",
"kind": "input",
"refersTo": "input",
"description": "保留可见标签与默认输入边界、悬停和焦点反馈。",
"html": "<label class=\"ds-field\">账号名称<input class=\"ds-input\" type=\"text\" placeholder=\"请输入账号名称\"></label>",
"css": ".ds-field{display:flex;flex-direction:column;gap:8px;color:rgba(0,0,0,0.88);font-family:-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial,\n'Noto Sans', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol',\n'Noto Color Emoji';font-size:14px;line-height:1.5714285714285714;font-weight:400;}.ds-input{box-sizing:border-box;height:32px;padding:4px 11px;border:1px solid #d9d9d9;border-radius:6px;background:#ffffff;color:rgba(0,0,0,0.88);font-family:-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial,\n'Noto Sans', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol',\n'Noto Color Emoji';font-size:14px;line-height:1.5714285714285714;font-weight:400;transition:all 0.2s cubic-bezier(0.645, 0.045, 0.355, 1);}.ds-input::placeholder{color:rgba(0,0,0,0.25);}.ds-input:hover{border-color:#4096ff;}.ds-input:focus-visible{border-color:#1677ff;outline:0;box-shadow:0 0 0 2px rgba(5,145,255,0.1);}"
},
{
"name": "ProLayout 侧栏导航",
"kind": "nav",
"refersTo": "nav-selected",
"description": "沿用 ProLayout 的浅灰选中底与深色文字,不套用普通 Menu 的蓝色选中样式。",
"html": "<nav class=\"ds-nav\" aria-label=\"运营菜单\"><button class=\"ds-nav-item ds-nav-selected\" type=\"button\" aria-current=\"page\">我的账号</button><button class=\"ds-nav-item\" type=\"button\">作品分析</button></nav>",
"css": ".ds-nav{display:flex;flex-direction:column;gap:4px;background:#ffffff;padding:4px;}.ds-nav-item{display:block;height:40px;text-align:left;padding:0 16px;border:0;border-radius:6px;background:transparent;color:rgba(0,0,0,0.65);cursor:pointer;font-family:-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial,\n'Noto Sans', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol',\n'Noto Color Emoji';font-size:14px;line-height:1.5714285714285714;font-weight:400;transition:all 0.2s cubic-bezier(0.645, 0.045, 0.355, 1);}.ds-nav-item:hover{background:rgba(0,0,0,0.03);color:rgba(0,0,0,0.95);}.ds-nav-selected,.ds-nav-selected:hover{background:rgba(0,0,0,0.04);color:rgba(0,0,0,0.95);}.ds-nav-item:active{background:rgba(0,0,0,0.04);}.ds-nav-item:focus-visible{outline:3px solid #91caff;outline-offset:1px;}"
},
{
"name": "点赞标签",
"kind": "chip",
"refersTo": "tag-like",
"description": "使用 magenta 预设表达点赞,保留文字,不是可点击的筛选按钮。",
"html": "<span class=\"ds-tag-like\">点赞</span>",
"css": ".ds-tag-like{display:inline-block;padding:0 7px;border:1px solid #ffadd2;border-radius:4px;background:#fff0f6;color:#c41d7f;font-family:-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial,\n'Noto Sans', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol',\n'Noto Color Emoji';font-size:12px;line-height:20px;white-space:nowrap;}"
},
{
"name": "内容卡片",
"kind": "card",
"refersTo": "card",
"description": "默认 Card 的白色内容面、轻边界与内容留白,不增加悬浮阴影。",
"html": "<section class=\"ds-card\"><div class=\"ds-card-heading\">我的账号</div><div class=\"ds-card-body\">查看账号状态及采集情况。</div></section>",
"css": ".ds-card{background:#ffffff;color:rgba(0,0,0,0.88);border:1px solid #f0f0f0;border-radius:8px;font-family:-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial,\n'Noto Sans', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol',\n'Noto Color Emoji';font-size:14px;line-height:1.5714285714285714;font-weight:400;}.ds-card-heading{padding:16px 24px;border-bottom:1px solid #f0f0f0;font-size:16px;font-weight:600;}.ds-card-body{padding:24px;}"
}
],
"narrative": {
"northStar": "日常运营工作台",
"overview": "界面是长期处理账号和数据的工作场所。视觉语言克制、常规、清晰,通过位置、分组、文字层级、轻微颜色和状态反馈帮助使用者判断信息,而不是通过装饰吸引注意。\n\n延续当前 Umi Max 4.7、React 19、antd 6.6.5、Pro Components 3.x 和 Ant Design X 的界面。使用 antd/pro 默认组件,不新建另一套按钮、表单、导航或视觉包装;组件不适合业务时优先调整交互。尺寸根据同屏密度与操作频率选择,不以统一放大来强调可用性。",
"keyCharacteristics": [
"熟悉的组件、图标与交互,不要求使用者学习新视觉隐喻。",
"内容和操作层级清楚,辅助入口不抢主操作的注意力。",
"列表保持适合日常工作的密度,长内容不撑宽页面。",
"未知、错误和处理中有明确反馈,不通过视觉制造假正常。"
],
"rules": [
{
"name": "The Semantic Color Rule",
"body": "颜色必须同时有文字说明:成功、警告和错误使用相应状态组件;事件类型固定为点赞粉色、评论蓝色、关注紫色、转发橙色,来源固定为同步金色、通知青色。不得仅靠颜色解释状态。",
"section": "colors"
},
{
"name": "The Honest Hierarchy Rule",
"body": "标题说明“这是哪里”,正文说明“发生了什么”,辅助文字说明“下一步怎么办”;不能用放大字号掩盖状态缺失或信息组织问题。",
"section": "typography"
},
{
"name": "The Quiet Depth Rule",
"body": "普通内容靠层次与分组,浮层靠默认深度;不要用厚阴影、粗描边或装饰性渐变提高存在感。",
"section": "elevation"
}
],
"dos": [
"**Do** 沿用原生 antd/pro 的样式和状态,通过组件 API 解决布局与交互需求。",
"**Do** 让标题、统计、筛选、列表与操作有清楚层级,主操作只在对应区域出现一次。",
"**Do** 用文字和组件状态同时解释未知、错误、处理中和完成。",
"**Do** 将长内容约束在列内,保持真实资料、作品入口和缺图提示可辨认。",
"**Do** 保留键盘焦点、可见标签和通用图标,修改后与同屏信息密度对照检查。"
],
"donts": [
"**Don't** 恢复 MUI、react-admin、旧深蓝导航或另一套独立主题。",
"**Don't** 新建视觉包装组件或通过 CSS 覆盖默认组件样式;现有业务组件不等于新的设计系统封装。",
"**Don't** 主动放大控件,增加厚边框、强阴影、大圆角、装饰性渐变或营销页式布局。",
"**Don't** 仅靠颜色传达状态,或把未知、断线、缺失数据呈现为正常、零或完成。",
"**Don't** 用编造的头像、指标、图表、远程封面或无限加载来隐藏真实缺失与未实现功能。"
]
}
}
+278
View File
@@ -0,0 +1,278 @@
---
name: CreatorHub
description: 延续 Ant Design 默认组件的克制、清晰的抖音运营工作台
colors:
primary: "#1677ff"
primary-hover: "#4096ff"
primary-active: "#0958d9"
primary-background: "#e6f4ff"
success: "#52c41a"
warning: "#faad14"
error: "#ff4d4f"
neutral-layout: "#f5f5f5"
neutral-container: "#ffffff"
neutral-text: "rgba(0,0,0,0.88)"
neutral-secondary: "rgba(0,0,0,0.65)"
neutral-disabled: "rgba(0,0,0,0.25)"
neutral-border: "#d9d9d9"
neutral-divider: "#f0f0f0"
nav-selected-background: "rgba(0,0,0,0.04)"
nav-selected-text: "rgba(0,0,0,0.95)"
event-like: "#eb2f96"
event-follow: "#722ed1"
event-repost: "#fa8c16"
source-sync: "#faad14"
source-notice: "#13c2c2"
tag-like-background: "#fff0f6"
tag-like-text: "#c41d7f"
typography:
title:
fontSize: "20px"
fontWeight: 600
lineHeight: 1.4
body:
fontFamily: >-
-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial,
'Noto Sans', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol',
'Noto Color Emoji'
fontSize: "14px"
fontWeight: 400
lineHeight: 1.5714285714285714
label:
fontSize: "14px"
fontWeight: 600
lineHeight: 1.5714285714285714
rounded:
sm: "4px"
md: "6px"
lg: "8px"
spacing:
xs: "8px"
sm: "12px"
md: "16px"
lg: "24px"
components:
button-primary:
backgroundColor: "{colors.primary}"
textColor: "{colors.neutral-container}"
typography: "{typography.body}"
rounded: "{rounded.md}"
padding: "4px 15px"
height: "32px"
button-primary-hover:
backgroundColor: "{colors.primary-hover}"
button-primary-active:
backgroundColor: "{colors.primary-active}"
button-default:
backgroundColor: "{colors.neutral-container}"
textColor: "{colors.neutral-text}"
typography: "{typography.body}"
rounded: "{rounded.md}"
padding: "4px 15px"
height: "32px"
button-text:
backgroundColor: "transparent"
textColor: "{colors.neutral-text}"
typography: "{typography.body}"
rounded: "{rounded.md}"
height: "32px"
input:
backgroundColor: "{colors.neutral-container}"
textColor: "{colors.neutral-text}"
typography: "{typography.body}"
rounded: "{rounded.md}"
padding: "4px 11px"
height: "32px"
nav-selected:
backgroundColor: "{colors.nav-selected-background}"
textColor: "{colors.nav-selected-text}"
typography: "{typography.body}"
rounded: "{rounded.md}"
tag-like:
backgroundColor: "{colors.tag-like-background}"
textColor: "{colors.tag-like-text}"
rounded: "{rounded.sm}"
card:
backgroundColor: "{colors.neutral-container}"
rounded: "{rounded.lg}"
padding: "{spacing.lg}"
---
# Design System: CreatorHub
## Overview
**Creative North Star: "日常运营工作台"**
界面是长期处理账号和数据的工作场所。视觉语言克制、常规、清晰,通过位置、分组、文字层级、轻微颜色和状态反馈帮助使用者判断信息,而不是通过装饰吸引注意。
延续当前 Umi Max 4.7、React 19、antd 6.6.5、Pro Components 3.x 和 Ant Design X 的界面。使用 antd/pro 默认组件,不新建另一套按钮、表单、导航或视觉包装;组件不适合业务时优先调整交互。尺寸根据同屏密度与操作频率选择,不以统一放大来强调可用性。
**Key Characteristics:**
- 熟悉的组件、图标与交互,不要求使用者学习新视觉隐喻。
- 内容和操作层级清楚,辅助入口不抢主操作的注意力。
- 列表保持适合日常工作的密度,长内容不撑宽页面。
- 未知、错误和处理中有明确反馈,不通过视觉制造假正常。
本文件记录现有系统,不是改版方案。机器可读数值来自已安装 antd 的 `theme.getDesignToken()`、默认组件与预设色阶,以及 Pro Components 自带的 `getLayoutDesignToken()` 和 `ProLayout` 菜单配置;活动代码没有额外自定义主题或全局样式覆盖。ProLayout 内部默认值优先于普通 antd Menu 的默认值,不能把两者混为一套导航配色。前言中的数值是当前默认主题快照,不要求在应用中重复配置。现有实现依据为 `web/src/layouts/index.tsx`、`web/src/utils/metadata.tsx`、`web/src/components/accounts/` 和 `web/src/pages/creator/`;产品约束见 `PRODUCT.md` 与 `AGENTS.md`。本次是代码分析,未进行浏览器视觉验收。
## Colors
使用白色内容区、浅灰页面背景和低对比度分隔,保留 Ant Design 的清晰蓝色作为交互强调;业务标签用少量预设色区分语义,不形成第二套品牌配色。
### Primary
- **清晰交互蓝**(`colors.primary`):主要按钮、链接及组件原生交互强调。悬停与按下使用默认蓝色色阶,不额外添加渐变、光晕或重色块。
- **浅蓝交互底**(`colors.primary-background`):组件原生的轻度强调,不用于整块表格或整页背景,也不替代 ProLayout 的侧栏选中底。
### Neutral
- **浅灰工作底**(`colors.neutral-layout`):页面底层,与白色卡片区分。
- **白色内容面**(`colors.neutral-container`):表单、卡片和弹层的主要承载面。
- **深色正文 / 次级说明 / 禁用文字**(`colors.neutral-text`、`colors.neutral-secondary`、`colors.neutral-disabled`):使用默认透明度表达层级,禁用色不承担重要正文。
- **控件边界 / 轻分隔**(`colors.neutral-border`、`colors.neutral-divider`):区分输入、表格和内容分组,不增粗边框制造强调。
- **侧栏浅灰选中底 / 深色选中文字**(`colors.nav-selected-background`、`colors.nav-selected-text`):沿用 ProLayout 自带侧栏配色,不改成普通 antd Menu 的浅蓝底与蓝字。
### Named Rules
**The Semantic Color Rule.** 颜色必须同时有文字说明:成功、警告和错误使用相应状态组件;事件类型固定为点赞粉色、评论蓝色、关注紫色、转发橙色,来源固定为同步金色、通知青色。不得仅靠颜色解释状态。
事件和来源采用 `Tag` 的 `magenta`、`blue`、`purple`、`orange`、`gold`、`cyan` 预设。前言中的事件色是色系主色;实际 Tag 文本、背景和边框由 antd 的该色系生成,不能把主色强行用作所有文字。`tag-like` 记录其中一个实际预设样例。
## Typography
**Body Font:** 使用前言中的 antd 系统字体栈,标题和标签沿用同一字体。中文由用户系统可用字体呈现;不新增网络字体、专属展示字体或装饰性字形。
**Character:** 字体服务于连续阅读和数据判断。中文标题与正文遵循默认组件层级,不使用大段全粗体或宣传式大标题。
### Hierarchy
- **Title**:默认四级标题角色,用于常规内容标题;页面标题的实际呈现由 `PageContainer` 决定,不单独覆盖。
- **Body**:表格内容、表单值、操作名称和说明的主要角色;默认字号与行高见前言。
- **Label**:需要强调的字段或小节名称,沿用默认加粗,不另设字体。
- 更小的辅助文字、其他标题级别和控件大小交由 `Typography` 与组件自身 API,不将三个记录角色当成全站强制字号表。
**The Honest Hierarchy Rule.** 标题说明“这是哪里”,正文说明“发生了什么”,辅助文字说明“下一步怎么办”;不能用放大字号掩盖状态缺失或信息组织问题。
## Layout
- 全局使用 `ProLayout` 的混合布局与固定侧栏,菜单按“运营”“资源”分组,工作台为独立入口。侧栏不另造品牌区;详情页选中所属菜单,不同时高亮多个前缀相近的入口。
- 页面标题和副标题来自 `PageContainer`,不在卡片内重复页面标题。当前全站关闭面包屑;登录页不经过主布局。
- 列表主操作放在表格 Card 内右上角,与左侧统计信息同行,使用 `Flex justify="space-between" align="center"`;操作行与后续内容间距为 `spacing.md`。不得同时在页头重复注册同一列表操作。
- 非列表页操作可使用现有页头槽,与实体信息同处 content 行;沿用默认布局能力,不另造浮动工具栏。
- ProLayout 自带的轻背景过渡属于库默认表现,不新增装饰性渐变,也不为消除它而改写主题。PageContainer 的默认内容留白由库处理,不把其较大边距推广到所有控件。
- 间距采用组件默认值和前言中的常用刻度。并非所有内容都必须使用同一内边距;普通 Card 默认内容留白与紧凑列表区应根据组件能力选择。
- 筛选行可以换行。内容必须约束在可用宽度内;长文本完整换行,不撑宽整页。确需横向空间的数据表使用组件的滚动能力,不把桌面表格硬挤成不可读的窄列。
- 默认组件保留自己的响应能力,但本项目没有已验收的独立手机布局。本次不承诺额外断点布局或移动端功能;侧栏折叠、菜单与控件适配优先使用 ProLayout/antd 原生能力。
- 返回列表保留筛选、TAB 和浏览位置;改变筛选或每页数量回到第一页。该行为避免工作中断,不通过重新排列页面表达状态变化。
## Elevation & Depth
界面以背景层次和轻边界组织信息;普通内容区不额外叠加强阴影。Modal、Dropdown、Tooltip、Popover 等短暂浮层保留组件默认深度,以区别于页面内容。不是“全站禁止阴影”,也不是每张卡片都需要悬浮感。
### Shadow Vocabulary
- **默认浮层阴影**:沿用 antd 的 `boxShadow` / `boxShadowSecondary`,准确值记录在 `.impeccable/design.json`;不在业务页面写另一套近似值。
- **默认控件反馈**:按钮自身的轻阴影和输入焦点反馈由组件保留,不扩大为卡片外发光。
**The Quiet Depth Rule.** 普通内容靠层次与分组,浮层靠默认深度;不要用厚阴影、粗描边或装饰性渐变提高存在感。
动效只用于组件状态、展开与关闭等反馈,沿用 antd 默认速度与缓动。不添加循环装饰动画,也不以假进度或永不结束的骨架屏掩盖未实现功能。
## Shapes
采用默认组件的轻圆角:常规输入与按钮使用 `rounded.md`,Tag 等小元素使用 `rounded.sm`,Card 等容器使用 `rounded.lg`。这些是默认刻度,不需要给所有现有元素手动写圆角。
表格、表单和内容区保持常规轮廓。不要将辅助开关、工具按钮或状态标签放大成胶囊式视觉中心,不增加厚边框、大圆角和装饰性形状。
作品小封面沿用现有业务组件。事件聚合的对应作品使用 48px 小封面;不同列表的封面尺寸按已有组件与信息密度处理,不推广为全站图片规格。
## Components
### Buttons
常规、清晰,突出当前任务而不是按钮本身。
- 主操作使用原生 `Button` 的主要样式;列表新增等入口采用通用图标并放在卡片内。
- 次级操作使用默认按钮、文字入口或“更多”菜单。每行只保留必要的状态感知主操作,不铺开所有动作。
- 默认大小、悬停、按下、焦点、禁用和 loading 由组件处理;不能把默认控件高度当成必须统一放大的下限。
- 暂停、停止和删除等有影响的动作明确确认。危险语义采用组件原生 `danger`,不通过自制颜色替代。
### Tags & Status
轻量、带明确文字,不作为大面积视觉装饰。
- 类型与来源使用预设彩色 `Tag`;采集状态仅一个 Tag,异常优先,原因去重后通过 Tooltip 说明。
- 未登录环境使用“待登录”;没有状态或读取失败不能显示成正常。
- 不以 Tag 替代按钮,不通过没有文字的圆点要求使用者猜测含义。
### Cards / Containers
白色内容面,边界轻,间距常规。
- 使用默认 `Card` 与内容留白,按需要通过组件属性区分边界,不用额外包装组件重做卡片。
- 卡片小节标题可保留;页面标题只能出现于全局页头。统计信息与主操作遵循 Layout 中的排列。
- 普通卡片不追加阴影、重底色或大圆角。
### Inputs / Fields
直接表达输入规则,错误紧邻对应字段。
- 使用 `Form`、`Input`、`InputNumber`、`Select` 等原生控件;数据来自可搜索下拉时不提供未经确认的手填替代。
- 保留默认焦点、错误、禁用和校验反馈。标签不能只放在 placeholder 中,重要错误不能只靠颜色表达。
- 原生 `Modal` 承载列表中的创建流程;创建成功关闭并刷新,失败保留填写内容和清楚的错误提示。
- 帮助文字说明实际格式或限制,不为短表单增加多余步骤。
### Navigation
可辨认、稳定,不抢内容的注意力。
- 菜单及中文标题以 `web/src/utils/metadata.tsx` 为准;延续 ProLayout 默认交互,不定制深蓝侧栏或另一套导航样式。
- 图标来自 `@ant-design/icons`,选用行业通用隐喻,不自行发明不熟悉的符号。
- 来源切换使用原生 Tabs;各来源独立保存筛选和分页,不让切换导致上下文丢失。
### Tables & Content
适合反复筛选、比较和追溯,不为装饰牺牲密度。
- 账号名、时间、指标和操作各有明确列语义。缺少指标显示“—”,缺少昵称使用已确认的前端提示,不伪造真实资料。
- 长事件内容在列内完整换行,不截断、不折叠、不添加“展开”,也不能撑宽页面。
- 作品入口配合本地小封面;缺图保持入口和明确提示,不以远程图片掩盖缓存失败。
- 分页、筛选、空状态和错误状态使用默认组件;空列表不是错误,读取失败不是空列表。
### Conversations
沿用聊天组件,让联系人、消息和输入保持熟悉关系。
- 私信使用 Ant Design X 的 `Conversations`、`Bubble.List` 与 `Sender`,不另造聊天皮肤。
- 同一联系人在不同账号下保持独立会话,所属账号清晰,刷新和加载更多不改变当前选中会话。
- 消息的发送中、失败和结果未确认需要可辨认说明,不展示没有证据的送达或已读状态。
### Feedback
出现问题时说明当前结果与可执行动作,不能悄悄消失。
- 使用 `Alert` 的 `title` / `description`,通过 `App.useApp()` 获取消息实例;不重新使用弃用 API。
- 纵向排列使用 `Flex vertical`,不恢复 `Space direction`。
- 断线、缺失资料、下载失败和校验失败显示不同原因;正在处理只对真实请求显示 loading。
- `Empty` 说明无数据或未实现功能。当前工作台以此说明看板尚未实现,不展示假图表、假数据或假骨架屏。
`.impeccable/design.json` 提供工具面板用的独立 HTML/CSS 样例、默认阴影、动效、断点及色阶。样例从现有默认组件翻译,供工具预览,不是可复制到应用中的自定义组件或样式;应用仍使用原生 antd/pro。工具预览不能替代实际界面验收。
## Do's and Don'ts
### Do:
- **Do** 沿用原生 antd/pro 的样式和状态,通过组件 API 解决布局与交互需求。
- **Do** 让标题、统计、筛选、列表与操作有清楚层级,主操作只在对应区域出现一次。
- **Do** 用文字和组件状态同时解释未知、错误、处理中和完成。
- **Do** 将长内容约束在列内,保持真实资料、作品入口和缺图提示可辨认。
- **Do** 保留键盘焦点、可见标签和通用图标,修改后与同屏信息密度对照检查。
### Don't:
- **Don't** 恢复 MUI、react-admin、旧深蓝导航或另一套独立主题。
- **Don't** 新建视觉包装组件或通过 CSS 覆盖默认组件样式;现有业务组件不等于新的设计系统封装。
- **Don't** 主动放大控件,增加厚边框、强阴影、大圆角、装饰性渐变或营销页式布局。
- **Don't** 仅靠颜色传达状态,或把未知、断线、缺失数据呈现为正常、零或完成。
- **Don't** 用编造的头像、指标、图表、远程封面或无限加载来隐藏真实缺失与未实现功能。
+109
View File
@@ -0,0 +1,109 @@
# Product
<!-- impeccable:product-schema 1 -->
## Platform
web
## Users
CreatorHub 面向维护多个抖音账号的新媒体运营团队。主要使用者需要管理自有账号及浏览器环境、跟踪竞品作品、查看评论和互动通知,并处理文字私信。
这是供运营人员持续使用的内部工具,不是面向访客的品牌官网或营销落地页。尚未确认团队规模、人员分工及商业销售方式,不为这些事项编造用户画像或产品承诺。
## Product Purpose
把分散在多个抖音账号、浏览器和平台页面中的运营信息集中到一个工作台,减少反复切换和手工核对,让使用者能辨认账号是否可用、数据是否采集完整,以及哪些互动需要人工处理。
成功意味着使用者能完成以下实际工作,而不是只看到操作成功提示:
- 创建独立浏览器环境,登录后绑定真实账号,并查看其运行、登录及采集情况。
- 分别查看自有和竞品的已采集作品、指标及评论,按需要筛选、排序和追溯原作品。
- 接收已开启账号的真实互动通知,在独立收件箱查看会话并手动发送文字私信。
- 遇到断线、缺少资料或处理失败时,明确知道当前结果及可执行的下一步。
没有已确认的增长率、节省工时或业务收入目标;不得把上述目标转换为未经验证的效果数字。
## Positioning
CreatorHub 是围绕真实浏览器登录环境运行的抖音多账号运营控制台,账号环境、数据采集、互动监听和人工处理在同一产品中关联。
产品的重要约定是保留真实状态和数据来源:未登录环境不冒充账号,已保存数据不冒充平台全量数据,发送请求不冒充已送达消息。这是产品机制,不是经过竞争研究验证的独家优势。
## Operating Context
- 使用者通过浏览器访问内部工作台;当前产品以列表、筛选、详情和表单为主要工作方式。
- 初次使用需要可用网关;在“我的账号”中创建抖音浏览器环境,登录并核验身份后才建立真实账号绑定。
- 自有账号用于日常运营;监控账号用于跟踪竞品,支持通过分享链接导入。两类来源必须明确区分。
- 日常工作在“我的账号”“监控账号”“作品分析”“评论聚合”“事件聚合”“私信管理”之间往返;返回列表时保留当前浏览器标签页内的筛选和浏览位置。
- 网关管理、网络出口和采集设置为运营提供资源与配置,不是独立的产品受众或营销入口。
- 当前部署目标是单机单节点。浏览器网关在宿主机运行,控制服务与数据库另行运行;多节点调度和跨机器恢复不是当前已交付承诺。
- 开发运行说明见 `README.md`;部署地址和目录由环境配置决定,不在产品文档中另设默认值。
## Capabilities and Constraints
### 账号与浏览器环境
- 仅支持抖音(`douyin`),不得恢复小红书等其他平台。
- 创建环境时不填写昵称、UID 或 Cookie,不创建占位账号。待登录环境与已绑定账号出现在同一列表。
- 首次身份核验后绑定真实 UID、昵称等资料;已绑定环境不能悄悄换绑另一个 UID,账号绑定不能改变原浏览器资料。
- 暂停账号与停止浏览器是不同操作。登录、启动或同步资料不能暗中恢复已暂停的账号。
- 调度状态、业务状态、采集状态及浏览器运行情况不能互相替代;读取失败必须明确展示。
### 作品、指标与评论
- 自有作品保存平台分页返回的全部历史作品;竞品作品和评论受采集回看范围限制。
- 平台作品总数与本地已采集数量是不同事实;总数未知不等于零。任务完成也不等于作品已采齐。
- “作品分析”和“评论聚合”通过独立 TAB 区分自有与竞品来源,各自保留筛选及页码。
- 筛选和排序作用于全部符合条件的已采集数据后再分页。缺少指标不能当作零,未采集数据不能参与分析。
- 作品封面来自本地缓存,下载失败或缺图应有说明,不回退展示远程图片。
- 评论聚合是已采集评论的只读展示,不因打开页面而另建采集流程。
### 互动通知与私信
- 自有账号逐个开启监听,默认关闭;未登录环境不能开启。监听需要已登录且运行中的浏览器。
- 事件聚合仅展示当前已开启账号的点赞、评论、关注和转发;私信不混入互动事件列表。
- 开启监听只是设置,不保证接收正常;界面需要说明实际读取结果、最后成功时间及错误。关闭后保留历史,重新开启可继续查看。
- 事件发生时间与接收时间不同;缺少发生时间不能用接收时间代替。用户和作品入口必须来自真实平台资料。
- 私信仅支持已开启监听的自有账号,展示聊天客户端已加载会话的最近 50 条消息,不承诺完整历史。
- 私信只支持手动发送文字,不自动回复、不群发;同一联系人在不同账号下是独立会话,草稿也必须隔离。
- 超时或结果无法确认时应显示“结果未确认”,不自动重发,不宣称送达或已读。资料读取失败不能阻止真实消息保存。
### 设置与当前边界
- 采集时长以正整数和 `s`、`m`、`h` 单位输入;保存配置不能无故重置已有采集进度。
- AI 服务仅支持 OpenAI Compatible,使用已保存的接口地址、密钥和从服务获取的模型;不可凭空预设可用模型,也不据此承诺自动运营能力。
- 当前导航名称以 `web/src/utils/metadata.tsx` 为准;其中 `/creator/settings` 目前显示为“采集设置”。AI 配置能力不代表另一个“系统设置”页面已经存在。
- “工作台”当前是明确说明尚未实现的数据看板占位页,不存在可作为宣传或设计证据的运营统计图表。
- 现有代码和产品约定不等于全部完成了真实平台验收;任何完成度或效果声明都需要单独证据。
## Brand Commitments
- 产品名称为 **CreatorHub**,界面主要使用简体中文。
- 产品表达直接、专业、克制,以真实结果和可执行操作为中心,不使用营销口号包装内部流程。
- 账号昵称、互动者资料及作品信息采用真实平台资料;缺失资料可以明确提示,但不得把前端占位文案保存成真实数据。
- 已确认延续现有运营工作台,而非重新塑造品牌。未建立独立品牌色、专属字体、吉祥物或营销图片体系;现有组件样式属于设计依据,见 `DESIGN.md`。
## Evidence on Hand
| 依据 | 可用于确认的事实 | 限制 |
| --- | --- | --- |
| `AGENTS.md` | 当前产品范围、关键业务约定及界面规则 | 用作现行约束,不作为每项能力都已验收的证据 |
| `web/src/utils/metadata.tsx`、`web/src/layouts/index.tsx` | 当前导航、页面命名和工作台结构 | 不代表页面说明中的规划全部实现 |
| `web/src/components/accounts/`、`web/src/pages/creator/` | 账号、作品、评论、事件、私信和设置的当前界面 | 代码检查不能替代平台、数据库和页面的真实核对 |
| `web/src/pages/dashboard/index.tsx` | 数据看板尚未实现的明确说明 | 不可生成假指标、假曲线或假加载进度 |
| `README.md`、`docs/native-browser-verification.md` | 运行条件及真实环境验证方法 | 旧步骤必须结合当前账号创建约定阅读 |
| `web/package.json`、`DESIGN.md` | 当前前端依赖和现有设计系统 | 不是未来新框架或新视觉方案的授权 |
`docs/plan01.md` 和其他历史计划中包含旧范围或旧实现,不能据此恢复多平台业务或旧界面。当前文档依据现行约束和活动代码整理;若文档与实现不一致,应明确指出差异,不擅自扩大产品范围。
没有随本次分析确认的客户案例、用户访谈、商业定价、品牌资产包或量化成效;后续页面不得编造这些证据。
## Product Principles
1. **真实优先。** 明确区分未知、失败、处理中、完成和完整,不用占位值掩盖信息不足。
2. **来源清楚。** 自有与竞品、互动通知与私信、平台事实与本地进度各自有明确边界。
3. **人工可控。** 关键操作意图清晰,有影响的操作要求确认;不暗中恢复账号、重发私信或改变绑定。
4. **工作连续。** 浏览环境、采集进度、筛选位置和会话草稿不因无关操作丢失。
5. **当前范围优先。** 围绕抖音单机运营闭环完善已有能力,不以未经确认的多平台、全自动运营或新看板替代可用产品。
-378
View File
@@ -1,378 +0,0 @@
---
version: alpha
name: CreatorHub Operations Console
description: Enterprise UI and interaction source of truth for CreatorHub.
colors:
primary: "#0866EF"
primary-hover: "#0759D4"
on-primary: "#FFFFFF"
secondary: "#061A38"
secondary-hover: "#0B2A54"
on-secondary: "#FFFFFF"
on-secondary-muted: "#A9B7CC"
surface-page: "#FFFFFF"
surface-raised: "#FFFFFF"
surface-subtle: "#F8FAFC"
text-primary: "#111827"
text-secondary: "#475467"
text-tertiary: "#667085"
divider: "#D7DDE7"
success: "#067647"
success-surface: "#ECFDF3"
warning: "#B54708"
warning-surface: "#FFFAEB"
error: "#B42318"
error-surface: "#FEF3F2"
typography:
page-title:
fontFamily: "Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, Segoe UI, sans-serif"
fontSize: 2.25rem
fontWeight: 760
lineHeight: 1.15
letterSpacing: -0.035em
section-title:
fontFamily: "Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, Segoe UI, sans-serif"
fontSize: 1.25rem
fontWeight: 700
lineHeight: 1.3
letterSpacing: -0.01em
body-lg:
fontFamily: "Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, Segoe UI, sans-serif"
fontSize: 1.05rem
fontWeight: 400
lineHeight: 1.55
body-md:
fontFamily: "Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, Segoe UI, sans-serif"
fontSize: 1rem
fontWeight: 400
lineHeight: 1.5
label:
fontFamily: "Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, Segoe UI, sans-serif"
fontSize: 0.875rem
fontWeight: 650
lineHeight: 1.4
caption:
fontFamily: "Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, Segoe UI, sans-serif"
fontSize: 0.75rem
fontWeight: 400
lineHeight: 1.4
mono:
fontFamily: "ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, Liberation Mono, monospace"
fontSize: 0.875rem
fontWeight: 400
lineHeight: 1.5
spacing:
xxs: 4px
xs: 8px
sm: 12px
md: 16px
lg: 24px
xl: 32px
page-desktop: 36px
section: 48px
control-height: 40px
primary-control-height: 56px
appbar-height: 70px
drawer-collapsed: 76px
drawer-expanded: 240px
content-max-width: 1280px
rounded:
none: 0px
subtle: 4px
control: 7px
panel: 8px
full: 9999px
components:
page:
backgroundColor: "{colors.surface-page}"
textColor: "{colors.text-primary}"
typography: "{typography.body-md}"
panel:
backgroundColor: "{colors.surface-raised}"
textColor: "{colors.text-primary}"
rounded: "{rounded.panel}"
padding: "{spacing.lg}"
panel-compact:
backgroundColor: "{colors.surface-raised}"
textColor: "{colors.text-primary}"
rounded: "{rounded.panel}"
padding: "{spacing.md}"
table-header:
backgroundColor: "{colors.surface-subtle}"
textColor: "{colors.text-secondary}"
typography: "{typography.label}"
page-title:
textColor: "{colors.text-primary}"
typography: "{typography.page-title}"
section-heading:
textColor: "{colors.text-primary}"
typography: "{typography.section-title}"
intro-copy:
textColor: "{colors.text-secondary}"
typography: "{typography.body-lg}"
metadata:
textColor: "{colors.text-tertiary}"
typography: "{typography.caption}"
code-value:
textColor: "{colors.text-primary}"
typography: "{typography.mono}"
button-primary:
backgroundColor: "{colors.primary}"
textColor: "{colors.on-primary}"
typography: "{typography.label}"
rounded: "{rounded.control}"
height: "{spacing.control-height}"
padding: 0 16px
button-primary-hover:
backgroundColor: "{colors.primary-hover}"
textColor: "{colors.on-primary}"
button-secondary:
backgroundColor: "{colors.surface-raised}"
textColor: "{colors.primary}"
typography: "{typography.label}"
rounded: "{rounded.control}"
height: "{spacing.control-height}"
padding: 0 16px
button-danger:
backgroundColor: "{colors.error}"
textColor: "{colors.on-primary}"
typography: "{typography.label}"
rounded: "{rounded.control}"
height: "{spacing.control-height}"
padding: 0 16px
icon-button:
backgroundColor: "{colors.surface-raised}"
textColor: "{colors.text-primary}"
rounded: "{rounded.control}"
size: "{spacing.control-height}"
input-field:
backgroundColor: "{colors.surface-raised}"
textColor: "{colors.text-primary}"
typography: "{typography.body-md}"
rounded: "{rounded.control}"
height: "{spacing.primary-control-height}"
padding: 0 14px
navigation-item:
backgroundColor: "{colors.secondary}"
textColor: "{colors.on-secondary-muted}"
typography: "{typography.body-md}"
rounded: "{rounded.panel}"
height: 52px
padding: 0 16px
navigation-item-hover:
backgroundColor: "{colors.secondary-hover}"
textColor: "{colors.on-secondary}"
navigation-item-active:
backgroundColor: "{colors.primary}"
textColor: "{colors.on-primary}"
status-success:
backgroundColor: "{colors.success-surface}"
textColor: "{colors.success}"
typography: "{typography.label}"
rounded: "{rounded.full}"
padding: 4px 8px
status-warning:
backgroundColor: "{colors.warning-surface}"
textColor: "{colors.warning}"
typography: "{typography.label}"
rounded: "{rounded.full}"
padding: 4px 8px
status-error:
backgroundColor: "{colors.error-surface}"
textColor: "{colors.error}"
typography: "{typography.label}"
rounded: "{rounded.full}"
padding: 4px 8px
separator:
backgroundColor: "{colors.divider}"
size: 1px
---
## Overview
CreatorHub should feel like an **audit-ready operations console**, not a consumer social dashboard: the calm, stable shift board in a control room where operators can see current state, take one deliberate action, and verify its outcome without visual noise.
The audience is an internal operator managing authorized accounts, isolated browser environments, network exits, confirmations, tasks, and audit evidence. The interface must communicate control, traceability, and safe stopping before speed or novelty.
Three principles decide ambiguous designs:
1. **State before decoration.** Current state, ownership, last evidence, and safe next action receive the strongest hierarchy.
2. **Explicit domain actions.** Start, stop, recycle, confirm, pause, and cancel remain named actions; never disguise them as generic CRUD.
3. **Quiet density.** Use type, spacing, borders, and alignment for hierarchy. Color is reserved for navigation, primary action, status, focus, and errors.
`design.md` is the normative design contract for future pages. Existing implementation differences are migration work for separate issues; do not silently fork this system inside page code.
## Colors
The palette keeps the existing CreatorHub blue and navy anchors while replacing ad hoc page colors with semantic roles.
- **Primary** `{colors.primary}` is the single interaction accent. Use it for the main action, active navigation, links, and focus—not for decoration.
- **Secondary** `{colors.secondary}` is the stable navigation shell. `{colors.secondary-hover}` is its only hover layer.
- **Surfaces** remain white. `{colors.surface-subtle}` is limited to table headers, grouped metadata, and quiet empty-state regions; it must not become a field of nested gray cards.
- **Text** uses `{colors.text-primary}` for decisions and data, `{colors.text-secondary}` for explanation, and `{colors.text-tertiary}` for metadata. Do not lower opacity to invent more tiers.
- **Semantic colors** mean state only. Success, warning, and error always pair color with a label or icon and never stand alone as the sole signal.
- **Dividers** `{colors.divider}` create structure. Regular panels use a 1px divider rather than a shadow.
Do not introduce gradients, glow, glass surfaces, decorative color blocks, or page-specific hex values. A new color requires a reusable semantic role and contrast verification before it enters this file and `web/src/theme.js`.
## Typography
Use the existing Inter-first system stack. Do not download a web font for a control console; system fallbacks preserve speed and Chinese coverage.
- `page-title` is reserved for one page-level `h1`.
- `section-title` labels major work regions, not every card.
- `body-lg` is limited to the page summary or a high-value empty-state explanation.
- `body-md` is the default reading and data size.
- `label` is used for controls, field labels, table headers, and status labels.
- `caption` is supporting metadata; it must still pass normal-text contrast.
- `mono` is for identifiers, endpoints, profile names, hashes, and machine values only.
Use sentence case in Chinese and English. Do not use all caps, decorative display type, or more than three weights in one view. Numeric identifiers must not be made smaller merely to fit; wrap them or expose a copy action.
## Layout
The shell reuses the current application geometry:
- fixed app bar: `{spacing.appbar-height}`;
- permanent desktop drawer: `{spacing.drawer-expanded}` expanded and `{spacing.drawer-collapsed}` collapsed;
- content width: at most `{spacing.content-max-width}`, left-aligned in the main work area;
- page padding: 16px on `xs`, 24px from `sm`, and `{spacing.page-desktop}` from `lg`;
- section separation: 24–48px; panel padding: 16px compact or 24px standard.
Every page follows the same anatomy: page title and one-sentence purpose, page-level primary action when present, filters or creation controls, results, then supporting evidence. Place the action near the object it changes; avoid a detached toolbar full of unrelated buttons.
Use MUI's existing breakpoints: `xs` below 600px, `sm` from 600px, `md` from 900px, and `lg` from 1200px. The application must remain usable without horizontal page scrolling at 320px.
## Elevation & Depth
CreatorHub is border-first and nearly flat. Regular panels, tables, forms, and cards use a 1px divider and no shadow. Hierarchy comes from containment, whitespace, and subtle surface changes.
Only transient overlays—menus, dialogs, popovers, and tooltips—may use MUI's default elevation. Do not stack raised cards inside raised cards, invent page-level z-index scales, or use shadows as focus indicators.
## Shapes
Controls use the 7px `control` radius; panels and navigation items use the 8px `panel` radius. The 4px `subtle` radius is reserved for small inline regions. Full pills are limited to compact statuses and tags.
Do not use oversized rounded cards, capsule buttons for ordinary actions, mixed corner systems, or decorative circles. Icons come from the installed MUI icon set and use the same stroke character within an action group.
## Components
### Shared state model
Interactive components must define, as applicable: `default → hover → focus-visible → active → disabled`, plus `busy`, `error`, `empty`, and `success` at the workflow level. Hover never substitutes for focus. Disabled controls explain the prerequisite in nearby text or a tooltip when the reason is not obvious.
### Buttons and domain actions
- Allow one contained primary button per work region. Secondary actions are outlined or text buttons.
- Use verb-first labels: “创建环境”, “启动”, “停止”, “回收”. Avoid “确定”, “处理”, and icon-only domain actions.
- Destructive or high-impact actions use the error color and a confirmation that names the target and consequence. Default focus stays on cancel.
- Busy labels describe progress (“创建中…”, “停止中…”), disable repeat submission, and preserve layout width.
- Disable only the affected row or object unless the operation invalidates the whole page.
### Inputs and forms
- Every input has a persistent visible label. Placeholder text is an example, never the label.
- Required fields use both native `required` semantics and a visible marker; validation runs on blur or submit, not on each keystroke unless the value can be checked without noise.
- Error text sits next to the field, states how to recover, and remains until corrected. Preserve valid input after request failure.
- Group no more than one primary submission action per form. Put advanced or risky fields behind explicit disclosure only when they actually exist.
### Navigation
- The active item uses primary blue, white text, and the router's current-location semantics.
- Collapsed navigation retains accessible names and tooltips; mobile navigation closes after a route selection and returns focus to the opener when dismissed.
- Disabled destinations must look unavailable and remain non-interactive. Do not present speculative routes as working navigation.
### Tables and responsive cards
- Desktop tables are for comparison across rows. Keep stable column order, left-align text, right-align numeric measures, and place actions last.
- At widths below `md`, replace wide tables with cards that preserve the same data, status, action availability, and reading order.
- Empty results show what is empty and the safe next action. Loading preserves the content region; errors use an alert with a retry action.
- Long identifiers wrap safely or truncate with an accessible full-value affordance. Never shrink below the type tokens to force fit.
### Status and feedback
- Status uses a localized label plus color and, when space permits, an icon or dot. Unknown backend states display a safe “未知状态” label and the raw value as secondary evidence.
- Success is shown by the changed object state; use a toast only when the result would otherwise be invisible.
- Page or action errors use a persistent alert near the affected work region. Do not expose secrets, raw stack traces, or unactionable transport errors.
- Polling updates data without stealing focus, resetting forms, moving the viewport, or repeatedly announcing unchanged content.
### Dialogs, menus, and tooltips
- Use a dialog only for a decision that cannot safely occur inline. The title names the action and target; the body states the consequence.
- Escape and the close control dismiss a dialog before submission. After completion or cancellation, return focus to the trigger.
- Tooltips supplement icon-only controls and unfamiliar terms; they never carry required instructions or validation errors.
## Do's and Don'ts
- **Do** make the current state and next safe action obvious before adding density.
- **Do** reuse MUI and react-admin interaction patterns, theme tokens, and installed icons.
- **Do** keep high-risk lifecycle actions explicit, confirmable, idempotent where supported, and backed by visible outcome evidence.
- **Do** design loading, empty, error, permission/policy hold, and stale-data states with the default state.
- **Do** keep filters, sort, and pagination stable when data refreshes.
- **Don't** add a hero, KPI vanity cards, illustrations, gradients, glassmorphism, or animation merely to make an internal tool feel “modern”.
- **Don't** mix Ant Design, Arco, Mantis code, or another component language into MUI.
- **Don't** encode status only with color, use disabled text as ordinary metadata, or hide critical actions behind hover.
- **Don't** auto-retry `needs_confirmation`, `policy_hold`, unknown results, platform warnings, authentication failures, or destructive actions.
- **Don't** show credentials, cookies, tokens, proxy secrets, or cross-account data in UI copy, logs, screenshots, errors, or exports.
## Naming
- Token names are lowercase kebab-case and semantic: `text-secondary`, not `gray-600`; `status-error`, not `red-pill`.
- Component variants use `<component>-<variant>-<state>`, for example `button-primary-hover`. Do not name tokens after a single page.
- React components use PascalCase, props and handlers use camelCase, and domain actions use exact verbs (`start`, `stop`, `recycle`, `confirm`, `pause`, `cancel`).
- Backend state keys stay stable lowercase English; user-facing labels are localized Chinese. Never use the display label as the program state.
- A user-visible resource has one canonical noun across navigation, title, table, form, dialog, error, and audit text. For the current runtime resource, use “运行环境”; use “Profile” only for the isolated profile artifact.
- Accessibility names describe action plus target (“停止 account-a”), not icon shape (“方形按钮”) or position (“右侧按钮”).
## Interaction
Interaction is quick and mechanical, like operating a reliable control panel:
| Token | Value | Use |
|---|---:|---|
| `motion.feedback` | 120ms | hover, press, focus, toggle |
| `motion.exit` | 160ms | dismissing menus and dialogs |
| `motion.layout` | 200ms | drawer and bounded layout transitions |
| `motion.easing` | `cubic-bezier(0.4, 0, 0.2, 1)` | all transitions |
Nothing bounces, overshoots, pulses indefinitely, or animates longer than 200ms. Under `prefers-reduced-motion: reduce`, durations collapse to effectively zero while state changes remain perceivable.
Actions update in this order: acknowledge the trigger, prevent duplicate submission, perform the request, expose the resulting state or actionable error, then restore focus. Never use optimistic UI for destructive actions or results whose server outcome may be unknown.
## Responsive Behavior
- **320–599px (`xs`):** temporary drawer; one-column forms; cards replace data tables; actions wrap without reordering; 16px page padding.
- **600–899px (`sm`):** 24px page padding; forms may use two columns only when labels and errors still fit.
- **900–1199px (`md`):** permanent drawer; comparison tables; two-column creation forms; no horizontal page overflow.
- **1200px+ (`lg`):** 36px page padding; up to three form columns; content stops growing at 1280px.
Responsive variants must preserve content and action parity. A mobile card may rearrange a row, but it may not omit state, evidence, errors, or a permitted action. Pointer and keyboard behavior must both work at every breakpoint.
## Accessibility
WCAG 2.2 AA is the minimum release boundary.
- Normal text requires 4.5:1 contrast; large text and non-text controls require 3:1. Disabled controls are exempt from text contrast but must remain distinguishable.
- All workflows operate by keyboard in a logical order with no trap. Use native controls first and preserve visible focus.
- Focus uses a 2px high-contrast ring with a 2px offset. On primary or navy surfaces, switch to a white ring when blue cannot reach 3:1.
- Pointer targets are at least 24×24px with sufficient separation; prefer 44×44px for primary, mobile, and destructive actions. A 40px dense control is allowed only when adjacent targets remain separated and keyboard access is equivalent.
- Page landmarks, one `h1`, ordered headings, table headers, field labels, descriptions, and error associations are semantic—not simulated with styling.
- Loading indicators and live results have concise accessible names. Announce completion and errors once; routine polling must not create repeated live-region output.
- Icons never replace text for domain actions. Decorative icons are hidden from assistive technology; icon-only shell controls require explicit accessible names and tooltips.
- Zoom to 200% and text spacing overrides must not clip content, hide actions, or force two-dimensional scrolling outside genuine data tables.
- Do not communicate state, required fields, or errors by color alone.
## Implementation Boundary and Review Checklist
CreatorHub owns its MUI theme, Layout/AppBar/Menu, and react-admin data-provider seam. Future UI work must update those existing seams instead of building a parallel component system. Mantis Free may inform visual judgment under its license, but its template code is not imported or forked. Do not add a token runtime, generator, Storybook, or new UI dependency until a concrete implementation issue proves the existing MUI theme insufficient.
Before a page is accepted, verify:
1. tokens and terminology come from this file or an explicitly reviewed extension;
2. default, hover, focus-visible, disabled, busy, loading, empty, error, and success states are covered where applicable;
3. the page works at 320px, 600px, 900px, and 1200px without losing content or actions;
4. keyboard order, focus return, accessible names, contrast, reduced motion, and 200% zoom pass;
5. domain actions retain their explicit semantics and failure/unknown outcomes never auto-retry;
6. focused interaction tests cover any changed theme, layout, navigation, resource action, or data-provider behavior.
Format and philosophy reference: [google-labs-code/design.md](https://github.com/google-labs-code/design.md) at `9bf8eae67128b6cc55ad9bf86665767deb4c11cd` (Apache-2.0), accessed 2026-08-28. This file adopts the structured-token-plus-rationale approach; CreatorHub's values, rules, and prose are project-specific.