一个属于 WordPress 设计系统的主题包。它包含两个部分:

  • 设计令牌(Design Tokens):一套全面的设计令牌系统,用于颜色、间距、排版等。
  • 主题系统(Theme System):一个灵活的主题提供器,用于在多个应用中保持一致的主题。

该包不是 WordPress 区块主题、站点主题或 theme.json API。它为 JavaScript 包和应用提供 WordPress 设计系统的设计令牌和 React 主题原语。

文档

本 README 是包使用者的入口。它介绍如何加载设计令牌、使用 ThemeProvider,以及配置该包的开发工具。

  • 要使用设计令牌和 ThemeProvider,请从这里开始。
  • 要选择合适的设计令牌或浏览所有可用令牌,请参阅生成的设计令牌参考。
  • 要编辑令牌源文件,请参阅设计令牌维护者指南。

公共 API

入口支持的用法
@wordpress/themeThemeProvider 和生成的令牌刻度类型,例如 PaddingSize 和 GapSize。如下所示,从组件派生提供者属性和回调类型。
@wordpress/theme/design-tokens.css默认的语义 --wpds-* 自定义属性。每个文档加载一次。
@wordpress/theme/design-tokens.js默认导出,包含语义 CSS 自定义属性名称列表。它包含的是名称,而不是令牌值,并且不会加载样式。
构建插件子路径列在 构建插件 下的四个公共集成。
Stylelint 插件子路径列在 Stylelint 插件 下的三个公共规则。

privateApis 导出的存在是为了临时兼容较旧的 WordPress 打包产物。它不是受支持的使用者 API。令牌源 JSON、色阶构建器、生成的回退映射,以及包的 exports 中未列出的其他路径,都属于实现细节。

由 WordPress 提供的运行时 API 遵循其向后兼容策略。安装较新的 npm 包不会升级由 WordPress 提供的运行时。在使用运行时 API 之前,请检查目标 WordPress 版本。工具子路径使用已安装的 npm 包。移除或重命名语义令牌属于兼容性变更;有意义的值变更应记录在变更日志中。

要在开发工具中检查令牌名称:

import tokenNames from '@wordpress/theme/design-tokens.js';

const isKnownToken = tokenNames.includes( '--wpds-dimension-gap-sm' );

设计令牌

设计令牌是命名值,用于描述某个值的视觉用途。不要硬编码诸如 #3858e9 或 16px 这样的值,而应使用语义自定义属性,例如 --wpds-color-background-interactive-brand-strong 或 --wpds-dimension-padding-2xl。

设计令牌参考介绍了命名模式、如何选择令牌,以及生成的完整可用设计令牌列表。

使用设计令牌

设计令牌通过 CSS 自定义属性提供(例如 var(--wpds-color-foreground-content-neutral))。要使用它们,页面必须加载一个定义令牌值的样式表。

可以使用 ThemeProvider 组件为应用的特定部分自定义颜色等令牌值。

交付模型

设计系统将令牌交付拆分为两个互补层:

  • 静态样式表(design-tokens.css)——在文档 :root 上为每个 --wpds-* 自定义属性定义默认值。每个文档加载一次(主页面,以及你渲染 React 的每个 iframe)。即使在任何 JavaScript 运行之前,它也能提供可用的基线。
  • 运行时 <ThemeProvider>——在静态默认值之上,为子树应用按实例的覆盖。用它来覆盖单个设置(例如 color.primary、cursor.control)。

在 WordPress 中

在标准 WordPress 管理界面和编辑器界面中,样式表会由系统代管。WordPress 将设计令牌样式表注册为 wp-theme 样式句柄,并在 WordPress 管理界面和编辑器包已经依赖共享基础样式的位置加载它。这包括管理页面和区块编辑器的内容 iframe。

如果你的插件在这些由 WordPress 管理的样式依赖之外渲染单独的应用外壳、iframe、弹出窗口或包产物,请在该文档中入队加载 wp-theme 样式表,而不是将 @wordpress/theme/design-tokens.css 打包进你的插件。有关如何指定样式表依赖项,请参阅 wp_enqueue_style 文档。

在 WordPress 外

在 WordPress 外部,请安装并加载设计令牌样式表,以支持完整的主题能力:

npm install @wordpress/theme
import '@wordpress/theme/design-tokens.css';

该包的 JavaScript 入口仅支持 ESM,并且需要 Node.js ^20.19.0 或 >=22.13.0。请在 ESM 或 TypeScript 配置文件中使用 import 语法。

TypeScript 使用者需要 TypeScript 5 或更高版本。

此样式表是通用的,没有单独的 RTL 版本。

如果你的应用将 React 内容渲染到其他文档(iframe、弹出窗口等),这些文档中的每一个都需要在自己的 <head> 中加载同一份样式表。请参阅 跨文档(iframe 和其他传送门)。

开发者工具

使用 Stylelint 插件验证令牌用法,并使用 构建插件注入生成的回退值。安装 @wordpress/theme 后,@wordpress/build 会自动启用构建插件。

无障碍

语义颜色令牌经过设计,使设计系统使用的默认前景/背景配对满足 WCAG AA 文本对比度。组件仍需为其渲染的 UI 状态选择正确的语义配对,并且不得仅依赖颜色来传达信息。

设计令牌不能替代组件级别的无障碍处理:

  • 强制颜色:当原生高对比度渲染可能会隐藏边框、图标、焦点环或状态指示器时,组件仍负责提供 forced-colors 覆盖。
  • 减少动态效果:动效令牌提供共享的持续时间和缓动曲线,但使用这些令牌的组件仍负责为非必要动画遵循 prefers-reduced-motion。

例如,仅在用户未请求减少动态效果时定义非必要过渡:

@media not ( prefers-reduced-motion ) {
    .example {
        transition-property: opacity;
        transition-duration: var( --wpds-motion-duration-md );
        transition-timing-function: var( --wpds-motion-easing-subtle );
    }
}

ThemeProvider

ThemeProvider 是一个 React 组件,应包裹你的应用,以便向子级 UI 组件提供设计令牌和主题上下文。它接受一组可自定义的种子值,并自动生成一组设计令牌;这些令牌会以 CSS 自定义属性的形式暴露出来,供整个应用使用。

import { ThemeProvider } from '@wordpress/theme';

function App() {
    return (
        <ThemeProvider color={ { primary: 'blue' } }>
            { /* 你的应用内容 */ }
        </ThemeProvider>
    );
}

当省略某个设置时,它会继承最近的父级 ThemeProvider。如果没有父级值,则应用 design-tokens 样式表中的预构建默认值。

ThemeProvider 不接受包装器自定义属性,例如 className、style、as、render 或 ref。

color 属性接受一个包含以下可选属性的对象:

  • primary:主色/强调种子颜色(默认:'#3858e9')。
  • background:背景种子颜色(默认:'#fcfcfc')。

两个属性都接受完全不透明且可被 sRGB 解析的字符串:十六进制值(例如 #3858e9)、rgb()/rgba() 字符串,或 CSS 命名颜色(例如 'blue')。非完全不透明的值、transparent 以及其他 CSS 颜色空间(例如 hsl()、oklch()、lab())均不被接受,并且会抛出错误。

种子值用于指导生成的色阶,但不保证会原样出现在最终令牌中。色阶构建器可能会调整其亮度以满足对比度目标。

使用 onColorWarnings 在提供者计算颜色后接收结构化警告。警告会标识未达到对比度目标的已生成色阶步骤或语义前景/背景对:

<ThemeProvider
    color={ {
        primary: '#ff0000',
        background: '#0000ff',
    } }
    onColorWarnings={ ( warnings ) => {
        // 为你的用户格式化或显示这些警告。
    } }
>
    { /* 你的应用内容 */ }
</ThemeProvider>

该回调会报告未通过的情况,这些情况来自生成的色阶检查和一组已定义的语义前景/背景对。它不会验证所有可能的令牌配对。当所有受检查目标均满足时,它会收到一个空数组。色阶警告会标识受影响的色阶和步骤。对比度警告会标识语义前景/背景令牌对,并包含所需对比度和实际达到的对比度值。在严格模式下的开发环境中,React 可能会多次调用该回调。

TypeScript 属性和警告

应从公共组件派生类型,而不是导入包内部实现:

import type { ComponentProps } from 'react';
import { ThemeProvider } from '@wordpress/theme';

type ThemeProviderProps = ComponentProps< typeof ThemeProvider >;
type OnColorWarnings = NonNullable< ThemeProviderProps[ 'onColorWarnings' ] >;
type ThemeProviderColorWarning = Parameters< OnColorWarnings >[ 0 ][ number ];

const onColorWarnings: OnColorWarnings = ( warnings ) => {
    for ( const warning of warnings ) {
        if ( warning.type === 'contrast' ) {
            console.log( warning.foregroundToken, warning.achievedContrast );
        } else {
            console.log( warning.ramp, warning.step );
        }
    }
};

派生的警告类型是一个联合类型。在访问特定于色阶或对比度警告的字段之前,请先检查 type。以相同方式派生各个设置,例如 ThemeProviderProps[ 'cornerRadius' ]。

光标和圆角半径

cursor 属性接受一个包含以下可选属性的对象:

  • control:非链接交互控件(例如按钮、复选框和开关)的光标样式。接受 'default' 或 'pointer'(默认值:'pointer')。

cornerRadius 属性为主题子树设置整体圆角预设。接受 'none'(直角)、'subtle'、'moderate' 或 'pronounced'(最圆润)(默认值:'subtle')。它会按比例缩放提供者子树的原始 --wpds-border-radius-* 令牌。该预设设置的是整体圆角程度,而不是单个 border-radius 令牌尺寸。

浅色和深色主题

ThemeProvider 没有浅色或深色模式。它接受颜色种子,并将其转换为语义设计令牌。使用者通过选择这些种子来定义浅色和深色主题:浅色背景种子会使生成的色阶偏向更深的对比色,而深色背景种子会使其偏向更浅的对比色。primary 种子控制强调色,而不是整体浅色或深色外观。

嵌套提供者

提供者可以递归使用,以覆盖或修改特定子树的主题。

<ThemeProvider color={ { background: 'white' } }>
    { /* 浅色主题 UI 组件 */ }
    <ThemeProvider color={ { background: '#1e1e1e' } }>
        { /* 深色主题 UI 组件 */ }
        <ThemeProvider color={ { primary: 'red' } }>
            { /* 带红色强调色的深色主题 */ }
        </ThemeProvider>
    </ThemeProvider>
    { /* 浅色主题 UI 组件 */ }
</ThemeProvider>

ThemeProvider 会重新定义部分设计系统令牌。使用语义设计系统令牌的组件会自动跟随所选主题。请注意,这些令牌使用 CSS 级联定义和继承,因此依赖的是 DOM 树,而不是 React 树。这在使用 React portals 时非常重要。

isRoot 与所在文档

默认情况下,<ThemeProvider> 输出的样式会限定在提供者的包裹 <div> 内,因此覆盖仅应用于其子树。

设置 isRoot 还会将这些覆盖提升到所在文档的 :root,因此渲染到该文档中的任何内容——包括通过 portal 渲染到提供者 React 树之外的浮层——都会获取这些覆盖。

<ThemeProvider color={ { primary: '#a00' } } isRoot>
    { /* …应用… */ }
</ThemeProvider>

在应用或页面的顶层提供者上使用 isRoot。对于渲染到独立文档(iframe、弹出窗口)中的最顶层提供者,这也是推荐模式。

每个文档最多渲染一个根提供者。不支持多个共享同一文档的 isRoot 提供者,因为每个提供者都会尝试定义文档级令牌值。省略 isRoot 时,嵌套和同级提供者仍可正常使用,独立文档可以各自拥有自己的根提供者。

静态设计令牌样式表仍提供默认值;只有当你希望 <ThemeProvider> 的覆盖作用于整个文档时,才需要 isRoot。

跨文档(iframe 和其他 portal)

当你将 React 内容渲染到另一个文档(通常是 iframe)时,要使设计令牌在该文档中正常工作,必须满足两个条件:

  1. 设计令牌样式表必须存在于文档的 <head> 中。这是定义每个 --wpds-* 自定义属性的静态 :root 块。

    在 WordPress 中,它会自动为后台页面和区块编辑器的内容 iframe 注册。

    对于自定义 iframe,使用者有责任在 iframe 文档中加载它。在 WordPress 中,为该文档注册 wp-theme 样式表。在 WordPress 之外,从 iframe 已加载的样式表中导入 @wordpress/theme/design-tokens.css,或直接注入 CSS 字符串。

  2. 动态注入的组件样式会被路由到 iframe 文档。一些 @wordpress/components 样式是在运行时注入文档的,而不是作为静态 CSS 提供——例如基于 Emotion 的样式,以及使用 @wordpress/build 构建的 CSS 模块样式。StyleProvider 会告诉该机制注入到哪个文档的 <head>。将 iframe 子树包裹在 <StyleProvider document={ iframeDocument }> 中。

标准模式会将两者与 <ThemeProvider isRoot> 结合使用,以将任何覆盖应用到 iframe 的 :root:

import { __experimentalStyleProvider as StyleProvider } from '@wordpress/components';
import { ThemeProvider } from '@wordpress/theme';
import { createPortal } from 'react-dom';

function IframeContent( { iframeDocument, children } ) {
    return createPortal(
        <StyleProvider document={ iframeDocument }>
            <ThemeProvider isRoot color={ { primary: '#a00' } }>
                { children }
            </ThemeProvider>
        </StyleProvider>,
        iframeDocument.body
    );
}

iframe 内部的静态样式表提供所有默认值;<ThemeProvider isRoot> 会在其上方添加(或省略)覆盖,与主文档中的行为完全一致。

旧版兼容

公共令牌接口是 设计令牌参考 中记录的语义 --wpds-* 自定义属性。

@wordpress/theme 也可能为现有 WordPress 后台和 @wordpress/components 内部实现维护旧版兼容别名。这些别名是过渡性实现细节,包括 --wp-components-* 命名空间,不是面向使用者的受支持 API。新代码应改用语义 --wpds-* 设计令牌。

构建

该包分两个步骤构建。在仓库根目录运行 npm run build 时,它会首先运行该包的“prebuild”步骤,该步骤由此包 package.json 中的 build 脚本定义。

该步骤将:

  1. 生成默认色阶。
  2. 生成原始令牌。
  3. 构建 CSS 和 JavaScript 令牌文件。
  4. 更新设计令牌文档。
  5. 格式化所有生成的文件。

此步骤生成的所有文件都将提交到仓库。

在 prebuild 步骤之后,该包会通过仓库的标准包构建脚本构建为最终形式。

Stylelint 插件

这些规则会验证 CSS 中的设计令牌用法。请在你的 Stylelint 配置中启用它们:

{
    "plugins": [
        "@wordpress/theme/stylelint-plugins/no-unknown-ds-tokens",
        "@wordpress/theme/stylelint-plugins/no-setting-wpds-custom-properties",
        "@wordpress/theme/stylelint-plugins/no-token-fallback-values"
    ],
    "rules": {
        "plugin-wpds/no-unknown-ds-tokens": true,
        "plugin-wpds/no-setting-wpds-custom-properties": true,
        "plugin-wpds/no-token-fallback-values": true
    }
}

plugin-wpds/no-unknown-ds-tokens

报告对未知 --wpds-* 令牌的引用。

plugin-wpds/no-setting-wpds-custom-properties

报告在 --wpds-* 命名空间中的定义或覆盖。

plugin-wpds/no-token-fallback-values

报告可能与生成的值产生偏差的手动回退值。

构建插件

构建插件会将生成的回退值注入到不带回退值的 var(--wpds-*) 引用中,以便在设计令牌样式表不可用时组件仍然能够渲染。例如,var(--wpds-color-foreground-content-neutral) 会变为 var(--wpds-color-foreground-content-neutral, #1e1e1e)。

当安装了 @wordpress/theme 时,@wordpress/build 已经会自动应用这些插件。你只需要为自定义构建设置手动配置它们。

导出工具适用范围
@wordpress/theme/postcss-plugins/postcss-ds-token-fallbacksPostCSSCSS
@wordpress/theme/lightningcss-plugins/lightningcss-ds-token-fallbacksLightning CSSCSS
@wordpress/theme/esbuild-plugins/esbuild-ds-token-fallbacksesbuildJS/TS
@wordpress/theme/vite-plugins/vite-ds-token-fallbacksViteJS/TS

现有的回退值保持不变。在转换后的值中,如果不含回退值的引用包含未知令牌,将导致构建失败。

JavaScript 插件会将字符串值、JSX 属性值和静态模板部分中的令牌引用视为 CSS。这包括带标签模板,例如 String.raw。它们不会更改注释、正则表达式、属性名、模块路径、JSX 文本和 TypeScript 类型。不会解析通过模板表达式拼接而成的令牌名。与之前一样,运行时消息字符串中的令牌引用也会被视为 CSS。

JavaScript 解析器无法读取的文件将保持不变,交由下游编译器处理。若要在使用自定义语法的 Vite 文件中添加回退值,请配置语法剥离插件,设置 enforce: 'pre',并将其列在令牌回退插件之前。

这两个 JavaScript 插件都会保留 source map。Vite 插件会在 JavaScript 和 TypeScript 编译之前运行,并支持带有查询字符串的模块 ID。它会跳过 ?raw 和 ?url 导入,因此这些导入导出的文件内容和 URL 保持不变。

PostCSS

// postcss.config.mjs
import dsTokenFallbacks from '@wordpress/theme/postcss-plugins/postcss-ds-token-fallbacks';

export default {
    plugins: [ dsTokenFallbacks ],
};

Lightning CSS

import { transform, composeVisitors } from 'lightningcss';
import dsTokenFallbacks from '@wordpress/theme/lightningcss-plugins/lightningcss-ds-token-fallbacks';

const { code } = transform( {
    filename: 'styles.css',
    code: Buffer.from( css ),
    visitor: composeVisitors( [ dsTokenFallbacks ] ),
} );

该访问器在添加回退值时,会保留 CSS Modules 的 from global 引用,例如 var(--wpds-dimension-gap-sm from global)。生成的回退值中的自定义属性(包括 --wp-admin-* 变量)也保持全局,因此管理端覆盖仍然生效。

esbuild

import dsTokenFallbacks from '@wordpress/theme/esbuild-plugins/esbuild-ds-token-fallbacks';

await esbuild.build( {
    plugins: [ dsTokenFallbacks ],
    // …
} );

Vite

Vite 配置同时使用 Vite 插件(用于 JS/TS)和 PostCSS 插件(用于 CSS):

// vite.config.ts
import dsTokenFallbacks from '@wordpress/theme/postcss-plugins/postcss-ds-token-fallbacks';
import dsTokenFallbacksJs from '@wordpress/theme/vite-plugins/vite-ds-token-fallbacks';

export default defineConfig( {
    plugins: [ dsTokenFallbacksJs() ],
    css: {
        postcss: {
            plugins: [ dsTokenFallbacks ],
        },
    },
} );

为此包做出贡献

这是一个独立包,是 Gutenberg 项目的一部分。该项目以 monorepo 形式组织。它由多个自包含的软件包组成,每个包都有特定用途。此 monorepo 中的包发布到 npm,并被 WordPress 以及其他软件项目使用。

若要了解更多关于参与此包或整个 Gutenberg 项目的信息,请阅读项目的主要 贡献者指南。