社区新闻

在 theme.json 中添加和使用自定义设置

查看官方原文 ↗ 发布于

theme.json 是我最喜欢的特性之一,它支持自定义设置。这是在区块系统之上构建的最强大方式之一,但主题社区往往未能充分利用它。

与大多数其他 theme.json 属性不同,自定义设置并不与界面中的控件或用户可以交互的东西绑定。甚至“设置”这个词在思考其工作原理时都有些牵强。

你实际上是在配置 WordPress 将在编辑器中和前端输出的 CSS 自定义属性(即 CSS 变量)。

让我们花些时间一起探索自定义设置如何工作以及它们能让你做什么。然后我们将通过一个实际示例进行讲解。最后,我希望你能看到其可能性,并开始突破你在自己主题中能为用户提供的功能的极限。

自定义设置如何工作

假设你想为整个网站使用一个全局的文本阴影值。在传统的样式表中,你可能会这样编写 CSS:

body {
	--text-shadow: 2px 2px 2px rgba( 0, 0, 0, 0.3 );
}

如果你想,继续这样做完全没问题,但我建议采用 WordPress 的方式

那么让我们试试看。首先,将此代码添加到你的 theme.json 文件中:

{
	"$schema": "https://schemas.wp.org/trunk/theme.json",
	"version": 2,
	"settings": {
		"custom": {
			"textShadow": "2px 2px 2px rgba( 0, 0, 0, 0.3 )"
		}
	}
}

WordPress 将自动为你生成 CSS 自定义属性,并在前端和编辑器中加载它。生成的 CSS 代码将是:

body {
	--wp--custom--text-shadow: 2px 2px 2px rgba( 0, 0, 0, 0.3 );
}

你的自定义 CSS 与 WordPress 生成的 CSS 之间的区别? CSS 自定义属性的名称。

这与来自 theme.json 的其他预设(自定义字体大小、间距比例、颜色等)的命名方式一致。例如,调色板中的颜色将获得 CSS 自定义属性名称 --wp--preset--color--{$slug}

简而言之,此功能创建遵循标准命名约定的 CSS 自定义属性。

WordPress 会在生成的 CSS 中自动将驼峰式属性名转换为连字符形式。所以 textShadow 变成了 text-shadow。请注意,数字也被视为与大写字母相同。

为什么要遵循 WordPress 约定?

你可能想知道,如果它所做的只是创建 CSS 自定义属性,为什么还要遵循 WordPress 标准。我的意思是,你自己完全可以做到,对吧?

一个好的经验法则是尽可能使用 WordPress 的内置功能。因为这通常能让你获得更多的功能和工具。自定义设置也不例外。

让我们深入一些例子。我相信你会看到好处!

在全局样式变体和子主题中覆盖

自定义设置使得通过全局样式变体和子主题覆盖值变得非常简单。

还记得上面的文本阴影例子吗?尝试从主题的变体中覆盖它。创建一个新的 /styles/example.json 文件,并向其中添加此 JSON 代码:

{
	"$schema": "https://schemas.wp.org/trunk/theme.json",
	"version": 2,
	"title": "Example",
	"settings": {
		"custom": {
			"textShadow": "2px 2px 2px rgba( 0, 0, 0, 0.7 )"
		}
	}
}

一旦你通过外观 > 编辑器 > 样式保存了“Example”样式变体,WordPress 将使用 /styles/example.json 中的 settings.custom.textShadow 值并输出此 CSS:

body {
	--wp--custom--text-shadow: 2px 2px 2px rgba( 0, 0, 0, 0.7 );
}

覆盖只是开始。你还可以为(并在)你的自定义变体或子主题定义全新的属性。

按区块自定义属性

采用 WordPress 的方式还允许你为特定区块编写自定义属性。你甚至可以在区块级别覆盖自己的全局设置。

假设你想在使用标题块时更改文本阴影属性。为此,请定位 settings.blocks[core/heading].custom.textShadow。试试看:

{
	"$schema": "https://schemas.wp.org/trunk/theme.json",
	"version": 2,
	"settings": {
		"custom": {
			"textShadow": "2px 2px 2px rgba( 0, 0, 0, 0.3 )"
		},
		"blocks": {
			"core/heading": {
				"custom": {
					"textShadow": "2px 2px 2px rgba( 0, 0, 0, 0.7 )"
				}
			}
		}
	}
}

WordPress 将生成此 CSS:

body {
	--wp--custom--text-shadow: 2px 2px 2px rgba( 0, 0, 0, 0.3 );
}

.wp-block-heading {
	--wp--custom--text-shadow: 2px 2px 2px rgba( 0, 0, 0, 0.7 );
}

过滤 theme.json 数据

对于更高级的用例,你可以通过 theme.json 相关的过滤器钩子和函数访问这些设置。例如,你可以过滤 wp_theme_json_data_theme 来动态修改数据。

如何使用服务器端过滤器修改 theme.json 数据中了解更多信息。

将自定义设置与区块插件集成

一些区块插件允许你的主题为 CSS 自定义属性定义默认值。当然,插件必须使用自定义属性,并以与 theme.json 兼容的方式命名它们。

假设你想添加对一个名为 Super Duper 的第三方区块的支持。它的 CSS 有几个自定义属性——用于设置区块的默认高度和应用 CSS 滤镜:

.wp-block-super-duper {
	height: var( --wp--custom--super-duper--height, 1rem );
	filter: var( --wp--custom--super-duper--filter, blur( 8px ) );
}

在做任何其他事情之前,去感谢那位插件作者考虑了主题兼容性,并为 CSS 自定义属性添加了命名空间。

如果那个第三方区块存在,你可以在你的 theme.json 中自定义它的 heightfilter 属性:

{
	"$schema": "https://schemas.wp.org/trunk/theme.json",
	"version": 2,
	"settings": {
		"custom": {
			"superDuper": {
				"height": "1.5rem",
				"filter": "grayscale( 100% )"
			}
		}
	}
}

我还没有在野外看到很多使用这种技术的区块插件,但确实有一些。我希望这能成为区块开发的一个标准。

你是区块开发者吗?主题作者喜欢你能让他们轻松地与你的代码集成。要了解更多关于此技术的信息,请阅读样式化区块:用 CSS 自定义属性赋能用户中的“关于主题兼容性的说明”。

一个实际用例:样式化表单输入

你可以在 theme.json 中通过样式化一些基础元素和区块走得很远。但你仅限于 WordPress 核心提供的设计工具。如果你想做更多,那就是使用自定义设置的时候了。

这实际上是我的一个痛点:theme.json 不支持使用标准设计功能样式化表单元素

有一些贡献者正在努力改进表单处理。但如果你面临截止日期,你不能等待它出现在某个版本中。所以,让我们通过 settings.custom 来逐步添加一些基础支持。

在 theme.json 中创建自定义属性

为了防止代码示例失控,我们只使用几种颜色。我将向你展示如何为 <input><select><textarea> 元素添加文本、背景和边框颜色。当然,你可能会将其扩展到排版、阴影或 CSS 规范中的任何其他内容。

首先,挑选出你想使用的三种颜色。我选择混合的灰色:

一个登录表单示例,设置在蓝色背景上。表单本身是白色的,带有灰色的输入字段。

将你的颜色添加到 theme.json 中的一个自定义 settings.custom.formInput 对象下:

{
	"$schema": "https://schemas.wp.org/trunk/theme.json",
	"version": 2,
	"settings": {
		"custom": {
			"formInput": {
				"color": "#000000",
				"background": "#f1f5f9",
				"borderColor": "#e2e8f0"
			}
		}
	}
}

你在代码中注意到新东西了吗? 在之前的例子中,你将自定义属性和值直接嵌套在 settings.custom 对象中。这段 theme.json 代码添加了另一个嵌套对象,将表单输入的所有 CSS 自定义属性分组在一起。

这是 WordPress 将输出的 CSS:

body {
	--wp--custom--form-input--color: #000000;
	--wp--custom--form-input--background: #f1f5f9;
	--wp--custom--form-input--border-color: #e2e8f0;
}

WordPress 会生成具有适当嵌套层次的属性名。它按顺序添加所有正确的层级,并用双连字符分隔。

你可以在 settings.custom 内部添加任意多层的嵌套。但你可能不应该嵌套得太深。层级越多,你的 CSS 自定义属性名称就越长、越复杂。

在 CSS 中使用自定义属性

下一步是使用这些自定义属性。这意味着在某个 CSS 文件中添加一些代码。在本教程中,我假设你正在为主题的主要 style.css 文件加载任何样式需求。

所以将此代码添加到你的主题的 style.css 中:

input:where( :not( [type=checkbox] ):not( [type=radio ] ) ),
select,
textarea {
	color: var( --wp--custom--form-input--color, inherit );
	background: var( --wp--custom--form-input--background, transparent );
	border: 1px solid var( --wp--custom--form-input--border-color, currentColor );
	/* 其他 CSS 规则... */
}

当然,你可能需要充实该 CSS 以获得更好的整体表单样式。此代码仅向你展示如何使用你定义的自定义属性。

我正在使用核心的登录/登出区块测试这个(在登出状态下以显示表单)。也许这是一个糟糕的例子选择,因为截至 WordPress 6.3,其默认样式——或者说缺乏样式——有点混乱

我们不妨趁此机会处理一下。使用此 CSS 使登录/登出区块的布局可用:

.wp-block-loginout form {
	display: grid;
	grid-template-columns: repeat( 1, 1fr );
	gap: var( --wp--style--block-gap );
}

.wp-block-loginout form > * {
	margin: 0;
}

.wp-block-loginout form input:not([type=submit]):not([type=checkbox]):not([type=hidden]) {
	box-sizing: border-box;
	display: block;
	width: 100%;
}

这只修复了区块的布局问题。我将把任何其他样式规则留给你。

在子主题或样式变体中覆盖

尝试通过子主题的 theme.json 文件或主题的 /styles 文件夹中的全局样式变体进行一些调整。两种方法都会产生相同的结果。

我将此 JSON 代码添加到我一个子主题的 theme.json 中:

{
	"$schema": "https://schemas.wp.org/trunk/theme.json",
	"version": 2,
	"settings": {
		"custom": {
			"formInput": {
				"background": "#f0ebe4",
				"borderColor": "#e9e1d8"
			}
		}
	}
}

如果你仔细观察代码,你会发现我省略了父主题 theme.json 中定义的 settings.formInput.color 属性。这是故意的。WordPress 的 theme.json 系统是分层的,因此属性会从层次结构中的父级继承。

WordPress 将生成此 CSS,从父级和子级 theme.json 文件中提取数据:

body {
	--wp--custom--form-input--color: #000000;
	--wp--custom--form-input--background: #f0ebe4;
	--wp--custom--form-input--border-color: #e9e1d8;
}

当子主题激活时,表单将如下所示:

一个登录表单示例,设置在棕红色背景上。表单本身是浅棕色,输入字段颜色稍深。

这种方法已迅速成为我在主题中添加自定义样式规则时的首选技术之一。我总是希望子主题作者有一种简单的方法来修改这些自定义设置。这是一个强大的功能,提供了极大的灵活性。所以,在你的主题中试试看吧。