From 52296b336899a89f97f58de727ce130f69c6219a Mon Sep 17 00:00:00 2001 From: stash Date: Wed, 28 Oct 2026 20:44:18 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20stash-ui=200.1.0=EF=BC=8840=20=E4=B8=AA?= =?UTF-8?q?=E7=BB=84=E4=BB=B6=20+=20=E6=96=87=E6=A1=A3=E7=AB=99=20+=20?= =?UTF-8?q?=E4=B8=BB=E9=A2=98=E6=9C=BA=E5=88=B6=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 8 + .oxlintrc.json | 10 + AGENTS.md | 17 + CLAUDE.md | 4 + DESIGN.md | 415 ++++++ README.md | 253 ++++ docs/ADDING-A-COMPONENT.md | 365 ++++++ docs/AI-USAGE.md | 102 ++ docs/ALRIGHT-DECOMPOSITION.md | 201 +++ docs/COMPONENTS.md | 668 ++++++++++ docs/STYLE-INTAKE.md | 88 ++ package-lock.json | 1300 ++++++++++++++++++ package.json | 51 + scripts/screenshot.mjs | 71 + site/App.tsx | 238 ++++ site/app.css | 636 +++++++++ site/components/Demo.tsx | 42 + site/components/PropsTable.tsx | 44 + site/components/Section.tsx | 29 + site/data/demo.ts | 35 + site/demo.html | 18 + site/demo/Page.tsx | 546 ++++++++ site/demo/demo.css | 48 + site/demo/demo.module.css | 333 +++++ site/demo/main.tsx | 12 + site/index.html | 19 + site/main.tsx | 12 + site/sections/Avatars.tsx | 94 ++ site/sections/Breadcrumbs.tsx | 58 + site/sections/Buttons.tsx | 153 +++ site/sections/Cards.tsx | 107 ++ site/sections/Checkboxes.tsx | 88 ++ site/sections/Composition.module.css | 102 ++ site/sections/Composition.tsx | 167 +++ site/sections/Covers.tsx | 89 ++ site/sections/Drawers.tsx | 89 ++ site/sections/Feedback.tsx | 74 ++ site/sections/Icons.tsx | 72 + site/sections/Images.tsx | 86 ++ site/sections/Inputs.tsx | 137 ++ site/sections/Layouts.tsx | 138 ++ site/sections/Marks.tsx | 170 +++ site/sections/MenuSection.tsx | 93 ++ site/sections/Modals.tsx | 120 ++ site/sections/Overview.tsx | 185 +++ site/sections/PageLayout.tsx | 244 ++++ site/sections/Paginations.tsx | 73 ++ site/sections/Progresses.tsx | 57 + site/sections/RadioGroups.tsx | 58 + site/sections/Results.tsx | 60 + site/sections/ScrollTopSection.tsx | 52 + site/sections/Segmenteds.tsx | 57 + site/sections/Selects.tsx | 63 + site/sections/Skeletons.tsx | 74 ++ site/sections/Spinners.tsx | 81 ++ site/sections/StepsSection.tsx | 51 + site/sections/Switches.tsx | 72 + site/sections/Tables.tsx | 78 ++ site/sections/TabsSection.tsx | 67 + site/sections/Toasts.tsx | 60 + site/sections/Tooltips.tsx | 92 ++ site/sections/Typographies.tsx | 129 ++ skills/stash-ui/SKILL.md | 82 ++ src/components/Avatar.module.css | 110 ++ src/components/Avatar.tsx | 79 ++ src/components/Badge.module.css | 30 + src/components/Badge.tsx | 27 + src/components/Breadcrumb.module.css | 41 + src/components/Breadcrumb.tsx | 46 + src/components/Button.module.css | 149 +++ src/components/Button.tsx | 75 ++ src/components/Card.module.css | 36 + src/components/Card.tsx | 39 + src/components/Checkbox.module.css | 80 ++ src/components/Checkbox.tsx | 43 + src/components/Cover.module.css | 79 ++ src/components/Cover.tsx | 85 ++ src/components/Divider.module.css | 38 + src/components/Divider.tsx | 24 + src/components/Drawer.module.css | 95 ++ src/components/Drawer.tsx | 78 ++ src/components/Empty.module.css | 18 + src/components/Empty.tsx | 23 + src/components/FormField.module.css | 29 + src/components/FormField.tsx | 56 + src/components/Grid.module.css | 29 + src/components/Grid.tsx | 52 + src/components/Icon.tsx | 24 + src/components/IconButton.module.css | 84 ++ src/components/IconButton.tsx | 48 + src/components/Image.module.css | 58 + src/components/Image.tsx | 67 + src/components/Input.module.css | 93 ++ src/components/Input.tsx | 64 + src/components/ItemBadge.module.css | 32 + src/components/ItemBadge.tsx | 23 + src/components/Layout.module.css | 191 +++ src/components/Layout.tsx | 160 +++ src/components/Menu.module.css | 113 ++ src/components/Menu.tsx | 127 ++ src/components/Modal.module.css | 92 ++ src/components/Modal.tsx | 95 ++ src/components/Notice.module.css | 54 + src/components/Notice.tsx | 37 + src/components/Pagination.module.css | 56 + src/components/Pagination.tsx | 94 ++ src/components/Popover.module.css | 32 + src/components/Popover.tsx | 55 + src/components/Progress.module.css | 58 + src/components/Progress.tsx | 38 + src/components/Radio.module.css | 92 ++ src/components/Radio.tsx | 66 + src/components/Result.module.css | 51 + src/components/Result.tsx | 45 + src/components/ScrollTop.module.css | 46 + src/components/ScrollTop.tsx | 53 + src/components/Segmented.module.css | 77 ++ src/components/Segmented.tsx | 62 + src/components/Select.module.css | 120 ++ src/components/Select.tsx | 143 ++ src/components/Skeleton.module.css | 70 + src/components/Skeleton.tsx | 60 + src/components/Space.module.css | 63 + src/components/Space.tsx | 50 + src/components/Spinner.module.css | 34 + src/components/Spinner.tsx | 27 + src/components/Steps.module.css | 76 ++ src/components/Steps.tsx | 40 + src/components/Switch.module.css | 63 + src/components/Switch.tsx | 44 + src/components/Table.module.css | 43 + src/components/Table.tsx | 83 ++ src/components/Tabs.module.css | 75 ++ src/components/Tabs.tsx | 90 ++ src/components/Tag.module.css | 85 ++ src/components/Tag.tsx | 52 + src/components/Toast.module.css | 68 + src/components/Toast.tsx | 31 + src/components/Tooltip.module.css | 56 + src/components/Tooltip.tsx | 29 + src/components/Typography.module.css | 133 ++ src/components/Typography.tsx | 144 ++ src/components/toast-store.ts | 69 + src/index.ts | 143 ++ src/theme/assets/iconfont.woff2 | Bin 0 -> 52136 bytes src/theme/iconfont.css | 1807 ++++++++++++++++++++++++++ src/theme/icons.ts | 455 +++++++ src/theme/tokens.css | 268 ++++ themes/example.css | 37 + tsconfig.build.json | 11 + tsconfig.json | 19 + vite.config.ts | 31 + vite.lib.config.ts | 25 + 153 files changed, 17129 insertions(+) create mode 100644 .gitignore create mode 100644 .oxlintrc.json create mode 100644 AGENTS.md create mode 100644 CLAUDE.md create mode 100644 DESIGN.md create mode 100644 README.md create mode 100644 docs/ADDING-A-COMPONENT.md create mode 100644 docs/AI-USAGE.md create mode 100644 docs/ALRIGHT-DECOMPOSITION.md create mode 100644 docs/COMPONENTS.md create mode 100644 docs/STYLE-INTAKE.md create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 scripts/screenshot.mjs create mode 100644 site/App.tsx create mode 100644 site/app.css create mode 100644 site/components/Demo.tsx create mode 100644 site/components/PropsTable.tsx create mode 100644 site/components/Section.tsx create mode 100644 site/data/demo.ts create mode 100644 site/demo.html create mode 100644 site/demo/Page.tsx create mode 100644 site/demo/demo.css create mode 100644 site/demo/demo.module.css create mode 100644 site/demo/main.tsx create mode 100644 site/index.html create mode 100644 site/main.tsx create mode 100644 site/sections/Avatars.tsx create mode 100644 site/sections/Breadcrumbs.tsx create mode 100644 site/sections/Buttons.tsx create mode 100644 site/sections/Cards.tsx create mode 100644 site/sections/Checkboxes.tsx create mode 100644 site/sections/Composition.module.css create mode 100644 site/sections/Composition.tsx create mode 100644 site/sections/Covers.tsx create mode 100644 site/sections/Drawers.tsx create mode 100644 site/sections/Feedback.tsx create mode 100644 site/sections/Icons.tsx create mode 100644 site/sections/Images.tsx create mode 100644 site/sections/Inputs.tsx create mode 100644 site/sections/Layouts.tsx create mode 100644 site/sections/Marks.tsx create mode 100644 site/sections/MenuSection.tsx create mode 100644 site/sections/Modals.tsx create mode 100644 site/sections/Overview.tsx create mode 100644 site/sections/PageLayout.tsx create mode 100644 site/sections/Paginations.tsx create mode 100644 site/sections/Progresses.tsx create mode 100644 site/sections/RadioGroups.tsx create mode 100644 site/sections/Results.tsx create mode 100644 site/sections/ScrollTopSection.tsx create mode 100644 site/sections/Segmenteds.tsx create mode 100644 site/sections/Selects.tsx create mode 100644 site/sections/Skeletons.tsx create mode 100644 site/sections/Spinners.tsx create mode 100644 site/sections/StepsSection.tsx create mode 100644 site/sections/Switches.tsx create mode 100644 site/sections/Tables.tsx create mode 100644 site/sections/TabsSection.tsx create mode 100644 site/sections/Toasts.tsx create mode 100644 site/sections/Tooltips.tsx create mode 100644 site/sections/Typographies.tsx create mode 100644 skills/stash-ui/SKILL.md create mode 100644 src/components/Avatar.module.css create mode 100644 src/components/Avatar.tsx create mode 100644 src/components/Badge.module.css create mode 100644 src/components/Badge.tsx create mode 100644 src/components/Breadcrumb.module.css create mode 100644 src/components/Breadcrumb.tsx create mode 100644 src/components/Button.module.css create mode 100644 src/components/Button.tsx create mode 100644 src/components/Card.module.css create mode 100644 src/components/Card.tsx create mode 100644 src/components/Checkbox.module.css create mode 100644 src/components/Checkbox.tsx create mode 100644 src/components/Cover.module.css create mode 100644 src/components/Cover.tsx create mode 100644 src/components/Divider.module.css create mode 100644 src/components/Divider.tsx create mode 100644 src/components/Drawer.module.css create mode 100644 src/components/Drawer.tsx create mode 100644 src/components/Empty.module.css create mode 100644 src/components/Empty.tsx create mode 100644 src/components/FormField.module.css create mode 100644 src/components/FormField.tsx create mode 100644 src/components/Grid.module.css create mode 100644 src/components/Grid.tsx create mode 100644 src/components/Icon.tsx create mode 100644 src/components/IconButton.module.css create mode 100644 src/components/IconButton.tsx create mode 100644 src/components/Image.module.css create mode 100644 src/components/Image.tsx create mode 100644 src/components/Input.module.css create mode 100644 src/components/Input.tsx create mode 100644 src/components/ItemBadge.module.css create mode 100644 src/components/ItemBadge.tsx create mode 100644 src/components/Layout.module.css create mode 100644 src/components/Layout.tsx create mode 100644 src/components/Menu.module.css create mode 100644 src/components/Menu.tsx create mode 100644 src/components/Modal.module.css create mode 100644 src/components/Modal.tsx create mode 100644 src/components/Notice.module.css create mode 100644 src/components/Notice.tsx create mode 100644 src/components/Pagination.module.css create mode 100644 src/components/Pagination.tsx create mode 100644 src/components/Popover.module.css create mode 100644 src/components/Popover.tsx create mode 100644 src/components/Progress.module.css create mode 100644 src/components/Progress.tsx create mode 100644 src/components/Radio.module.css create mode 100644 src/components/Radio.tsx create mode 100644 src/components/Result.module.css create mode 100644 src/components/Result.tsx create mode 100644 src/components/ScrollTop.module.css create mode 100644 src/components/ScrollTop.tsx create mode 100644 src/components/Segmented.module.css create mode 100644 src/components/Segmented.tsx create mode 100644 src/components/Select.module.css create mode 100644 src/components/Select.tsx create mode 100644 src/components/Skeleton.module.css create mode 100644 src/components/Skeleton.tsx create mode 100644 src/components/Space.module.css create mode 100644 src/components/Space.tsx create mode 100644 src/components/Spinner.module.css create mode 100644 src/components/Spinner.tsx create mode 100644 src/components/Steps.module.css create mode 100644 src/components/Steps.tsx create mode 100644 src/components/Switch.module.css create mode 100644 src/components/Switch.tsx create mode 100644 src/components/Table.module.css create mode 100644 src/components/Table.tsx create mode 100644 src/components/Tabs.module.css create mode 100644 src/components/Tabs.tsx create mode 100644 src/components/Tag.module.css create mode 100644 src/components/Tag.tsx create mode 100644 src/components/Toast.module.css create mode 100644 src/components/Toast.tsx create mode 100644 src/components/Tooltip.module.css create mode 100644 src/components/Tooltip.tsx create mode 100644 src/components/Typography.module.css create mode 100644 src/components/Typography.tsx create mode 100644 src/components/toast-store.ts create mode 100644 src/index.ts create mode 100644 src/theme/assets/iconfont.woff2 create mode 100644 src/theme/iconfont.css create mode 100644 src/theme/icons.ts create mode 100644 src/theme/tokens.css create mode 100644 themes/example.css create mode 100644 tsconfig.build.json create mode 100644 tsconfig.json create mode 100644 vite.config.ts create mode 100644 vite.lib.config.ts diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c9abc34 --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +node_modules/ +dist/ +dist-site/ +shots/ +*.local +.DS_Store + +.ref/ diff --git a/.oxlintrc.json b/.oxlintrc.json new file mode 100644 index 0000000..ec79e6a --- /dev/null +++ b/.oxlintrc.json @@ -0,0 +1,10 @@ +{ + "$schema": "./node_modules/oxlint/configuration_schema.json", + "plugins": ["react", "typescript", "oxc"], + "rules": { + "react/rules-of-hooks": "error", + "react/only-export-components": ["warn", { "allowConstantExport": true }], + "react/jsx-key": "error" + }, + "ignorePatterns": ["dist", "dist-site", "shots"] +} diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..298b996 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,17 @@ +# AGENTS.md + +本仓库是 **Stash UI**:一套独立的设计系统 + 组件库(React + CSS Modules + 设计令牌,零运行时依赖)。 + +改任何页面或组件之前: + +1. 读 [`skills/stash-ui/SKILL.md`](skills/stash-ui/SKILL.md) —— AI 使用入口(任务分流 + 硬约束 + 验证命令)。 +2. 需要细节时再读 [`DESIGN.md`](DESIGN.md)、[`docs/COMPONENTS.md`](docs/COMPONENTS.md)、 + [`docs/ADDING-A-COMPONENT.md`](docs/ADDING-A-COMPONENT.md)。 + +提交前必须通过: + +```bash +npm run typecheck && npm run lint && npm run build +``` + +视觉改动还要截图核对(`scripts/screenshot.mjs`,深浅色各看一次)。 diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..2c3844d --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,4 @@ +# Claude Code 入口 + +规则与流程统一写在 [`AGENTS.md`](AGENTS.md) 和 [`skills/stash-ui/SKILL.md`](skills/stash-ui/SKILL.md), +这里只做指路,避免同一份规则维护两次。 diff --git a/DESIGN.md b/DESIGN.md new file mode 100644 index 0000000..cb8492b --- /dev/null +++ b/DESIGN.md @@ -0,0 +1,415 @@ +# Stash UI 设计系统 · 规格总纲 + +> 版本 0.1 · 适用 `@stash/ui` +> 本文件定义**所有组件共同遵守的规则**。新增组件、改动组件、评审组件都以下面这些条目为准。 + +四份文档的分工: + +| 文档 | 回答什么问题 | 什么时候看 | +| --- | --- | --- | +| **DESIGN.md**(本文) | 整体结构与硬性规则:分层、令牌、视觉、代码约定、验收标准 | 动手前看,评审时逐条对照 | +| [docs/COMPONENTS.md](docs/COMPONENTS.md) | 有哪些组件、各自的契约、缺什么组件、缺的时候该做什么 | 想知道「用哪个 / 有没有」时看 | +| [docs/ADDING-A-COMPONENT.md](docs/ADDING-A-COMPONENT.md) | 从零加一个组件的完整流程、模板、示例 | 确认要新增时照着做 | +| [docs/ALRIGHT-DECOMPOSITION.md](docs/ALRIGHT-DECOMPOSITION.md) | 参考站拆解:实测令牌、组件尺寸、交互节奏、暗色映射、保留差异 | 怀疑「这个值对不对」时查依据 | + +展示站(`site/`)是这三份文档的运行实例:**文档里写的规则,展示页上必须能直接看到**。 + +--- + +## 1. 三条硬约束 + +违反任意一条,组件不合并。 + +1. **只认令牌** + 组件 CSS 里的颜色、字号、行高、圆角、阴影、动效、间距,一律 `var(--su-*)`。 + 允许的字面量只有四类:`0`、`1px` 边框、`100%`、以及组件内声明的局部尺寸变量 `--xxx-size`(见 §5.3)。 +2. **组件不认识业务** + 不请求接口、不读路由、不 import 业务类型(`Category` / `entry` 之类)。 + 组件只接收 props,数据由页面喂给它。 +3. **每个组件都有展示页** + 展示页只能用真实组件渲染,不能写「演示专用版本」;新增组件必须同时加一节。 + +--- + +## 2. 分层结构 + +| 层 | 职责 | 现有组件 | 硬性规则 | +| --- | --- | --- | --- | +| **L1 原子** | 不可再分的小元素 | `Icon` `Badge` `Tag` `Divider` | 不负责布局,**不带外边距**,尺寸由内容或父级决定 | +| **L2 控件** | 接收输入 / 触发操作 | `Button` `IconButton` `Input` | 高度只用 `--su-control-height-*` 或 `--su-icon-button-*` | +| **L3 容器与布局** | 承载内容、排布 | `Card` `Grid` `Space` | 不假设内容形态;不写死宽度;不感知业务 | +| **L4 反馈** | 表达状态 | `Notice` `Empty` `ScrollTop` | 不阻塞、不弹层 | +| **页面级(不进库)** | 顶栏、侧栏、详情页、业务卡片 | — | 放在应用里,用 L1–L4 拼装 | + +依赖方向: + +```text +L4 ──→ L3 ──→ L1 + │ │ + └──────┴──→ L1 L2 ──→ L1 +``` + +- 只允许「上层依赖下层」,**禁止下层 import 上层**(例如 `Card` 里不 import `Empty`)。 +- 组件之间不共享 CSS。除 `Card` 这种明确的容器外,组件不假设自己处在什么容器里。 +- 业务组件(分区卡片、应用条目、海报卡……)属于页面层:**用库里组件拼,不进库**。 + +--- + +## 3. 令牌(唯一的视觉来源) + +完整定义见 [`src/theme/tokens.css`](src/theme/tokens.css)。前缀 `--su-` 是为了避免和宿主应用变量冲突。 +所有取值都来自参考站实测(见 [docs/ALRIGHT-DECOMPOSITION.md](docs/ALRIGHT-DECOMPOSITION.md)),不是拍脑袋定的。 + +### 3.1 颜色 + +| 令牌组 | 用途 | 不要用于 | +| --- | --- | --- | +| `--su-gray-1..8` | 灰阶底座(与参考站一致) | 直接当品牌色用 | +| `--su-bg-page` / `--su-bg-card` / `--su-bg-hover` / `--su-bg-subtle` / `--su-bg-light` | 页面底、卡片底、悬浮底、浅色块(标签)、浅色块(按钮) | 做强调色 | +| `--su-bg-notice` / `--su-bg-danger-soft` | 提示条底色 | 卡片底色 | +| `--su-text-main` / `-secondary` / `-sub` / `-muted` / `-placeholder` | 文字层级(主文 → 次级 → 说明 → 占位) | 互相替代 | +| `--su-border` / `--su-border-strong` / `--su-card-border` | 默认细边 / 需要看清的分隔 / 卡片描边(几乎不可见) | 画装饰线 | +| `--su-color-primary` (+`-hover` `-soft`) | 主操作、链接、选中 | 大块背景 | +| `--su-color-danger` (+`-hover` `-soft` `-border`) | 危险、错误 | 普通强调 | +| `--su-color-warning` (+`-soft` `-text`) / `--su-color-orange-dark` `-light` | 提醒、付费角标 | 错误态 | +| `--su-color-success` (+`-soft`) | 在线、成功 | 主操作 | +| `--su-gradient-verified` / `-hot` / `-paid` / `-red` | 认证角标 / 置顶 / 付费 / 热门 | 大面积背景 | + +### 3.2 间距 + +`--su-space-1..10` = 4 / 8 / 12 / 16 / 20 / 24 / 32 / 40。 +只用这些值;需要 2px、6px 这类视觉微调时才允许字面量(且仅限内边距/圆点尺寸)。 + +### 3.3 字号与字重 + +| 令牌 | 值 | 用途 | +| --- | --- | --- | +| `--su-font-size-xs` / `--su-line-height-xs` | 12 / 16 | 角标(`Badge` / `ItemBadge`) | +| `--su-font-size-sm` / `--su-line-height-sm` | 13 / 20 | 标签、辅助文字 | +| `--su-font-size-md` / `--su-line-height-md` | 14 / 22 | 控件文字(按钮、输入、表格) | +| `--su-font-size-base` / `--su-line-height-base` | 16 / 24 | 内容正文、卡片标题(`Paragraph` / `Title level=3`) | +| `--su-font-size-lg` / `--su-line-height-lg` | 18 / 26 | 区块标题、大图标 | +| `--su-font-size-xl` / `--su-line-height-xl` | 24 / 32 | 页面标题(`Title level=1`) | + +> 参考站的实际排版是 `13/20`、`14/22`、`16/24`、`18/26`、body `16/24`。 +> 组件库把「控件文字」定在 14、把「内容正文」定在 16:控件更紧凑,文章更好读。 + +字重只有三档:`normal 400`、`bold 500`(默认「加粗」)、`strong 700` +(**只给两类**:字形符号如认证角标的 ✓,以及 12px 的 `ItemBadge` 状态角标)。 +组件内**禁止**写 `font-weight: 600`。 + +### 3.4 圆角、阴影、控件尺寸、动效 + +| 令牌 | 值 | 用途 | +| --- | --- | --- | +| `--su-radius-xs` / `sm` / `md` / `lg` / `pill` | 4 / 6 / 12 / 24 / 999 | 标签 / 控件 / 卡片 / 大容器 / 胶囊 | +| `--su-shadow-card` | `0 6px 16px 0 rgb(27 26 49 / 1%)` | 卡片静止(参考站实测,极轻) | +| `--su-shadow-hover` | 同位置参数,3% | 卡片悬停 | +| `--su-shadow-pop` | `0 8px 24px rgb(27 26 49 / 12%)` | 浮层 | +| `--su-control-height-sm` / `md` | 32 / 36 | 按钮、输入框、未来的 Select | +| `--su-icon-button-xs` / `sm` / `md` | 28 / 36 / 44 | 圆形图标按钮(参考站 `.btn-icon`) | +| `--su-duration-fast` / `base` | 0.2s / 0.3s | 颜色变化 / 展开淡入 | +| `--su-ease-in-out` / `-out` / `-in` | ease-in-out / ease-out / ease-in | 默认用 `-in-out`(参考站同款) | +| `--su-skeleton-shine` | 浅色 50% 白 / 深色 8% 白 | 骨架屏扫光 | +| `--su-focus-ring` / `-offset` | `2px solid primary` / 2px | 键盘焦点环 | + +### 3.5 布局与阅读 + +| 令牌 | 值 | 用途 | +| --- | --- | --- | +| `--su-layout-width` | 1380px | 页面容器最大宽度(Layout `contained` 与页面共用) | +| `--su-header-height` | 60px | 应用型页头高度 | +| `--su-radius-cover` | 8px | 封面卡圆角(比卡片小一档) | +| `--su-reading-line-height` | 1.9 | 长文正文行高(`Paragraph leading="relaxed"`) | +| `--su-cover-scrim` | 黑 85% → 40% → 透明 | 封面标题的压暗层 | +| `--su-shadow-card-strong` | 2% 卡片阴影 | 需要更明显层次时(如首页分区卡) | +| `--su-z-header` / `--su-z-float` | 100 / 90 | 顶栏 / 浮动元素 | + +--- + +## 4. 视觉规则(拿不准就按这里来) + +### 4.1 尺寸与间距 + +- 控件高度只有两档;**并排出现时必须同档**(`Input` 与 `Button size="md"` 都是 36px)。 +- 组件**不带 margin**;元素间距交给 `Space` 或父容器。例外:`ScrollTop` 是浮动元素、`Divider` 自身带间距。 +- 卡片内边距:`sm 12` / `md 20` / `lg 24`,不要出现 18、22 这类值。 + +### 4.2 颜色的用法 + +- 文字三档:主文 `text-main`、次级 `text-sub`、说明 `text-muted`;不要自造灰。 +- **行式条目(列表行、作者行)不加底色、不加边框、不加阴影**——这是主题的核心观感,`Card` 不要套在行上。 +- 卡片用「1px 细边 + 极轻阴影」,不要用重阴影或彩色描边制造层级。 + +### 4.3 状态矩阵 + +每个交互组件都要覆盖下表;展示页也要逐个状态摆出来。 + +| 状态 | 实现 | 备注 | +| --- | --- | --- | +| 默认 | 令牌默认值 | — | +| hover | 主色/危险按钮 `opacity: .8`;次要按钮换 `gray-3` 底;图标按钮换 `gray-2` 底 + 文字加深 | 参考站同款,不做位移、不缩放 | +| active | 与 hover 同色,不再额外变形 | 主题本身没有按压态 | +| disabled | 换浅底 + 弱文字 + `cursor: not-allowed`(不是整体降透明) | 不改变布局 | +| loading | 同色 spinner 占用图标位 + 自动 disabled + `aria-busy` | 仅 `Button` / `IconButton` | +| focus-visible | 组件自己写 `outline: var(--su-focus-ring)` + `offset` | 参考站把焦点样式删了,我们**必须**补(WCAG) | +| 空 | `Empty` | 不复用 `Notice` | +| 错误 | `Notice tone="error"` | 带 `role="alert"` | + +### 4.4 动效 + +- 过渡时长统一用 `--su-duration-base`(0.3s)+ `--su-ease-in-out`,与参考站一致; + 只有 hover 的快速反馈可以用 `--su-duration-fast`(0.2s)。 +- 只做颜色 / 背景 / 边框 / 阴影 / 透明度,不做位移、缩放、旋转(加载 spinner 除外)。 +- 需要 `transition` 的:`Button`、`IconButton`、`Card hoverable`、`Input`、`ScrollTop`、`Tag`(如加交互)。 +- **每个带动效的组件都要有 `@media (prefers-reduced-motion: reduce)` 分支**(过渡置 none,spinner 放慢)。 + +### 4.5 响应式 + +断点只有三个档,**组件内部**处理,页面不要各写一套: + +| 断点 | 谁在用 | 行为 | +| --- | --- | --- | +| `1200px` | `Layout` | 右栏(aside)从并排变成堆到内容下方 | +| `1100px` | `Layout`、`Grid` | 侧栏堆到内容上方;`Grid` 降为 2 列 | +| `820px` | 页面(如示例页) | 顶部导航收进抽屉、左侧资料栏隐藏 | +| `720px` | `Grid` | 降为 1 列 | + +- 组件宽度默认 `100%`,由容器决定;不写死像素宽度(演示代码除外)。 +- **不靠隐藏做适配**:`Layout` 在窄屏下把侧栏/右栏堆叠,内容一律保留;页面如果确实要隐藏(如移动端的资料栏),由页面自己决定。 +- 高度用 `100dvh` 优先(`100vh` 兜底),地址栏收起时不会跳动。 +- 弹层宽度必须夹住:Modal `calc(100vw - 32px)`、Popover `min(320px, calc(100vw - 32px))`、Drawer `max-width: 100vw`。 +- 表格/标签栏/分段控件在窄屏横向滚动,不做压缩截断。 + +### 4.6 触屏 + +- 交互只用 `click`,不依赖 hover;`Tooltip` 的 hover 样式包在 `@media (hover: hover)` 里,触屏走 `:focus-within`。 +- 可点区域:`xs` 图标按钮 28×28 是**桌面密集场景**下限;移动端布局请用 `sm(36)` / `md(44)`。 +- 按钮类加 `touch-action: manipulation`,去掉双击缩放带来的点击延迟。 +- 原生控件(checkbox/radio/switch)都包在 `