WordPress theme.json 详解:settings、styles 与层叠顺序
搞清楚 theme.json 里 settings 和 styles 各管什么,再顺着 WordPress 的层叠顺序排查,修好那些改了却毫无反应的区块主题样式。
发布于
theme.json 是区块主题根目录下的一个配置文件,它同时干着两件互不相干的事:settings 决定区块编辑器向用户开放什么,styles 决定站点默认长什么样。WordPress 解析这个文件,把它和自己的默认值、以及站点编辑器里保存的内容合并,最后在每个页面上输出一组 CSS 自定义属性和一段内联样式表。
把这两件事混为一谈,正是大多数 theme.json 改动看起来毫无作用的原因。在 settings 里声明一个颜色,并不会把这个颜色应用到任何地方。它只是创建了一个变量,外加编辑器侧边栏里的一个色块。
顶层的那几个键
一个最小可用的文件长这样:
{
"$schema": "https://schemas.wp.org/trunk/theme.json",
"version": 3,
"settings": {},
"styles": {},
"templateParts": [],
"customTemplates": []
}
| 键 | 作用 |
|---|---|
$schema | 只为代码编辑器提供自动补全和校验。WordPress 会忽略它。 |
version | 告诉 WordPress 按哪套 schema 结构解析。不是你的主题版本号。 |
settings | 生成 CSS 变量,并控制编辑器开放哪些选项。 |
styles | 真正应用默认样式。 |
templateParts | 声明 parts/ 里的部件,以及它们属于哪个区域。 |
customTemplates | 把 templates/ 里的页面模板注册到文章编辑器中。 |
version 最容易被误解,因为它看起来像个主题版本号。它不是。它选择的是解析模式,而旧版 WordPress 遇到自己不认识的 schema 结构里的键时会悄悄跳过。如果你要支持老站点,就按那些站点能理解的版本来写,别假设新键会自动优雅降级。写这篇文章时版本 3 是当前版本;版本 2 仍然被大量使用,也仍然能被解析。
$schema 指向 WordPress 托管的一个 schema 地址。把它固定到 trunk 能让你在代码编辑器里拿到最新的提示;如果你是有意停留在旧 schema 上,用带具体版本号的地址更稳妥。
settings:编辑器提供什么
settings 下面的每一项都会生成一个 CSS 自定义属性,多数情况下还会生成编辑器里的一个控件。但 settings 下面没有任何东西会真的产生样式。
{
"settings": {
"color": {
"palette": [
{ "slug": "brand", "color": "#1f4ed8", "name": "Brand" },
{ "slug": "ink", "color": "#111827", "name": "Ink" }
],
"custom": false
},
"typography": {
"fontSizes": [
{ "slug": "small", "size": "0.875rem", "name": "Small" },
{ "slug": "large", "size": "1.5rem", "name": "Large" }
]
},
"layout": {
"contentSize": "680px",
"wideSize": "1200px"
}
}
}
上面这段会生成一批以预设路径和 slug 命名的变量:
--wp--preset--color--brand
--wp--preset--color--ink
--wp--preset--font-size--small
--wp--preset--font-size--large
同时还会生成 .has-brand-color、.has-brand-background-color 之类的工具类,编辑器就是靠它们把用户选中的色块套到区块上的。
"custom": false 那一行代表 settings 的另一半职责:关掉某些选项。加上它之后,任意取色的拾色器就消失了,用户只剩下你给的调色板。这个套路在各处都通用——settings.typography.customFontSize、settings.spacing.customSpacingSize、settings.border,等等。settings 下面没有一个键是装饰性的;每个键要么加一个变量,要么加一个控件,要么去掉一个控件。
settings.custom 是留给你自己定义变量的自由区域:
{
"settings": {
"custom": {
"lineHeight": { "body": 1.6 },
"shadow": { "card": "0 1px 3px rgba(0,0,0,0.12)" }
}
}
}
它们会变成 --wp--custom--line-height--body 和 --wp--custom--shadow--card。注意这个转换规则:驼峰命名会被拆成短横线连接,嵌套层级之间用双短横线拼接。变量名写错,是数值悄无声息地回退到默认值的常见原因。
你还可以按区块划分 settings,想在按钮上开放拾色器、在段落上关掉,就靠这招:
{
"settings": {
"blocks": {
"core/paragraph": {
"color": { "custom": false, "customGradient": false }
}
}
}
}
styles:真正渲染出来的东西
styles 才是数值真正生效的地方。它的结构和编辑器一致:先是根级别,然后是 elements,再然后是 blocks。
{
"styles": {
"color": {
"background": "var(--wp--preset--color--base)",
"text": "var(--wp--preset--color--ink)"
},
"typography": {
"lineHeight": "var(--wp--custom--line-height--body)"
},
"elements": {
"link": {
"color": { "text": "var(--wp--preset--color--brand)" },
":hover": { "typography": { "textDecoration": "none" } }
}
},
"blocks": {
"core/quote": {
"typography": { "fontStyle": "italic" },
"spacing": { "padding": { "left": "var(--wp--preset--spacing--40)" } }
}
}
}
}
根级别的 styles 落在 body 上。elements 管的是那些不属于区块的 HTML 基础元素——链接、按钮、标题、图注。blocks 则按名称精确指向某一类区块。
说句实在话:styles 表达不了 CSS 能表达的一切。你没法自己写媒体查询,没有复杂选择器,没有动画,支持的伪类状态也只有很少几个。流式排版和基于布局的尺寸控制确实替代了不少原来靠断点做的事,但不是全部。绝大多数正式上线的区块主题都是 theme.json 加一份不大的样式表,这很正常,不代表你没做好。
settings 和 styles 之间那个坑
技术支持里最常见的问题,翻来覆去都是同一句:“我加了品牌色,可什么都没变。”
没错,本来就不会变。把颜色加进 settings.color.palette,只是创建了 --wp--preset--color--brand 和一个色块,它什么都没应用。想让它成为默认的链接颜色,你还得在 styles 里写一遍:
{
"settings": {
"color": {
"palette": [{ "slug": "brand", "color": "#1f4ed8", "name": "Brand" }]
}
},
"styles": {
"elements": {
"link": { "color": { "text": "var(--wp--preset--color--brand)" } }
}
}
}
可以这么理解:settings 是你递给用户的那盒颜料,styles 是他们进门之前你自己先刷好的那一层。
层叠顺序,从低到高
正是这一节能解释为什么有的站点“无视”了主题文件。WordPress 在输出 CSS 之前会合并好几层:
- WordPress 核心默认值 —— 核心自带一份
theme.json,里面有一套基础调色板、字号和间距刻度。 - 父主题的
theme.json - 子主题的
theme.json—— 逐个键合并到父主题之上,而不是整份文件覆盖。 - 数据库里保存的全局样式 —— 也就是站点编辑器“样式”面板写入的内容。
- 单个区块上的样式 —— 存在文章内容里的属性,渲染成内联样式或类名。
第 4 层就是把站点搞坏的那一层。只要有人打开“样式”面板并保存,WordPress 就会存下一条全局样式记录,这条记录在它涵盖的每一个属性上都胜过你的文件。此后你再改 theme.json,那些属性在页面上不会有任何变化——部署完站点看起来一模一样,可磁盘上的文件明明改了。
正确的做法是清掉那条保存的记录,而不是升级用 !important 硬顶。打开站点编辑器,进入“样式”,打开修订面板,重置为主题默认值。动手前先跟客户或站点所有者确认一句——重置会丢掉他们做过的定制,这是实打实的代价,不是免费操作。
第二个嫌疑对象是缓存。WordPress 会缓存 theme.json 的解析结果,而不是每次请求都重新读文件;这在生产环境里正是你想要的,在开发时正是你不想要的。开启 WP_DEBUG,或者在较新版本里打开主题开发模式设置,都能让 WordPress 每次重新读取该文件。如果你的改动非要等到部署或清缓存之后才出现,那就是这个原因。
templateParts 和 customTemplates
这两个都是数组,用来描述主题里已经存在的文件。
{
"templateParts": [
{ "name": "header", "title": "Header", "area": "header" },
{ "name": "footer", "title": "Footer", "area": "footer" }
],
"customTemplates": [
{ "name": "page-wide", "title": "Wide Page", "postTypes": ["page"] }
]
}
name 对应去掉扩展名的文件名——parts/header.html、templates/page-wide.html。area 的值很关键:它决定这个部件会不会被包进 header 或 footer 这样的地标元素里,以及编辑器如何给它归类。不写的话,这个部件就算未分类。
customTemplates 决定了一个模板能不能出现在文章编辑器的模板下拉框里。没有这一条,文件是存在的,但谁也选不到它。
一个快速排查顺序
当区块主题的表现和文件里写的不一致时,先按这个顺序走一遍,再去动别的:
- 查看网页源代码,搜索生成的全局样式那一段。如果你的变量根本不在里面,问题出在
settings或者 slug 拼错了。 - 如果变量在,但没人用它,问题是缺少对应的
styles条目。 - 如果两边都有,却被别的东西盖住了,去查有没有保存下来的全局样式记录。
- 走完上面三步,再去看你自己的样式表、插件,或者某个页面构建器在层叠顺序后面注入的 CSS。
FAQ
常见问题
WordPress 里的 theme.json 是什么?
它是区块主题根目录下的一个 JSON 配置文件,管两件事:区块编辑器向用户开放哪些设计选项,以及默认样式是什么。WordPress 会读取它,据此生成 CSS 自定义属性和一份样式表,再和核心默认值、用户在站点编辑器里保存的全局样式合并到一起。
theme.json 里的 settings 和 styles 有什么区别?
settings 定义有哪些东西可用,它会生成 CSS 变量和编辑器控件,但自己不会应用任何样式。styles 才是真正用这些变量把值套上去的地方。往调色板里加一个颜色,在你于 styles 里引用它、或者用户在编辑器里手动选中它之前,页面上不会有任何变化。
为什么我改了 theme.json,前台却没反应?
十有八九是数据库里保存的全局样式盖过了 theme.json。只要有人动过站点编辑器的“样式”面板,那条保存下来的记录就会在它涵盖的每一个属性上胜出。打开“样式”,用修订面板重置回主题默认值,然后刷新页面。第二个嫌疑对象是 theme.json 解析结果的缓存。
有了 theme.json,还需要 style.css 吗?
需要。WordPress 靠 style.css 顶部的主题头部注释来获取主题名称、版本等元数据,没有它主题根本不会被识别。除此之外这个文件可以留空,把设计决策全部放进 theme.json,也可以保留少量 CSS 来处理 theme.json 表达不了的东西。
theme.json 的 version 该写多少?
写你所支持的最低 WordPress 版本能理解的最高版本号。version 这个键告诉 WordPress 按哪套 schema 结构来解析,而旧版 WordPress 会直接忽略它不认识的键。如果你要兼容老站点,就停在它们支持的那个版本上,别指望新键会优雅降级。
theme.json 能完全取代 CSS 吗?
不能。它在预设、区块默认值、元素样式、布局宽度和间距这些方面表现很好,但它没有让你自己编写媒体查询的概念,也不支持复杂选择器、动画,以及它内置支持之外的伪类状态。大多数正式上线的区块主题都是 theme.json 加上一份不大的样式表。