跳到正文
主题

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/ 里的部件,以及它们属于哪个区域。
customTemplatestemplates/ 里的页面模板注册到文章编辑器中。

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.customFontSizesettings.spacing.customSpacingSizesettings.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 之前会合并好几层:

  1. WordPress 核心默认值 —— 核心自带一份 theme.json,里面有一套基础调色板、字号和间距刻度。
  2. 父主题的 theme.json
  3. 子主题的 theme.json —— 逐个键合并到父主题之上,而不是整份文件覆盖。
  4. 数据库里保存的全局样式 —— 也就是站点编辑器“样式”面板写入的内容。
  5. 单个区块上的样式 —— 存在文章内容里的属性,渲染成内联样式或类名。

第 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.htmltemplates/page-wide.htmlarea 的值很关键:它决定这个部件会不会被包进 header 或 footer 这样的地标元素里,以及编辑器如何给它归类。不写的话,这个部件就算未分类。

customTemplates 决定了一个模板能不能出现在文章编辑器的模板下拉框里。没有这一条,文件是存在的,但谁也选不到它。

一个快速排查顺序

当区块主题的表现和文件里写的不一致时,先按这个顺序走一遍,再去动别的:

  1. 查看网页源代码,搜索生成的全局样式那一段。如果你的变量根本不在里面,问题出在 settings 或者 slug 拼错了。
  2. 如果变量在,但没人用它,问题是缺少对应的 styles 条目。
  3. 如果两边都有,却被别的东西盖住了,去查有没有保存下来的全局样式记录。
  4. 走完上面三步,再去看你自己的样式表、插件,或者某个页面构建器在层叠顺序后面注入的 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 加上一份不大的样式表。