公众号图文排版(微信安全 HTML)
目标:产出一个 HTML 文件,用户复制粘贴进公众号编辑器后,版式、配色、间距、图片全部保留。
微信编辑器会对粘贴内容做结构清洗,所以「浏览器里好看」不等于「粘进去好看」。下面所有结论都围绕这一点。
一、三条铁律
- 样式全部内联。
class/id/<style>都会被剥离,只剩style=""里的声明有效。 - 块级容器一律用
<section>。<div>看似能用,但粘贴时常被转成p/section,卡片底色和圆角直接消失。 - 不给文字定宽、定高。宽度用
%或max-width,高度交给内容撑开。
二、官方红线(踩了真出问题)
依据微信官方《公众号编辑器插件开发规范》与开源校验器。
| 禁止项 | 后果 | 正确做法 |
|---|---|---|
容器固定 px 宽度(如 width:586px) |
不同屏幕不居中、可能横向溢出 | 一律 %,需要上限用 max-width |
height:0 或固定高度装文字 |
移动端内容被裁切或不可见 | 不设高度 |
line-height 小于字号(多行文字) |
行与行重叠 | 行高 ≥ 字号;纯装饰细线、无缝拼接图片不算违规 |
text-align: start / end |
各终端解析不一致 | 只用 left / center / right / justify |
<pre> 包正文 |
white-space:pre 不换行,移动端横向截断 |
用 section / p |
img 设 opacity:0 再叠背景图 |
发布后无法在后台换图 | 别这么做 |
设置 font-family |
官方明确不建议,破坏各端一致性 | 一律不写 |
!important |
破坏平台公共样式与 Dark Mode 算法 | 一律不写 |
| 同标签 + 同样式 + 单子节点连续嵌套 > 10 层 | 编辑器自动精简结构 | 保持扁平 |
| 文字背景用渐变 | Dark Mode 下可能被转纯色导致失读 | 用纯色背景,保证对比度 |
三、可以放心用
- 标签:
pspansectionstrong/bem/ius/delh1–h6ul/ol/liblockquotetable/tr/td/thimgbrsvg - CSS:
color、background-color、font-size、font-weight、font-style、margin、padding、border(含border-left竖线)、border-radius、text-align、line-height、letter-spacing、text-indent、text-decoration、overflow、opacity(≠0)、display(block/inline-block)、vertical-align、box-shadow、text-shadow - img 属性:
src、data-src、data-w(原图宽,官方建议必加)、data-ratio
> box-shadow 官方禁止清单里没有,多数情况能保留;但别让阴影承担唯一的分隔作用。
四、别用(不是一定被删,是表现会变)
| 写法 | 实情 | 替代方案 |
|---|---|---|
display:flex |
编辑器可能保留,但移动端内核支持碎片化,容易塌 | 横向排列用 inline-block + vertical-align;多列用真实 table |
position(absolute/fixed/relative) |
多来源确认被删 | 不用 |
css display:table |
表现不稳定 | 用真实 <table> 标签 |
动画 / 过渡 / @keyframes / @media / 伪元素伪类 / CSS 变量 |
完全不支持 | 动效只能用 gif |
float / transform / linear-gradient |
有条件生效,不可依赖 | 不用 |
五、版式结构(可直接复用)
整体骨架:
<div style="max-width:720px;margin:0 auto;background:#ffffff;padding:0 4px;">
<!-- 标题区 -->
<p style="text-align:center;font-size:22px;font-weight:bold;color:#1a3a5c;line-height:1.5;margin:18px 0 0;">主标题</p>
<!-- 金色分隔线(见组件章节,必须同行连写) -->
<!-- 引言框 -->
<section style="background:#f4f7fb;border-left:3px solid #1a3a5c;border-radius:0 6px 6px 0;padding:14px 16px;margin:18px 0;">
<p style="font-size:14px;color:#4a5a6a;line-height:1.9;margin:0;">导语文字。</p>
</section>
<!-- 小节标题(圆形编号章 + 文字) -->
<!-- 正文卡片 -->
<section style="background:#faf8f3;border:1px solid #e6d9bf;border-radius:12px;padding:22px 18px;margin:0 0 18px;">
<p style="font-size:15px;color:#333333;line-height:2.1;text-indent:2em;text-align:justify;margin:0;">正文段落。</p>
</section>
<!-- 结语框 -->
<section style="background:#faf6ec;border-left:3px solid #c89b3c;border-radius:0 6px 6px 0;padding:16px;margin:18px 0;">
<p style="font-size:14px;color:#6b5b3e;line-height:1.9;margin:0;">结语。</p>
</section>
</div>
正文段落统一参数(全文一致,不要随手加粗):
font-size:15px; color:#333; line-height:2.1; text-indent:2em; text-align:justify
配色建议(一套主题色用到底,正文别超过两种颜色):
| 主题 | 主色 | 辅色 | 适用 |
|---|---|---|---|
| 稳重蓝金 | #1a3a5c |
#c89b3c |
通知公告、活动纪实、会议通讯 |
| 清透蓝珊瑚 | #3a7ca5 |
#e8a87c |
心理、健康、生活类 |
| 深绿暗金 | #2d5016 |
#b8860b |
读书、学术、荐读 |
六、可直接抄的组件
完整代码见 references/components.md,这里只给要点:
- 金色分隔线:三段
inline-block的span必须同行连写,中间不能有换行(换行会被渲染成一个空格导致不居中)。装饰线用border-top画,不要用background;span里必须填&nbsp;,否则空标签会被整个删掉。 - 圆形编号章:
width与line-height取同一个值(如 22px)实现文字垂直居中,border-radius:50%。编号与标题两个span同样要同行连写。 - 并排两图 / 2×2 图组:用真实
<table style="width:100%;table-layout:fixed;border-collapse:collapse">+<td style="width:50%;box-sizing:border-box">,图片width:100%。table-layout:fixed是关键,否则列宽随图片加载时序变化,手机端会出现「两图忽大忽小、刷新还变」。 - 条目卡片(适合 3 条以上的并列规定/要点):每条把头部提示词加粗 + 主色,正文常规字号。比整段同色扫读效率高得多。
七、图片处理
- 必须内嵌 base64。用本地路径的图粘不进编辑器,会显示破图。单文件 HTML 因此会变大,这是正常的。
- 压缩参数:最大宽度 1080px、JPEG quality 82。整篇超过 30 张图时,把并排小图降到 760px / q80,否则单文件可能到 10MB 以上,粘进编辑器会卡。
- 每个
<img>都带data-w(原图宽 px)和data-ratio(高/宽)。微信按这两个值在图片加载前撑出占位,能减少加载跳动。 - 同一行并排的图先裁成同一宽高比。混排竖版和横版会让行高悬殊(实测同一 2×2 里竖版行 237px、横版行 134px),版面立刻散掉。
- 单图:横版
width:100%;竖版收窄到width:60%左右居中。
八、怎么把 HTML 弄进编辑器
不要尝试「注入 HTML」,会被清洗。可靠做法是「复制为富文本 + 手动粘贴」:
- 在浏览器里把 HTML 渲染出来,编辑一段脚本选中渲染结果,调用
document.execCommand('copy'),再由人在编辑器里Ctrl+V。 - 承载容器用
position:fixed; left:-10000px,不能用display:none(隐藏元素复制不出内容)。 - 复制动作必须在真实点击手势里触发,脚本定时触发会被浏览器拦。
- 粘贴前剥离
<html><head>,只取body内容。
如果是自有已认证公众号,也可以走接口直接创建草稿(draft/add),HTML 里的图片改为本地路径由接口自动上传转存——这条路线产出的文件只有几十 KB,但需要 AppID/AppSecret 与 IP 白名单,适合批量或自动化场景。
九、交付前自检(强烈建议)
用微信官方开源校验器在真实浏览器里跑一遍,isValid: true 才算过:
git clone https://github.com/wechatjs/verify-article-structure-spec
cd verify-article-structure-spec/cli
npm install
PUPPETEER_EXECUTABLE_PATH="<你的 Chrome 路径>" npx tsx src/index.ts "/绝对路径/文章.html"
它能准确抓出 line-height 过小、height:0、text-align:start 等违规,比肉眼可靠。
另外两条目检建议:
- 量位置,不要靠猜:用 puppeteer 以 375px 视口实测元素坐标,判断「装饰线 + 符号 + 装饰线」整组是否与父容器居中(差值 0 即居中)。
- 整页截图再看一遍:420px 视口出整页图,能一眼看出条目疏密、加粗是否恰当、有没有大片突兀空白。比逐段读 HTML 快得多。
十、几个容易踩的坑
- 块级元素塞进
<p>:<p><section>…</section></p>会被浏览器自动提前闭合<p>,里面内容逃出居中容器变成靠左。装饰线、色块一律用span;要块级容器就整体用section。 - 空标签被整个删除:没有文字内容的
span会消失,装饰线必须填&nbsp;。 - 两个
inline-block之间换行会渲染成空格,影响居中和间距——必须同行连写。 h1–h6会被编辑器改写并套上自己的 class,样式不可控,标题改用带内联样式的<p>。- 连续空格会被合并,要空格用
&nbsp;。 - 改已发布的内容不要重新创建,用更新接口覆盖,否则会多出一篇草稿。
十一、文件说明
| 路径 | 内容 |
|---|---|
SKILL.md |
本文档:红线清单、结构约定、自检流程 |
references/components.md |
分隔线、编号章、并排图、条目卡片等可复制代码 |
scripts/gen_demo.py |
最小可跑示例:生成一篇带编号章与条目卡片的演示 HTML |