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

13 KiB
Raw Permalink Blame History

Alright 参考站拆解

对标站点:https://alright.demo.nicetheme.xyz/ 拆解日期:2026-10-26 · 对照版本:@stash/ui 0.1.0 产物:DESIGN.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):

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"] 覆盖整套变量,灰阶整体反相:

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)

  • 阴影 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 优先级补。