Files
stash-ui/docs/ALRIGHT-DECOMPOSITION.md
T

202 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Alright 参考站拆解
> 对标站点:<https://alright.demo.nicetheme.xyz/>
> 拆解日期:2026-10-26 · 对照版本:`@stash/ui` 0.1.0
> 产物:[DESIGN.md](../DESIGN.md)(规则)、[COMPONENTS.md](COMPONENTS.md)(契约)都按这份拆解更新。
## 1. 怎么拆的
不看截图猜,全部取实测值:
| 手段 | 用途 | 落盘位置 |
| --- | --- | --- |
| 抓取主题 CSS(620KB,Autoptimize 合并包) | 读原始规则:令牌、`:hover`、`@media`、暗色覆盖 | `.ref/theme.css` |
| Playwright 跑计算样式 | 拿到真实渲染值(字号、行高、圆角、阴影、尺寸、过渡) | `.ref/probe.json` |
| Playwright 截图 | 桌面 1440 / 移动 390、首页 / 详情页 | `.ref/shots/*.png` |
复现命令(都要先 `npm run dev`):
```bash
node .ref/probe.mjs # 重新采集计算样式 + 截图
node .ref/layout.mjs # 只量布局(列宽、头部高度)
```
`.ref/` 只是拆解产物,不进构建、不进发布。
## 2. 站点底座(决定了哪些东西不该抄)
- **WordPress + Bootstrap 5**:按钮、表单、模态、分页的基础行为来自 Bootstrap;
- **Alpine.js**(`x-data` / `@click`):交互状态,例如点赞、主题切换;
- **Swiper / Fancybox / Plyr / CKEditor**:轮播、灯箱、播放器、编辑器,各有独立样式;
- **自建 iconfont**(Project 4809463):线性图标,字号 18~24;
- 主题自己的工具类:`.text-lg`、`.flex-avatar`、`.item-badge`、`.box-block`、`.tags-list`……
结论:**只对齐主题层**(令牌、尺寸、间距、圆角、阴影、交互节奏、组件外观),
不复刻 Bootstrap 的组件实现(下拉、模态那种带 JS 状态机的东西我们自己做,见 COMPONENTS.md §5)。
## 3. 设计令牌对照
| 参考站变量 | 值 | 我们的令牌 | 对齐情况 |
| --- | --- | --- | --- |
| `--gray-1 … --gray-8` | `#f7f8fa` `#f6f6f6` `#eee` `#c9cdd4` `#86909c` `#4e5969` `#2c343c` `#1d2129` | `--su-gray-1 … -8` | ✅ 完全一致 |
| `--blue` | `#3858f6` | `--su-color-primary` | ✅ |
| `--red` | `#ff3e3e` | `--su-color-danger` | ✅ |
| `--orange` / `--orange-dark` / `--orange-light` | `#ffba3a` / `#ff8338` / `#fffbe8` | `--su-color-warning` / `--su-color-orange-dark` / `--su-color-orange-light` | ✅ 本轮补齐后两项 |
| `--green` | `#4abd86` | `--su-color-success` | ✅ |
| `--gradient-red` | `linear-gradient(to right,#ff6034,#ff3e3e)` | `--su-gradient-red` | ✅ 本轮新增 |
| `.item-badge` 置顶 / 付费 | `135deg #ff2f43→#ff6327` / `#ff952f→#ff662b` | `--su-gradient-hot` / `--su-gradient-paid` | ✅ 本轮新增 |
| 认证角标 SVG | `#FFB74A → #FD900F` | `--su-gradient-verified` | ✅ |
| `--border-radius-xs/sm/md/lg/max` | 4 / 6 / 12 / 24 / 999 | `--su-radius-*` | ✅ |
| `--shadow` | `0 6px 16px 0 rgb(27 26 49 / 1%)` | `--su-shadow-card` | ⚠️ 原为 2%,**本轮改成 1%** |
| `--shadow-2` | `rgb(27 26 49 / 3%)` | `--su-shadow-hover` | ⚠️ 原为 6%,**本轮改成 3%** |
| `--border-color` / `--border-light` | `gray-2` / `gray-3` | `--su-border` / `--su-border-strong` | ✅ |
| `--card-border-color` | `gray-1` | `--su-card-border` | ✅ 本轮新增(卡片描边几乎不可见) |
| `--color-text` / `--color-secondary` | `gray-7` | `--su-text-secondary` | ✅ 本轮新增 |
| `--color-muted` | `gray-6` | `--su-text-sub` | ✅(我们的 `text-muted` 保留 `gray-5` 给更弱的占位) |
| `--bg-body` / `--card-bg` | `gray-1` / `#fff` | `--su-bg-page` / `--su-bg-card` | ✅ |
| `--bg-light` | `gray-2` | `--su-bg-subtle`(本轮改为 gray-1) | ⚠️ 参考站标签用的是 `gray-1`,见 §6 |
| `--animation-duration-fast/base` | `.2s` / `.3s` | `--su-duration-fast/base` | ✅ |
| `--animation-timing-function-enter/leave` | `ease-out` / `ease-in` | `--su-ease-out` / `--su-ease-in` | ✅ |
| 组件里的过渡 | 绝大多数是 `all .3s ease-in-out` | `--su-ease-in-out` | ✅ 本轮新增,组件过渡改用它 |
## 4. 字号与行高(最容易抄错的一处)
参考站 `:root` 里的 `--font-size-*`(10/12/14/16/24)**并不是页面实际字号**,
真正生效的是 `.text-*` 工具类,且在小屏还有一组覆盖:
| 类 | 桌面 | 小屏(≤600px) | 典型用途 |
| --- | --- | --- | --- |
| `.text-xs` | **13 / 20** | 12 / 16 | 标签、元信息 |
| `.text-sm` | **14 / 22** | — | 正文(侧栏简介) |
| `.text-md` | **16 / 24** | 15 / 24 | 正文、条目摘要 |
| `.text-lg` | **18 / 26** | — | 条目标题、卡片标题 |
| `body` | **16 / 24** | — | 全局 |
`.item-badge` 是 12 / 14.4(700 字重),标签 `.tags-list a` 是 13 / 20。
我们的处理:把 `--su-font-size-*` 改成 12 / 13 / 14 / 18 / 24,行高 16 / 20 / 22 / 26 / 32;
组件内文字只用到 `sm 13`(标签)、`md 14`(控件)、`lg 18`(图标),
`xs 12` 给角标。**基础字号仍是 14**(见 §10 的保留差异)。
## 5. 布局
| 项 | 参考站实测 | 说明 |
| --- | --- | --- |
| 容器 | 1320 宽,居中,左右 60 | 对应 `--layout-max-width` 一类变量 |
| 三栏 | 左 330 / 中 660 / 右 330(gutter 24) | `col-xl-3 / col-xl-6 / col-xl-3` |
| 顶栏 | 80 高,透明叠在渐变背景上 | 导航项 48 高、字号 16 |
| 内容起始 | y=112(顶栏 80 + 32) | 卡片与顶栏留 32 的呼吸 |
| 卡片内边距 | `40px 24px`(侧栏 widget) / `12px`(缩略图卡片) | 竖向留白明显大于横向 |
| 缩略图 | 3 列,间距 8,圆角 8 | 图集区块 |
这些是**页面级**参数,不进组件库;卡片的内边距档位(12 / 20 / 24)继续由 `Card padding` 管。
## 6. 组件实测(对齐依据)
| 组件 | 参考站选择器 | 实测关键值 | 我们的对应 |
| --- | --- | --- | --- |
| 卡片 | `.box-block` | `#fff`、radius 12、`1px solid gray-1`、shadow `0 6px 16px 1%`、padding `40/24`、过渡 opacity/transform .3s | `Card` |
| 缩略图卡 | `.item` | `#fff`、radius 12、同款描边与阴影、padding 0(内容 12) | `Card padding="none"` |
| 标签 | `.tags-list li a` | 高 24、`13/20`、radius 30、`gray-1` 底、`gray-6` 字、左侧 20×15 图标、hover 字色变 `gray-8`;hot 变体是 `rgba(255,94,68,.1)` 底 + 红字 | `Tag`(soft / solid / icon / dot) |
| 状态角标 | `.item-badge` | `12/14.4`、700、padding `2px 4px`、radius 4、白字、红/橙渐变底 | `ItemBadge`(本轮新增) |
| 数字角标 | `.btn-icon small` | 12px、padding `2px 5px`、radius 10、红底白字 | `Badge`(18 高) |
| 头像 | `.flex-avatar` + `.avatar-badge` | 96 / 40 / 20 三档;角标是 SVG 渐变对勾,尺寸 12 / 16 / 20 | `Avatar`(24/36/48 + 16/20 角标) |
| 图标按钮 | `.btn-icon` | 圆形,默认 42(变体 28 / 36 / 44),图标字号 16 / 20 / 24,无阴影 | `IconButton`(本轮改为圆形 28/36/44) |
| 按钮 | `.btn` / `.btn-rounded` | radius 6 / 32、padding `8px 20px`、字号 14、`transition all .3s`、`primary:hover` 是 **opacity .8** | `Button`(新增 `shape="pill"`,hover 用 opacity) |
| 输入框 | `.form-control` | 高 36、radius 6、padding `8px 14px`、`1px solid gray-3`、focus 换主色边框、无阴影 | `Input`(描边改 `gray-3`,focus 主色) |
| 图片悬浮 | `.custom-hover` / `.hover-content` | 图片 `scale(1.1) rotate(2deg)`;遮罩 `rgba(0,0,0,.65)` 淡入、图标上移 10px | 组合模式(见 COMPONENTS.md §4) |
| 分页 | `.page-numbers` | 32×32、radius 4、`gray-2` 底、当前项主色底白字 | 规划中(P3) |
| 弹层 | `.nice-tips` | 遮罩 `rgba(0,0,0,.6)`、内容 radius 12、padding 28、`translateY(-40px)→0 .3s` | 规划中(P3 Modal) |
| 滚动工具条 | `.scroll-toolbar` | `position:fixed; right:30; bottom:50%`,内部是 42px 圆形按钮 | `ScrollTop` |
## 7. 交互语言
| 行为 | 参考站做法 | 我们的做法 |
| --- | --- | --- |
| 颜色 / 背景过渡 | `all .3s ease-in-out`(部分图标 `color .3s`) | 具体属性 + `--su-duration-base` + `--su-ease-in-out` |
| 主按钮 hover | `opacity: .8`(不改色) | ✅ 一致(primary / danger) |
| 次要按钮 hover | 换 `gray-3` 底 | ✅ 一致 |
| 图标按钮 hover | 轻微底色 + 文字加深,无阴影 | ✅ 一致 |
| 卡片 hover | 部分列表 `translateY(-5px) scale(1.01)`;`hoverable` 只加深阴影 | 组件层只做阴影加深,位移留给页面(避免列表抖动) |
| 图片 hover | 图片放大 1.1 + 旋转 2°,遮罩淡入 | 组合模式,不做进组件 |
| 焦点 | 把 Bootstrap 的 `box-shadow` 去掉了(`box-shadow:none`) | **反着做**:补 2px 主色 focus ring(a11y 要求) |
| 减少动效 | 用 `@media (not (prefers-reduced-motion))` 包裹动画 | ✅ 每个带动效的组件都有 `prefers-reduced-motion` 分支 |
| 禁用 | 换浅底 + 弱文字(不是整体透明) | ✅ 一致(`bg-subtle` + `text-muted`) |
| 加载 | `.btn.loading` 浅底 + `cursor: wait` + `pointer-events:none` | `loading` 自动 disabled + spinner + `aria-busy` |
## 8. 暗色方案(参考站已支持,我们留接口)
参考站用 `body[data-scheme="dark|auto"]` 覆盖整套变量,灰阶整体反相:
```css
body[data-scheme=dark] {
--gray-1:#f7f8fa; --gray-2:#f2f3f5; --gray-3:#e5e6eb; --gray-4:#c9cdd4;
--gray-5:#494949; --gray-6:#303030; --gray-7:#202022; --gray-8:#0f0f11;
--color-text:var(--gray-1); --color-secondary:var(--gray-2);
--color-muted:var(--gray-4); --color-light:var(--gray-3);
--bg-body:var(--black); --bg-dark:var(--gray-8); --bg-secondary:var(--gray-7);
}
```
要点:**语义变量跟着翻转,灰阶保持不变**——所以组件只要引用语义令牌就自动适配。
我们的 `--su-*` 结构相同,后续加一份 `[data-scheme='dark']` 覆盖即可;
仍有少量组件直接用了 `--su-gray-*`(Button secondary、Tag neutral、IconButton hover),
深色主题落地时要把这些改成 `--su-bg-subtle` / `--su-bg-hover` 之类的语义令牌。
## 9. 本轮对齐清单
**令牌**([tokens.css](../src/theme/tokens.css))
- 阴影 2%/6% → **1%/3%**,补 `0 … 0` 的位置参数;
- 字阶 10/12/14/16 → **12/13/14/18**,行高 16/20/22/26;
- 新增:`--su-color-orange-dark/light`、`--su-gradient-hot/paid/red`、`--su-text-secondary`、
`--su-bg-subtle`、`--su-card-border`、`--su-ease-in-out`、`--su-focus-ring(-offset)`;
- 控件高度:`sm 28→32`;图标按钮 `24/32 → 28/36/44`。
**组件**
| 组件 | 变更 |
| --- | --- |
| `Button` | 新增 `shape="pill"`;hover 改 opacity .8;禁用改浅底弱字;补 focus ring;补 reduced-motion |
| `IconButton` | 改圆形,尺寸 `xs/sm/md = 28/36/44`,图标 16/20/24;补 focus ring |
| `Card` | 描边改 `--su-card-border`(gray-1),阴影用新令牌 |
| `Tag` | 重写:默认 `neutral` 浅底胶囊 24 高 13px;新增 `variant="solid"`、`icon`、`dot`;保留 `shape="rounded"` 做元信息前缀 |
| `Badge` | 高度 16→18,字号 10→12 |
| `Input` | 描边 `gray-3`;focus 主色边框 + 键盘 focus ring(`:has(:focus-visible)`) |
| `Avatar` | `lg` 认证角标 18→20,对齐参考站三档 |
| `ScrollTop` | 过渡改 `ease-in-out`,补 focus ring |
| `ItemBadge` | **新增**:置顶 / 付费 / HOT / 草稿 状态角标 |
**展示页**:Buttons 补「形状」与图标按钮三档;Marks 重构为「浅底 / 实底 / 状态角标 / 数字角标 / 组合」;
组合示例的卡片标题加入 `ItemBadge`。
## 10. 保留的差异(有意为之)
| 差异 | 参考站 | 我们 | 原因 |
| --- | --- | --- | --- |
| 基础字号 | body 16/24 | 14/22 | 这是本地工具的控制台,不是阅读型博客;14 更紧凑。内容页(文章正文)建议用 16 |
| 卡片内边距 | `40px 24px` | 12 / 20 / 24 档位 | 参考站的竖向留白是为 widget 设计的,工具里太松 |
| 标签图标 | 每个标签强制带 20×15 图标 | 可选 `icon` | 图标是主题的分类素材,通用组件不该强绑 |
| Bootstrap 组件 | 下拉、模态、分页都用 BS | 自己实现(规划中) | 不引 CSS 框架,避免 200KB+ 和样式覆盖战 |
| hover 位移 | 部分列表卡片抬升 5px | 组件不做位移 | 列表里位移会造成抖动;需要时页面自己加 |
| 动画时长 | 全站 `all .3s` | 按属性、默认 .3s | `all` 会把布局属性也带上,性能差 |
## 11. 生产标准补充(参考站没有的)
参考站是展示型主题,下面这些是我们作为组件库必须补的:
- **focus-visible**:所有可交互组件补 2px 主色焦点环(参考站把焦点样式删了);
- **reduced-motion**:`Button` / `IconButton` / `Card` / `Input` / `ScrollTop` 都有降级分支;
- **disabled 语义**:`Button` 用真实 `disabled`,`loading` 同时 `aria-busy`;
- **图标按钮 label 必填**:`IconButton.label` 是必填 prop,防呆;
- **错误提示 `role="alert"`**、`Empty` 与 `Notice` 分工(§COMPONENTS);
- **键盘可达**:`ScrollTop` 隐藏时 `tabIndex=-1`;Input 清除按钮是真实 `button`;
- **令牌约束**:组件 CSS 里没有裸色值(`DESIGN.md` §1 硬约束)。
## 12. 后续对齐项
1. ~~**暗色主题**~~:已完成(`data-scheme="dark|auto"`,见 DESIGN §7);
2. **ScrollTop → 滚动工具条**:参考站是「点赞 / 收藏 / 回到顶部」三键竖排,等有收藏功能再做;
3. **图片悬浮遮罩**:抽成组合模式(`Card hoverable` + 遮罩层),配合未来的 `Image` 组件;
4. **分页 / 弹层**:按 COMPONENTS.md §5 的 P3 优先级补。