跳到主要内容

10 篇博文 含有标签「FSE」

查看所有标签

修改 WordPress Block Theme 不生效?FSE 开发 5 大难题排查指南

· 阅读需 8 分钟

在为客户开发 WordPress Block Theme 时反复遇到这五个问题,每次排查都花了不少时间。整理成指南,帮助同样在做 FSE 开发的同学快速定位。

TL;DR

五个问题按频率排序:文件修改不生效(数据库缓存覆盖文件)、块嵌套错乱(注释未关闭)、子主题内容不渲染(缺少 post-content 块)、SVG 图标消失(WP_Filesystem 被插件污染)、WP-CLI 邮件失败(SMTP 插件在命令行不生效)。每个场景都给出可直接复用的排查命令。

场景一:改了主题文件,页面没变化

问题现象

修改了主题目录下的 theme.jsontemplates/*.htmlparts/*.html,刷新页面无变化。甚至 git pull 更新了代码,前端仍然显示旧样式。

根因

FSE 主题的模板和全局样式会被 Site Editor 保存到数据库——wp_templatewp_template_partwp_global_styles 三种自定义文章类型。WordPress 读取时数据库版本优先于文件版本。即使你改了文件,只要数据库里有对应记录,就会用数据库的。

解决方案

不同文件类型对应不同的清理方式:

修改内容清理方式
templates/*.htmlwp_template
parts/*.htmlwp_template_part
theme.jsonwp_global_styles + 缓存
patterns/*.php直接生效,无需清理

一键清理所有数据库模板缓存:

# 本地 Docker 环境
docker exec wp_cli bash -c 'wp post delete $(wp post list --post_type=wp_template --format=ids --allow-root) --force --allow-root'
docker exec wp_cli bash -c 'wp post delete $(wp post list --post_type=wp_template_part --format=ids --allow-root) --force --allow-root'
docker exec wp_cli bash -c 'wp post delete $(wp post list --post_type=wp_global_styles --format=ids --allow-root) --force --allow-root'
docker exec wp_cli wp cache flush --allow-root

如果 theme.json 修改后仍不生效,先验证 JSON 格式是否有语法错误(如尾随逗号,JSON 规范不允许):

docker exec wp_cli wp eval 'echo json_encode(json_decode(file_get_contents(get_template_directory() . "/theme.json")));' --allow-root
# 返回空字符串 → JSON 有语法错误

注意事项

  • 开发期禁止在 Site Editor 中保存,避免产生数据库覆盖
  • 生产环境清理前确认 Site Editor 中没有自定义修改需要保留
  • patterns/*.php 不受此问题影响——Pattern 注册走 PHP 代码,不经过数据库
  • Site Editor 保存的 wp_global_styles 如果 JSON 损坏,会导致全站 WP_Theme_JSON_Resolver 解析错误,表现为所有页面样式崩溃

场景二:Site Editor 显示"尝试恢复",页面布局全乱了

问题现象

Site Editor 中 Pattern 显示"尝试恢复"(Attempt Recovery),保存后页面布局完全错乱。某些块被错误地嵌套在其他块内部,层级关系与源码不一致。

根因

WordPress 块编辑器使用 HTML 注释标记块的边界:

<!-- wp:group {"layout":{"type":"constrained"}} -->
<div class="wp-block-group">
<!-- 内容 -->
<!-- /wp:group -->

当容器块(如 wp:groupwp:columns)缺少关闭注释 <!-- /wp:group --> 时,Gutenberg 的 parse_blocks() 会将后续所有块视为该容器的子块。后果:

  1. 父块的 save 输出为空
  2. 触发 Block validation failed
  3. 后续所有块的嵌套关系全部错位

解决方案

排查:在浏览器控制台检查块树层级:

wp.data.select('core/block-editor').getBlocks()

检查返回的块树结构,确认每个容器块的 innerBlocks 是否符合预期。如果一个 group 块内部包含了不应该在里面的块,大概率是前面某个容器缺少关闭注释。

修复:打开 Pattern 源文件,逐个检查每个 <!-- wp:xxx --> 都有对应的 <!-- /wp:xxx -->。建议在编辑器中搜索 <!-- wp:<!-- /wp:,计数确认数量一致。

预防技巧

使用支持括号匹配的编辑器(如 VS Code),配合 Block Comment 高亮插件,可以在编写时立即发现未关闭的注释。对于复杂的 Pattern,建议先写骨架结构(所有开闭注释配对),再填充内容。

场景三:子主题覆盖模板后,编辑器内容消失了

问题现象

创建子主题覆盖父主题模板后,在 WordPress 页面编辑器中输入的内容(文本、图片等)在前端完全空白。但模板文件中硬编码的 Pattern(如 hero 区域、CTA 区块)正常显示。

根因

FSE 模板通过 <!-- wp:post-content /--> 块来渲染页面编辑器中的 post_content。如果子主题覆盖的模板文件中没有这个块,WordPress 不知道在哪里输出页面内容。

结果就是:模板的固定结构(header、hero、sidebar)正常显示,但编辑器里写的内容全部丢失。

解决方案

确保子主题模板包含 post-content 块:

<!-- wp:group {"layout":{"type":"constrained"}} -->
<div class="wp-block-group">

<!-- 模板固定结构(hero、sidebar 等) -->

<!-- wp:post-content {"layout":{"type":"constrained"}} /-->

<!-- 更多固定结构(CTA、footer 引用等) -->

</div>
<!-- /wp:group -->

排查"改了没效果"时,按以下顺序确认:

  1. 模板文件是否包含 <!-- wp:post-content /-->
  2. 你修改的是模板文件还是页面内容——两者控制不同的内容区域
  3. 模板中内联的 cover block 由模板文件控制,与数据库 post_content 无关

场景四:SVG 图标突然消失,但文件还在

问题现象

主题中使用 WP_Filesystem 读取 SVG 图标文件,突然所有 SVG 图标消失。直接访问 SVG 文件 URL 返回正常内容,但页面上图标位置是空的。

根因

WordPress 的 $wp_filesystem 全局变量默认使用 WP_Filesystem_Direct(直接读写本地文件)。某些插件(备份、安全类)在初始化时会将 $wp_filesystem 替换为 WP_Filesystem_ftpsocketsWP_Filesystem_SSH2

FTP/SSH 适配器通过远程连接读取文件,对本地路径(如 /var/www/html/wp-content/themes/...)无法正确访问,返回空字符串。由于替换发生在全局作用域,所有使用 WP_Filesystem 的主题和插件代码都受影响。

解决方案

第一步——诊断:检查当前 $wp_filesystem 的实际类型:

# 本地 Docker 环境
docker exec wp_cli wp eval 'global $wp_filesystem; echo get_class($wp_filesystem);' --allow-root

# 返回 WP_Filesystem_Direct → 正常
# 返回 WP_Filesystem_ftpsockets 或其他 → 已被污染

第二步——定位污染源:逐个禁用插件,检查哪个插件替换了适配器:

docker exec wp_cli wp plugin deactivate <plugin-name> --allow-root
docker exec wp_cli wp eval 'global $wp_filesystem; echo get_class($wp_filesystem);' --allow-root
# 重复直到返回 WP_Filesystem_Direct

第三步——代码兜底:在主题中加 file_get_contents() 作为 fallback:

function mytheme_get_svg( $path ) {
global $wp_filesystem;

// 优先使用 WP_Filesystem
if ( $wp_filesystem && method_exists( $wp_filesystem, 'get_contents' ) ) {
$content = $wp_filesystem->get_contents( $path );
if ( $content ) {
return $content;
}
}

// WP_Filesystem 失败时回退到直接读取
if ( file_exists( $path ) ) {
return file_get_contents( $path );
}

return '';
}

注意事项

  • file_get_contents() 在某些受限主机可能被 disable_functions 禁用,但 VPS 和 Docker 环境通常可用
  • 根治方案是定位并处理污染源插件,代码兜底只是临时方案
  • 此问题具有隐蔽性——SVG 文件本身完好,直接访问正常,只有在 PHP 中通过 WP_Filesystem 读取时才返回空

场景五:命令行发邮件失败,网页端正常

问题现象

通过 wp eval 在命令行调用 wp_mail() 发送邮件,始终失败。但通过 Web 请求触发的邮件(用户注册、联系表单)发送正常。WP Mail SMTP 等插件已正确配置 SMTP。

根因

SMTP 插件通过 Hook 拦截 wp_mail(),将发信通道从 PHP sendmail 切换到 SMTP 服务。但这些插件的 Hook 注册依赖 WordPress 完整启动流程——特别是 wp_loaded 之后的阶段。

WP-CLI 的 wp eval 虽然加载了 WordPress 核心,但部分插件 Hook 在 CLI 环境下不会被注册。wp_mail() 回退到 PHP sendmail,而大多数服务器没有配置 sendmail,导致发送失败。

解决方案

方法一——Web 请求测试:在主题中临时添加测试路由,通过浏览器触发:

// 临时添加到 functions.php,测试完立即删除
add_action( 'wp_loaded', function() {
if ( ! isset( $_GET['test_mail'] ) ) return;
if ( '1' !== $_GET['test_mail'] ) return;

$result = wp_mail( '[email protected]', 'SMTP Test', 'Test email body' );
var_dump( $result ); // true = 发送成功
exit;
} );

访问 https://yoursite.com/?test_mail=1 触发测试。

方法二——eval-file 确保完整加载

cat > /tmp/test-smtp.php << 'EOF'
<?php
require_once ABSPATH . 'wp-load.php';
do_action('wp_loaded');

$result = wp_mail('[email protected]', 'CLI SMTP Test', 'Test body');
echo $result ? "Sent\n" : "Failed\n";
EOF

docker exec wp_cli wp eval-file /tmp/test-smtp.php --allow-root

最佳实践

生产环境中,邮件发送验证应通过 Web 请求测试。WP-CLI 适合定时任务和批量操作,但不适合验证依赖完整 WordPress Hook 链的功能(如邮件、缓存预热等)。


WooCommerce 升级后编辑器显示异常、页面 404?Block Theme 排查指南

· 阅读需 7 分钟

在为客户构建 WooCommerce Block Theme 时遇到了这四个与 WooCommerce 相关的问题,每个都与 FSE 架构和 WooCommerce 的块系统有关。记录排查过程,帮助同样在做 WooCommerce 主题开发的同学避坑。

TL;DR

四个 WooCommerce Block Theme 开发中的常见问题:块重命名导致 core/missing(升级后块名加了 -block 后缀)、修改 shop slug 后产品归档 404(rewrite 缓存未刷新)、模板 HTML 与 Gutenberg save 不匹配(动态块验证失败)、Cart/Checkout 模板未自动分配(需要手动指定)。每个场景给出可直接复用的修复方案。

场景一:WooCommerce 升级后编辑器出现 core/missing

问题现象

升级 WooCommerce 后,Site Editor 中多个块显示为 core/missing(Block Recovery 提示)。前端渲染看起来正常,但编辑器中无法编辑这些块。

根因

WooCommerce 在新版本中将多个内部块重命名,统一添加了 -block 后缀。例如:

旧名称新名称
cart-order-summary-subtotalcart-order-summary-subtotal-block
cart-order-summary-shippingcart-order-summary-shipping-block
cart-order-summary-taxescart-order-summary-taxes-block
cart-order-summary-totaltotals-block
proceed-to-checkoutproceed-to-checkout-block

如果你的模板或 Pattern 中使用了旧名称的块注释(如 <!-- wp:woocommerce/cart-order-summary-subtotal -->),Gutenberg 找不到对应的块注册,将其渲染为 core/missing

前端渲染正常是因为 WooCommerce 的 PHP 回调函数仍然兼容旧名称,但编辑器依赖块的 JavaScript 注册信息,找不到就显示为 missing。

解决方案

诊断:在浏览器控制台确认已注册的块名称:

wp.blocks.getBlockTypes()
.map(b => b.name)
.filter(n => n.includes('cart-order-summary'))

对比输出与模板中使用的名称,找出不一致的。

修复:批量替换模板和 Pattern 文件中的旧块名为新名称:

# 在主题目录下执行
find . -name "*.html" -exec sed -i 's/woocommerce\/cart-order-summary-subtotal/woocommerce\/cart-order-summary-subtotal-block/g' {} +
find . -name "*.html" -exec sed -i 's/woocommerce\/cart-order-summary-shipping/woocommerce\/cart-order-summary-shipping-block/g' {} +
find . -name "*.html" -exec sed -i 's/woocommerce\/cart-order-summary-taxes/woocommerce\/cart-order-summary-taxes-block/g' {} +
find . -name "*.html" -exec sed -i 's/woocommerce\/cart-order-summary-total/woocommerce\/totals-block/g' {} +
find . -name "*.html" -exec sed -i 's/woocommerce\/proceed-to-checkout/woocommerce\/proceed-to-checkout-block/g' {} +

替换后清理数据库中的模板缓存(见上一篇"修改不生效"场景),确保新名称生效。

注意事项

  • 升级 WooCommerce 前备份模板文件,块重命名通常出现在大版本升级中
  • 前端渲染正常不代表没有问题,一定要在 Site Editor 中检查
  • 订阅 WooCommerce 开发者 changelog,提前了解块名称变更

场景二:改了商店网址后,产品列表页打不开了

问题现象

修改了 WooCommerce 商店页面的 slug(如从 /shop/ 改为 /products/),或修改了固定链接结构后,访问新的商店 URL 返回 404 或显示为普通页面(没有产品列表)。

根因

WordPress 的 Rewrite API 会在首次访问时将 URL 路由规则缓存到数据库中。修改页面 slug 后,缓存中的路由映射仍然指向旧 slug。新的 URL 没有对应的路由规则,WordPress 将其渲染为普通页面而非 WooCommerce 产品归档。

解决方案

修改 slug 或固定链接结构后,必须刷新 rewrite 缓存:

# 本地 Docker 环境
docker exec wp_cli wp rewrite flush --allow-root

# 服务器环境
ssh yougu-wp "docker exec prod_cli wp rewrite flush --allow-root"

一条命令即可解决。也可以在 WordPress 后台手动操作:设置 → 固定链接 → 保存更改,保存时会自动刷新 rewrite 规则。

最佳实践

任何涉及 URL 结构变更的操作(修改 slug、变更固定链接、新增自定义文章类型)之后,都应该执行 wp rewrite flush。建议在部署脚本中加入此命令,避免遗忘。

场景三:Cart/Checkout 块报验证错误

问题现象

WooCommerce 的 Cart、Checkout 等动态块在 Site Editor 中显示 Block validation failed。错误信息提示 Expected HTML 和 Actual HTML 不一致。

根因

WooCommerce 的许多块是服务端渲染(SSR)的动态块——前端内容由 PHP 实时生成,不依赖 JavaScript 的 save() 函数。但 Gutenberg 仍然要求模板 HTML 中的块标记与 save() 输出完全匹配。

如果模板中写的 HTML 与 Gutenberg 期望的输出不一致(多了或少了 class、style、内部元素),就会触发验证错误。

WooCommerce 常见块的 save 输出对照:

save 输出
product-price<div class="is-loading"></div>
price-filter<div class="wp-block-woocommerce-price-filter is-loading"><span aria-hidden="true" class="wc-block-product-categories__placeholder"></span></div>
woocommerce/cart<div class="wp-block-woocommerce-cart alignwide is-loading"></div>
woocommerce/checkout<div class="wp-block-woocommerce-checkout alignwide wc-block-checkout is-loading"></div>
cart-order-summary-*-block<div class="wp-block-woocommerce-{block-name}"></div>
proceed-to-checkout-block<div class="wp-block-woocommerce-proceed-to-checkout-block"></div>

解决方案

关键规则

  1. html: false 的块(save 返回 null,如 core/query-pagination)可以使用自闭合标签
  2. JS save 返回 placeholder HTML 的块禁止自闭合,必须包含完整内容
  3. core/query-pagination 禁止写 wrapper div,InnerBlocks 直接放在 open/close 注释之间

排查流程

  1. 打开 Site Editor,打开浏览器 DevTools Console
  2. 找到 Block validation failed 日志
  3. 对比 Expected(Gutenberg 生成)和 Actual(你的模板中写的)
  4. 按上方表格修复 HTML

可忽略的验证错误

以下 WooCommerce 验证错误可以安全忽略:

错误说明
woocommerce/cartwoocommerce/checkout 验证失败动态块,PHP 实时渲染,前端显示正常
woocommerce-blocktheme-css 加载错误WooCommerce 插件 CSS 问题,等待官方修复

场景四:自定义购物车/结算模板没生效

问题现象

创建了自定义的 Cart 或 Checkout 模板文件(如 templates/cart.htmltemplates/checkout.html),但对应的 WooCommerce 页面仍然使用默认模板。在 Site Editor 中能看到自定义模板,但它没有自动应用到 Cart/Checkout 页面。

根因

WooCommerce 的 Cart 和 Checkout 页面会自动匹配对应名称的模板。但如果匹配失败(如模板 slug 不完全对应,或页面创建顺序问题),就需要手动分配。

My Account 等依赖 shortcode 的页面不会自动匹配模板,必须手动分配。

解决方案

手动将模板分配给对应页面:

# 1. 查找 Cart 页面 ID
docker exec wp_cli wp post list --post_type=page --fields=ID,post_title,post_name --allow-root | grep -i cart

# 2. 分配模板
docker exec wp_cli wp post meta update <page_id> _wp_page_template <template_slug> --allow-root

# 示例:将 cart-block 模板分配给 Cart 页面
docker exec wp_cli wp post meta update 42 _wp_page_template cart-block --allow-root

验证分配结果:

docker exec wp_cli wp post meta get <page_id> _wp_page_template --allow-root

模板命名建议

确保模板文件名与 WooCommerce 页面功能对应。标准命名:

  • cart.htmlcart-block.html → Cart 页面
  • checkout.htmlcheckout-block.html → Checkout 页面
  • single-product.html → 产品详情页
  • archive-product.html → 产品归档页(shop)

保持命名一致可以提高自动匹配的成功率。


解决 WooCommerce 与 FSE Block Theme 的 4 个 CSS 冲突

· 阅读需 6 分钟

在为客户开发 WordPress FSE Block Theme 并集成 WooCommerce 时,遇到 4 个 WooCommerce 静默篡改主题样式的陷阱。每个都很隐蔽,排查耗时。记录根因与解法,帮助同样开发 FSE + WooCommerce 主题的开发者避坑。

TL;DR

  1. 字号预设覆盖:WooCommerce 注册 small/medium/large/x-large 四个预设覆盖 theme.json,用 h-1~h-6 自定义 slug 规避
  2. font-size class 连字符has-h-1-font-size(48px) vs has-h1-font-size(20px),手写 HTML 必须带连字符
  3. contrast 颜色反转--wp--preset--color--contrast 被 WooCommerce 改为浅色(#f8fafc),白色文字完全不可见
  4. ul.products 伪元素:WooCommerce 注入 ::before/::after 破坏 CSS Grid 布局,需 display:none 清除

陷阱一:字号预设被覆盖

问题现象

在 theme.json 中定义了字号预设:

{
"settings": {
"typography": {
"fontSizes": [
{ "slug": "small", "size": "0.875rem" },
{ "slug": "large", "size": "1.125rem" }
]
}
}
}

安装 WooCommerce 后,使用 has-large-font-size 的段落实际渲染为 36px,而非预期的 18px。H1 标题也可能从 48px 缩水到 20px。

根因

WooCommerce 通过 WP_Theme_JSON_API 注册自己的字号预设:

slugtheme.json 原值WooCommerce 覆盖值
small0.875rem (14px)13px
medium1rem (16px)20px
large1.125rem (18px)36px
x-large1.25rem (20px)42px

同 slug 的预设会被 WooCommerce 的值覆盖。但自定义 slug(如 h-1~h-6base)不受影响。

解法

theme.json 使用自定义 slug,避免与 WooCommerce 冲突:

{
"fontSizes": [
{ "slug": "h-1", "size": "3rem" },
{ "slug": "h-2", "size": "2.25rem" },
{ "slug": "h-3", "size": "1.75rem" },
{ "slug": "base", "size": "16px" }
]
}

模板中用 has-h-5-font-size 代替 has-large-font-size

如果需要用到 WooCommerce 的字号值,在 woocommerce.css 顶部定义自定义变量:

:root {
--cclee-font-sm: 13px;
--cclee-font-md: 20px;
--cclee-font-lg: 36px;
--cclee-font-xl: 42px;
}

陷阱二:font-size class 的连字符陷阱

问题现象

手写 FSE 模板 HTML 时,H1 标题只显示 20px 而非 theme.json 定义的 48px:

<!-- 错误:渲染为 20px -->
<h1 class="has-h1-font-size">标题</h1>

<!-- 正确:渲染为 48px -->
<h1 class="has-h-1-font-size">标题</h1>

两个 class 只差一个连字符,但字号差 2.4 倍。

根因

Gutenberg 将 fontSize: "h1" slug 渲染为 has-h-1-font-size(slug 中的数字前加连字符)。这是正确行为。

但当 WooCommerce 覆盖了字号系统后,has-h1-font-size(无连字符)匹配到的是 WooCommerce 的覆盖值(20px),而 has-h-1-font-size(有连字符)匹配到 theme.json 原始值(48px)。

验证方法 -- 浏览器控制台:

// 创建临时元素测试
const el = document.createElement('div');
document.body.appendChild(el);

el.className = 'has-h1-font-size';
console.log('无连字符:', getComputedStyle(el).fontSize); // 20px(错误)

el.className = 'has-h-1-font-size';
console.log('有连字符:', getComputedStyle(el).fontSize); // 48px(正确)

document.body.removeChild(el);

解法

手写 FSE 模板 HTML 时,font-size class 必须带连字符:

<!-- wp:heading {"fontSize":"h1"} -->
<h1 class="has-h-1-font-size">标题</h1>
<!-- /wp:heading -->

<!-- wp:heading {"fontSize":"h3"} -->
<h2 class="has-h-3-font-size">区块标题</h2>
<!-- /wp:heading -->

规则:has-h-{N}-font-size(带连字符),适用于所有 h-* 预设。

陷阱三:contrast 颜色反转

问题现象

contrast 背景色上使用 base(白色)文字,发现文字完全不可见:

<div class="has-contrast-background-color">
<h2 class="has-base-color">白色标题在深色背景上</h2>
</div>

预期 contrast 是深色背景 + 白色文字,实际 contrast 是浅色背景。

根因

WooCommerce 将 --wp--preset--color--contrast 注册为 #f8fafc(极浅灰),不是深色。这是 WooCommerce 的设计意图(其默认主题用 contrast 做浅色背景区域),但与大多数 FSE 主题的 contrast 语义(深色)冲突。

解法

contrast 背景上用 primary(深色)作为文字颜色:

<div class="has-contrast-background-color">
<h2 class="has-primary-color has-text-color">深色标题在浅色背景上</h2>
</div>

或者在 theme.json 中定义自己的深色 preset(如 surface),不依赖 contrast

{
"settings": {
"color": {
"palette": [
{ "slug": "surface", "color": "#0f172a", "name": "Surface" }
]
}
}
}

陷阱四:ul.products 伪元素破坏 Grid 布局

问题现象

WooCommerce 商品列表使用 CSS Grid 布局时,卡片排列错位、多出空白格:

ul.products {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 24px;
}

预期 3 列等宽,实际出现空位,部分卡片换到下一行。

根因

WooCommerce 在 ul.products 上注入了 ::before::after 伪元素:

ul.products::before,
ul.products::after {
display: table;
content: '';
}

ul.products::after {
clear: both;
}

这些伪元素是为传统 float 布局设计的 clearfix。但 CSS Grid 把伪元素当作 grid items,占用了两个隐式格子位。

解法

对 Grid 布局的 ul.products 隐藏伪元素:

ul.products {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 24px;
}

ul.products::before,
ul.products::after {
display: none;
}

注意事项

  • 每个陷阱都与 WooCommerce 的 WP_Theme_JSON_API 注册或 CSS 注入有关,禁用 WooCommerce 后这些覆盖会消失
  • 陷阱一和二是联动的:WooCommerce 覆盖字号预设后,连字符和无连字符的 class 映射到不同值
  • 陷阱三的 contrast 值取决于 WooCommerce 版本,升级后可能变化,建议用自定义 slug(如 surface)代替
  • 手写 FSE 模板前,先用浏览器 DevTools 检查 Gutenberg 自动渲染的 class 名称,不要想当然地缩写

正在部署 WooCommerce 站点?

推荐以下云服务器,稳定可靠


修复 FSE Block Theme 的风格预览单色块与首页白屏问题

· 阅读需 4 分钟

在为客户开发 WordPress FSE Block Theme 时,遇到风格预览显示异常和首页编辑白屏两个问题。记录根因与解法。

TL;DR

  1. Style variation 的 palette/gradients整体替换而非合并,只写 1 个颜色会丢失其余全部 -- 必须补全完整列表,只改需要变化的颜色。
  2. front-page.html 模板硬编码 pattern 会导致 Site Editor 编辑白屏,且用户无法在编辑器中调整布局 -- 应改为页面内容驱动。

坑一:Style Variation 风格预览只显示一个颜色块

问题现象

Site Editor > Browse Styles 中,默认风格显示 16 个颜色块,而第二个风格(Amber)只显示 1 个颜色块。切换到 Amber 风格后,所有引用 primarybasecontrast 等 CSS 变量的元素全部失去颜色。

根因

WordPress theme.json 的 style variation 机制中,settings.color.palettesettings.color.gradients 是**整体替换(replace)**父主题的对应声明,而非增量合并(merge)。

原始 styles/amber.json

{
"settings": {
"color": {
"palette": [
{ "slug": "accent", "color": "#b45309", "name": "Amber" }
]
}
}
}

这段配置的本意是"只把 accent 颜色换成琥珀色",但实际效果是"整个调色板只剩这一个颜色"。父主题 theme.json 中定义的 primarysecondarybasecontrastsurfaceneutral-50 ~ neutral-900 全部丢失。

gradients 同理,只声明 1 个就会替换掉父主题的 7 个渐变。

解决方案

在 variation 文件中补全完整的 palette 和 gradients 列表,只修改需要变化的值:

{
"$schema": "https://schemas.wp.org/trunk/theme.json",
"version": 3,
"title": "Amber",
"settings": {
"color": {
"palette": [
{ "slug": "primary", "color": "#0f172a", "name": "Primary" },
{ "slug": "secondary", "color": "#334155", "name": "Secondary" },
{ "slug": "accent", "color": "#b45309", "name": "Amber" },
{ "slug": "base", "color": "#ffffff", "name": "Base" },
{ "slug": "contrast", "color": "#f8fafc", "name": "Contrast" },
{ "slug": "surface", "color": "#0f172a", "name": "Surface" },
{ "slug": "neutral-50", "color": "#fafafa", "name": "Neutral 50" }
// ... 其余 neutral 色保持不变
],
"gradients": [
// 补全所有 7 个渐变,只修改 accent gradient 的目标色
]
}
}
}

关键原则:variation 中声明的任何数组类型配置项(palette、gradients、fontSizes、spacingSizes 等)都是整体替换,必须包含完整内容。

坑二:Site Editor 编辑首页白屏

问题现象

在 Site Editor 中通过 Pages > Home 打开首页编辑,画布完全空白。但在 Templates > Front Page 中能看到完整布局。

根因

这是两个不同的编辑入口,对应不同的数据源:

入口编辑对象数据来源
Templates > Front Pagefront-page.html 模板主题文件
Pages > Homepage_on_front 页面内容数据库 wp_posts.post_content

原始 front-page.html 在模板层面硬编码了三个 pattern:

<!-- wp:pattern {"slug":"cclee-theme/hero-centered"} /-->
<!-- wp:pattern {"slug":"cclee-theme/features-grid"} /-->

<!-- wp:group {"tagName":"main","align":"full"} -->
<main class="wp-block-group alignfull">
<!-- wp:post-content /-->
</main>
<!-- /wp:group -->

<!-- wp:pattern {"slug":"cclee-theme/cta-banner"} /-->

post-content 块渲染 page_on_front 页面的数据库内容。该页面内容为空,所以 Pages > Home 画布空白 -- 这是 WordPress 预期行为。

问题在于:模板硬编码 pattern 导致用户无法在页面编辑器中调整首页布局顺序和内容。

解决方案

将首页布局从"模板硬编码"改为"页面内容驱动"。

1. 模板只保留骨架front-page.html):

<!-- wp:template-part {"slug":"header-transparent","tagName":"header"} /-->

<!-- wp:group {"tagName":"main","align":"full","layout":{"type":"constrained"}} -->
<main class="wp-block-group alignfull">
<!-- wp:post-content {"layout":{"type":"constrained"}} /-->
</main>
<!-- /wp:group -->

<!-- wp:template-part {"slug":"footer-columns","tagName":"footer"} /-->

2. 激活时自动注入默认内容functions.phpinc/setup.php):

add_action( 'after_switch_theme', function () {
$front_page_id = (int) get_option( 'page_on_front' );
if ( ! $front_page_id ) {
return;
}

$post = get_post( $front_page_id );
// 仅在内容为空时注入,不覆盖用户已有内容
if ( ! $post || ! empty( trim( $post->post_content ) ) ) {
return;
}

$default_content = '<!-- wp:pattern {"slug":"cclee-theme/hero-centered"} /-->' . "\n"
. '<!-- wp:pattern {"slug":"cclee-theme/features-grid"} /-->' . "\n"
. '<!-- wp:pattern {"slug":"cclee-theme/cta-banner"} /-->';

wp_update_post( [
'ID' => $front_page_id,
'post_content' => $default_content,
] );
} );

设计决策

  • 模板只负责 header / main(post-content)/ footer 骨架
  • 首页布局完全由页面内容驱动,用户可在页面编辑器中增删、排序 pattern
  • after_switch_theme 钩子保证主题首次激活时有默认内容
  • 内容为空判断确保不覆盖用户的自定义内容

对类似需求感兴趣?联系合作

修复通用 hover 选择器穿透嵌套 Group 导致的卡片悬浮错乱

· 阅读需 3 分钟

在为客户开发 WordPress FSE Block Theme 时发现:博客列表页的卡片悬浮时文字区域发生偏移,但卡片外框不动,视觉上非常违和。记录根因与解法。

TL;DR

通用卡片 hover 选择器 .wp-block-columns .wp-block-column > .wp-block-group:hover 会匹配到卡片内部嵌套的文字 group,导致悬浮时内层文字偏移而外层卡片不动。修复方式:用 .wp-block-post-template 前缀重置内层 group 的 hover 效果。

问题现象

博客列表页使用卡片式布局(外层 border group 包裹图片 + 文字 group)。鼠标悬浮卡片时:

  • 内部的标题和摘要文字产生 translateY(-4px) 位移
  • 外层卡片的 border 和 shadow 没有任何变化
  • 视觉效果像文字"飘出"了卡片

根因

FSE 中卡片 pattern 的 HTML 结构是嵌套的:

<!-- 外层卡片 group (有 border) -->
<div class="wp-block-group has-border-color ...">
<img ... /> <!-- 特色图片 -->

<!-- 内层文字 group -->
<div class="wp-block-group" style="padding:...">
<h2>文章标题</h2>
<p>摘要文字...</p>
</div>
</div>

主题中定义了一个通用 hover 特效:

.wp-block-columns .wp-block-column > .wp-block-group:hover {
transform: translateY(-4px);
box-shadow: 0 12px 32px rgba(0, 0, 0, 0.1);
}

这个选择器的本意是让整张卡片悬浮上移。但在博客列表页中,post-template 块内部的卡片也被 wp-block-columns 包裹,导致选择器同时命中了内层文字 group(它也是 .wp-block-column > .wp-block-group)。

由于内层 group 没有 border 和 shadow,只表现为文字位移,而外层卡片没有位移效果。

解决方案

分两步处理:重置内层 + 给外层单独加 hover

1. 重置内层 group 的 hover

.wp-block-post-template 前缀提高优先级,把内层 group 的所有 hover 效果清零:

/* Reset hover for inner groups inside post template cards */
.wp-block-post-template .wp-block-columns .wp-block-column > .wp-block-group {
transition: none;
}
.wp-block-post-template .wp-block-columns .wp-block-column > .wp-block-group:hover {
transform: none;
box-shadow: none;
}

2. 给外层卡片加独立的 hover

外层卡片有 .has-border-color 类,用它做精确匹配:

/* Blog card (outer bordered group) -- hover lift + shadow */
.wp-block-post-template .wp-block-group.has-border-color {
transition:
transform 0.3s ease-out,
box-shadow 0.3s ease-out;
}
.wp-block-post-template .wp-block-group.has-border-color:hover {
transform: translateY(-4px);
box-shadow: 0 12px 32px rgba(0, 0, 0, 0.08);
}

为什么不直接缩小通用选择器范围?

通用选择器被多个 pattern 共享(features-grid、pricing、testimonial 等),这些 pattern 的 group 只有一层,不存在嵌套穿透问题。缩小通用选择器反而会破坏其他 pattern 的 hover 效果。

重置内层比限制外层更可靠,因为:

  • 不影响其他使用通用 hover 的 pattern
  • 精确针对有嵌套问题的场景
  • CSS 层面完全隔离,不需要修改任何 HTML/PHP

经验总结

在 FSE Block Theme 中添加 hover 特效前,务必:

  1. 检查 pattern 的实际 HTML 嵌套结构
  2. 确认选择器只命中目标元素(外层容器),不会穿透到内部 group
  3. 如果多个 pattern 共享同一选择器,优先用"重置内层"而非"限制外层"

对类似需求感兴趣?联系合作

修复 WooCommerce FSE Cart Block 空车白屏与商品无图塌陷

· 阅读需 4 分钟

在为客户开发 WooCommerce FSE Block Theme 时遇到这两个问题:Cart Block 空车时页面白屏、商品无特色图片时卡片高度塌陷。记录根因与解法。

TL;DR

  1. Cart Block 必须显式声明 filled-cart-blockempty-cart-block 子块,否则空车时无任何内容输出。
  2. 商品无特色图片时,FSE 的 post-featured-image 块渲染为空字符串,导致卡片高度塌陷。通过 post_thumbnail_html filter 补上 WooCommerce 占位图。

问题一:Cart Block 空车白屏

问题现象

购物车页面在有商品时正常显示,清空购物车后整个页面内容区变成空白 -- 没有提示文案,没有"继续购物"按钮,用户无法自助返回。

根因

WooCommerce Cart Block (wp:woocommerce/cart) 的设计要求开发者显式声明两个子块:

  • wp:woocommerce/filled-cart-block -- 有商品时显示
  • wp:woocommerce/empty-cart-block -- 空车时显示

如果只写了 Cart Block 本体而没有嵌套这两个子块,WooCommerce 在空车状态下不知道该渲染什么,输出为空。

这个问题在经典主题中不存在,因为经典主题使用 PHP 模板 cart.php,其中已经内置了空车处理逻辑。但 FSE 的 HTML 模板是声明式的,必须完整声明所有状态。

解决方案

cart.html 模板的正确结构:

<!-- wp:woocommerce/cart {"className":"cclee-cart"} -->
<div class="wp-block-woocommerce-cart alignwide is-loading">

<!-- wp:woocommerce/filled-cart-block -->
<div class="wp-block-woocommerce-filled-cart-block">
<!-- 有商品时的完整布局:商品列表 + 汇总 -->
<!-- wp:columns -->
<div class="wp-block-columns">
<!-- wp:column {"width":"65%"} -->
<div class="wp-block-column" style="flex-basis:65%">
<!-- wp:woocommerce/cart-items-block -->
<div class="wp-block-woocommerce-cart-items-block"></div>
<!-- /wp:woocommerce/cart-items-block -->
</div>
<!-- /wp:column -->
<!-- wp:column {"width":"35%"} -->
<div class="wp-block-column" style="flex-basis:35%">
<!-- wp:woocommerce/cart-totals-block -->
<div class="wp-block-woocommerce-cart-totals-block">
<!-- wp:woocommerce/cart-order-summary-block /-->
<!-- wp:woocommerce/proceed-to-checkout-block /-->
</div>
<!-- /wp:woocommerce/cart-totals-block -->
</div>
<!-- /wp:column -->
</div>
<!-- /wp:columns -->
</div>
<!-- /wp:woocommerce/filled-cart-block -->

<!-- wp:woocommerce/empty-cart-block -->
<div class="wp-block-woocommerce-empty-cart-block">
<!-- 空车提示 + 继续购物按钮 -->
<!-- wp:paragraph {"align":"center","textColor":"neutral-500"} -->
<p class="has-text-align-center has-neutral-500-color has-text-color">Your cart is currently empty.</p>
<!-- /wp:paragraph -->
<!-- wp:buttons {"layout":{"type":"flex","justifyContent":"center"}} -->
<div class="wp-block-buttons">
<!-- wp:button {"backgroundColor":"accent","textColor":"base"} -->
<div class="wp-block-button"><a href="/shop/" class="wp-block-button__link has-base-color has-accent-background-color has-text-color has-background wp-element-button">Browse Products</a></div>
<!-- /wp:button -->
</div>
<!-- /wp:buttons -->
</div>
<!-- /wp:woocommerce/empty-cart-block -->

</div>
<!-- /wp:woocommerce/cart -->

关键点filled-cart-blockempty-cart-block 必须是 wp:woocommerce/cart 的直接子块,WooCommerce 通过这两个子块实现有货/空车的条件渲染。

问题二:商品无特色图片时卡片塌陷

问题现象

在商品列表页(archive-product)中,没有设置特色图片的商品卡片只显示文字部分,没有图片区域。与有图卡片混排时,布局高度不统一,视觉上非常突兀。

根因

FSE 的 wp:post-featured-image 块在文章/商品没有缩略图时直接渲染为空字符串,不会输出占位图。经典主题中通常在 PHP 模板里手动处理这种情况(has_post_thumbnail() 判断),但 FSE HTML 模板无法嵌入这种条件逻辑。

解决方案

在主题的 functions.php 或 WooCommerce 集成文件中添加 filter:

/**
* Product placeholder image when no featured image is set.
*
* FSE post-featured-image block renders empty when no thumbnail,
* causing card height collapse. This filter injects the WooCommerce
* placeholder image for product post type.
*
* @param string $html The post thumbnail HTML.
* @param int $post_id The post ID.
* @return string
*/
add_filter( 'post_thumbnail_html', function ( $html, $post_id ) {
// Already has image or not a product -- skip.
if ( $html || get_post_type( $post_id ) !== 'product' ) {
return $html;
}

// Use WooCommerce's built-in placeholder.
$src = function_exists( 'wc_placeholder_img_src' )
? wc_placeholder_img_src()
: '';

if ( ! $src ) {
return '';
}

return sprintf(
'<img src="%s" alt="%s" loading="lazy" decoding="async" style="width:100%%;height:100%%;object-fit:cover;">',
esc_url( $src ),
esc_attr( get_the_title( $post_id ) )
);
}, 10, 2 );

注意

  • wc_placeholder_img_src() 依赖 WooCommerce 插件激活,用 function_exists() 做防护
  • object-fit: cover 确保占位图和正常特色图片的裁剪行为一致
  • 只针对 product 类型生效,不影响博客文章等其他 post type

对类似需求感兴趣?联系合作

修复 Gutenberg Gradient Class 命名变更导致的 Block 验证失败

· 阅读需 2 分钟

在为客户开发 WordPress FSE 主题时遇到此问题,记录根因与解法。

TL;DR

Gutenberg 升级后,gradient 的 CSS class 命名规则从 has-{slug}-gradient 变为 has-{slug}-gradient-background。手写 Pattern HTML 中的旧 class 与 Gutenberg 验证逻辑不匹配,导致 Site Editor 报错。解决方案是批量替换 class 名称。

问题现象

所有历史 Pattern 在 Site Editor 中显示错误:

Block contains unexpected or invalid content
  • 新建 Pattern 无此问题
  • 前台渲染正常
  • 点击 "Attempt Recovery" 可恢复显示

根因分析

通过 DevTools Console 查看 Block validation failed 日志,对比 Expected 与 Actual:

Expected(Gutenberg 生成)

<div class="wp-block-group has-accent-gradient-background has-background">

Actual(Pattern 中手写)

<div class="wp-block-group has-accent-gradient has-background">

Gutenberg 版本升级后,gradient class 命名规则变更:

旧命名新命名
has-{slug}-gradienthas-{slug}-gradient-background

Block 验证时,Gutenberg 会根据块注释中的 JSON 属性(如 "gradient":"accent-gradient")重新计算期望的 HTML,与实际 HTML 比对。class 不匹配即报验证失败。

解决方案

1. 确认影响范围

# 查找使用旧 class 的文件
grep -r "has-[a-z0-9-]*-gradient " patterns/ --include="*.php"

2. 批量替换

# macOS/Linux 通用
sed -i '' 's/has-\([a-z0-9-]*\)-gradient /has-\1-gradient-background /g' patterns/*.php

# Linux (GNU sed)
sed -i 's/has-\([a-z0-9-]*\)-gradient /has-\1-gradient-background /g' patterns/*.php

注意

  • 只替换 HTML 标签中的 class 属性
  • 块注释中的 "gradient":"accent-gradient" 声明不需要改动
  • 正则末尾有空格,避免误匹配 has-accent-gradient-background

3. 验证修复

  1. 清理 WordPress 缓存:wp cache flush
  2. 刷新 Site Editor,确认错误消失
  3. 抽查几个 Pattern,验证前后台显示正常

预防措施

  1. 优先使用块注释属性:通过 JSON 属性(如 "gradient":"accent-gradient")设置样式,让 Gutenberg 自动生成 class,避免手写
  2. 关注 Gutenberg 更新日志:升级前检查 Breaking Changes
  3. 开发环境先验证:升级后在开发环境测试所有 Pattern,确认无验证错误再部署

对类似需求感兴趣?联系合作

修复 FSE Group 块 layout 属性覆盖自定义 CSS 的问题

· 阅读需 3 分钟

TL;DR

WordPress FSE Group 块的 layout 属性会自动生成 is-layout-* CSS 类,这些类的样式优先级高于普通自定义 CSS,导致尺寸设置失效。解决方案:1) 块注释中使用 "layout":{"type":"default"} 避免生成额外布局类;2) CSS 中使用 !important 强制覆盖;3) 关键:添加 padding: 0 !important 清除 Group 块默认内边距。

问题现象

Timeline 组件的年份圆点应显示为 80px 正圆,实际却呈现为椭圆:

<!-- 块注释中的尺寸设置 -->
<!-- wp:group {"style":{"dimensions":{"width":"80px","height":"80px"}},"layout":{"type":"flex",...}} -->
/* 自定义 CSS */
.cclee-timeline-dot {
width: 80px;
height: 80px;
border-radius: 50%;
}

无论调整 CSS 还是块属性,圆点始终被拉伸变形。

根因

WordPress FSE 的 Group 块会根据 layout 属性自动添加布局相关的 CSS 类:

<div class="wp-block-group cclee-timeline-dot is-layout-flow">

这些 is-layout-* 类来自 WordPress 核心样式表,其样式规则会覆盖自定义 CSS。同时,Group 块存在默认 padding,会撑大元素导致尺寸计算偏差。

关键问题点:

  1. layout: {"type": "flex"} 生成 is-layout-flex 类,子元素受 flexbox 拉伸影响
  2. 块注释中的 style.dimensions 转为 inline style,但被布局类样式覆盖
  3. Group 块默认 padding 增加了元素实际尺寸

解决方案

1. 修改块注释,使用 default layout

<!-- wp:group {"className":"cclee-timeline-dot","style":{"border":{"radius":"50%"}},"backgroundColor":"accent","textColor":"base","layout":{"type":"default"}} -->
<div class="wp-block-group cclee-timeline-dot has-base-color has-accent-background-color has-text-color has-background" style="border-radius:50%">

移除 style.dimensions 和复杂的 flex layout,改用 "layout":{"type":"default"}

2. CSS 强制覆盖 + 清除默认 padding

/* Timeline: Fixed circle dot */
.wp-block-group.cclee-timeline-dot {
width: 80px !important;
height: 80px !important;
min-width: 80px !important;
min-height: 80px !important;
flex-shrink: 0 !important;
aspect-ratio: unset !important;
border-radius: 50% !important;
display: flex !important;
align-items: center !important;
justify-content: center !important;
align-self: center !important;
box-sizing: border-box !important;
text-align: center !important;
padding: 0 !important; /* 关键:清除默认 padding */
}

.wp-block-group.cclee-timeline-dot p {
margin: 0 !important;
white-space: nowrap !important;
line-height: 1 !important;
overflow: visible !important;
}

3. 使用 :has() 控制父容器

防止父级 Column 被 flexbox 拉伸:

.wp-block-columns .wp-block-column:has(.cclee-timeline-dot) {
flex-shrink: 0 !important;
flex-basis: 100px !important;
width: 100px !important;
}

关键发现

padding: 0 !important 是最终解决方案。Group 块的默认 padding 会撑大元素,即使设置了 width/height,实际渲染尺寸仍会超出预期。


对类似需求感兴趣?联系合作

修复 WordPress FSE 主题 Footer 文字不可见的 WCAG 对比度问题

· 阅读需 3 分钟

在为客户开发 WordPress FSE 企业主题时,发现 Footer 区块在多个 Style Variation 下文字几乎不可见。本文记录从 WCAG 对比度诊断到引入语义色、处理全局样式覆盖的完整修复过程。

TL;DR

问题:FSE 主题的 contrast 颜色 token 语义混乱,浅色主题中 contrast ≈ 浅灰 ≈ base(白色),导致 Footer 对比度仅 1.05:1。

解法

  1. 引入 surface 语义色,专用于深色区块背景
  2. 删除 wp_global_styles 中的覆盖样式
  3. 所有 Style Variations 同步添加 surface 定义

结果:对比度从 1.05:1 提升至 15.8:1(WCAG AAA 级)。

问题现象

Footer 区块使用 backgroundColor="contrast" + textColor="base"

<!-- wp:group {"backgroundColor":"contrast","textColor":"base"} -->
<div class="has-base-color has-contrast-background-color">
Footer 内容
</div>

在默认主题下,Footer 文字几乎不可见:

组合前景色背景色对比度WCAG
Footer 文字#ffffff (base)#f8fafc (contrast)1.05:1❌ 失败
Footer 链接#f59e0b (accent)#f8fafc (contrast)1.78:1❌ 失败

WCAG AA 标准要求普通文字对比度 ≥ 4.5:1,当前状态远不达标。

根因分析

1. contrast 语义混乱

contrast 的设计意图是"与 base 形成对比的背景色",但在不同主题模式下语义矛盾:

Variationbasecontrast期望 vs 实际
默认(浅色)#ffffff#f8fafc 浅灰期望深色,实际浅色
Tech(深色)#0f0f1a 深黑#1e1e2e 深紫期望浅色,实际深色

Footer Pattern 假设 contrast 是深色背景,但 5/6 的 Style Variations 中它是浅色。

2. 颜色语义缺乏明确用途定义

原设计系统只有 contrast 一个"对比色",没有区分:

  • 浅色对比区块(CTA Banner 等强调区域)
  • 深色对比区块(Footer、暗色 Hero 等)

解决方案

Step 1:引入 surface 语义色

theme.json 中新增 surface token,专用于深色区块背景:

{
"slug": "surface",
"color": "#0f172a",
"name": "Surface"
}

Step 2:更新所有 Style Variations

每个 variation 定义自己的 surface 色(通常等于 primary):

// styles/commerce.json
{ "slug": "surface", "color": "#1f2937", "name": "Surface" }

// styles/nature.json
{ "slug": "surface", "color": "#14532d", "name": "Surface" }

// styles/tech.json(深色主题)
{ "slug": "surface", "color": "#1e1e2e", "name": "Surface" }
<!-- wp:group {"backgroundColor":"surface","textColor":"base"} -->
<div class="has-base-color has-surface-background-color">
Footer 内容
</div>

Step 4:删除全局样式覆盖

修改 theme.json 后颜色仍不生效?检查全局样式:

# 检查是否存在全局样式
docker exec wp_cli wp post list --post_type=wp_global_styles --fields=ID,post_title --allow-root

# 删除全局样式
docker exec wp_cli wp post delete <ID> --force --allow-root
docker exec wp_cli wp cache flush --allow-root

原因wp_global_styles 中的 color.palette完全覆盖(非合并)theme.json 的调色板。

修复结果

Variationsurface + base 对比度WCAG 级别
默认15.8:1✅ AAA
Commerce13.1:1✅ AAA
Industrial12.6:1✅ AAA
Professional9.9:1✅ AAA
Nature10.8:1✅ AAA
Tech11.5:1✅ AAA

颜色语义总结

Token用途
primary品牌主色(Logo、主按钮)
secondary次要元素
accent行动召唤(CTA、链接)
base页面主背景
contrast浅色对比区块背景
surface深色区块背景(Footer、暗色 CTA) ← 新增

对类似需求感兴趣?联系合作

解决 WordPress FSE Pattern 块验证失败的 5 种原因

· 阅读需 5 分钟

在为客户开发 WordPress FSE 主题时,频繁遇到 Block Pattern 验证失败问题。本文总结 5 种常见原因与解决方案。

TL;DR

块验证失败通常是以下原因之一:颜色 slug 未定义JSON 重复 keyStyle Variation 调色板覆盖HTML 属性与块注释不一致全局样式覆盖 theme.json。逐一排查即可解决。

问题现象

编辑器中 Pattern 显示红色警告:

Block contains unexpected or invalid content

尝试恢复块内容后,可能暂时正常,但刷新后问题复现。


原因一:颜色 slug 未定义

根因

Pattern 块属性引用了 theme.json 中不存在的颜色 slug:

<!-- 错误:neutral-text 不存在 -->
<!-- wp:paragraph {"textColor":"neutral-text"} -->
<p class="has-neutral-text-color">...</p>

解决方案

  1. 打开 theme.json,检查 settings.color.palette 定义的所有颜色
  2. 将 Pattern 中的无效 slug 替换为有效值
# 批量替换示例
cd patterns/
sed -i 's/"neutral-text"/"neutral-500"/g' *.php
sed -i 's/has-neutral-text-color/has-neutral-500-color/g' *.php

有效 slug 参考: primary, secondary, accent, base, contrast, neutral-50 ~ neutral-900


原因二:JSON 重复 key

根因

块注释 JSON 中同一层级出现重复 key(常见于复制粘贴):

// 错误:两个 style
{"style":{"typography":{...}},"style":{"spacing":{...}}}

JSON 规范不允许重复 key,解析器行为未定义。

解决方案

合并为单一 key:

// 正确
{"style":{"typography":{...},"spacing":{...}}}

排查命令:

# 搜索可能重复的 key
grep -n ',"style":{' patterns/*.php | head -20

原因三:Style Variation 覆盖调色板

根因

styles/*.json 中的 color.palette完全覆盖(非合并)父主题调色板。

当 Pattern 引用 neutral-500,但当前 Style Variation 未定义该颜色时,验证失败。

解决方案

每个 Style Variation 必须包含完整的 neutral 系列:

// styles/ocean.json
{
"version": 3,
"settings": {
"color": {
"palette": [
{ "slug": "primary", "color": "#0d9488", "name": "Primary" },
{ "slug": "secondary", "color": "#0f766e", "name": "Secondary" },
{ "slug": "accent", "color": "#f59e0b", "name": "Accent" },
{ "slug": "base", "color": "#f8fafc", "name": "Base" },
{ "slug": "contrast", "color": "#0f172a", "name": "Contrast" },
{ "slug": "neutral-50", "color": "#fafafa", "name": "Neutral 50" },
{ "slug": "neutral-100", "color": "#f5f5f5", "name": "Neutral 100" },
{ "slug": "neutral-200", "color": "#e5e5e5", "name": "Neutral 200" },
{ "slug": "neutral-300", "color": "#d4d4d4", "name": "Neutral 300" },
{ "slug": "neutral-400", "color": "#a3a3a3", "name": "Neutral 400" },
{ "slug": "neutral-500", "color": "#737373", "name": "Neutral 500" },
{ "slug": "neutral-600", "color": "#525252", "name": "Neutral 600" },
{ "slug": "neutral-700", "color": "#404040", "name": "Neutral 700" },
{ "slug": "neutral-800", "color": "#262626", "name": "Neutral 800" },
{ "slug": "neutral-900", "color": "#171717", "name": "Neutral 900" }
]
}
}
}

关键: neutral 系列色值必须与 theme.json 完全一致,只改变品牌色。


原因四:HTML 属性与块注释不一致

根因

这是最隐蔽的问题。WordPress save 函数对生成的 HTML 有严格要求。

问题 4.1:class 顺序错误

WordPress 生成 class 的固定顺序:

has-border-color has-{slug}-border-color has-{slug}-background-color has-background

手写 HTML 时顺序错误会导致验证失败。

问题 4.2:背景色属性混用

背景色只能用 backgroundColor 属性,不可在 style.color.background 中声明:

<!-- 错误:混用导致 inline style 生成非法 CSS -->
<!-- wp:group {"style":{"color":{"background":"#f5f5f5"}}} -->

<!-- 正确 -->
<!-- wp:group {"backgroundColor":"neutral-100"} -->

问题 4.3:border style 属性顺序

border-width 必须在 border-style 之前:

<!-- 正确 -->
<div style="border-width:1px;border-style:solid;border-radius:8px;">

解决方案

最佳实践:从编辑器复制块代码,不要手写 HTML class 和 style。

  1. 在编辑器中配置好块
  2. 切换到代码编辑器视图
  3. 复制完整的块注释 + HTML
  4. 粘贴到 Pattern 文件

正确示例:

<!-- wp:group {"backgroundColor":"accent","borderColor":"neutral-200","style":{"border":{"radius":"8px","width":"1px","style":"solid"}}} -->
<div class="wp-block-group has-border-color has-neutral-200-border-color has-accent-background-color has-background" style="border-width:1px;border-style:solid;border-radius:8px;">
<!-- content -->
</div>
<!-- /wp:group -->

原因五:全局样式覆盖 theme.json

根因

Site Editor 保存的自定义样式存储在 wp_global_styles CPT 中,优先级高于 theme.json

修改 theme.json 后前端仍显示旧值,是因为全局样式覆盖了主题默认设置。

排查

# 检查是否存在全局样式
wp post list --post_type=wp_global_styles --fields=ID,post_title --allow-root

# 查看全局样式内容
wp post get <ID> --fields=post_content --allow-root

解决方案

# 删除全局样式
wp post delete <ID> --force --allow-root

# 清除缓存
wp cache flush --allow-root

预防: 开发阶段避免使用 Site Editor 自定义样式,所有配置通过 theme.json 管理。


排查流程总结

块验证失败

├─→ 检查颜色 slug 是否在 theme.json 中定义

├─→ 检查 JSON 是否有重复 key

├─→ 检查所有 Style Variation 是否包含完整调色板

├─→ 检查 HTML class/style 是否与块注释一致

└─→ 检查 wp_global_styles 是否覆盖了 theme.json

对类似需求感兴趣?联系合作