feat: stash-ui 0.1.0(40 个组件 + 文档站 + 主题机制)
This commit is contained in:
@@ -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)都包在 `<label>` 里,点文字也能操作。
|
||||
|
||||
> 现状:组件是响应式的、也能在手机上用(示例页与文档站都在 390 / 820 宽实测过),
|
||||
> 但**还没有在真机上跑过**(iOS Safari 的 `dvh`、软键盘、`backdrop-filter` 需要真机复核)。
|
||||
> 结论性的兼容承诺见 §10「已知坑」与 README 的「浏览器支持」。
|
||||
|
||||
---
|
||||
|
||||
## 5. 代码约定
|
||||
|
||||
### 5.1 文件结构
|
||||
|
||||
```text
|
||||
src/components/
|
||||
├── Xxx.tsx # 组件 + props 类型(同文件,不拆 types 目录)
|
||||
└── Xxx.module.css # 只属于这个组件的样式
|
||||
src/index.ts # 唯一出口:组件 + 类型(export type)
|
||||
```
|
||||
|
||||
### 5.2 props 命名
|
||||
|
||||
| 类型 | 约定 | 例子 |
|
||||
| --- | --- | --- |
|
||||
| 语义变体 | `variant` | `primary / secondary / ghost / danger` |
|
||||
| 尺寸 | `size` | `sm / md / lg` |
|
||||
| 颜色语义 | `tone` | `primary / success / warning / danger / neutral` |
|
||||
| 形状 | `shape` | `rounded / pill`、`circle / square` |
|
||||
| 布尔 | 直接用形容词,**不加 is/has 前缀** | `block` `loading` `clearable` `wrap` |
|
||||
| 透传 | 展开 `...rest` 到最外层原生元素 | 原生属性一律可用 |
|
||||
| 受控 | 值由使用方持有;组件不偷偷维护业务状态 | `value` + `onChange` |
|
||||
| 内部 UI 状态 | 只在确实属于组件自身时保留 | `Avatar` 的图片加载失败 |
|
||||
|
||||
- 事件保持原生语义:`onChange` 传事件,不要偷偷换成值;需要值由调用方 `event.target.value`。
|
||||
- `className` / `style` 一律作用在**最外层**元素;要作用到内部就用具名 props(如 `inputRef`)。
|
||||
- 原生 `button` 必须显式 `type`,默认 `"button"`,避免表单里误提交。
|
||||
|
||||
### 5.3 样式写法
|
||||
|
||||
- CSS Modules,类名短:`.root`、`.sm`、`.primary`、`.hoverable`;修饰类平铺,不嵌套。
|
||||
- 选择器最多两层;禁止 `!important`;禁止标签选择器(伪类、`::before` 除外)。
|
||||
- 组件专属尺寸(头像 24/36/48、角标 18、回到顶部 42)写成**组件内局部变量**,集中在 `.root` 或尺寸类里:
|
||||
|
||||
```css
|
||||
.sm {
|
||||
--avatar-size: 24px;
|
||||
width: var(--avatar-size);
|
||||
height: var(--avatar-size);
|
||||
}
|
||||
```
|
||||
|
||||
- 颜色 / 字号 / 间距永远从全局令牌取,**不要**复制一份到局部变量。
|
||||
|
||||
### 5.4 无障碍基线
|
||||
|
||||
- 装饰性图标 `aria-hidden="true"`;**只有图标**的控件必须传 `label`(进 `aria-label` 与 `title`)。
|
||||
- 错误提示 `role="alert"`;说明类提示不加 live region,避免每次渲染都被播报。
|
||||
- 不得移除 `focus-visible`;隐藏但仍可聚焦的元素要 `tabIndex={-1}`。
|
||||
- 文案用中文标点;数字与单位之间不加空格(`24px`、`0.3s`)。
|
||||
|
||||
---
|
||||
|
||||
## 6. 验收清单(合并前逐条打勾)
|
||||
|
||||
```text
|
||||
[ ] 1. 组件 CSS 里没有 hex / rgba / 字号 / 间距字面量(只用 --su-*)
|
||||
[ ] 2. 覆盖默认 / hover / disabled / focus-visible(交互组件再加 loading)
|
||||
[ ] 3. 透传 ...rest,button 的 type 默认 "button"
|
||||
[ ] 4. className / style 作用在最外层
|
||||
[ ] 5. 无外边距(margin),间距交给 Space / 父容器
|
||||
[ ] 6. 展示页新增一节:变体 / 状态 / props 表 / 代码片段
|
||||
[ ] 7. 导航锚点、README 清单、COMPONENTS.md 契约三处同步
|
||||
[ ] 8. npm run typecheck && npm run lint && npm run build 通过
|
||||
[ ] 9. 截图核对(scripts/screenshot.mjs),无视觉回归
|
||||
[ ] 10. 没有新增运行时依赖
|
||||
[ ] 11. 深色主题下不破:不写死白底/黑字假设,颜色全部走令牌
|
||||
[ ] 12. props 名符合 §5.2;没有出现「业务字段名」(如 categoryId)
|
||||
[ ] 13. 键盘走一遍:Tab 能看到焦点环,Enter/Space 能触发,Esc 能退出浮层
|
||||
[ ] 14. 打开「减少动态效果」后没有持续动画(spinner 允许放慢)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. 深色主题
|
||||
|
||||
已实现,组件零改动:所有颜色走语义令牌,深色只覆盖令牌块([tokens.css](src/theme/tokens.css) 末尾)。
|
||||
|
||||
用法:在 `<html>` 上设置 `data-scheme`:
|
||||
|
||||
| 值 | 效果 |
|
||||
| --- | --- |
|
||||
| 不设置 / `light` | 浅色 |
|
||||
| `dark` | 深色 |
|
||||
| `auto` | 跟随系统(`prefers-color-scheme`) |
|
||||
|
||||
```html
|
||||
<html lang="zh-CN" data-scheme="dark">
|
||||
```
|
||||
|
||||
取值来自参考站 `body[data-scheme=dark]` 的实测映射(见 [拆解文档 §8](docs/ALRIGHT-DECOMPOSITION.md)):
|
||||
|
||||
- 灰阶整体反相:`gray-5..8` 变深,文字改用浅色;
|
||||
- `--su-bg-card` → `#202022`、`--su-border` → `#303033`、`--su-bg-page` → `#0f0f11`;
|
||||
- 浅色底(`primary-soft` / `danger-soft` / `success-soft` / `warning-soft`)透明度提高,否则标签看不见;
|
||||
- 阴影换成更深的黑(深色下浅色阴影没有意义)。
|
||||
|
||||
**新增组件的深色要求**:
|
||||
|
||||
- 颜色只用语义令牌(`bg-card` / `bg-subtle` / `bg-light` / `bg-hover` / `text-*` / `border-*`),
|
||||
不要直接写 `--su-gray-*`——灰阶在深色下是反相的,直接用会串色;
|
||||
- 展示页用 `demo.html?scheme=dark` 或右上角开关看一眼深色;
|
||||
- 验收清单第 11 条会在深色下逐项检查。
|
||||
|
||||
---
|
||||
|
||||
## 8. 决策记录(为什么是这样)
|
||||
|
||||
| 决策 | 结论 | 理由 |
|
||||
| --- | --- | --- |
|
||||
| 视觉基准 | 以参考站实测值为准 | 之前按文档里的旧值做(阴影 2%、字阶 10/12/14/16),实测是 1% 和 13/14/16/18,见拆解文档 |
|
||||
| 自研 vs 直接用 antd | 自研 | 视觉目标是 Alright 主题,用 antd 要重写大半 token;单二进制发布也不想多 300KB+ |
|
||||
| 样式方案 | CSS Modules | Vite 内置、零依赖、类名可读;Tailwind 会把样式散进 className,emotion 要运行时 |
|
||||
| 令牌前缀 | `--su-` | 宿主应用(stash-web)自己有一批 `--primary-color` 之类变量,必须能共存 |
|
||||
| 组件边界 | 不收业务组件 | 业务卡片变化快,进库会把组件库拖成第二个页面层 |
|
||||
| 展示站 | 自建 Vite 站 | 见 README「为什么不直接用 Storybook」 |
|
||||
| 图标 | iconfont 字形子集 | 主题自带线性图标;按需加字形(流程见 COMPONENTS.md §5) |
|
||||
| 焦点样式 | 与参考站相反:保留并加粗 | 参考站把 focus 样式删了(`box-shadow:none`),标了 tab 看不见焦点;组件库必须可访问 |
|
||||
|
||||
---
|
||||
|
||||
## 9. 已知坑(都踩过)
|
||||
|
||||
1. **展示站要显式 import 主题 CSS**:只靠 `src/index.ts` 的副作用引入时,生产构建会把 `iconfont.css` 摇掉(dev 正常、build 后图标全空)。
|
||||
2. **库产物的 CSS 内联了图标字体**:lib 模式下 Vite 把 woff2 转成 data URI,`dist/stash-ui.css` 有 80KB+(gzip 56KB),换来「只引一个 CSS」。
|
||||
3. **比例封面不要放进 `flex-direction: column` 的父容器**:纵向 flex 会把子项按内容高度压缩,`aspect-ratio` 封面被裁掉一截;用 grid 或 block 容器包一层。
|
||||
4. **并排控件必须同高**:`Input` 与 `Button size="md"` 都是 `--su-control-height-md`。
|
||||
5. **展示站里不要用 `.demo-*` 之外的自造类**:文档站样式只写在 `site/app.css`,组件样式不外溢。
|
||||
6. **别照着旧文档抄数值**:参考站的 token(`--font-size-lg: 16px`)和它实际渲染的 `.text-lg: 18px` 不是一回事,
|
||||
以 [拆解文档](docs/ALRIGHT-DECOMPOSITION.md) 的实测表为准。
|
||||
|
||||
---
|
||||
|
||||
## 10. 版本与变更
|
||||
|
||||
- 当前 `0.x`:API 仍可能调整,破坏性改动在 [COMPONENTS.md](docs/COMPONENTS.md) 末尾的「变更记录」记一行。
|
||||
- 组件新增 / 删除 / 重命名,必须同步三处:`src/index.ts`、展示页、COMPONENTS.md。
|
||||
|
||||
## 11. 外部风格接入(2026-10-28 记录,讨论中,尚未执行)
|
||||
|
||||
> **场景**(之前记错了,这里更正):
|
||||
> 开新项目时,使用方会给一张**别的网站的截图**,风格与 stash / Alright **不一致**,
|
||||
> 要求「照这个做布局 / 这个组件」。这时必然出现三种情况:
|
||||
> 库里没有、参数不够、组件不灵活。
|
||||
>
|
||||
> 这不是「迁移时补 CSS」的问题,而是**库要不要吸收外来设计语言**的问题。
|
||||
> 如果不设闸门,结果只有两个:要么每个项目各写一套页面 CSS(库永远长不大),
|
||||
> 要么把品牌专属样式塞进通用组件(库变成第二个页面层)。
|
||||
|
||||
### 11.1 为什么会撞墙(现有结构的三层限制)
|
||||
|
||||
| 层 | 现状 | 外来截图会撞到的地方 |
|
||||
| --- | --- | --- |
|
||||
| **颜色 / 字体** | 语义令牌 + CSS 变量,可整体覆盖 | 基本够用:换 `--su-color-*` / `--su-font-*` 即可,但**前提是页面只用语义令牌** |
|
||||
| **形态**(圆角 / 阴影 / 高度 / 密度) | 一部分是令牌(radius、shadow、control-height),一部分写死在组件里(卡片圆角、按钮高度档位、Tab 高度) | 外来设计常用「32 / 40 / 48 三档高度」「24px 大圆角」「无阴影」——现在只能新增组件参数或覆盖内部类名 |
|
||||
| **结构 / 交互** | 组件是按 stash 的形态做的(分区卡、封面卡、侧栏菜单) | 外来截图可能是「瀑布流 + 悬浮操作条」「顶部 Tab + 全宽表格」——这是**新组件**,不是参数问题 |
|
||||
|
||||
结论先说:**令牌对「配色/字体」够用,对「形态」还不够;组件对「结构」永远需要增量。**
|
||||
所以闸门不是「能不能加」,而是「加在哪一层、以什么粒度加」。
|
||||
|
||||
### 11.2 闸门流程(收到截图后,先停再动手)
|
||||
|
||||
```text
|
||||
截图
|
||||
→ ① 拆解:把截图拆成「布局骨架 / 组件清单 / 形态值 / 交互」四张表
|
||||
→ ② 映射:每一项对到库里的组件 + 令牌,标出「完全匹配 / 参数可调 / 缺失」
|
||||
→ ③ 决策:按 11.3 的矩阵定归属(主题覆盖 / 加参数 / 加新组件 / 页面私有)
|
||||
→ ④ 评审:把「缺失项 + 最小 API 方案」给使用方确认(此时不动代码)
|
||||
→ ⑤ 实施:库先行(组件 + 契约 + 展示页 + 令牌),页面后接
|
||||
→ ⑥ 验收:深浅色、焦点、reduced-motion、移动端、无业务字段
|
||||
```
|
||||
|
||||
第 ② 步的产物就是一张 **「截图 → 组件/令牌」映射表**,模板见 [docs/STYLE-INTAKE.md](docs/STYLE-INTAKE.md)。
|
||||
|
||||
### 11.3 决策矩阵(外来样式该落到哪)
|
||||
|
||||
| 情况 | 归属 | 例子 |
|
||||
| --- | --- | --- |
|
||||
| 只是配色 / 字体不同 | **主题覆盖**:新增 `themes/<name>.css`,只改变量 | 紫色主色、衬线标题 |
|
||||
| 同结构、形态不同(圆角 / 高度 / 密度) | **加参数或加尺寸档**到现有组件 | 按钮 40 / 48 档、卡片 16 圆角 |
|
||||
| 同结构、交互不同(悬浮操作、展开方式) | **加 variant**(受控、可访问) | Tab 改成胶囊 + 下划线、卡片 hover 出操作 |
|
||||
| 结构不同(新骨架 / 新组合模式) | **新组件**,前提是「≥2 处会用」或「属于通用模式」 | 瀑布流、顶部筛选条、全宽表格 |
|
||||
| 只此一页的装饰(背景图、悬浮搜索、文章排版) | **页面私有 CSS**,必须在注释里写清「为什么不是组件」 | stash 的背景图层 |
|
||||
|
||||
三条硬边界:
|
||||
|
||||
1. **不许 fork 库**(复制组件到项目里改),要么参数化,要么进库;
|
||||
2. **不许在页面覆盖组件内部类名**(`.su-xxx` / hashed class),只能传 props 或改令牌;
|
||||
3. **品牌专属值不许进组件默认值**:外来项目的紫色不能改 `--su-color-primary` 的默认值,只能在主题文件里覆盖。
|
||||
|
||||
### 11.4 吸收的判定标准(避免库被拖成页面层)
|
||||
|
||||
一个能力同时满足下面三条才进库:
|
||||
|
||||
- **可复用**:至少两个使用方 / 两个页面会用到,或属于公认的通用模式;
|
||||
- **无业务**:不携带字段、不请求数据、不 import 业务类型;
|
||||
- **可验收**:能用 props 表达,且不破坏现有契约(破坏性改动走 COMPONENTS 变更记录)。
|
||||
|
||||
### 11.5 改共享组件必须报兼容性影响
|
||||
|
||||
组件不只一个地方用,改之前(以及交付说明里)必须显式列出:
|
||||
|
||||
1. **调用方清单**:哪些页面 / 组件用了它(至少列出仓库内已知调用点);
|
||||
2. **变更性质**:新增可选 prop(兼容)/ 改默认值(视觉回归)/ 改或删 prop(破坏性)/ 只改样式(视觉回归);
|
||||
3. **回归范围**:哪些展示页 demo、哪些应用页面需要重新截图;
|
||||
4. **迁移方式**:破坏性改动要给出替代写法(旧 → 新 的对照),必要时保留一个版本周期。
|
||||
|
||||
> 经验:这次 `Layout` 把「页头」从限宽容器里移出来(页头通栏),属于**视觉回归**,
|
||||
> 虽然 API 没变,但所有用 `contained` 的页面都会变——这种改动必须在说明里点出来,而不是静默提交。
|
||||
|
||||
### 11.5 待办(只记录,不执行)
|
||||
|
||||
- **令牌分层**:现在 `--su-*` 把「原语(灰阶、色板)」和「语义(bg-card、text-sub)」混在一起,
|
||||
外来主题想换「形态」时没有落点。候选方案:
|
||||
`--su-primitive-*`(色板/尺度)→ `--su-*`(语义)→ 组件只吃语义;另外把「形态值」抽成
|
||||
`--su-shape-*`(radius / shadow / control-height / density),让主题能整体换形态。
|
||||
- **尺寸档位**:Button / Input / Avatar 的高度档是否需要补齐 40 / 48,以及是否引入 `density="compact|comfortable"`。
|
||||
- **主题文件规范**:`themes/<name>.css` 只允许改变量,禁止写选择器;需要一个 lint 或 review 检查项。
|
||||
- **外部截图进库的示例**:拿一个真实的外来设计走一遍 11.2,验证流程是否可落地。
|
||||
Reference in New Issue
Block a user