本文へ移動
cccskills
無料GitHub で公開

agent-html

为 AI Agent 提供基于 shadcn/ui 极简现代美学的单文件 HTML 组件化规范与母版体系。当用户要求“生成 HTML 页面/网页”、“前端可视化大盘/看板”、“面试/分析/测试报告”、“对比矩阵/方案比对”、“review 结果做成可视化页面”、“代码审查报告 / 把 diff 做成可视化审查页”、“不要输出 markdown 墙/不要文字墙”、“把分析结果做成可交互单文件网页”等场景时,务必使用本 Skill。零构建、零 npm、严禁引入外部 CDN(无断网白屏风险),纯原生 HTML/CSS/SVG,支持暗黑模式与双击秒开。

インストール方法を見る

含まれるファイル(21)

  • SKILL.md34.0 KB
  • assets/templates/en/compare.html16.1 KB
  • assets/templates/en/dashboard.html24.2 KB
  • assets/templates/en/inspector.html18.2 KB
  • assets/templates/en/kanban.html12.2 KB
  • assets/templates/en/report.html23.3 KB
  • assets/templates/en/review.html36.9 KB
  • assets/templates/en/timeline.html13.0 KB
  • assets/templates/zh/compare.html17.3 KB
  • assets/templates/zh/dashboard.html26.2 KB
  • assets/templates/zh/inspector.html19.5 KB
  • assets/templates/zh/kanban.html18.2 KB
  • assets/templates/zh/report.html25.2 KB
  • assets/templates/zh/review.html36.9 KB
  • assets/templates/zh/timeline.html16.7 KB
  • evals/evals.json22.5 KB
  • evals/README.md7.8 KB
  • references/components.md63.0 KB
  • references/failures.md16.9 KB
  • scripts/smoke.mjs20.6 KB
  • scripts/validate.mjs31.8 KB

SKILL.md(原文)

インストールする前に、エージェントに与えられる指示の中身を確認できます。

Agent HTML 设计系统与组件化规范

本规范指导 AI Agent 如何生成高信息密度、工业级质感、零外部依赖、双击秒开的单文件 HTML。


零、价值研判准则:何时用 HTML,何时坚决不用 (Judgment Rules)

黄金法则:Markdown 是事实来源(Source of Truth),HTML 是人类决策与评审表面(Human Review Surface)。

并不是所有输出都需要制作成 HTML。在动手前,AI Agent 必须先做价值研判:

1. 优先使用 HTML 的场景(具备以下任一特征):

  • 空间性与多维性 (Spatial):宽屏数据大盘、左右双栏日志审查器、指标统计网格、密集状态看板;
  • 并行与对比性 (Parallel / A-B):基准 vs 挑战者、版本 Diff、模型评测、方案取舍矩阵;
  • 可交互与即时过滤 (Interactive / Filterable):包含数十条记录需毫秒级搜索、多 Tab 切换、手风琴折叠展开;
  • 供人类正式决策/归档 (Human Review Surface):正式架构评审、面试录用报告、技术复盘白皮书、发布日志。

2. 坚决不要使用 HTML 的场景(留在终端 Markdown 即可):

  • 线性单线条内容:普通的问答、短解释、单次代码段说明(< 200 字);
  • 纯终端命令/脚本:用户需要立即在终端复制执行的 shell 命令;
  • 无空间结构的信息:没有表格、没有对比、没有维度的流水账文本;强行输出 HTML 会打断用户终端心流并浪费 Token。

3. 定了要用 HTML 之后,再定粒度 (Output Granularity)

这是第二问,和第一问同等重要。判错粒度会让用户拿到一份远超或远低于预期的东西:

粒度交付物什么时候用
片段单个组件卡片 / 一张图 / 一个表格单点结论、单个对比,用户只想看一眼
证据页一页:KPI 概览 + 图表 + 关键明细表2–3 个独立结论,需要摆证据但不需要完整叙事
整页母版七种通用母版之一(见「通用布局母版」一节)完整叙事交付:报告、复盘、评审、白皮书

默认档位是「证据页」,不是「整页母版」。 用户说「分析一下这组数据」「帮我把这个看板做出来」但没有说「报告 / 白皮书 / 年报 / 复盘 / 评审」时:

  • 必须先交付证据页(KPI + 最强的一张或两张图 + 关键明细),并在回复里用一句话提示「如需完整报告可以再展开」;
  • 不得因为「数据很丰富」「结论很多」「用户说了分析」就自行升格为整页母版;
  • 反过来,用户明确要了「报告 / 白皮书」时,也不得只交付一张图。

拿不准时选更细的粒度:多给一张卡片,用户会自己说「再展开」;多给一份六章节报告,用户只会觉得被淹没。


一、反模式规避清单 (Anti-Patterns to Avoid)

在生成 HTML 时,严禁出现以下常见 AI 反模式。分两组:工程与交付层(1–10,多数可被 scripts/validate.mjs 机器判定)与数据与语义层(11–17,多数只能人眼复核)。

1. 工程与交付层(1–10)

  1. 穿 HTML 外衣的普通 Markdown (Markdown in an HTML Trenchcoat):只是用 <p> 和 <h1> 包裹几大段流水账文字,完全没用上卡片、网格、状态徽章与 Callout。
  2. 偷渡外网 CDN / 远程字体:引入 Tailwind CDN、Google Fonts、CDN 脚本。在离线环境、内网机房直接白屏失效。
  3. 交互死胡同 (Dead-end UI / No Copy-back):用户在页面上做了筛选、勾选或人工审核,但页面没有任何“复制结果”或“导出 Markdown”按钮,导致操作无法带回终端给 Agent。
  4. 硬编码固定高度截断 (Fixed-height Truncation):设置 height: 400px; overflow: hidden;,在不同系统或字体大小下导致内容直接被切断。
  5. 假装支持暗黑模式:部分元素硬编码 #ffffff 或 #000000,在暗黑模式下文字或背景融为一体不可见。
  6. 缺乏移动端/小屏响应式:未加 <meta name="viewport"> 或缺乏 @media 断点,分屏小窗口下横向滚动条爆炸。
  7. 缺乏空状态处理 (Empty State):表格或列表在搜索无果时白茫茫一片,未提供“暂无匹配记录”提示。
  8. 打印样式缺失 (@media print):报告类页面未做 @media print 样式优化,用户需要导出 PDF 时按钮乱飞。
  9. 图表引入重型第三方库:为了画简单折线图引入 Chart.js/ECharts(有断网白屏风险),必须坚持纯原生矢量 SVG。
  10. 丢失关键元数据与数据精度:过度美化排版却丢掉了关键错误码、时间戳、原始 ID 或 Trace,导致失去工程核验价值。

2. 数据与语义层(11–17)

这一组是**「把图画错」而不是「把页面写坏」。它们不会让页面报错、不会白屏,校验器也测不出来,但会让读者得出错误结论**——比技术 bug 更危险,因为看不出来。

  1. 面积/半径编码未开方:用半径或圆点大小表示数值时,必须 Math.sqrt(v)。直接拿数值当半径会让面积按平方增长——2 倍的差异被画成 4 倍,系统性夸大差距。
  2. 柱状图断轴:柱的契约是长度 ∝ 数值,截断 Y 轴等于毁约。极端值场景的正确做法只有三条:① 让极端值冲天(最诚实)② 主图 + 放大镜小图 ③ 撕柱不撕轴(明说画不下)。
  3. 聚合数冒充原始记录:把「37%」摊成 37 个点是可以的,但必须在副标题或底注写明 1 点 = 1 个百分点;不许为了凑密度编造不存在的个体记录。
  4. 取整不认账:占比取整后加总不足 100(如 49+27+14+5+3 = 98)时,底注写明「另外 2% 被四舍五入吃掉了」。不凑假数据把它补到 100。
  5. 颜色无数据含义:禁止「拿到五个颜色平均撒到所有元素上」。颜色必须连接真实维度——序数梯接数值、分类色接类目、强调色只给唯一主角——并在底注写清它在编码什么。去掉颜色后,图的结构仍应读得懂。
  6. 演示数据不确定:演示/占位数据禁用 Math.random(),必须用确定性伪随机。刷新两次必须长得一样,否则截图、录屏、回归对比全部失效。
  7. 给无真实记录的元素加交互:给纯装饰元素加 hover / 点击是欺骗。加交互前先问「这个元素背后有没有一条真实记录?」——完整判定流程见「交互」一节的三问。

📕 失败记忆:以上反模式的成因、真实案例与复查方式见 references/failures.md(12 条)。写新组件前先扫一眼——规范告诉你该做什么,失败记忆告诉你不要再重蹈什么。该文档末尾有一张索引表,诚实标注了哪些教训已经变成机器约束、哪些还只能靠人。


二、为什么这样设计?(Design Principles & The "Why")

  1. 为什么坚决追求纯单文件与零外部依赖(Zero-Dependency Standalone)?

    • 很多可视化报告、用例审查工具常在内网机房、无公网访问的隔离环境(Air-gapped)、或本地离线存档中打开。
    • 引入 Tailwind CDN (cdn.tailwindcss.com) 或第三方字体库,会在弱网下引发白屏(FOUC)、CDN 节点下线失效或公司 CSP 策略拦截。因此必须将精炼的 CSS 变量与极简微脚本直接内嵌。
  2. 为什么严格采用 shadcn/ui 的 Zinc/Slate 中性色体系?

    • 大模型在自由发挥时容易产生“AI审美漂移”——随机的大圆角、大面积刺眼渐变色、过大的无意义留白。
    • shadcn 的核心是:冷灰中性底色、精准 1px 微细浅色边框、高信息密度、语义化状态点(Green/Amber/Red)。这种排版能让任何数据报表看起来都像资深前端工程师耗费数日精心调校过的专业企业级产品。
  3. 为什么必须原生内置暗黑模式(Light / Dark Theme)?

    • 工程师与运维人员大量在暗黑 IDE/终端环境下工作。通过 CSS 变量原生映射,仅需 5 行原生 JS 即可实现丝滑切换,零构建成本却能极大提升使用体验。

三、七大通用布局母版与渐进式披露 (Progressive Disclosure)

不要从零手写完整页面。接到需求后,首先从以下 7 种通用布局母版中选择最贴近的骨架。支持中英文双语母版体系,根据用户输入语言对齐:

  • 英文场景:参考 assets/templates/en/<name>.html
  • 中文场景:参考 assets/templates/zh/<name>.html

🗂 本仓库的资产分三层,别搞混:

层位置是什么怎么用
骨架(母版)`assets/templates/zhen/*.html`整页版式的正本
零件(组件字典)references/components.md按钮/卡片/图表等片段的正本按需取用单个片段
成文(案例)examples/(仅仓库)真实数据走完全流程、且通过全部门禁的成品拿不准交付物长什么样就先看这里

交付给用户的永远是单文件成品,是从母版或组件拼装出来的。 每个资产只有一个正本。 同一段代码不允许在两处各存一份——两份手抄副本必然漂移(见 references/failures.md F-014)。发现重复时,保留一处、另一处改为指针。 examples/README.md 逐条记录了那个案例示范了哪几条容易忘的规矩,包括一条门禁测不出、只能靠人眼的图表刻度错误(F-015)。

📌 本文件里所有相对路径都相对于本 Skill 的根目录(即 SKILL.md 所在目录),不是相对于你当前的工作目录。安装到 ~/.agents/skills/agent-html 后,scripts/validate.mjs 指的是 ~/.agents/skills/agent-html/scripts/validate.mjs。

布局模式读者时间信息密度核心交互中文母版路径英文母版路径适用需求与核心结构
Report 报告<br>(Document / Report)30s+中悬浮目录 ScrollSpy、折叠手风琴assets/templates/zh/report.htmlassets/templates/en/report.html技术选型、架构审查、故障复盘、面试报告、需求说明、发布日志。<br>结构:自适应宽屏容器(默认 1200px ~ 1280px,支持宽屏自适应扩展至 96%,避免宽屏下表格被挤扁)、悬浮目录(Sticky TOC with ScrollSpy)、核心结论 Callout、KPI 概览栏、多章节 <details> 折叠手风琴、一键复制为 Markdown 导出。
Dashboard 数据面板<br>(Dashboard & Data Grid)<10s高实时双重过滤、复制表格assets/templates/zh/dashboard.htmlassets/templates/en/dashboard.html资源监控、用量大盘、考勤/调休管理、订单/任务流管理。<br>结构:宽屏网格,顶部操作栏、4 列自适应 KPI 统计卡、原生 SVG 走势图与柱状图、实时双重过滤表格、一键复制表格 (MD)。
Inspector 审查工作台<br>(Master-Detail Workbench)逐条读高Master-Detail 联动、人工裁决assets/templates/zh/inspector.htmlassets/templates/en/inspector.html日志/Trace 审查、Prompt 调试器、JSONL 编辑器、配置管理。<br>结构:视口充满(100vh),左侧条目列表过滤,右侧动态联动渲染选中条目详情、人工审查裁决条(Pass/Fix/Reject)、复制审查结论发回 Agent。
Compare 对比<br>(Side-by-Side Comparison)~30s中并排对照、Delta 判定assets/templates/zh/compare.htmlassets/templates/en/compare.html模型 A/B 测试、Prompt 改版前后对比、架构版本 diff、产品套餐/特性矩阵。<br>结构:并排双栏卡片(基准 vs 挑战者)、核心裁决 Callout、量化差异对照表(Delta 胜负判定标签)、一键导出 Markdown。
Timeline 时间线<br>(Timeline & Postmortem)30s+中折叠诊断日志assets/templates/zh/timeline.htmlassets/templates/en/timeline.html发布路线图 (Roadmap)、变更历史 (Changelog)、突发事件复盘 (Postmortem)。<br>结构:左侧单轨垂直时间线、状态节点小圆点、精确时间戳与操作人 Tag、可展开诊断日志、一键复制时间轴为 Markdown。
Kanban 看板<br>(Triage & Agile Kanban)<10s中原生 HTML5 拖拽排序assets/templates/zh/kanban.htmlassets/templates/en/kanban.html缺陷分类整理、需求优先级排序、任务状态流转。<br>结构:4 列敏捷看板(Backlog, In Progress, Blocked, Done)、纯原生 HTML5 拖拽排序(零依赖)、一键复制分拣结果回 Agent 闭环。
Review 代码审查<br>(Diff Review)逐条读高行级发现项、逐文件裁决assets/templates/zh/review.htmlassets/templates/en/review.htmlPR / 分支代码审查、补丁评审、重构前后差异说明。<br>结构:原样粘贴 git diff 到 <script type="text/plain" id="diffSource"> 即自动解析为统一 diff;发现项按「新版本行号」锚定并以行内评论呈现(blocker / major / minor / nit + 建议修改);每个文件分段单选裁决(通过 / 评论 / 要求修改);文件树过滤 + 空状态;Tabs 切换 diff 与发现项列表;一键复制审查结论回 Agent。

用「读者时间」和「信息密度」选型,不要按行业词选型。 母版名字里的「报告」「看板」「复盘」描述的是版式性格,不是使用场景限制——「时间线」可以承载个人生活数据,「对比」可以承载两个方案的取舍。选型看的是:读者愿意花几秒、内容有多密、需不需要特定的交互。

⚠️ 中英母版必须结构同构。 zh 与 en 是同一套骨架的两个语言版本,不是两个平行维护的模板;改动其一必须同步另一个。scripts/validate.mjs 的 [ZH_EN_PARITY] 会把「中文有而英文没有的 <nav> / <aside> / <details>」判为阻断。

💡 组件字典查阅:所有按钮变体、胶囊徽章、32 个矢量图标、原生纯 SVG 图表、Tabs、表单控件与交互脚本的完整代码,都在 references/components.md。它是组件代码的唯一正本——本文件不再重复任何组件代码。


四、微 CSS 核心基座 (Micro-CSS Base)

在生成 HTML 时,务必将以下约 75 行 CSS 基座放入 <head><style> 中,它是所有组件质感一致的基石:

:root {
  --bg: #fafafa;
  --card: #ffffff;
  --card-fg: #09090b;
  --popover: #ffffff;
  --popover-fg: #09090b;
  --primary: #18181b;
  --primary-fg: #fafafa;
  --primary-hover: #27272a;
  --secondary: #f4f4f5;
  --secondary-fg: #18181b;
  --secondary-hover: #e4e4e7;
  --accent: #f4f4f5;
  --accent-fg: #18181b;
  --destructive: #dc2626;
  --destructive-fg: #fafafa;
  --muted: #f4f4f5;
  --muted-fg: #71717a;
  --border: #e4e4e7;
  --input: #e4e4e7;
  --ring: #18181b;
  --radius: 8px;
  --radius-sm: 6px;
  --radius-lg: 12px;
  --shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
  --shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1);
  --shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
  --shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1);

  --ok: #16a34a;   --ok-bg: #f0fdf4;   --ok-border: #bbf7d0;
  --warn: #d97706; --warn-bg: #fffbeb; --warn-border: #fde68a;
  --err: #dc2626;  --err-bg: #fef2f2;  --err-border: #fecaca;
  --info: #2563eb; --info-bg: #eff6ff; --info-border: #bfdbfe;

  /* 图表专用色(Tremor 风格)。语义固定,不可当作装饰色随机分配:
     每个色相各承担一种数据含义,见「轻量原生图表规范」一节。 */
  --chart-indigo: #6366f1;   /* 主指标时序走势 */
  --chart-emerald: #10b981;  /* 正常 / 健康 / 增长 / 放行 */
  --chart-amber: #f59e0b;    /* 警戒 / 抖动 / 温和上升 */
  --chart-rose: #f43f5e;     /* 异常瓶颈 / 错误峰值 / 降级 */
  --chart-violet: #8b5cf6;   /* P95 / P99 峰值高亮 */
  --chart-cyan: #06b6d4;     /* 辅助对比时序 / 出向流量 */
  --chart-slate: #64748b;    /* 低优对比柱 / 中性刻度 */
}

[data-theme="dark"] {
  --bg: #09090b;
  --card: #121215;
  --card-fg: #fafafa;
  --popover: #121215;
  --popover-fg: #fafafa;
  --primary: #fafafa;
  --primary-fg: #18181b;
  --primary-hover: #e4e4e7;
  --secondary: #27272a;
  --secondary-fg: #fafafa;
  --secondary-hover: #3f3f46;
  --accent: #27272a;
  --accent-fg: #fafafa;
  --destructive: #ef4444;
  --destructive-fg: #fafafa;
  --muted: #18181b;
  --muted-fg: #a1a1aa;
  --border: #27272a;
  --input: #27272a;
  --ring: #d4d4d8;
  --shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.5);
  --shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.5);
  --shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.5);
  --shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.5);

  --ok: #4ade80;   --ok-bg: #052e1680;   --ok-border: #166534;
  --warn: #fbbf24; --warn-bg: #451a0380; --warn-border: #854d0e;
  --err: #f87171;  --err-bg: #450a0a80;  --err-border: #991b1b;
  --info: #60a5fa; --info-bg: #17255480; --info-border: #1e40af;

  --chart-indigo: #818cf8;
  --chart-emerald: #34d399;
  --chart-amber: #fbbf24;
  --chart-rose: #fb7185;
  --chart-violet: #a78bfa;
  --chart-cyan: #22d3ee;
  --chart-slate: #94a3b8;
}

*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
body {
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  background-color: var(--bg);
  color: var(--card-fg);
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}

.card {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow-sm);
  overflow: hidden;
}

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  font-size: 13px;
  font-weight: 500;
  height: 34px;
  padding: 0 12px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border);
  background: var(--card);
  color: var(--card-fg);
  cursor: pointer;
  text-decoration: none;
  transition: all 0.15s ease;
}
.btn:hover { background: var(--secondary); }
.btn-primary { background: var(--primary); color: var(--primary-fg); border-color: var(--primary); }
.btn-primary:hover { background: var(--primary-hover); opacity: 0.95; }
.btn-destructive { background: var(--err); color: #fff; border-color: var(--err); }
.btn-sm { height: 28px; padding: 0 8px; font-size: 12px; }

.badge {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-size: 12px;
  font-weight: 500;
  height: 20px;
  padding: 0 7px;
  border-radius: 9999px;
  border: 1px solid var(--border);
  background: var(--secondary);
  color: var(--secondary-fg);
}
.badge-dot { width: 5px; height: 5px; border-radius: 9999px; background: currentColor; }
.badge-success { background: var(--ok-bg); color: var(--ok); border-color: var(--ok-border); }
.badge-warning { background: var(--warn-bg); color: var(--warn); border-color: var(--warn-border); }
.badge-danger  { background: var(--err-bg);  color: var(--err);  border-color: var(--err-border); }
.badge-info    { background: var(--info-bg); color: var(--info); border-color: var(--info-border); }

五、组件索引与图表契约 (Components & Chart Contract)

组件代码只有一个正本:references/components.md。按需读取对应小节,不要凭记忆手写。 下表告诉你去哪一节找:

需要去 components.md 的哪一节
按钮、状态徽章§1 按钮 · §2 徽章
KPI 指标卡、迷你趋势§3 指标卡片
核心结论 / 风险提示条§4 Callout
可搜索表格(含空状态)§5 交互式数据表格
趋势 / 分布 / 占比 / 对比 / 排行 / 水位 / 构成 / 交叉分布§6 图表 A–H(面积折线、柱状、环形、双线、排行榜、仪表、堆叠柱、热力图)
图表悬停数值§6-I 悬停提示(全页共用一份,给图元加 data-tip)
图标§7(32 个,stroke="currentColor" 自动适配暗色)
弹窗、折叠、标签页§8 Dialog / Details / Tabs
需要人工输入并带回 Agent§9 表单控件(输入框、下拉、复选、开关、分段单选、校验错误态)
主题切换、表格过滤、复制 Markdown、裁决回传、ScrollSpy、页脚印章、拖拽§10 交互脚本

轻量原生图表规范 (Zero-CDN Pure SVG Charts)

在生成数据大盘与报表时,严禁引入 Chart.js / ECharts / Recharts 等外部 CDN 库。图表统一采用纯原生矢量 SVG 实现:

  • 零网络依赖:脱机、内网机房 100% 秒开,绝无 CDN 挂掉或白屏风险;
  • 暗黑模式自适应:直接使用主题变量(含 --chart-* 图表语义色,定义见「微 CSS 核心基座」),无须额外 JS 监听重绘;
  • 自适应视口:统一 viewBox + style="width: 100%; height: auto;",Retina 与打印完美保真。

📖 八种形态的完整代码在 references/components.md §6(面积折线 / 垂直柱状 / 复合环形 / 双线时序 / 水平排行榜 / 半环仪表 / 堆叠柱 / 热力图),悬停提示见 §6-I。 代码在那边,契约在这边。改代码前先读本节。

图表四件套(缺一不可,违反即返工)

每张图必须齐全这四个部分,顺序固定:

  1. 结论式标题 —— 写判断,不写图型名。峰值落在 16:00 的批处理窗口 可以,24小时吞吐量趋势 不行。
  2. 副标题写单位契约 —— 用 1 X = 1 Y 说清一个视觉单位等于多少真实数量,再用 · 追加图例与时间范围。这是数据诚实性的核心,不是装饰:
    • 1 格 = 1 个百分点 · 空心 = 周末 · 近 30 天
    • 1 tick = 1 天 · 每 5 天一个刻度
    • 摊开聚合数时必须写明口径(见反模式 13);取整加总不足 100 要认账(见反模式 14)。
  3. 图体 —— 数据层诚实。密度来自单位,不来自修饰。
  4. 底部全大写编码说明行 —— font-size: 10px; letter-spacing: .08em; color: var(--muted-fg),说明颜色与刻度在编码什么:
    • 橙色 = 唯一主角(本轮最高耗时)
    • 灰阶 = 数值大小,越深越大

轻度家具(让图不寒酸,但不改变 shadcn 语言)

只画数据 + 一根基线的小图必然寒酸。补下面四件——它们不携带数据,只提供阅读轨道:

家具做法
基线加重底部一根 1px 实线(var(--border)),区别于上方 stroke-dasharray="3 3" 的参考虚线
rim 刻度沿基线每 5 或 10 个单位一个 tick 点,让读者能估读而不必逐个看数字
参考导轨2–3 条水平虚线,对齐 Y 轴整数值
峰值引线峰值气泡引一根 1px 折线到数据点,避免气泡悬空

⚠️ 不要照搬编辑叙事风格(Lupi 系)的家具:用发丝线构成面积、用横档线构成柱、去掉卡片边框与阴影——那是另一种设计语言的签名,搬进来会同时破坏两边的身份。 agent-html 的图表语言是 shadcn 企业看板:白色卡片 + 1px 边框 + 语义状态色。保持它。

六、交互:先问三问,再选脚本 (Interaction)

不要默认「加了交互就更高级」。 给没有内容的元素加行为是欺骗;给一眼能读完的内容加行为是噪音。加任何交互之前,按顺序回答三问:

  1. 这个元素背后有没有一条真实记录? 没有(纯肌理装饰:分隔线、装饰性图形、背景点缀)→ 禁止加交互。给没有内容的元素加 hover 或点击,是在暗示它有什么可以查看。
  2. 有记录 —— 不点能不能读出来? 能(元素少于约 50 个,且两端或关键处有标注)→ 静态就够用,hover 只是锦上添花,不是必需品。
  3. 元素超过约 50 个,或存在多段路径? → 必须提供 hover / pin,否则读者只能看到一团氛围,无法逐条查询。

三问定完之后,再决定用下面哪个脚本。反过来说:如果三问的答案指向「不需要交互」,就不要因为「母版里有这个功能」而硬加。

脚本代码统一在 references/components.md §10(主题切换、表格过滤、复制为 Markdown、裁决回传、ScrollSpy、页脚印章、看板拖拽)。三条不需要问的底线:

  • 每页右上角放主题切换按钮,并在 <head> 里先设好 data-theme,避免暗色用户看到浅色闪屏;
  • 页面上只要有筛选、勾选、裁决或表单输入,就必须有「复制结果」出口(反模式 3);
  • 每个交付物底部带 Colophon 页脚印章(生成时间、数据来源、基准版本)。

七、交付自验与质量门禁 (Self-Verification Gate)

交付前要做两件事:能跑门禁就跑门禁;跑不了门禁就逐条人工过清单,并把「门禁未运行」如实讲出来。

1 · 先探测环境(不要假设有 Node)

门禁脚本是 Node 写的。很多 Agent 运行环境根本没有 Node,也可能没有 Chrome——这不是错误,但你必须先探测、再决定走哪条路,绝不能因为跑不了就默认产物没问题。

command -v node >/dev/null 2>&1 && echo "有 node" || echo "无 node"
环境走哪条路
有 node跑静态门禁(下面第 2 节),必须。这是最快、最确定的一层
有 node 且有 Chrome/Chromium再跑渲染烟测(第 3 节)。可选但强烈建议
无 node跳过脚本,直接执行第 4 节的人工自检清单,并在交付说明里明确写出「本环境无 Node,门禁未运行,已按人工清单逐条核对」
有 node 无 Chrome跳过烟测即可。烟测退出码 2 表示环境不具备条件,不是产物缺陷,不要据此判定交付物不合格

透明度高于面子。 一条「门禁未运行」的诚实交代,比一句「已校验通过」的虚假声明有用得多——后者会让用户拿着没验过的产物去做决策。

2 · 静态门禁(有 Node 时必跑)

路径相对本 Skill 根目录(不是你的工作目录):

node <skill>/scripts/validate.mjs <generated_file.html>   # 校验单个产物
node <skill>/scripts/validate.mjs --all                    # 校验技能全部产物
node <skill>/scripts/validate.mjs --all --json             # 机器可读输出

该脚本零网络、零 Token、纯 Node 内置模块,共 19 条规则 + 2 条跨文件检查。

10 条阻断级规则(违反必须返工):

  1. [ZERO_CDN] 是否含有外部 script / link / @import CDN 引用与远程字体;
  2. [THEME_TOKENS] 是否声明完整 CSS 变量底座(--bg --card --primary --border)并支持暗黑模式;
  3. [VIEWPORT] 是否具备移动端小屏响应式 viewport;
  4. [TAG_HYGIENE] svg / details / dialog / table / nav / aside 等关键标签是否对称闭合;
  5. [DUP_ID] 同一文件内 id 是否唯一(否则 JS 取到的元素不确定);
  6. [SCRIPT_SYNTAX] 每个内联 <script> 能否通过编译——"没报错" ≠ "跑得起来";
  7. [DARK_MODE_LEAK] token 块之外是否硬编码了 #fff / #000 / white / black(假暗黑模式);
  8. [WIDTH_HYGIENE] 含表格是否提供 overflow-x 滚动容器、主容器是否被硬编码得过窄;
  9. [DOC_FENCES] 规范文档的 Markdown 代码围栏是否配对(未闭合会让后续全文变成代码块,Agent 会读错规范);
  10. [CSS_VAR_DEFINED] var(--x) 引用的变量是否在同文件内定义。单文件零依赖下没有外部样式表可救;且不带 fallback 的 var() 失效是整体失效而非降级——stroke 会变成 none,图形直接消失,而不是颜色不对。

第 8 条的常见修法:.container 基础宽度用 max-width: 1280px(或 width: 100%; max-width: 1380px),表格外层包裹 <div class="table-wrap" style="overflow-x: auto; width: 100%;">。严禁把主容器硬编码成 800–860px 的死宽度。

9 条提醒级规则(建议人工复核):

  1. [NO_RANDOM] 演示/图形数据是否使用了 Math.random()(必须确定性);
  2. [CHART_TOKEN_ONLY] SVG 的 fill / stroke 是否硬编码色值(应取 CSS 变量以跟随主题);
  3. [FIXED_HEIGHT] 是否固定像素高度与 overflow:hidden 并存导致截断;
  4. [PRINT_STYLE] 文档型母版是否提供 @media print;
  5. [EMPTY_STATE] 实现了筛选/搜索是否同时实现了空状态;
  6. [AFFORDANCE] 是否具备复制/导出出口(杜绝交互死胡同);
  7. [COLOPHON] 是否包含页脚溯源归档印章;
  8. [DOC_SECTIONS] 章节编号是否重复或跳号;
  9. [MIN_FONT] SVG 内字号是否低于 9px(装不下应改 hover 显示,不许缩小字号硬塞)。

2 条跨文件检查:

  • [ZH_EN_PARITY]:zh 母版有而 en 缺失的结构元素(nav / aside / details / table / dialog / section)判为阻断;同一功能的导出按钮 id 在中英两版不一致判为提醒。
  • [DOC_TOKEN_COVERAGE]:规范文档里的 ```html 代码样例若引用了 Micro-CSS 基座之外的 CSS 变量,判为阻断。这条防的是「照文档做反而做坏」——基座与样例是两份手抄副本,缺了这道检查就会各自漂移(见 references/failures.md F-014)。

3 · 渲染烟测(静态 lint 覆盖不到的盲区;有 Chrome 时建议跑)

静态 lint 能证明"代码没报错",证明不了"页面真的画出来了"。交付前再跑一次渲染烟测:

node <skill>/scripts/smoke.mjs            # 技能全部产物
node <skill>/scripts/smoke.mjs <file.html>  # 单个产物

它把产物复制到临时目录(不改动仓库文件),注入探针脚本后用 headless Chrome 以 --dump-dom 输出渲染后的 DOM,再断言运行时事实:

  • 内联脚本是否有运行期报错(语法合法 ≠ 跑得起来,这条静态 lint 永远测不到);
  • SVG 是否真的画出了图元(而不是容器在、内容空的"空壳");
  • 点击 #themeToggle 前后 --bg 是否真的变化(假暗黑模式);
  • 是否存在被 overflow:hidden 截断的内容;
  • 窄视口下是否横向溢出。

需要本机有 Chrome/Chromium(可用 CHROME_PATH 指定,支持 macOS / Linux 常见路径)。找不到浏览器时它以退出码 2 跳过,并且在语言上明确这是环境问题、不是产物缺陷。 node <skill>/scripts/smoke.mjs --selftest 会注入缺陷,证明探针本身确实能失败。

4 · 豁免机制(白名单先于规则)

误报会摧毁对门禁的信任,因此容器的 token 定义块(:root / [data-theme] / prefers-color-scheme)与 @media print 块内的颜色字面量永远合法,不需要任何豁免。

确实需要豁免时,在命中行的上一行写:

/* @lint-allow: DARK_MODE_LEAK — 该按钮悬浮在恒为深色的代码块表面上,不随主题反转 */
.copy-btn:hover { background: #3f3f46; color: #fff; }
  • 豁免作用域仅为「注释所在行 + 下一行」,不会掩盖同文件其他位置的真实违规(这条是被 F-003 逼出来的:最初的实现是文件级豁免,放行一处会顺带掩盖别处);
  • 必须写明理由(≥4 字符),不写理由的豁免不生效(F-004);
  • 典型可豁免场景:恒为深色的代码块/终端表面上的按钮(F-005)。不要用它来掩盖真正的暗黑模式缺陷。

白名单为什么先于规则、豁免为什么只作用于一行,见 references/failures.md F-002 / F-003 / F-004。

5 · 门禁本身的有效性(维护者向)

仓库开发时用 npm test(静态 lint + 规则变异测试)与 npm run test:smoke(渲染烟测)。改规则后必须重跑变异测试——一个「什么都能过」的校验器等于没有校验器。

6 · 人工自检清单(没有 Node 时这是唯一的验证路径)

校验器与烟测加起来仍然测不了「图有没有画对」。以下 6 项无法静态判定,脚本全绿也不代表没问题——必须由 Agent 在交付前对照「反模式规避清单」的数据与语义层逐条自查:

  • 柱状图没有断轴?(截断 Y 轴等于毁约;需理解 Y 轴语义)
  • 面积/半径编码开了平方根?(需知道某个 r 绑定的是数值还是面积)
  • 颜色连接了真实数据维度,且底注写清了它在编码什么?(语义判断)
  • 卡片标题写的是结论,不是图型名?("Revenue by plan" 可以,"柱状图" 不行)
  • 图型与数据形状匹配?(语义判断)
  • 演示数据是确定性的?(刷新两次必须长得一样)

另外两项虽可静态发现,但代价高、容易漏,交付前顺手确认一次:

  • 语言纯度:中文母版与英文母版不混排,一份交付只用一种语言;
  • 粒度正确:符合「价值研判准则」第 3 条的判定——没有把「分析一下」做成整页报告,也没有把「要一份报告」做成一页图表。

这份清单不是补充说明,而是门禁的边界声明,也是没有 Node 时的唯一验证路径。 门禁全绿同样只证明「工程上没坏」,不证明「图上没错」——F-015 就是一个 18 条规则、14 个产物烟测、28 个变异用例全绿、图却画错了的实例。

レビュー

まだレビューはありません。使ってみた感想をお寄せください。

このスキルの問題を報告する