feat: stash-ui 0.1.0(40 个组件 + 文档站 + 主题机制)

This commit is contained in:
stash
2026-10-28 20:44:18 +08:00
commit 52296b3368
153 changed files with 17129 additions and 0 deletions
+201
View File
@@ -0,0 +1,201 @@
# 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 优先级补。