设计系统
Design System — 引导 + 共享品牌 Token
design-system 技能是 markdown-html 插件的共享品牌所有者。只需运行一次引导程序。每个转换器(md-document, md-review, md-slides)都会通过 config_loader.py 读取生成的配置,并将其 12 个 CSS 自定义属性应用于输出。否则,转换结果将使用占位默认值渲染——虽然技术上可行,但缺乏品牌化。
该技能包含三个 Python 工具:
1. onboard.py — 交互式(或支持 --defaults / --set / --show / --reset)向导。
2. config_loader.py — 可导入的自定义配置加载器,遵循 项目 > 全局 > 默认值的优先级,并支持 MARKDOWN_HTML_NO_CONFIG=1 跳过。
3. brand_palette_validator.py — WCAG-AA 对比度检查器 + HSL 调色板推导器。
这三个工具仅依赖标准库,不包含 LLM 调用(符合 Path-B 确定性原则)。
调用时机
| 现象 | 操作 |
|---|---|
| 用户在该工作区首次要求 "convert this markdown to HTML" | 运行 python3 markdown-html/skills/design-system/scripts/onboard.py |
| ~/.config/markdown-html/design-system.json 不存在 或 setup_completed_at 为空 | 拒绝转换,提示进行引导 |
| 用户希望针对特定仓库覆盖品牌配置 | python3 .../onboard.py --scope project |
| 用户希望以非交互方式修改单个字段 | python3 .../onboard.py --set brand.primary=#FF6B35 |
| 用户希望重置并重新引导 | 运行 python3 .../onboard.py --reset 然后重新执行 |
| 用户需要零接触默认值(CI,临时会话) | python3 .../onboard.py --defaults |
| 无头/容器化运行且应忽略已保存配置 | MARKDOWN_HTML_NO_CONFIG=1 ... |
引导问题集(10 个问题)
| # | 键名 | 选项 / 验证器 | 默认值 |
|---|---|---|---|
| 1 | default_output_dir | 路径; os.access(parent, os.W_OK) | ./markdown-html-out/ |
| 2 | brand.primary | HEX ^#?[0-9a-fA-F]{6}$ | #0A1628 |
| 3 | brand.accent | HEX 或留空(自动推导) | 由主色推导 |
| 4 | typography.heading_font | Google 字体名称(12 个安全默认值) | Inter |
| 5 | typography.body_font | Google 字体名称 | Inter |
| 6 | design_style | editorial / technical / minimal / playful | technical |
| 7 | code_t | (此处文本截断) | |
heme | light / dark / auto | auto |toc.behavior
| 8 | | sticky-sidebar / collapsible-top / inline / none | sticky-sidebar |company_name
| 9 | | 字符串 (可为空) | "" |logo_url
| 10 | | URL 或为空 (渲染时以 base64 嵌入) | "" |
硬性规则
1. 正文文本必须通过 WCAG AA 对比度测试。 每次更改后都会运行 brand_palette_validator.validate()。背景上的正文文本和链接必须达到 4.5:1 的对比度。如果任一项失败,onboard.py 将拒绝保存(退出码 4),并提示用户选择更深的 primary 色,或将 brand.bg/brand.text 留空以由派生机制选择安全组合,或者直接覆盖 brand.text。标准依据:WCAG 2.2 §1.4.3。onboard.py
2. 输出目录必须可写。 会向上追溯路径以寻找现有的祖先目录并检查 os.W_OK。路径为空或不可写 $\rightarrow$ 退出码 3。编排器的 output_path_resolver.py 在每次转换时遵循同一规则。design_style
3. 自定义必须改变行为,而非仅作为装饰。 每个消费者(md-document, md-review, md-slides)必须读取配置,并在用户更改 、brand.primary、code_theme 或 toc.behavior 时呈现不同的渲染效果。仅起装饰作用的字段不符合设计规范。brand.primary
4. 优先级固定。 项目级 > 全局级 > 默认值。深度合并(deep-merge)会保留嵌套键(例如,你可以在项目配置中覆盖 而不会丢失全局配置中的 typography.heading_font)。MARKDOWN_HTML_NO_CONFIG=1
5. 环境变量绕过机制有其特定用途。 适用于无头 CI、临时测试容器和自动研究风格的评估循环。绝不要在交互式用户界面中静默设置此项。
派生的 12 标记色板
一旦获取用户品牌色,brand_palette_validator.derive_palette() 将生成 12 个 CSS 自定义属性,存储在同一配置文件的 derived_palette 下。每个转换器都会将这些属性内联到其 <style> 块中。
| 标记 (Token) | 用途 | 派生逻辑 |
|---|---|---|
| --md-bg | 文档背景 | 若为深色则使用 Primary,若为鲜艳色则使用近中性色 |--md-surface
| | 卡片 / 标注 / 引用块背景 | 背景亮度 $\pm$ 4-6% |--md-border
| | 细分线、表格边框 | 背景亮度 $\pm$ 8-12% |--md-text
| | 正文文本 | 深色背景用米白色,浅色背景用近黑色 |--md-text-muted
| | 标题、元数据、页脚 | rgba(text, 0.68) |--md-accent
| | 主要 CTA、标注标题、链接强调 | 鲜艳色使用 Primary,深色则进行色相偏移并减淡 |--md-accent-soft
| | 强调色背景、悬停状态 | rgba(accent, 0.14) |--md-code-bg
| | 行内代码、代码块背景 | 背景亮度 $\pm$ 4-5% |--md-link
| | 超链接 | 通过迭代调整以确保在背景上达到 4.5:1 对比度 |--md-link-hover
| | 悬停状态 | 链接亮度 $\pm$ 6-8% |--md-success
| | 成功 / 已批准 / 通过 | 以绿色为基准,亮度匹配 |--md-warn
| | 注意 / 琐碎修改 / TODO | 以琥珀色为基准,亮度匹配 |
强制引导问题库 (Matt Pocock 结合文档的质询模式)
每轮一个问题,提供推荐答案和标准引用。
1. 您的品牌主色是什么? 推荐:您在产品或文档中已使用的 HEX 色值,而非标准的蓝色。标准依据:Aarron Walter, *Designing for Emotion* (颜色承载品牌情感)。
2. 强调色 (Accent) 应该是派生的还是手动设置的? 推荐:首次运行时选择派生(色相偏移 + 减淡可产生协调的配套色);仅在品牌指南有明确规定时才手动设置。标准依据:Adobe Spectrum, *Color Foundations*。
3. 风格是编辑风、技术风、极简风还是俏皮风? 推荐:technitechnical 用于工程规范/报告,editorial 用于长篇叙事,minimal 用于精简的参考文档,playful 用于营销/落地页内容。参考标准:Ellen Lupton,《Thinking with Type》(风格服务于修辞目的)。sticky-sidebar
4. 目录(TOC)采用粘性侧边栏还是内联? 建议:800 字以上的文档使用 ,短文使用 inline。参考标准:Nielsen-Norman,《Table of Contents Best Practices》(2023)。--scope project
5. 保存至全局还是单个项目? 建议:默认全局(确保所有工作的一致性);仅当该仓库具有不同品牌标识时,使用 。参考标准:research-ops 入职模式,research-ops/CLAUDE.md §8。
定制化使用示例(实际操作)
# 首次运行入职引导(交互式,引导完成所有 10 个问题)
python3 markdown-html/skills/design-system/scripts/onboard.py
为 CI / 首次测试提供零接触默认值
python3 .../onboard.py --defaults
仅修改主色调和设计风格
python3 .../onboard.py --set brand.primary=#FF6B35 --set design_style=editorial
针对单个仓库进行覆盖
python3 .../onboard.py --scope project --set design_style=minimal
重置并重新引导
python3 .../onboard.py --reset
python3 .../onboard.py
查看生效的配置(优先级:项目 > 全局 > 默认值)
python3 .../config_loader.py --show
python3 .../config_loader.py --status
绕过已保存的配置(仅返回 DEFAULTS)
MARKDOWN_HTML_NO_CONFIG=1 python3 .../config_loader.py --show
在确定品牌色前快速检查 WCAG 对比度
python3 .../brand_palette_validator.py --primary "#FF6B35" --accent "#00D4AA"前提假设
1. 用户至少有一个希望在 HTML 转换中保持一致的品牌 HEX 色值。
2. 用户接受 1-2 分钟的一次性设置。
3. 用户接受使用 Google Fonts 作为字体来源(通过 CDN,无需本地托管字体)。
4. 无障碍标准底线为 WCAG 2.2 AA(正文 4.5:1,大文本/UI 3:1)。AAA (7:1) 不在本次范围之内。
非目标(Non-goals)
- 不是完整的设计令牌(Design Token)系统(如 Style Dictionary, Theo)。仅包含 12 个令牌,而非上百个。
- 不是自定义字体托管方案。仅支持 Google Fonts。
- 转换器中不提供深色/浅色模式切换。code_theme: auto
处理语法高亮的prefers-color-scheme情况;布局色板在每次引导后为单模式。
- 不是无障碍审计套件(请使用 axe-core / pa11y)。我们仅强制执行对比度检查。
- 不转换现有的 CSS —— 派生色板将被注入到新生成的 HTML 中。
区别于
- marketing/landing/skills/landing/scripts/brand_palette_validator.py
—— 该脚本的 derive_palette()生成 8 个专为 Hero 页面渲染设计的令牌(--navy,--teal,--card-bg,--card-border)。本脚本生成 12 个专为文档渲染设计的令牌(粘性表面、细边框、代码背景、链接、链接悬停、成功、警告)。两者使用相同的 WCAG + HSL 计算逻辑,但令牌分类不同。
- research-ops/skills/clinical-research/scripts/onboard.py
—— 采用相同的模式(交互式 + --defaults/--set/--show/--reset/--scope),但问题集不同(临床 alpha/power/dropout vs. 品牌色板/字体/布局)。
输出产物
~/.config/markdown-html/design-system.json(全局)或 ./.markdown-html/design-system.json(项目)。JSON 架构定义在 assets/design_system_schema.json。
反模式(避免这样做)
- ❌ 跳过入职引导直接运行带有占位默认值的转换器 —— 输出结果将缺乏品牌感。
- ❌ 直接将高饱和度的品牌主色选作 brand.bg
(会导致文本对比度过低)。应将其用作 ac
- ❌ 不要为交互式用户静默设置 MARKDOWN_HTML_NO_CONFIG=1
—— 否则他们会疑惑为什么自己的 token 消失了。
- ❌ 不要将品牌语义编码在 12 个 token 分类之外的 derived_palette
中。只有在明确定义了名称 + 用途 + 推导规则的情况下,才能添加新 token。
参考资料
- WCAG 2.2 — §1.4.3 (对比度), §1.4.4 (缩放), §1.4.11 (非文本对比度)
- Aarron Walter — *Designing for Emotion* (A Book Apart)
- Ellen Lupton — *Thinking with Type*
- Adobe Spectrum — *Color Foundations*
- Nielsen-Norman — *Table of Contents Best Practices* (2023)
- research-ops 入职模式:research-ops/CLAUDE.md
§8
- 品牌调色盘数学源码:marketing/landing/skills/landing/scripts/brand_palette_validator.py`