微信排版规范 · 落地说明
依据:微信公众平台官方文档《能力接入 / 微信公众平台编辑器插件开发规范》 (https://developers.weixin.qq.com/doc/service/guide/product/plugin_spec.html)
博客号在三个层面落地了这份规范:
- 编辑器「排版体检」:发布前一键静态检查,逐条指出违规点并引用规范编号(编辑器右上角「体检」按钮)
- 存储净化器(
src/sanitize.ts):保留规范友好的属性(data-w、data-ignore-width、data-no-dark…),剥除危险内容 - 默认主题(
src/themes/wechat.css):正文 17px / 1.75 行高,响应式无固定宽;默认保持明亮配色(站长偏好关闭了自动 Dark Mode,恢复方式见该文件内注释)
以下是规范要点摘要与博客号的对应实现,供主题/插件开发者参考。
1. CSS 属性使用规范
1.1 opacity —— 禁止隐藏真图叠 SVG
- 规范:不要把
img的opacity设为 0,再用带背景图的 SVG 叠在原位。真图被隐藏后,发布后无法在编辑器里修改图片。 - 博客号:体检规则
1.1 opacity;粘贴内容中的此类结构会被警告。
1.2 caret-color —— 光标必须可见
- 规范:禁止把输入光标颜色设为全透明,作者会找不到输入位置。
- 博客号:编辑区强制
caret-color: var(--green);体检规则1.2 caret-color检查粘贴内容。
1.3 line-height —— 不得小于字号
- 规范:容器行高小于字号时,多行文字会重叠。排除场景:纯图片无缝拼接(元素内无文字)、实际只渲染一行的文字。
- 博客号:体检规则
1.3 行高过小(px 与 unitless 两种写法都检查);默认主题正文 17px / 1.75。
1.4 width —— 禁止固定宽度破坏响应式
- 规范:固定宽度会造成「居中不一致 / 水平溢出 / 不同屏幕占比差异」。建议给
<img>加data-w(图片原始像素宽度)作为加载超时后的宽度兜底。 - 豁免:横向滚动容器、视觉裁剪特效、第三方固定尺寸组件等有意为之的场景,在节点上加
data-ignore-width(对自身及子树生效)。 - 博客号:编辑器插入图片时自动探测并写入
data-w;体检规则1.4 固定宽度(识别data-ignore-width豁免并提示确认)与1.4.3 data-w;净化器原样保留这两个属性。
1.5 height —— 别让内容不可见
- 规范:
height:0且含文字 → 移动端整段不可见;固定小高度 + 内容溢出 → 被裁剪。排除:SVG 交互容器、无文字内容、可滚动容器。 - 博客号:体检规则
1.5 高度为 0。
1.6 text-align —— 禁用 start / end
- 规范:不同终端对
start/end兼容不一,会造成部分设备居中、部分居左。 - 博客号:体检规则
1.6 text-align;「一键修复」会把start/end改为left。
1.7 SVG animate begin —— 兼容 PC
- 规范:SVG 动画若只在
touchstart触发,PC 端无法点击,应写begin="touchstart; click"。 - 博客号:出于安全考虑净化器会整体移除
<svg>(内联 SVG 是常见 XSS 载体),如需 SVG 动图请使用 GIF/视频或截图。
1.8 pre —— 不要包裹普通正文
- 规范:
<pre>自带white-space:pre不自动换行,窄屏会被截断。普通段落用p/section。 - 博客号:编辑器「代码块」按钮只往
pre里放<code>;体检规则1.8 pre 标签检查无 code 的纯文本 pre。
2. 文章结构规范
- 2.1 嵌套层级:同标签名 + 同内联样式 + 单子节点的连续嵌套 ≤ 10 层(图片/视频/SVG 等媒体标签除外),超出会被编辑器自动精简。→ 体检规则
2.1 嵌套层级。 - 2.2
span[leaf]:只能包含行内元素或文本。→ 从公众号粘贴的内容经净化后仅保留白名单标签。 - 2.3
section[nodeleaf]:只能包裹官方特定组件或img。
3. 字体使用规范
- 规范:不建议设置任何
font-family,公众号默认字体栈为"mp-quote", PingFang SC, system-ui, -apple-system, BlinkMacSystemFont, … - 博客号:默认主题沿用该字体栈;体检规则
3 字体使用提示自设字体族(净化器不强行剥除,保留作者自由度)。
4. Dark Mode 规范
- 4.1 颜色:文字与背景对比度适中;文字下方的渐变背景在 Dark Mode 下会被算法转成纯色,尽量避免;纯装饰渐变(上方无文字)不受影响。
- 4.2 结构:同一段多文本共用一个背景时,把背景写在公共容器上,别逐个文本节点设置;保持「结构顺序 = 视觉顺序」,别用绝对定位打乱。
- 4.3 图片:别用图片承载纯文本(算法无法转换其中的文字);透明底图片注意在 Dark Mode 正文底色
#191919上的对比度。 - 4.4 SVG:如需 SVG 线条颜色跟随深浅色,使用
stroke="currentColor" fill="currentColor"。 - 4.5 技巧:
data-no-dark让单个节点跳过转换;不要使用!important;确认无碍的告警可用data-ignore-dm="low-contrast text-bg-gradient"豁免检测。 - 博客号:
!important在净化时被剥除、体检提示;data-no-dark/data-ignore-dm属性原样保留;wechat 主题默认保持明亮配色(站点不做prefers-color-scheme自动 Dark Mode)。
5. 博客号体检规则一览(编辑器 → 体检)
| 规则号 | 名称 | 级别 | 可一键修复 |
|---|---|---|---|
| 1.1 | opacity 隐藏图片 | ⚠️ | |
| 1.2 | caret-color 透明 | ⚠️ | |
| 1.3 | 行高小于字号 | ⚠️ | |
| 1.4 | 固定宽度(含 data-ignore-width 提示) | ⚠️ | |
| 1.4.3 | 图片缺 data-w | 💡 | |
| 1.5 | height:0 隐藏文字 | ⚠️ | |
| 1.6 | text-align: start/end | ⚠️ | ✅ |
| 1.8 | pre 包裹普通正文 | ⚠️ | |
| 2.1 | 同标签嵌套 ≥ 10 层 | ⚠️ | |
| 3 | 自设 font-family | 💡 | |
| 4.1.2 | 渐变背景上有文字 | 💡 | |
| 4.5.2 | 使用 !important | ⚠️ | ✅ |
| 无障碍 | img 缺 alt | 💡 |
官方还提供基于 Puppeteer 的完整校验 CLI(含需真实布局测量的 width/height/叠字检测): https://github.com/wechatjs/verify-article-structure-spec —— 需要更严格校验时可本地运行。