mcpField
D
design-component

design-component

github.com/plugin87设计与 UI3.0k535 次安装1 天前更新
代码仓库

关于 design-component

design-component 是用于编写组件规范的 Skill,依据项目的设计、令牌和无障碍规则组织内容。它还要求检查组件的渲染状态与响应式表现,再判断组件是否完成。

主要功能

  1. 01按项目格式编写组件规范
  2. 02定义结构、变体、尺寸与状态
  3. 03映射设计令牌和无障碍要求
  4. 04检查渲染状态与响应式表现
  5. 05目视检查并验证交互

适用场景

  1. 01基于现有设计套件编写组件规范
  2. 02检查组件变体在不同交互状态下的表现
  3. 03审查组件的无障碍性与响应式布局

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/plugin87/ux-ui-agent-skills --skill design-component

安装后在 Claude Code 中输入/design-component确认已加载。

详细介绍

技能:设计组件

步骤 0:工具包在这里吗? 此技能会读取工具包中的文件。请检查一次: ls ${CLAUDE_SKILL_DIR}/../../../tokens >/dev/null 2>&1 && echo KIT_OK || echo KIT_MISSING 只有出现 KIT_MISSING 时,才表示仅安装了技能文件夹,这是 npx skills add 的行为。请明确说明这一点,告诉用户使用 npx ux-ui-agent-skills init 或安装插件,然后停止。不要猜测 无法打开的文件内容。

生成符合项目格式的完整组件规范。

步骤

  1. 阅读 ${CLAUDE_SKILL_DIR}/../../../.claude/rules/components.md 中的“组件质量标准”(8 状态表)和“原子设计”;始终生效的 8 状态表位于 ${CLAUDE_SKILL_DIR}/../../../CLAUDE.md 中的“不可妥协事项”部分。
  2. 检查组件是否已存在:${CLAUDE_SKILL_DIR}/../../../components/atoms.md、molecules.md、organisms.md、templates.md、navigation.md、feedback.md、forms-advanced.md、overlays.md。匹配现有规范格式。
  3. 从 ${CLAUDE_SKILL_DIR}/../../../accessibility/aria-patterns.md 获取 ARIA 模式,并从 ${CLAUDE_SKILL_DIR}/../../../accessibility/wcag-checklist.md 获取对比度和目标尺寸规则。
  4. 将每个值映射到令牌(${CLAUDE_SKILL_DIR}/../../../tokens/*.json):尺寸使用 sizing.json,状态使用 states.json。
  5. 运用 ${CLAUDE_SKILL_DIR}/../../../taste/design-taste.md 中的视觉判断(状态、焦点、不粗制滥造)。
  6. 可选的快速起步方式:运行 python3 ${CLAUDE_SKILL_DIR}/../../../scripts/scaffold_component.py "<Name>" 生成框架,然后补全内容。

输出

规范需包含:结构示意图、变体表、尺寸表、所有 8 种适用状态、令牌映射、无障碍设计(角色/键盘/屏幕阅读器),以及一条通过 ${CLAUDE_SKILL_DIR}/../../../frameworks/adapter-protocol.md 进行渲染的说明。

准确性:验证每种状态,不要想当然(生成代码时为强制要求)

只有当每个变体 × 状态都能正确渲染时,组件才算“正确”,而不只是默认静止状态。构建一个状态测试页:在一个 HTML 文件中渲染每个适用状态(默认、悬停、焦点、激活、禁用、加载 aria-busy、错误 aria-invalid、选中 aria-pressed/aria-selected)× 每个变体(参见 ${CLAUDE_SKILL_DIR}/../../../examples/component-states/button.html)。然后运行各项检查并报告实际输出(CLAUDE.md → 验证协议):

  • node ${CLAUDE_SKILL_DIR}/../../../scripts/gate.mjs verify_states.mjs <harness> [--dark]:检查默认、悬停、焦点状态下每个元素的对比度
  • node ${CLAUDE_SKILL_DIR}/../../../scripts/gate.mjs axe_audit.mjs <harness> [--dark]:检查 ARIA/角色/名称/标签是否正确
  • node ${CLAUDE_SKILL_DIR}/../../../scripts/gate.mjs measure_render.mjs <harness> [--dark]:检查每个文本元素是否达到 AA 标准
  • 覆盖层/模态框还需运行:node ${CLAUDE_SKILL_DIR}/../../../scripts/gate.mjs verify_focustrap.mjs <harness> --open=<trigger> 每种状态都必须在浅色和深色模式下通过,组件才算“完成”。没有检查结果证明时,绝不要声称某种状态正确。

门禁能验证对比度和无障碍性,但不能验证像素效果。请渲染并检查。

对比度和 axe 门禁通过了,但界面仍可能明显损坏:复选框无法切换、短横线位于方框底部、勾号和不确定状态短横线的笔画粗细不一致、控件显得过于粗重。**在声称完成之前,必须对每种状态和交互后的界面截取测试工具页面的屏幕截图并检查。**使用 Playwright + 系统 Chrome:

const b = await chromium.launch({channel:'chrome'});
const p = await b.newPage({deviceScaleFactor:4});
await p.goto('file://'+abs); await p.addStyleTag({content:'*{transition:none!important}'});
await p.mouse.move(2000,2000);                 // park pointer OFF the component
await p.locator('.stack').first().screenshot({path:'/tmp/x.png'});

读取 PNG。然后重点检查以下内容:

  • 功能:点击每个交互元素,并断言状态确实发生变化(await loc.click(); expect(await loc.isChecked()))。如果自定义控件的覆盖框挡住了真实的 <input>,除非该框设置了 pointer-events:none(或由外围的 <label> 转发点击),否则控件不会切换。
  • 几何位置:图形应居中,不能堆叠或偏移。如果 display:grid 容器中有一个 opacity:0 的同级元素和一个 ::after,伪元素会落在第 2 行 → 对隐藏的同级元素使用 display:none,或只保留一个容器子元素。
  • 笔画一致性:勾号和不确定状态短横线必须使用相同的渲染方式(同一个 <svg>,通过状态切换两条 <path>,且使用相同的 stroke-width),切勿使用 SVG 勾号搭配 CSS ::after 矩形(两者看起来粗细不同)。
  • 过渡效果伪影:如果不禁用过渡并截取屏幕截图,刚点击的控件看起来会像是处于淡化动画中途。这不是缺陷。判断状态时,请务必禁用过渡并移开指针。

不同文件间的一致性不可妥协。 同一个组件(例如复选框)在每个测试工具页面中都必须使用逐字节相同的 CSS 和标记。在 form-controls 中看起来纤细、在 data-table 中却显得粗重(使用原生 accent-color)的复选框就是缺陷。提取一种模式,并在各处原样复用。

已验证的自定义复选框/单选框模式(纤细、由设计令牌驱动,并通过门禁和目视检查)

在底层保留真实的 <input>(保留原生无障碍支持和键盘操作);叠加绘制 .box,并设置 pointer-events:none;在同一个 <svg> 中用两条 <path> 绘制勾号和短横线,并通过 :checked / :indeterminate 切换;使用 1.5px 的 border-strong、.25rem 圆角、.62rem 图形、stroke-width:2 和圆头线条。参考:${CLAUDE_SKILL_DIR}/../../../examples/component-states/form-controls.html 和 data-table.html(全选使用 indeterminate)。原生 accent-color 的渲染过于粗重。如果产品的统一视觉风格要求“纤细”,请勿使用它。

响应式:每个组件都不得产生横向滚动(须通过门禁)

采用移动优先的方式构建;无法收缩的固定像素宽度属于缺陷。运行 node ${CLAUDE_SKILL_DIR}/../../../scripts/gate.mjs verify_responsive.mjs <file|dir>。该命令会在 280/320/414px 宽度下加载每个测试工具页面,并在出现任何横向溢出时失败。四种常见原因及其修复方法:

  • 固定的 inline-size:Npx → inline-size:100%;max-inline-size:Npx(设置上限,而非固定宽度)。
  • <ul>/<ol> 默认有 40px 的行内起始内边距(*{margin:0} 重置并不会清除内边距) → 为每个列表设置 padding:0;margin:0。这也会导致列表边缘与相邻块元素悄然错位(看起来宽度不相等)。使用相同的修复方法。
  • 不换行的 flex 行(面包屑、步骤指示器、标签页) → 使用 flex-wrap:wrap;对于标签页,使用 overflow-x:auto + .tab{flex:none}。
  • grid minmax(Npx,1fr) 的最小值大于视口宽度 → 使用 minmax(min(Npx,100%),1fr)。

动效:使用令牌、真实缓动,并为运动的元素添加动画

时间和缓动都是令牌(主题中的 --duration-fast|normal|slow、--ease-out|in|in-out|emphasized;--transition-micro = fast ease-out)。不要硬编码毫秒数或曲线。切换打开/关闭状态的组件必须为其高度添加动画,而不只是旋转箭头。仅通过 hidden/display:none 折叠会显得“生硬,没有过渡”。平滑高度模式(无需 JS 测量):将内容包在一个会裁切溢出的内层元素中,为网格轨道添加动画:

.panel{display:grid;grid-template-rows:0fr;transition:grid-template-rows var(--duration-base) var(--ease-spring)}
.panel.open{grid-template-rows:1fr}
.panel > .inner{overflow:hidden;min-block-size:0}

确保无障碍:展开时,先移除 hidden,然后在下一帧添加 .open;折叠时,移除 .open,并在 transitionend 时设置 hidden。参考:${CLAUDE_SKILL_DIR}/../../../examples/component-states/overlays.html 中的手风琴组件。务必遵循 @media(prefers-reduced-motion:reduce){…transition:none}。

布局:填满空间,不要交付空荡荡的 AI 填充内容

  • 卡片网格使用 auto-fit,绝不使用 auto-fill。auto-fill 会保留空的虚拟轨道,导致 3 张卡片挤在左侧,右侧留空;auto-fit 会折叠空轨道,让卡片拉伸以填满整行。始终使用 repeat(auto-fit,minmax(min(Npx,100%),1fr))。
  • 一行中的面板等高:在网格上设置 align-items:stretch,并让较短面板的主体填满空间:.panel{display:flex;flex-direction:column} + 内部区域设置 flex:1。通过 block-size:% 设置尺寸的子元素(例如图表条)需要一个确定高度的祖先元素(flex:1 盒子或显式高度),否则百分比会解析为 0,导致元素塌缩。将图表条包在 flex:1 的 .barbox 中,并将图表条的 block-size:% 设为相对于该元素。
  • **主体区域有 80% 的留白会显得像机器生成的。**在仪表板中填入真实、合理的内容(统计信息行 + 活动列表 + 图表),而不是只放一个孤零零的小部件。有意安排的内容密度,正是“设计过”和“AI 垃圾内容”的区别。
  • 工具栏/标题栏末尾的空隙:带有 flex:1 且受 max-inline-size 限制的 flex 项目会停止增长,并在最后一个项目之后留下空白。为右侧组合的第一个元素设置 margin-inline-start:auto,将其推到右侧。
  • 移动端导航不得重叠。将侧边栏和主体放在同一网格区域,会导致打开侧边栏时覆盖内容。在移动端将 shell 切换为 display:block,这样打开侧边栏会把主体向下推。参考:${CLAUDE_SKILL_DIR}/../../../examples/component-states/app-shell.html。

图标:使用真正的 lucide 图标并按名称引用(绝不手绘路径)

手工近似的 SVG 路径数据会渲染成错误的字形(帮助图标的“?”变成了圆点;设置图标变成了汉堡菜单)。使用原样的 lucide路径,通过注入的 <symbol> sprite 按名称引用:${CLAUDE_SKILL_DIR}/../../../examples/component-states/icons.js 中每个图标只定义一次,再通过 <svg class="ico" aria-hidden="true"><use href="#i-NAME"/></svg> 使用。不要为每次使用重复路径,不依赖网络,离线可用且安全。只需在 icons.js 中添加一次新图标;绝不要将原始路径直接粘贴到标记中。(仅当路径原样复制自 lucide 时,才可以内联 lucide。).ico{stroke:currentColor;fill:none;stroke-width:2}:通过 currentColor 设置颜色。

图形化/仅图标控件(3:1,主题稳定)

无文本控件(轮播圆点、三点菜单、图标按钮)的对比度要求为 3:1(WCAG 1.4.11),而不是 4.5。verify_states 会自动应用此规则,只要元素没有直接文本节点。需要注意两个陷阱:

  • 空的 <button> 会保留 UA 的 color:buttontext(约为黑色),不受主题影响 → 将其 color 设为实际指示器颜色,并通过 currentColor 驱动视觉呈现(例如圆点使用 ::before{background:currentColor}),这样门禁测得的才是真实颜色。
  • 随主题翻转的令牌(--color-chart-N、--color-surface-brand)会在浅色/深色主题之间反转;白色文本或青绿色指示器在一种模式下可能通过,另一种模式下却会失败。头像、活动圆点和选中状态应使用适配深色主题的值(在 [data-theme="dark"] 中覆盖),或使用稳定令牌(会自适应的 --color-action-primary、--color-text-link)。

评论

支持 Markdown