Gutenberg Block Selectors API:三级 CSS 选择器定制机制详解
【免费下载链接】gutenbergThe Block Editor project for WordPress and beyond. Plugin is available from the official repository.项目地址: https://gitcode.com/GitHub_Trending/gu/gutenberg
Block Selectors 是 Gutenberg(WordPress 区块编辑器项目)中允许区块自定义其样式生成所用 CSS 选择器的 API。它在区块元数据(block.json 或register_block_type)中以selectors字段声明,支持 root(根选择器)、feature(特性选择器)、subfeature(子特性选择器)三级定制,并额外提供控制 Global Styles 自定义 CSS 输出位置的css选择器。读完本文,你将理解这三级选择器的语法与回退规则、css选择器与 theme.json 的映射关系,以及 Gutenberg 源码中解析这些选择器并生成样式表的完整实现链路。
背景:区块样式为什么需要可定制的选择器
当区块声明了对某个 block support(如border、color、typography)的支持时,Gutenberg 会基于 theme.json 与区块属性为区块生成 CSS 规则,每条规则都需要挂载在一个选择器之下。如果没有通过 Block Selectors API 提供选择器,Gutenberg 会为每个区块生成一个默认根选择器,形式为.wp-block-<区块名>。
但在复杂区块中,默认结构并不总是够用的。典型场景包括:
- 区块的包装元素与内部元素需要不同的样式:例如颜色(color)应用在区块 wrapper 上,而排版(typography)样式只应用于内部的标题元素;
- 某个子特性无法与其他子特性共用同一元素:例如
text-decoration,由于浏览器对该样式的渲染特性,把它加在 wrapper 上后很难被覆盖,需要单独指向目标元素; - 自定义 CSS 需要输出到与根选择器不同的位置。
Block Selectors API 就是为这些场景设计的。在区块元数据中,selectors字段的 TypeScript 类型定义如下,来自 packages/blocks/src/types.ts:
/** * Block selectors for styles. */ selectors?: Record< string, string | Record< string, string > >;这个类型定义揭示了 API 的完整形状:selectors是一个对象,其值要么是字符串(作为该特性的统一选择器,即简写形式),要么是字符串到字符串的映射对象(为每个子特性指定独立选择器)。这与后文介绍的 shorthand 与 fallback 机制一一对应。
Root selector:区块的主选择器
Root selector 是区块的主 CSS 选择器。所有区块都需要一个主选择器来承载其样式声明;如果开发者没有通过 Block Selectors API 提供,Gutenberg 会生成默认的.wp-block-<name>。
在 block.json 中声明根选择器:
{ ... "selectors": { "root": ".my-custom-block-selector" } }在源码层面,根选择器的解析入口位于 lib/class-wp-theme-json-gutenberg.php 的get_blocks_metadata()方法中:
$root_selector = wp_get_block_css_selector( $block_type ); static::$blocks_metadata[ $block_name ]['selector'] = $root_selector; static::$blocks_metadata[ $block_name ]['selectors'] = static::get_block_selectors( $block_type, $root_selector );其中wp_get_block_css_selector( $block_type )负责返回区块的根选择器(未定制时即.wp-block-<name>),而区块自定义的选择器映射则由get_block_selectors()生成,其实现见 lib/class-wp-theme-json-gutenberg.php:
protected static function get_block_selectors( $block_type, $root_selector ) { if ( ! empty( $block_type->selectors ) ) { return $block_type->selectors; } $selectors = array( 'root' => $root_selector ); foreach ( static::BLOCK_SUPPORT_FEATURE_LEVEL_SELECTORS as $key => $feature ) { $feature_selector = wp_get_block_css_selector( $block_type, $key ); if ( null !== $feature_selector ) { $selectors[ $feature ] = array( 'root' => $feature_selector ); } } return $selectors; }从源码结构看,这段逻辑体现了两条清晰的优先级规则:
- 只要区块声明了
selectors字段,就整体原样采用(if ( ! empty( $block_type->selectors ) )直接返回),区块拥有完全的选择器控制权; - 未声明
selectors的区块,则退回到通过wp_get_block_css_selector()逐项探测各特性选择器的兼容路径。
此外,Gutenberg 在判断“区块是否使用了自定义选择器”时,会同时检查两处声明,见 lib/block-supports/settings.php:
// We only want to append selectors for block's using custom selectors // i.e. not `wp-block-<name>`. $has_custom_selector = ( isset( $block_type->supports['__experimentalSelector'] ) && is_string( $block_type->supports['__experimentalSelector'] ) ) || ( isset( $block_type->selectors['root'] ) && is_string( $block_type->selectors['root'] ) );这说明除了selectors.root之外,历史遗留的supports.__experimentalSelector字符串声明同样会被识别为自定义根选择器;该判断用于在生成 block-level preset 变量时,把自定义选择器追加进根选择器列表,保证预设 CSS 变量能命中这些区块。
Feature selectors:把不同特性指向区块内不同元素
Feature selectors 对应于某个 block support(如 border、color、typography)生成的样式。区块可能希望把特定特性的样式应用到区块内不同元素上——例如把颜色应用在区块 wrapper 上,而排版样式只应用于内部的h2:
{ ... "selectors": { "root": ".my-custom-block-selector", "color": ".my-custom-block-selector", "typography": ".my-custom-block-selector > h2" } }这里color的值是一个字符串,意味着该特性的所有子特性样式都输出在这个选择器之下;typography同理指向直接子级h2。
并非所有特性都天然支持这种特性级选择器。Gutenberg 用一个常量明确列出了可拥有特性级选择器的 block support,见 lib/class-wp-theme-json-gutenberg.php:
const BLOCK_SUPPORT_FEATURE_LEVEL_SELECTORS = array( '__experimentalBorder' => 'border', 'color' => 'color', 'dimensions' => 'dimensions', 'spacing' => 'spacing', 'typography' => 'typography', );从源码结构看,可以得出两点实用结论:
- 支持特性级选择器的特性为 border、color、dimensions、spacing、typography 五项。其中
__experimentalBorder是旧版实验性声明键,会归一化为标准的border键; - 注意这与 Style Engine 声明的生成样式范围(background、border、color、dimensions、shadow、spacing、typography,见 packages/style-engine/docs/using-the-style-engine-with-block-supports.md)并不完全相同:background 和 shadow 不在特性级选择器常量中,它们的样式默认跟随根选择器或其他机制处理。
一个来自核心区块的真实用例印证了这个 API 的设计动机。lib/block-supports/block-style-variations.php 中的注释写道:
Block styles support custom selectors to direct specific types of styles to inner elements. For example, borders on Image block's get applied to the inner
imgelement rather than the wrappingfigure.
即 Image 区块的 border 样式被定向到内部img元素而非包裹的figure,正是靠特性级选择器实现的。
Subfeature selectors:子特性独立选择器
Subfeature selectors 对应 block support 提供的单个样式属性,例如background-color。一个子特性可以在自己独立的选择器下生成样式,这在同一个 support 的不同子特性无法作用于同一元素时尤为有用。
文档给出的经典例子是text-decoration:浏览器对它的渲染方式使其在被加到 wrapper 元素上后难以覆盖,因此为它分配一个自定义选择器,让样式只作用于应该应用它的元素:
{ ... "selectors": { "root": ".my-custom-block-selector", "color": ".my-custom-block-selector", "typography": { "root": ".my-custom-block-selector > h2", "text-decoration": ".my-custom-block-selector > h2 span" } } }注意此时typography从字符串变成了对象:root键作为该特性的默认选择器(未被单独指定的子特性会落到它下面),text-decoration子特性则被单独指向h2 span。
子特性的回退逻辑在源码中有直接对应。get_feature_selector()(lib/class-wp-theme-json-gutenberg.php)在特性选择器未设置时返回传入的默认选择器,即回退链条在代码中是显式实现的。而当生成样式表时,特性与子特性的自定义选择器会经由scope_style_node_selectors()(lib/class-wp-theme-json-gutenberg.php)逐层做作用域封装,字符串选择器与子特性映射对象两种形态都会被处理。
Custom CSS selector:css选择器
css选择器控制区块的自定义 CSS 规则(通过 Global Styles 界面设置的 CSS)所挂载的选择器。它映射到 theme.json 中的styles.blocks.<block-type>.css属性。如果未设置,则使用区块的根选择器。
字符串形式:
{ ... "selectors": { "root": ".my-custom-block-selector", "css": ".my-custom-block-selector > .inner-wrapper" } }对象形式(带root键):
{ ... "selectors": { "root": ".my-custom-block-selector", "css": { "root": ".my-custom-block-selector > .inner-wrapper" } } }在源码中,css选择器从区块元数据中提取并用于处理styles.blocks.<block-type>.css,见 lib/class-wp-theme-json-gutenberg.php:
$css_feature_selector = $block_metadata['selectors']['css'] ?? null; ... if ( isset( $node['css'] ) && ! $is_root_selector ) { $block_rules .= $this->process_blocks_custom_css( $node['css'], $css_selector ); }这里的?? null与后续以根选择器兜底的逻辑,正对应文档中“若未设置则使用区块根选择器”的描述。换言之,为css指定独立选择器后,区块在 Global Styles 中编辑的自定义 CSS 将被注入到你指定的选择器下(例如内部的.inner-wrapper),而不必与根选择器的样式混排。
Shorthand:特性级字符串简写
不必为每个子特性分别指定选择器。你可以把统一的选择器作为特性值的字符串来声明,前文color特性的用法即是这种简写:
{ ... "selectors": { "root": ".my-custom-block-selector", "color": ".my-custom-block-selector", "typography": ".my-custom-block-selector > h2" } }字符串与对象两种形态的合法性直接由selectors的类型定义保证:Record< string, string | Record< string, string > >(packages/blocks/src/types.ts)。字符串等价于一个只含root键的对象。
Fallbacks:选择器回退规则
这是使用 Block Selectors API 时最重要的规则。完整的回退链条为:
- 特性级:某个特性未配置选择器时,回退到区块的根选择器;
- 子特性级:某个子特性未配置选择器时,先回退到其父特性的选择器;若父特性也未定义,则进一步回退到区块的根选择器。
这一规则的实际价值在于:可以把公共选择器设为父特性的root选择器,只为少数有差异的子特性定义独立选择器。文档给出的完整示例:
{ ... "selectors": { "root": ".my-custom-block-selector", "color": { "text": ".my-custom-block-selector p" }, "typography": { "root": ".my-custom-block-selector > h2", "text-decoration": ".my-custom-block-selector > h2 span" } } }按回退规则推演各子特性的最终落点:
| 子特性 | 是否显式配置 | 最终选择器 | 回退来源 |
|---|---|---|---|
color.text | 是 | .my-custom-block-selector p | — |
color.background-color | 否 | .my-custom-block-selector | color未定义root,继续回退到区块根选择器 |
typography.font-size | 否 | .my-custom-block-selector > h2 | 回退到父特性typography的root选择器 |
typography.text-decoration | 是 | .my-custom-block-selector > h2 span | — |
源码中get_feature_selector()的签名get_feature_selector( $feature_selectors, $feature_key, $default_selector )与isset( $feature_selectors[ $feature_key ] )判断(lib/class-wp-theme-json-gutenberg.php),以及get_block_selectors()中以根选择器构造默认映射的逻辑,共同在实现层落实了这条“子特性 → 父特性 → 根选择器”的三级回退。
源码级实现链路:从区块注册到样式表输出
把上述规则串起来,Gutenberg 处理 Block Selectors 的完整链路如下(以下路径均基于当前仓库):
- 元数据声明:区块在 block.json 或
register_block_type中声明selectors字段,区块注册时被透传到区块类型对象上,相关注册逻辑与测试见 packages/blocks/src/api/registration.ts 与 packages/blocks/src/api/test/registration.jsx; - 元数据构建:
WP_Theme_JSON_Gutenberg::get_blocks_metadata()(lib/class-wp-theme-json-gutenberg.php)遍历所有已注册区块,为每个区块缓存selector(根选择器)、selectors(自定义选择器映射)、elements(元素选择器)、duotone以及styleVariations;构建结果缓存在静态属性中,只有新注册区块或新样式才会增量更新; - 节点选择器解析:样式生成时,
get_style_nodes()/get_block_nodes()从元数据中取出根选择器、特性选择器($feature_selectors)与变体选择器,构建带选择器的样式节点,随后scope_style_node_selectors()对特性与子特性的自定义选择器做作用域封装(lib/class-wp-theme-json-gutenberg.php); - 样式输出:
get_feature_declarations_for_node()等方法依据节点元数据中的选择器,把 color、typography 等特性的 CSS 声明输出到正确的选择器之下。
两个值得注意的扩展点:
- 自定义状态选择器:当前代码库中
selectors还支持states子键,用于把自定义状态(非 CSS 伪选择器)映射到具体 CSS 类选择器,格式如"selectors": { "states": { "-current": ".some-css-selector" } },见 lib/class-wp-theme-json-gutenberg.php 的文档注释及get_blocks_metadata()中selectors['states']的读取逻辑(lib/class-wp-theme-json-gutenberg.php); - 区块样式变体:区块样式(
is-style-*)的选择器由get_block_style_variation_selector()基于根选择器推导并缓存到元数据的styleVariations中(lib/class-wp-theme-json-gutenberg.php),变体样式生成同样依赖区块级自定义选择器机制来把样式定向到内部元素。
小结
Block Selectors API 用一个小而完整的selectors对象,解决了区块样式生成中“样式该挂在哪个选择器下”的问题:
root:区块主选择器,缺省为.wp-block-<name>;- 特性键(border / color / dimensions / spacing / typography):字符串简写或子特性对象,把整个特性的样式定向到区块内特定元素;
- 子特性键(如
text-decoration):为单个样式属性指定独立选择器; css:控制 Global Styles 自定义 CSS(theme.json 中styles.blocks.<block-type>.css)的输出位置,缺省回退到根选择器;- 回退链条:子特性 → 父特性
root→ 区块根选择器,使声明保持最小化。
所有规则均可在当前仓库中验证:API 定义见 docs/reference-guides/block-api/block-selectors.md,类型定义见 packages/blocks/src/types.ts,核心解析与输出实现集中在 lib/class-wp-theme-json-gutenberg.php,配套的 Style Engine 使用说明见 packages/style-engine/docs/using-the-style-engine-with-block-supports.md。
【免费下载链接】gutenbergThe Block Editor project for WordPress and beyond. Plugin is available from the official repository.项目地址: https://gitcode.com/GitHub_Trending/gu/gutenberg
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考