Impeccable /typeset 实战指南:在既有视觉世界内打磨字体层级、字面与阅读体验
排版承载着信息、层级与产品声音。对于使用 Impeccable 技能驱动 AI 编码助手(Claude Code、Cursor、Gemini CLI、Codex、Qoder 等)做前端界面设计的开发者而言,/impeccable typeset 是把"字体选择与层级"这一单项打磨到可交付质量的标准化工作流。本文基于 .qoder/skills/impeccable/reference/typeset.md 展开,梳理从"访客模式判定 → 双轨评估 → 系统设定 → 逐条应用 → 证据化验收 → Live 模式参数化"的完整链路,并给出仓库内可对照的源码、数据与测试证据,让读者既能照单执行一次排版评审,也能理解其背后的设计决策与工程约束。
typeset 在整个技能中的定位
在 Impeccable 的指令体系中,typeset 属于 Enhance(增强) 类目,SKILL.md 的命令表中将其描述为 "Improve typography hierarchy and fonts",统一入口是:
/impeccable typeset [target]
其核心边界在于第一条原则:排版承载信息、层级与声音,但一切改进都应发生在已确立的视觉世界之内;除非用户明确要求更换身份(identity),否则不要替换现有视觉身份。 这正是 typeset 与 new-work 的分界——如果更换字体意味着创造一个新的视觉身份,则应当路由到 new-work.md,并同步更新 DESIGN.md;反之则应保留已确认的字体族、优化其使用方式。
同一份参考文档在仓库中针对不同 AI 运行时被多路复制(.claude/、.cursor/、.gemini/、.grok/、.qoder/、.opencode/、skill/reference/、plugin/skills/impeccable/reference/ 等),以便每种 harness 都能按统一契约加载;其中 skill/ 目录下的副本可视为基准源。技能总入口 SKILL.md 的 allowed-tools 也明确授权运行 node .qoder/skills/impeccable/scripts/* 这类脚本,这也是后文机械扫描命令能作为统一契约存在的原因。
第一步:先定访客模式(Visitor mode),再谈字体策略
typeset 的第一步不是挑字体,而是判断当前页面属于哪种访客模式,因为三种模式的排版优先级完全不同:
| 访客模式 | 排版策略重心 |
|---|---|
| Persuade(说服)+ Experience(体验) | 展示性字体(display type)可以承载品牌声音;当构图受益时,可用果断的对比(decisive contrast)与响应式字号阶(responsive scale) |
| Operate(操作)+ Read(阅读) | 稳定、可扫描、行宽(measure)优先;往往一套经过精心调校的字体族 + 固定角色阶(fixed role scale)就是正确解 |
| Native(原生) | 遵循 ios.md 或 android.md,包括平台的字号缩放与无障碍行为 |
模式应按"表面(surface)"而非"产品类型"选择:工具页的落地页仍是 Persuade,时尚品牌的文档仍是 Read。同源规则也见于 operate.md 中对 Operate/Read 更深层的指引。文档对类型的"身份替换闸门"给出明确裁决:排版替换若会造成新身份 → 走 new-work.md;否则保留已确认的字体族,只改进其使用方式。
第二步:双轨评估——感知评审与机械扫描各自独立
当有子代理(sub-agent)工具可用且被允许时,typeset 要求把两类评估分开运行;即便没有子代理,也应由同一个代理按顺序独立执行。关键约束是:不要让检测器的发现先入为主地锚定设计评审的结论。
轨一:Typographic assessment(排版感知评审)
检查代表性页面与样式,并对下列每一问给出"文件 + 选择器 + 计算值"级别的证据:
- 权威性与契合度(Authority and fit):已确立哪些字族、字重与角色?它们契合产品与所选视觉世界,还是未经审视的默认值?每个字体族是否都必要?
- 层级(Hierarchy):标题、正文、标签(label)、元数据(metadata)、数据(data)角色能否一眼区分?相邻字号或字重是否过于接近、难以承担不同职能?
- 字号阶与一致性(Scale and consistency):是精心设计的角色阶,还是一堆随意数值?重复角色在不同屏与状态间是否保持一致?
- 阅读性(Reading):正文行宽是否落在舒适的 45–75 字符区间?行高、段落节奏、对比度与字距(tracking)是否针对真实字面、字宽、语言与表面做了调校?
- 压力测试(Stress):长标题、本地化扩展(localization expansion)、缩放(zoom)、窄容器、缺失字重、字体回退(font fallback)时会发生什么?
- 交付(Delivery):是否只加载被用到的资源?回退度量(fallback metrics)、加载策略、可变字体(variable-font)设置是否避免隐形文字与破坏性回流(reflow)?
仓库为"度量感知"提供了一处可验证的工程底座:.qoder/skills/impeccable/scripts/data/font-index.json 是一个面向字体的测量索引,其 schema 记录了测试文本(如 "The quick brown fox jumps over the lazy dog 0123456789 HAMBURGEVONS")、测量字号(48 / 14 / 48c)以及一整套感知特征键,包括 advance、xRatio(x 高度比)、descRatio、stemW(主干宽)、contrast、serif、roundFrac、runDensity、按垂直与水平切分统计的剖面特征(vprof0-9、hrun25-90、vrun25-90)等,并将字体归入 sans / serif / display / handwriting / mono 类别,按 "字体名 + 字重" 逐条给出测量向量。这说明"行高要适配真实字面而非套用通用比例"这类规则在工程上是有数据支撑的——配套的 font-index-failures.json 则用于记录无法完成测量的字体集合,构成回退依据。
轨二:Mechanical scan(机械扫描)
按文档约定运行检测器,作用域限定在 typography:
node .qoder/skills/impeccable/scripts/detect.mjs --json --scope type [target files or dirs]
同时还需人工检查检测器无法解析的动态或任意字体取值。两份结论合成后再动手编辑,并记录"每一轨各自捕获了什么、漏掉了什么"。文档特别强调:一次干净的扫描只是地板(floor),不是好排版的证明。
仓库中 tests/fixtures/antipatterns/ 下的夹具也从侧面印证了该检测体系的存在与粒度——其中既有应被标记的反例(如 typography-should-flag.html、extreme-negative-tracking.html、overused-font.html、undersized-ui-text.html),也有应放行的正例(typography-should-pass.html、css-in-prose-should-pass.html),还有对 em-dash 实体(em-dash-entities.html)、非渲染文本(nonrendered-text.html)等边缘情况的覆盖,可作为"机械扫描能发现什么"的补充观察。
第三步:Set the system——动手前先把系统说清楚
编辑前,typeset 要求代理先用文字陈述排印系统的设定,包括:
- 界面需要的角色(roles);
- 角色之间预期的对比强度;
- 阅读行宽与密度(measure and density);
- 哪些既有字面与字重是权威的(authoritative);
- 任何性能、本地化或无障碍约束。
设定阶段的核心纪律是:用最少的角色与字体族把层级做到不容误读;刻意组合字号、字重、空间与音调,而不是让字号独自承担全部工作;角色名与 token 应描述用途而非取值。这与技能整体的 token/design-system 思路(可参考 extract.md 对抽取可复用 token 的描述)保持一致——排版角色先于排版数值而存在。
第四步:Apply——把规则落到 CSS 与内容上
应用阶段是一组可以直接作为评审 checklist 的硬规则:
- 正文下限:正文保持舒适的可读性与可缩放性。普通网页正文以 1rem / 16px 为底(floor),除非密集角色、平台惯例或用户设置能给出更低的正当理由。
- 行宽:散文段落保持在 45–75ch;行高与行宽大致反向调节——更宽的版面通常需要更大的行距。
- 深色表面上的浅色文字:在三个感知轴上都做补偿——行高略增、字距略松、当字面需要时字重再加一档。
- 行高依字面而定:按真实字面、字宽、语言与对比度调节,而不是套一个"万能比例"。
- 一致性:重复角色在不同屏与状态间保持一致。
- 特性使用:当内容受益时使用数字(numeric)、表格(tabular)、代码(code)与标签(label)等 OpenType 特性。
- 加载策略:只加载被使用的字体资源与字重;提供度量兼容的回退字体(metric-compatible fallbacks),避免阻塞文本显示。
- 空间响应:营销展示字可以按可用空间响应;密集产品与阅读表面应保持空间上的可预测性。
- 无障碍:保留浏览器缩放、用户字体设置、iOS Dynamic Type 与平台文本缩放。
- 段落节奏:用段落间距或首行缩进作为主要段落节奏;两者同时使用通常会双重标记边界。
可以翻译成 CSS 形态的常见落地示例(此处为模式示意,非仓库内代码):
/* 正文以 1rem/16px 为底,行宽 45–75ch */
body { font-size: 1rem; line-height: 1.6; }
article p { max-width: 65ch; }
/* 行高随行宽反向调节:窄栏用小行距、宽栏用大行距 */
.prose { max-width: 45ch; line-height: 1.45; }
.prose--wide { max-width: 75ch; line-height: 1.7; }
文档同时划出两条红线:不要让字型装饰凌驾于可理解性之上;也不要在没有明确角色分工的情况下引入第二个字体族——一个字体族必须拥有"只有它能承担的清晰职能"。
第五步:Verify——证据化验收,拒绝"裸 yes"
验收不是口头确认,而是逐条用渲染或源码证据作答后再重跑机械扫描:
- 主级、次级、正文、元数据角色在不读正文的情况下也能被认出;
- 长文本在相关宽度与语言下保持舒适;
- 排版归属于产品及其已确立的视觉世界;
- 加载过程不产生破坏性回流或隐形文字;
- 缩放、文本缩放、焦点、对比度与收窄视口路径仍然可用;
- 最终机械扫描无未解释的发现。
当层级成立后,文档规定交棒给 /impeccable polish 做最终交付前质量收尾——对应技能参考 polish.md。
第六步:Live 模式下的排版参数契约
当 typeset 在 live.md 描述的 Live 变体模式(在浏览器中选择元素、生成 HTML+CSS 变体并通过 dev server 的 HMR 热切换)中被调用时,存在一条强制的参数签名:
每个变体都必须声明一个粗粒度(coarse)的
scale参数,并把字号阶(type ramp)写成基于var(--p-scale, 1)的公式。
参数 schema 原文如下:
{"id":"scale","kind":"range","min":0.85,"max":1.3,"step":0.05,"default":1,"label":"Scale"}
kind: "range"表示滑块类参数,驱动一个 CSS 变量--p-scale;min/max/step/default定义了滑块的取值范围(0.85–1.3)、步进(0.05)与默认值(1);label决定浏览器内嵌控件显示的文字。
排版实践上,可将整条字号阶梯写成对 scale 的缩放:
:root { --p-scale: 1; }
.hero-title {
font-size: calc(clamp(2.5rem, 6vw, 5rem) * var(--p-scale, 1));
line-height: calc(1.05 / (1 + (var(--p-scale, 1) - 1) * 0.4));
}
live.md 的参数契约还给出了额外的预算约束:参数按"组合的视觉重量"配给(叶级/极小元素 0 个参数,中等组合约 2 个,大型组合 2–3 个、最多 4 个),有三种参数种类——range(驱动 --p-<id>)、steps(分段单选,驱动 data-p-<id>)、toggle(同时驱动变量与属性)。具体到 typeset,live.md 在"动作特异性变体"一节明确要求:typeset 的每个变体必须使用不同的字体配对(pairing)且搭配不同的字号比例(scale ratio)。此外,只有当"配对或字重"代表真实系统选择时,才允许追加至多一个 pairing 或 weight 参数;任何追加参数都必须遵守 live.md 的参数契约。
小结:把 typeset 当作可执行的排版质量闭环
综上,/impeccable typeset 提供了一条可重复的质量闭环:判定访客模式 → 感知评审与机械扫描双轨独立取证 → 显式设定排印系统 → 在既有身份内逐条应用规则 → 证据化验收并交棒 polish → 在 Live 模式中把 scale 参数化供人在浏览器内实时微调。它既反对"用一套默认值糊弄排版",也反对"脱离既有视觉世界另起炉灶";既承认机械扫描的价值,又清醒地把干净扫描定义为地板而非终点。对于希望在 AI 驱动的界面开发中稳定产出高质量字排版的团队,这份参考文档配合仓库中的字体度量数据(font-index.json)、检测夹具(typography 相关 tests fixtures)与 live.md 参数契约,正好构成一套可引用、可验证、可执行的完整工具箱。
若需深入其他维度,可在 SKILL.md 的命令表中找到与 typeset 相邻的 layout、audit、critique、polish 等指令的参考入口;同类参考文档的基准源位于 skill/reference/typeset.md。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0629
MiniCPM5-2BMiniCPM5-2B 是一款面向端侧、本地部署和资源受限场景的 2B 稠密 Transformer,能够达到同尺寸开源模型 SOTA 水平。Markdown00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
HivisionIDPhotos⚡️HivisionIDPhotos: a lightweight and efficient AI ID photos tools. 一个轻量级的AI证件照制作算法。Python07
DragonOSDragonOS is an operating system developed from scratch using Rust, with Linux compatibility. It is designed for **Serverless** scenarios. 使用Rust从0自研内核,具有Linux兼容性的操作系统,面向云计算Serverless场景而设计。Rust00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00