关于 better-ui
better-ui 是用于交互完善后检查界面表面、图标和动效的视觉润色技能。它提供具体规则和数值,并将无障碍、排版、布局与颜色检查交由相关技能处理。
主要功能
- 01嵌套表面的圆角规则
- 02阴影、边框和图片描边规范
- 03交互过渡与按压缩放参数
- 04减少动态效果和悬停条件
- 05图标颜色、线宽与方向规则
- 06按严重程度组织问题并验证
适用场景
- 01润色表面层次与圆角
- 02检查交互状态下的动效
- 03审查图标一致性和悬停行为
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ 23jmo/ jmo-skills --skill better-ui安装后在 Claude Code 中输入/better-ui确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
animations.mdenter-exit.mdicon-transitions.mdicons.mdLICENSEperformance.mdsurfaces.mdagents/openai.yaml详细介绍
UI 润色
此技能规定了界面、图标和动效的视觉润色细节,以及每项细节的确切取值。只有在底层交互正常后才适用,润色方面的问题绝不能优先于交互故障。
精确取值,可选润色
以下取值都是精确值,不是供近似参考的范围。cubic-bezier(0.2, 0, 0, 1) 不等于 cubic-bezier(0.4, 0, 0.2, 1),0.96 也不等于 0.95。视觉微调和同心内边距的临界值是例外,它们只是起点,应根据视觉效果判断。
遵循项目的组件库、设计令牌和密度;如果这里没有规定具体取值,就与项目的动效风格保持一致。有意且一致的项目约定(例如完全不用阴影)属于偏好,不是问题。同一细节在项目中出现两种不同做法,则是问题。
文本换行、字体渲染、等宽数字和文字间距归 better-typography 负责。点击区域、键盘支持、ARIA 和减少动效要求归 better-accessibility 负责。分组、区块间距、断点和空间布局 RTL 归 better-layout 负责,方向性图标的镜像除外。颜色令牌和对比度测量归 better-colors 负责。
外层圆角等于内层圆角加内边距
当嵌套表面具有可见且均匀的内缩时,外层圆角等于内层圆角加内边距,再加上任何边框宽度。内边距超过 24px,或有意采用不对称内边距时,应将各层视为独立表面,并为每层保留其圆角令牌。具体做法见 surfaces.md。
几何对齐不佳时进行视觉微调
几何居中看起来不协调时,应根据视觉效果进行微调。按钮图标一侧的内边距减少 2px,将播放三角形朝尖端方向移动,并直接修正 SVG 中不对称的字形。具体做法见 surfaces.md。
阴影用于表现层级,边框用于表现结构
如果边框仅用于营造深度效果,应将其替换为多层透明 box-shadow 值。分隔线、分隔符、表格单元格和选中状态应保留边框。表单输入框也应保留边框,因为根据 better-accessibility,其边界需要达到 3:1 的非文本对比度。焦点环同样归 better-accessibility 负责。
强制颜色模式会移除所有 box-shadow。在阴影环下保留 border: 1px solid transparent,这样该模式仍会绘制边缘。具体做法见 surfaces.md。
为图像添加纯黑或纯白描边
为内容图像添加向内偏移 1px 的 1px 描边。浅色模式使用 oklch(0 0 0 / 0.1),深色模式使用 oklch(1 0 0 / 0.1)。切勿使用调色板中的近黑色、带色调的中性色或强调色,因为这些颜色会在图像边缘形成彩色边缘。对于 Logo 和插画等透明艺术图像则跳过。具体做法见 surfaces.md。
交互状态使用过渡,而非关键帧
使用 CSS 过渡或动效库的弹簧效果来驱动交互状态变化;当用户在动效进行中反向操作时,它们会重新确定目标。关键帧会按固定时间线运行,无法反向,因此仅用于只运行一次的序列。参见 animations.md。
按下时缩放至 0.96
按钮按下时,以 ease-out 在 150ms 内缩放至 0.96;禁用状态下则绝不缩放。如果动效会造成干扰,可通过组件现有的变体 API 或 static 属性为按钮提供退出选项。参见 CSS、Tailwind 和 Motion 的做法。
高频交互不添加动画
按键输入、行悬停和标签页切换应即时反馈,或仅对 opacity 或 background-color 添加不超过 150ms 的过渡。将富有表现力的动效留给不频繁出现的时刻,例如视图首次加载、成功状态或空状态。
每种动画状态变化还应通过颜色、图标或标签提供静态提示。动效绝不能是唯一的反馈渠道。
根据减少动态效果偏好控制动态效果
better-accessibility 负责这项要求,所有方案都必须遵守。仅在 prefers-reduced-motion: no-preference 下运行移动、缩放和模糊效果。减少动态效果时,应改用透明度交叉淡入淡出,而不是直接移除元素。CSS、Tailwind 和 Motion 的方案见 animations.md。
低频入场效果间隔 100ms
当分阶段入场的顺序能够体现层级关系时,应将内容拆分为标题、描述和操作等语义区块。各区块的入场效果间隔 100ms,在 300ms 内使用 ease-out,同时应用透明度、4px 模糊和 12px 的 translateY。日常交互绝不使用交错动画。参见 enter-exit.md。
退场效果应比入场更短、更轻
退场时使用透明度、4px 模糊和固定 -12px 的 translateY,时长为 150ms,缓动为 ease-out,绝不使用整个容器的高度。只有在目标位置本身有意义时才完全滑出,例如抽屉关闭。动态效果没有提供信息时,应立即移除元素。参见 enter-exit.md。
首次渲染时跳过状态动画
在包裹状态切换的 AnimatePresence 上设置 initial={false},这样默认状态在挂载时就不会播放动画。对于有意设置的入场效果(例如交错显示的主视觉区),绝不要这样设置。参见 animations.md。
使用精确数值交叉淡入淡出上下文图标
图标在低频状态变化时切换,例如播放变为暂停,或复制变为已复制,应使用交叉淡入淡出效果。缩放从 0.25 变为 1,透明度从 0 变为 1,模糊从 4px 变为 0px。使用动态效果库时,采用 { type: "spring", duration: 0.3, bounce: 0 },且 bounce 始终为 0。不使用动态效果库时,将两个图标都保留在 DOM 中,并使用 cubic-bezier(0.2, 0, 0, 1) 在 300ms 内完成交叉淡入淡出。
标签页图标切换,以及悬停时行内显示的操作,都属于高频交互不使用动画。两种方案见 icon-transitions.md。
切换主题时禁用过渡效果
主题切换时禁用所有过渡效果,强制刷新样式,并在下一帧后恢复过渡效果。否则,所有颜色过渡会同时触发,使切换过程显得拖沓。参见方案。
只过渡发生变化的属性
像 transition-property: scale, opacity 这样明确指定属性,绝不要使用 all。参见 performance.md。
在 will-change 中指定动画属性
只有在确认首帧卡顿后才添加 will-change,并明确指定动画属性:scale 对应 scale,transform 对应 transform。仅限变换属性、opacity 和 filter,绝不要使用 all。参见 performance.md。
仅对支持悬停的指针应用悬停效果
将仅用于悬停的样式放在 @media (hover: hover) 内。触屏设备上,轻触后 :hover 会保持激活,呈现类似选中状态的效果。Tailwind 4 的 hover: 变体已在该查询条件下编译,而 Tailwind 3 没有。若控件会自行绘制按压状态,则设置 -webkit-tap-highlight-color: transparent,避免默认的灰色闪烁叠加其上。
限制覆盖层内的滚动
为可滚动的对话框、抽屉、菜单和侧边面板设置 overscroll-behavior: contain,以避免滚动超出其边界后,页面背景跟着滚动。
让图标描边粗细与文本字重匹配
图标的实际描边粗细应与旁边文本的字重相匹配,从字重 400 时的 1.5px 到字重 700 时的 2.5px。同一个界面中只使用一个图标库。表格、尺寸和网格规则见 icons.md。
一个 SVG,按状态重新着色
图标使用 currentColor,并通过 CSS 颜色和不透明度呈现悬停、选中和禁用状态,不使用单独的资源。默认变体为描边,填充表示激活状态。在 dir="rtl" 下,仅镜像含义随阅读方向变化的图标。请参阅 icons.md。
完成前检查
| 检测项 | 修复方法 |
|---|---|
带内边距的父元素和子元素使用相同的 rounded-* 或 border-radius | 将内边距加到外层圆角上 |
box-shadow: 0 0 0 1px 用作边框环,但旁边没有 border | border: 1px solid transparent |
img 上的 outline-slate-*、outline-zinc-* 或十六进制轮廓颜色 | outline-black/10 和 dark:outline-white/10 |
transition-all 或 transition: all | 指定发生变化的属性 |
在 :hover、.open 或其他切换类上设置 animation: | 对相同属性使用过渡 |
active:scale-95、scale-90 或低于 0.96 的 whileTap | 0.96 |
:active 缩放没有排除 :disabled | enabled:active: 或 :not(:disabled):active |
bounce: 设置为非 0 值 | bounce: 0 |
基础规则中的 opacity: 0 仅通过 animation 显示 | 在 from 关键帧中设置隐藏状态,并使用 animation-fill-mode: both |
translate、scale、filter 或 motion.* 动画没有 prefers-reduced-motion、motion-safe: 或 MotionConfig 防护 | 添加防护,并在减少动态效果时交叉淡入淡出不透明度 |
颜色过渡加上主题切换,但没有 disableTransitionOnChange 或覆盖规则 | 使用主题切换抑制方案 |
will-change: all,或在从未播放动画的元素上设置 will-change | 指定动画属性或删除该声明 |
图标 SVG 中的 fill="#..." 或 stroke="#..." | currentColor |
@media (hover: hover) 之外的纯 CSS :hover 或 Tailwind 3 hover: | 用媒体查询包裹 |
对话框、抽屉或菜单上的 overflow: auto 或 overflow-y-auto 没有 overscroll-behavior | overscroll-behavior: contain |
报告
严重程度。 HIGH 表示会破坏交互,例如无法反向播放的关键帧切换,或在触屏设备上卡住的悬停状态。better-interface 的两个升级触发条件都属于此类,一经发现即为 HIGH。一个是忽略 prefers-reduced-motion 的动态效果,另一个是仅通过动态效果呈现的状态变化。MEDIUM 表示表面样式、图标或动态效果存在明显不一致。LOW 表示个别细节需要打磨。
验证。 没有浏览器时,请根据代码读取组件定义的所有状态,例如悬停、按下、选中、加载和空状态,并检查其持续时间和缓动方式。使用浏览器时,请逐一查看各状态,并在浏览器的 Animations 面板中以 10% 速度重放动态效果。将所有无法执行的检查报告为 Not verified。
格式。 按违规原则分组,按严重程度排序;每个根本原因占一行,并列出其出现的所有位置:
| 严重程度 | 位置 | 修复前 | 修复后 | 原因 |
|---|
位置 格式为 path/to/file:line。原因需指出违反的原则及对用户的影响。
若仍存在任何 HIGH,结尾写上 Block;否则写上 Approve,并在表格中保留其余待处理事项。不得对未检查的覆盖范围写 Approve。若没有需要报告的问题,请写明“没有可采取行动的 UI 润色问题”,并报告验证情况。