Files
hesu-JT/docs/superpowers/specs/2026-08-15-hesu-wordpress-theme-design.md
T
2026-08-27 11:54:26 +08:00

24 KiB
Raw Blame History

HESU WordPress 主题设计文档

  • 日期:2026-08-15
  • 状态:已获用户批准(三轮设计审查修正后)
  • 来源:d:\wp\demojt 下 10 页静态 HTML 模板 → WordPress 经典主题

1. 目标与约束

把现有静态站(HESU,外贸企业站,暗色+橙色主题,英文)1:1 迁移为 WordPress 主题:

  • 固定模板:页面结构锁死,编辑者在后台只能改内容(文字/图片/数字),不能增删或调换板块。
  • 公共板块可复用:抽为原生自定义 Gutenberg 区块(block.json + PHP render.php),内容存在 post_content 区块标记中(非 post meta)。
  • 内容后台填充:区块编辑器可视化编辑;新闻走完整文章系统。
  • 不依赖 ACF / 付费插件:唯一插件依赖 Contact Form 7(联系表单)。
  • 严格复刻设计稿:前端输出 HTML 与静态站逐字节对齐(项目既定原则)。
  • 交付位置 d:\wp\demojt\hesu-theme\hesu\(zip 后即为标准主题包)。
  • 环境:本机无 PHP、无 WP;验证采用"便携 PHP + WordPress + SQLite 插件"一次性测试站(用户已确认)。

2. 关键决策记录

决策 结论 备选与理由
主题形态 经典主题(PHP 模板)+ 原生自定义区块 FSE 全区块主题需重写全部 JS 交互(fullpage/pinned/时间线),风险高,弃
区块实现 原生 Gutenberg 区块,无构建工具链(编辑器脚本用原生 wp.element,非 JSX) ACF Pro 区块被用户排除;无 npm 依赖,交付即用
内容存储 区块标记写入 post_content 所见即所得、导入导出原生支持
结构锁定 页面包裹区块 InnerBlocks templateLock:"all" + 全部区块实例级 lock:{move:true,remove:true} 见 §5 锁定机制
新闻 CPT news + 分类法 news_cat,Tab 用分类归档地址(非自定义 query var) 原生分页/高亮/canonical 免费获得
联系表单 Contact Form 7,seed 时程序化创建默认表单 未装 CF7 时显示引导占位
资产加载 has_block() 判断内容区块条件加载 CSS/JS 与 slug/模板解耦,编辑者改 slug 不影响
Header/Footer WP 菜单(自定义渲染)+ Customizer,不做区块 见 §6
Seed after_switch_theme 一次性播种(幂等守卫 + 管理员手动重跑按钮)

3. 主题目录结构

wp-content/themes/hesu/
├── style.css              # 主题头信息 + 站点基础样式(原 style.css)
├── functions.php          # 入口,require inc/ 各模块
├── inc/
│   ├── setup.php          # 主题支持、菜单注册、add_post_type_support('page','excerpt')
│   ├── assets.php         # has_block() 映射表条件加载 + 编辑器样式
│   ├── blocks.php         # 扫描 blocks/ 注册区块
│   ├── cpt-news.php       # CPT news + 分类法 news_cat
│   ├── customizer.php     # 社交链接、页脚标语、版权、Mega Menu 左侧文案
│   ├── seed.php           # 激活播种 + 主题设置页(重新创建预置页面按钮)
│   └── walker-mega.php    # Mega Menu 自定义渲染
├── header.php / footer.php
├── page.php               # 所有以区块组成的页面共用(唯一渲染入口)
├── archive-news.php       # 新闻列表(Tab + 分页)
├── single-news.php        # 新闻详情
├── css/ js/ fonts/ image/ # 静态站资产原样迁移(保持相对路径结构)
├── editor-style.css       # 编辑器预览样式(含页面级 class 场景)
└── blocks/                # 每区块一个目录:block.json / render.php / index.js

无构建步骤;PHP 目标 7.4+,WP 6.0+(用到区块实例锁)。输出一律转义(esc_html/esc_attr/esc_url)。

前端 body class 注入(关键):各页面基底 CSS 挂在 body.is-* 上(如 body.is-bp { background:#fff; ... } 切换浅色主题),page-shell 只在 <main> 上输出 class 覆盖不到 body。静态站实际只有 3 种 body 钩子:is-bp(4 个业务页和发展历程页,main 同为 bp)、is-news、is-contact;首页/全球布局/关于我们三页 <body> 无 class。方案:page.php 经 body_class 过滤器解析当前 post_content 中 page-shell 的变体,注入 is-bp(含发展历程)/ is-contact(main 为 ct)/ is-gp / is-au(后两者 CSS 无对应钩子,仅为语义标记);archive-news.php / single-news.php 直接注入 is-news。编辑器侧则由 page-shell 的容器 class 提供同场景(§10)。

4. 区块清单(hesu/ 命名空间,22 个)

勘误(2026-08-17):标题的 22 为早期概数,下表实列 33 个;实现最终为 37 个(补充 brand-card/cap/caps/partner-slide 以对应真实静态 DOM)。以实现与 readme 为准。

通用

区块 属性/结构
hesu/hero 背景图(ID)、标题、副标题(RichText)、CTA(文字+链接)、SCROLL 指示器开关、对齐变体(左/居中/home/dh/ct);home 变体内置锁定 InnerBlocks 槽位放 stats-bar(DOM 与静态站一致:stats 在 hero section 内)
hesu/stats-bar → 子块 hesu/stat 子项:图标图(可选)、数字、后缀、标签;输出 data-count
hesu/business-intro 固定两栏:左(标题+RichText 段落+stats-bar) 右(图片)
hesu/section-heading 标题(+副文+可选外框按钮)
hesu/view-more 文字+链接
hesu/page-shell 页面级锁定容器:templateLock:"all",class 由 seed 写死(如 is-bp/page-content),渲染为语义化 <main>(home 的 page-content 为 div,与静态站一致),无编辑 UI

首页专属

区块 说明
hesu/bento-grid → hesu/banner-cell + hesu/media-card 四大业务磁贴,4 卡结构锁死
hesu/presence 标题+副文(含加粗)+按钮+世界地图图
hesu/hub-carousel → hesu/hub-card 区域轮播卡(图+区域名+链接)
hesu/partner 轮播 slide(图) + 橙色覆盖层(标题+4 特性项:图标+词)
hesu/news-grid 动态查询块:自动拉最新 4 篇新闻(图=特色图、分类+年份自动),仅"查看更多"链接可配

所有带锚点目标的区块(gp-screen 的 gp-1..gp-5、产品区 #products 等)的 id 为可配置属性,seed 沿用静态站原值,保证页脚锚点链接(如 global-presence#gp-2)不失效。

业务页专属

区块 说明
hesu/large-image 全宽大图框(bm-stock/gl-port 共用)
hesu/product-cards → hesu/product-card 产品分类卡:主题内置 SVG 图标集选择(或自定义图)+名称
hesu/feature-cards → hesu/feature-card 统一"图标+标题+描述"卡片组,变体 class 区分 as-services / ae-region / as-region / gl-services 四处样式
hesu/showcase / hesu/brand-wall / hesu/model-list 汽车出口页专属

特殊页面

区块 说明
hesu/timeline → hesu/timeline-item 发展历程横向时间线(年份/标题/描述)
hesu/gp-shell → hesu/gp-screen 全球布局 fullpage。容器含 .gp-pagebg 背景层;每屏变体(概览/区域,id gp-1..gp-5 可配);概览屏内部含 gp-nav-list 链接列表(屏内容的一部分,非独立导航)——列表项为锁定子区块(文案+#gp-N 锚点+data-i)。global-presence.js 零适配原样迁移(它只做 active 高亮,按 data-i 匹配)
hesu/au-stage → hesu/au-screen-1 / hesu/au-screen-2 关于我们 pinned 视屏:内部结构(背景图/角图/遮罩/标语层/缩放图/大 logo)固定,开放文案+图片编辑。注意 au-screen-1 背景为 style 内联 background-image
hesu/au-gps 关于我们底部区,内部 4 层锁定结构:① head(标题+文案) ② 4 图一行 ③ 宽幅图 ④ 左右分栏——左侧数据轮播(au-gps__data-card: 图+标题+描述 ×3,圆点由 about-us.js 驱动) + 右侧图文覆盖卡(图+overlay 标题+文案)
hesu/contact-form CF7 表单下拉选择器+容器样式;CF7 未装显示安装引导占位
hesu/contact-map 总部地图:默认嵌入静态站的 OpenStreetMap iframe(URL 可配置)

编辑器脚本与渲染约定

  • 主文案内联 RichText;数字/后缀/链接等用右侧栏 TextControl(控制代码量)。
  • RichText 属性存储(关键陷阱):本主题区块全部为动态渲染(render.php、无 save 函数),RichText 属性禁止用 source:'html'(无保存标记可解析会丢内容),一律普通 string 属性 + value/onChange。
  • stat-num DOM 微结构(关键):main.js 计数器以 el.firstChild.nodeValue 写数字——<span class="stat-num" data-count="20">20<span class="plus">+</span></span> 中数字文本节点必须是 firstChild,render.php 输出不得多包元素。
  • 容器类动态区块 render.php 必须输出 $content(InnerBlocks 内容)。
  • 不做 inserter example 配置(页面锁死,无人手动插入区块)。
  • 共享 helper(createElement 包装、公共 Inspector)提为 blocks/editor/helpers.js 单 handle。

5. 结构锁定机制(核心)

  1. 页面级:每个页面 = page-shell(或首页 = hero(home) + page-shell 两个顶层块,因静态站 DOM 中 hero 在 page-content 之外)。顶层块带实例锁 lock:{move:true,remove:true}。
  2. 容器级:page-shell 内 InnerBlocks templateLock:"all"——不能增删/移动子板块,只能进入编辑内容。
  3. 编辑器加固:非管理员隐藏区块插入器与解锁入口(editor settings 过滤)。
  4. 残余风险(已向用户说明):WP 核心无法阻止管理员手动解锁破坏结构;文档注明"解锁=破坏页面"。
部位 配置方式
Logo 原生 Custom Logo
主导航 WP 菜单;Mega Menu 用 wp_get_nav_menu_items() 自定义渲染:卡片 = 菜单项,卡片图 = 目标页特色图片(无图回退主题占位图,如指向归档/锚点的 News 卡),卡片文案 = 页面摘要(页面启用 excerpt)或菜单项描述
Mega Menu 左侧文案 × 2 组 Customizer 文本字段
Contact Us 按钮 指向联系页
Footer 5 栏 1 个 WP 菜单(4 个一级项=4 栏) + 标语走 Customizer
社交图标 4 个 Customizer(平台+URL)
版权 / Privacy / Terms / Sitemap Customizer
菜单高亮 nav_menu_css_class 过滤器补 active 类(静态 CSS 认 .active,WP 原生输出 .current-menu-item)

Mega Menu 卡片恰好 4 张是布局约束,seed 保证初始 4 项;文档说明增删项会改变布局。

7. 页面编排(seed 写死顺序)

页面 组成
首页 Home hero(home,含stats) + page-shell(page-content) → bento-grid → presence → hub-carousel → partner → news-grid
建材出口 page-shell(is-bp) → hero → business-intro(+stats,4 项 data-count) → large-image → product-cards → view-more
汽车出口 page-shell(is-bp) → hero(居中) → business-intro(无 stats,静态站此页没有) → showcase → brand-wall → model-list → feature-cards(region) → view-more
售后改装 page-shell(is-bp) → hero → business-intro(无 stats,静态站此页没有) → feature-cards(services) → feature-cards(region)
国际物流 page-shell(is-bp) → hero → business-intro(+stats,4 项 data-count) → large-image(port) → feature-cards(services)
全球布局 page-shell(main 为 gp-main) → gp-shell(1 概览屏+4 区域屏)
关于我们 page-shell(main 为 au-main) → au-stage(2 pinned 屏) → au-gps
发展历程 page-shell(is-bp,静态站此页 body 即 is-bp) → hero(dh) → timeline
联系我们 page-shell(is-contact,main 为 ct) → hero(ct) → contact-form → contact-map
新闻列表/详情 PHP 模板渲染,不用区块

8. 新闻系统

  • CPT news:支持标题/编辑器/特色图片/摘要;归档 /news/(不建 News 页面,避免 URL 冲突;导航 News 链接指向 get_post_type_archive_link('news'))。
  • 分类法 news_cat(非层级):seed 建 4 词——Company News / Delivery Cases / Exhibition Info / Global Network(第 4 个供首页卡片 meta 与页脚 News 栏使用;Tab 自动列出全部词,初始即 4 个,较静态站多出 Global Network 一项为有意取舍)。Tab = 全部分类词的归档地址链接;分页原生。
  • archive-news.php:新闻 Hero → Tab → 左文右图行列表(.nw-row,与静态站一致,非卡片网格) → 分页(每页 5 条,与静态 news.js 的 PER_PAGE 一致)。行内 [data-reveal]/.nw-row 默认 opacity:0,reveal 逻辑并入 main.js(见 §10)。Hero 背景图来源:Customizer 新增"新闻 Hero 图"设置(theme mod,默认主题资产 image/carexport/export-bg.jpg),归档与详情共用。
  • single-news.php(按 demo-html/新闻详情1.jpg 已读图确认):新闻 Hero+面包屑 → 分类标签+日期+标题 → 特色图 → 正文(核心区块编辑器,零自定义区块)→ 上一篇/下一篇 → 返回列表。
  • 首页 news-grid 自动拉最新 4 篇。
  • 静态 news.js(207 行,客户端渲染 15 篇硬编码数据 + Tab 过滤 + 分页)服务端化后大部分作废,不迁移;仅 reveal 行为由 main.js 承接。

9. Seed 流程

守卫(option hesu_seeded) → 图片入库(image/ 内容图经临时文件 + media_handle_sideload)
→ 建 9 个页面(锁定区块标记,引用图片附件 ID;9 页全部设置特色图片,供 Mega Menu 卡片使用)
→ 设 show_on_front/page_on_front → 建 4 个分类词
→ 建 15 篇示例新闻(迁移 news.js 内置的全部 15 篇数据,保证分页可见 3 页)
→ 建主菜单+页脚菜单并绑定卡位 → CF7 已激活时创建默认表单(字段=静态表单)
→ 写入 hesu_seeded
  • CF7 晚于主题激活的场景:另挂 activated_plugin 钩子,检测到 CF7 启用且默认表单缺失时补建;主题设置页"重新创建预置页面"按钮作为最终兜底。

  • Mega Menu 卡片无特色图时的占位:复刻静态站 ph-* 占位样式(底色 + "Photo" 标签的 div),不使用裸 <img>。

  • 主题设置页提供管理员"重新创建预置页面"按钮(幂等重跑)。

  • 若在已有内容的站点激活:seed 仍会创建预置页面并设置首页,文档说明预期用于全新安装。

10. 资产加载

层级 策略
全站 style.css + fonts.css + main.js(footer 加载,与静态站 body 尾一致;扩展:新增对 [data-reveal] 与 .nw-row 的 IntersectionObserver reveal——业务页 [data-reveal] 与新闻行默认 opacity:0,无 JS 则内容不可见)
按区块 CSS business-intro→business.css、hero→business.css、product-cards→building-materials.css、brand-wall/showcase/model-list→auto-export.css、feature-cards→after-sales.css+auto-export.css+global-logistics.css(交叉装载无害:各文件选择器以自身变体 class 作用域)、timeline→development-history.css+js、gp-shell→global-presence.css+js、au-stage→about-us.css+js、news-grid→news.css、contact-form→contact.css+js
按区块 JS 出现任一业务页区块(hero/business-intro/product-cards/brand-wall/showcase/model-list/feature-cards/large-image)→ 加载 4 个业务页 JS(building-materials.js / auto-export.js / after-sales.js / global-logistics.js,共约 6KB)。理由:各文件自带选择器守卫(无目标即 return),其中 data-count 计数(建材/物流两页的 bp-stats)与部分 reveal 由它们驱动,main.js 的 .stat-num 计数器不覆盖 bp-stat__num 结构
news.js 不迁移(客户端渲染/过滤/分页已服务端化),reveal 逻辑由 main.js 扩展承接
模板直载 archive/single-news 直接 enqueue news.css
编辑器 编辑器侧加载 fonts.css + style.css + 全部页面 CSS(约 130KB,仅后台开销,无法按页条件);page-shell 输出页面级 class(如 is-bp)使作用域 CSS 在编辑器 iframe 生效
行为边界 交互(计数/轮播/fullpage)仅在.front 前端运行,编辑器内为静态预览——WP 服务端渲染区块的固有限制

CF7 与静态表单的样式映射

静态表单是自绘结构(ct-form/ct-field/ct-label/ct-input/ct-selectwrap + #ctForm 客户端校验 + #ctNote 成功态);CF7 输出 form.wpcf7-form + span.wpcf7-form-control-wrap 包裹。约定:

  • seed 建表单时为每个字段 shortcode 挂静态 class(如 [text* your-name class:ct-input])。
  • contact.css 增补桥接规则:以 CF7 实际 DOM(.wpcf7-form / .wpcf7-form-control-wrap / .wpcf7-not-valid / 响应元素 .wpcf7-response-output)为目标复刻 ct-* 视觉。CF7 6.x 起状态类(invalid / sent / failed / aborted / unaccepted / spam)挂在 form 元素上(如 form.wpcf7-form.sent),响应元素仅保留 .wpcf7-response-output,桥接规则按 form.{invalid,sent,...} .wpcf7-response-output 匹配;校验与提交反馈全部交给 CF7 原生状态 class,contact.js 的表单分支不迁移。

JS 依赖事实:静态站全部 JS 为原生手写(无 GSAP/fullpage 库),全部原样迁移、零适配(global-presence.js 的 active 高亮依赖 gp-nav-list a[data-i] 标记,由概览屏锁定子区块保证)。main.js 的 network 无限轮播克隆逻辑要求 .network-card 为 .network-track 直接子元素,hub-carousel 渲染须保持该层级。

11. 验证方案(用户已确认搭一次性环境)

  1. 下载便携版 PHP(≥7.4,含 pdo_sqlite)解压至项目临时目录(不入库)。
  2. WordPress + SQLite drop-in(如 wp-sqlite-db)搭建一次性站点,安装主题与 CF7。
  3. 实跑验收清单(§12)。
  4. 测完删除临时站点;若用户后续装正式环境可复用清单。

12. 验收清单

  1. 干净 WP 激活 → 页面/菜单/示例新闻自动生成,首页即成型。
  2. 10 个路由与静态站逐页对照:板块顺序、数字滚动、Mega Menu、轮播拖拽、fullpage 翻屏、pinned 视屏、时间线横滑全部工作(含 .reveal 滚动动画)。
  3. 以"编辑"角色打开任意页面:结构不可增删/移动,文字图片可改;尝试删除被锁区块被拒绝。
  4. 新闻:各类别发文 → 归档 Tab 过滤 + 分页 + 首页卡片自动更新。
  5. 联系表单:样式按 §10 桥接规则还原静态视觉;测试环境将 CF7 邮件设为 dummy(便携 PHP 无邮件服务),验收标准为提交成功态/校验态正确展示,不要求真实发信。
  6. 导航高亮、Customizer 各字段生效。
  7. php -l 全过、输出转义审查、控制台无报错、本地字体正常加载。
  8. CF7 缺失时联系页显示引导提示而非报错。

13. 设计审查修正记录(三轮)

# 级别 问题 修正
1 漏洞 templateLock 锁不到顶层区块 全部区块实例级 lock:{move,remove}
2 漏洞 News 页面与 CPT 归档 /news/ URL 冲突 不建 News 页面
3 漏洞 按 slug 条件加载资产脆弱 改 has_block()
4 漏洞 页面级 CSS 依赖 body class,编辑器不生效 page-shell 输出等价 class
5 漏洞 首页 hero 与 page-content 的 DOM 层级套不进单容器 两个顶层锁死块;hero(home) 内嵌 stats 槽位
6 缺口 Mega Menu News 卡片无特色图/摘要 占位图+菜单描述兜底
7 缺口 菜单高亮 .active vs .current-menu-item nav_menu_css_class 过滤器
8 缺口 wp_nav_menu 输出不了 mega 结构 wp_get_nav_menu_items 自定义渲染
9 缺口 新闻 Tab 分页保参易错 改用分类归档地址,原生分页
10 缺口 seed 无重跑出口 主题设置页按钮
11 缺口 News 卡用 query_var 方案繁琐 改原生归档 URL
12 矛盾 验收清单需实机 vs 本机无 PHP 便携 PHP + SQLite 一次性测试站
13 漏洞 页面基底 CSS 挂在 body.is-*(如 body.is-bp 浅色主题),page-shell 的 main class 覆盖不到 body body_class 过滤器按 page-shell 变体注入前端 body class(spec 审查发现)
14 漏洞 4 个业务页 JS(data-count 计数/reveal)未纳入资产映射;[data-reveal] 默认 opacity:0,JS 缺失内容不可见 has_block 命中业务区块即加载 4 个业务 JS;main.js 扩展承接 news reveal(spec 审查发现)
15 错误 汽车出口/售后改装两页静态站没有 stats,编排误写 business-intro(+stats) 改为无 stats(spec 审查发现)
16 错误 新闻列表实为左文右图行、每页 5 条、news.js 内置 15 篇,spec 误写 3 列卡片/9 条/4 篇 按静态站修正;Tab 有意保留 4 词(spec 审查发现)
17 缺口 CF7 DOM 与静态表单差异未定义、CF7 晚激活无法补建表单、seed 未设页面特色图、锚点 id 保留未覆盖、测试环境无法真实发信 §10 桥接规则、activated_plugin 补建、seed 设特色图+ph-* 占位、区块 id 可配置、验收措辞调整(spec 审查发现)
18 错误 body 变体名臆造:静态 CSS 只有 is-bp/is-news/is-contact 三种钩子;发展历程页实为 is-bp+main.bp,联系页为 is-contact+main.ct,不存在 is-dh/is-ct §3/§7 按静态站事实改注 is-bp / is-contact(二审发现)
19 错误 gp"圆点导航由 JS 从 data-nav-title 生成"系错误假设:gp-nav-list 实为概览屏内部链接列表(屏内容),js 仅做 active 高亮,无需任何适配;导航文案与屏标题在静态站本就不一致,不应自动生成 §4 改为概览屏锁定子区块,global-presence.js 零适配(四轮源码核查发现)
20 不足 au-gps 实为 4 层结构(head/4 图行/宽图/数据轮播+图文卡分栏),spec 仅一句带过 §4 细化内部结构(四轮源码核查发现)
21 缺口 动态区块 RichText 用 source:html 会丢内容;stat-num 的 firstChild 文本节点微结构未约定;编辑器 CSS 加载范围未明确;新闻 Hero 图来源未定义;contact-map 实为 OSM iframe 编辑器约定补两条关键约定、§10 明确全量页面 CSS、§8 Customizer 新闻 Hero 图、contact-map 改 iframe(四轮源码核查发现)

内容备注(非缺陷):静态站 gp-3(Africa)屏的 lead 文案讲的是 Minsk/Belarus(静态站自身的复制粘贴错误)。迁移按原文保留,seed 后可在后台修正。

14. 范围外(明确不做)

  • 多语言(结构不阻碍后续接多语言插件,内容在 post_content 中天然可翻译)。
  • Classic Editor 插件兼容(文档注明必须使用区块编辑器)。
  • 性能优化超出 §10 所列(CDN、缓存策略交由宿主环境)。

15. v2 同步记录(2026-08-17)

静态站改版 v2(commit a68ff43)后的主题同步:mega 精简(Business 只留 title/About 空 left 2 卡)、footer 社交 img 化(LinkedIn→Instagram)、hero scroll 全删、bp-stats→bp-caps(含物流双 label)、SVG 图标全面 img 化、as-loc 简化、ae-models 去 tags、ae-map 去 markers、au-gps 重构(img 化+4 数据卡+feature-copy)、au-zoom-mask 新增、ct-map 三层结构、新闻系统重做(4 篇/nd-* 详情页/nw-empty 空态)、news-banner 新图。详见 plans/2026-08-17-static-v2-sync-report.md 与验收日志。已接受偏差:footer 按正确闭合(不复刻旧页嵌套错误);"Four Core Businessesk"与"99% Awards Winner"原样复刻。