用户 2026-10-27 提出:
可以了。下一个需求,现在侧边栏的文章分类那个小挂件,是咱们主题的吗?有点丑,如果是,就美化一下,如果不是,咱们就重做一个好看一点的。
三条独立证据一致:
主题自带的挂件类只有 6 个(grep -n "^class widget_" inc/theme-widgets.php):
grep -n "^class widget_" inc/theme-widgets.php
430:class widget_search extends WP_Widget 484:class widget_ad extends WP_Widget 598:class widget_about extends WP_Widget 872:class widget_tags extends WP_Widget 950:class widget_posts extends WP_Widget 1095:class widget_recent_comments extends WP_Widget
没有分类挂件。
全仓库零命中(grep -rn "WP_Widget_Categories\|widget_categories\|categories" --include=*.php .):空。主题从未碰过分类挂件,既没美化也没复用。
grep -rn "WP_Widget_Categories\|widget_categories\|categories" --include=*.php .
主题注销的默认挂件列表里有 8 个,WP_Widget_Categories 不在其中(inc/theme-widgets.php:22-31):
WP_Widget_Categories
inc/theme-widgets.php:22-31
WP_Widget_Pages / WP_Widget_Meta / WP_Widget_Recent_Posts / WP_Widget_Recent_Comments WP_Widget_RSS / WP_Widget_Search / WP_Widget_Tag_Cloud / WP_Nav_Menu_Widget
⇒ 侧栏那个分类挂件是 WordPress 核心自带的 WP_Widget_Categories,主题既没接管、也没美化,原样输出核心的裸 <ul><li> 列表。
<ul><li>
结论:不是主题的。 按用户要求走「重做一个好看一点的」。
widget_categories
categories
widgets_init()
unregister_widget('WP_Widget_Categories')
register_widget()
id_base
yang_categories
assets/css/kratos.css
w-xxx
.title
.w-comments
.w-tags
number_format_i18n
overflow-wrap
ul/li
current-cat
trim()
empty()
!empty('0')
/theme-update/
wp-content/themes/kratos_yang/
update.json
侧栏分类挂件是 WP 核心自带、主题未接管也未美化,属新增自建挂件,与 #20(既有挂件一致性)/#22(最近评论)可独立验收
建单正文 2.1 写的是「新建 widget_categories 类(主题自有 id_base,不复用 categories),计划用 yang_categories」。
实际做成了相反的方向:沿用 categories 直接顶替核心挂件。 原因如下,请确认。
建单时那条判断的依据是「换了 id_base 会接管用户已有实例、把存量配置搞成孤儿」。查了线上实际情况后发现恰好相反:
categories-3
array('title' => '文章分类', 'count' => 1, 'hierarchical' => 1, 'dropdown' => 1)
widget_yang_categories
沿用 categories 则完全相反:
_register_widgets()
⇒ 所以建单时那句「必须不同」的前提("接管会把已有配置搞成孤儿")实测不成立,结论要反过来。
这里也有个真实代价:update() 必须容忍核心那套老字段(dropdown / hierarchical)。 处理方式是:不读它们、不显式删它们 —— 只在用户重新保存表单时才让它们自然消失。 widget() 完全不读 = 无害。
update()
dropdown
hierarchical
widget()
这是本单最重要的一个发现,比"重做一个挂件"本身更值:
那个实例的 dropdown = 1,核心走的是 wp_dropdown_categories(),输出的是一个 <select>。
dropdown = 1
wp_dropdown_categories()
<select>
而主题 CSS 里早就有一整套 .widget_categories ul li 规则(kratos.css 2293 行起)—— 它只匹配 ul li,下拉框里根本没有 ul。
.widget_categories ul li
ul li
ul
线上实际渲染: <select name="cat" ...><option>...</option></select> 主题规则匹配: .k-main .sidebar .widget_categories ul li → 命中 0 个元素
⇒ 写了但一条都没生效。 不是"样式没写",是样式和渲染形态对不上。 这解释了为什么它跟侧栏其它挂件完全不搭 —— 从来没被美化过。
所以这次除了做新挂件,也把那一整套失效规则(.widget_categories ul li 及其 @media 里的宽度规则)删掉了。留着只会误导下一个维护的人。
@media
用 <details>/<summary>,不写一行 JS。 理由:
<details>/<summary>
父级链接放在 <summary> 里不会误触折叠,这是 HTML 规范给的: <summary> 的"激活即折叠"只在该点击目标是 non-interactive 时才触发,<a> 属于 interactive content ⇒ 点名字是纯导航,点箭头/数字/空白才折叠。两个动作各归各的。
<summary>
<a>
⚠️ 这条有个隐含前提,已在代码注释里写死:若哪天有人把 <a> 换成 <span onclick>,这条就失效了。
<span onclick>
pad_counts
核心 wp_list_categories() 是在 show_count && hierarchical 时隐式打开 pad_counts (category-template.php:392)。实测本站:
wp_list_categories()
show_count && hierarchical
category-template.php:392
生活记录 原始 count = 188 生活记录 带 pad_counts = 220 (188 + 博士6 + 美国22 + 游泳4)
线上那个下拉框显示的也是 220。若这里取原始 count,同一个分类会在升级前后 显示成 220 → 188,用户会以为丢了 32 篇文章。⇒ 显式 'pad_counts' => true。
'pad_counts' => true
orderby=count
orderby=count 在 WP_Term_Query::parse_orderby() 里被翻成 ORDER BY tt.count (class-wp-term-query.php:926-927),排的是数据库里的原始 count。 而 pad_counts 是查询之后由 PHP 补的。两者不一致的实测后果:
WP_Term_Query::parse_orderby()
ORDER BY tt.count
class-wp-term-query.php:926-927
生活记录 220 ← 排第 1(原始 188) 评论杂谈 168 信息技术 233 ← 排第 3(原始 233,实际最大)
界面上看就是"数字大的排在小的后面"。⇒ 改成本地取回后按补过的 count 在 PHP 里排, 并且显式做 tie-break(count 相同时按名称 strnatcasecmp),否则相等项顺序随请求变化。
strnatcasecmp
线上有一个分类名库里存的就是 Ubuntu&Debian(id=122),不是 Ubuntu&Debian。 我一开始认定必须 html_entity_decode() 一层,理由是"下拉框会显示成 Ubuntu&Debian"。
Ubuntu&Debian
Ubuntu&Debian
html_entity_decode()
实测证明这个判断是错的:
raw = [Ubuntu&Debian] decode = [Ubuntu&Debian] esc(raw) = [Ubuntu&Debian] esc(decode) = [Ubuntu&Debian] ← 两者完全相同
关键点:esc_html() 对 & 的输出恒为 &。所以库里存 & 还是存 &, 经 esc_html() 后字节完全一致,浏览器里都是 Ubuntu&Debian,本来就是正常的。
esc_html()
&
&
⇒ 不 decode,直接用 esc_html()。少一层处理 = 少一处出错的地方; 而且 decode 有真实风险:若将来分类名里有人写了字面量 <script>,decode 会让它变成真标签。
<script>
样式写完不算,我把新 CSS 套到线上真实页面上量了实际坐标,抓出三处:
gaps=[6,6,6,6,6,6,6]
ul > li { margin-bottom:6px; padding-left:2px; list-style:circle; line-height:22px }
pl:2px
display:list-item
:not(.has-children)
<li>
[220.5, 220.5, 227.6, 227.6, 220.5]
<details>
space-between
第 3 条尤其值得记:<summary> 自己写 width 是没用的 —— 父级 <details> 收缩了,子级撑满的只是那个收缩后的宽度。修法是给 <details> 加 flex: 1 1 auto; min-width: 0(min-width:0 不能省,长分类名才能换行不撑破)。
width
flex: 1 1 auto; min-width: 0
min-width:0
端到端(真实 WP 加载待测主题副本):通过 77 / 失败 0
覆盖:接管是否真生效(callback / 工厂注销 / 注册表名字)· 渲染结构 · pad_counts 三个数字(188→220、233、子级 6)· 转义四种情况 · 后台表单 7 个字段 · update() 存取 12 项(含"标题就叫 0")· 开关生效 7 项 · 边界与安全 5 项 · 代码质量 15 项
反证 12 组(_rev_cats.sh):全部命中预期判据,无一组空跑
_rev_cats.sh
CSS 视觉反向验证 3 组(_rev_cats_css.js):把三处样式缺陷逐个注回去, 全部被量出来(gaps=[6] / rightsUniq=[220.5,227.6] / 颜色变红)
_rev_cats_css.js
gaps=[6]
rightsUniq=[220.5,227.6]
视觉几何 18 项全绿:行距均匀 · 胶囊同高同圆角同底色 · 父级数字同一列 · 左右留白对称 · 无箭头行贴最左 · 有箭头行缩进正好等于箭头占位 · 与相邻挂件 23px · 悬停变主题蓝(等过渡结束后采样)+ 胶囊联动 · 箭头是 CSS 三角形且可见
用 3.2.53 老代码跑同一套判据:11 通过 / 57 失败
这一步比测试本身更值。用老代码跑时发现它居然"通过 16 条",逐条查全是假绿:
已删除 @media 里的分类宽度规则
/@media[^{]*\{[^}]*widget_categories/
[^}]*
}
文章数没走 number_format_i18n
$cls_src=''
!preg_match(...,'')
类内没把 empty() 用在开关字段上
.w-cats 没绑 .k-main .sidebar 祖先
$cm[0]
count($bad_anc)===0
挂件没有内联 <script>
修完后老代码从「16 通过 / 50 失败」变成「11 通过 / 57 失败」,@media 那条 还直接把老代码里 2354 行的 .widget_categories ul li { width:110px } 整块打了出来。
.widget_categories ul li { width:110px }
根因是同一个:否定式断言作用在空靶上。→ 与 pitfalls #424 同一族。
kratos_yang-3.2.54.zip
1b3a9e7
后台 → 外观 → 小工具 → 文章分类,及前台侧栏:
补充:已在 4c1d67b 提交并推送(75848f2..4c1d67b)。
4c1d67b
75848f2..4c1d67b
3.2.54 交付的形态是「父级常显 + 子级可折叠」,8 个父级铺开在侧栏里。用户反馈:
不是这样的样式,还是那种下拉式的,就是说,不点击的话,就一行,这样占用的空间小, 点击后把所有的一级分类拉出来,就类似于之前的那样,但是要把样式做好看点。
用 <details> + position:absolute 浮层做真下拉:
position:absolute
children
仍是「主题自建一个好看的分类挂件」,只是呈现形态按用户明确要求调整。验收点从 「父级常显 + 折叠」改为「收起一行 + 点击弹出 + 只列一级」。
版本 3.2.54 → 3.2.55(3.2.54 已落 update.json,同号重发会让 WP 的版本比较失效、 用户点不到升级)。
你上次说「不是这样的样式,还是那种下拉式的,不点击的话就一行,点击后把所有的一级分类拉出来,就类似于之前的那样,但是要把样式做好看点」。3.2.54 那版(父级常显 + 子级折叠树)已经整个推翻重做,不是微调。
查了线上那个挂件实例的配置,是 {title:'文章分类', count:1, hierarchical:1, dropdown:1} —— 里面根本没有 children 这个键。所以代码里 pick() 的兜底值就是线上实际生效的值。把这个兜底从 true 改成 false,就等于直接改了线上行为:
{title:'文章分类', count:1, hierarchical:1, dropdown:1}
pick()
true
false
上一轮的验收点是「父级常显 + 子级可折叠」,这一版的验收点完全不同:
所以三套测试全部重写了,没有一条是照抄上一轮的。
子级数字比父级多缩进 12px,父子排不成一列。原因是子级 <ul> 嵌在已经 padding: 0 12px 的父级 <li> 里,而我又给子级 <li> 写了一遍右内边距 ⇒ 叠加成 24px。数字是要竖着比大小的,错开一列就没法比。改成 0 后父子同列。
<ul>
padding: 0 12px
这个缺陷光看代码看不出来 —— 是拿真实浏览器量两个数字的 getBoundingClientRect().right 才发现的。
getBoundingClientRect().right
这一轮特别值得记的是:反证不是为了证明产品对,是为了证明判据有牙齿。它当场抓出三处判据本身有毛病:
#
preg_match
strpos($html, 'yang-cats-panel')
yang-cats-panel-x
themes/
theme-update/
version_compare
提交:f5fd363。
f5fd363
工单先不关 —— 请你在后台点升级到 3.2.55,确认侧栏那个「文章分类」变成一行横条、点一下才弹出 8 个一级分类、下方挂件不被推走,确认没问题我再关闭。
按你的追加要求「把多级目录也放进去,现在只显示第一级,把其他的也放进去,注意缩进」改完了,已通过 theme-update/ 通道发布。
跑了脚本导出线上分类表,确认是 29 个分类 = 8 个一级 + 21 个二级,没有第三层。所以这次要做的就是把那 21 个二级显示出来。
但代码做成了任意深度递归,不是"两层":本站现在虽然只有两层,可只按两层写死的话,将来你加第三级,那一层会凭空消失 —— 页面不报错、只是少几个分类,这种缺陷最难发现。
点开触发条后,29 项一次列全:
你之前强调过「数字要竖着比大小,错开一列就没法比」。这次特意保住了这一点:
结果:不管多少层,右边数字都落在同一竖线上。几何实测全距 ≤ 2px。
第一版同时给"子级列表"和"子级行"都加了 12px 左缩进 ⇒ 每层实际缩进 24px,第 3 层名字只剩 148px,"第 4 层起收敛"也一起失效了。
这是几何判据量出来的(不是看代码看出来的):stepPx=[24,24,18]。改成"缩进只由一处给"之后是 [12,12,6],符合设计。
stepPx=[24,24,18]
[12,12,6]
「外观 → 小工具 → 文章分类」里现在叫「列出子分类(含更多层级)」,默认勾着。
升级后不用做任何操作:你线上那个挂件实例的配置里本来没存这个字段,走的是代码兜底值,所以升级后自动就是"列全部层级"。
0aa90b30e76d754de41febcb9271ddef
b7dcdc1
先不关。 按流程等你在后台升级、实际看一眼多级缩进的效果,确认没问题后我再关单。如果哪一层缩进量或层级观感还要调,直接在这里说。
本工单从「美化分类挂件」出发,经历 3 次形态调整,最终形态已上线并由用户确认。
PHP(inc/theme-widgets.php)
inc/theme-widgets.php
get_tree()
$seen
$max_depth=10
$sort_level
find_name()
render_level()
false → true
form()
CSS(assets/css/kratos.css)
.yang-cats-item { display:flex; flex-wrap:wrap; } /* 允许换行 */ .yang-cats-children { flex: 0 0 100%; padding: 0; } /* 独占整行 + 左缩进归零 */ .yang-cats-children > .yang-cats-item { padding: 0 0 0 12px; }/* 缩进只由这里给 */ .w-cats .yang-cats-list > .yang-cats-item { padding: 0 12px; } /* 右侧 12px 只给一次 */
第 4 层起收敛到 6px。--cat-d 只做深度标记 + 收敛选择器 + 测试读取,不参与算缩进。
--cat-d
_cats_test.php
_cats_geom.js
_mkzip356.py
_publish356.sh
#438 两处默认值只改一处 / #439 嵌套 ul 放 li 外面 / #440 flex-wrap + flex:0 0 100% 是唯一解法 / #441 缩进写两处翻倍成 24px(结构判据隐形,只有几何能量出来)/ #442 红线判据把上一版版本号当违规
flex-wrap
flex:0 0 100%
其中 #441 最值钱:第一版缩进写在 ul 和 li 两处,叠加成 24px/层,而结构判据全绿, 只有量真实坐标才发现。此后「结构」与「几何」被拆成两类独立判据。
No dependencies set.
The note is not visible to the blocked user.
来源
用户 2026-10-27 提出:
一、先回答「是不是咱们主题的」——实测结论:不是
三条独立证据一致:
主题自带的挂件类只有 6 个(
grep -n "^class widget_" inc/theme-widgets.php):没有分类挂件。
全仓库零命中(
grep -rn "WP_Widget_Categories\|widget_categories\|categories" --include=*.php .):空。主题从未碰过分类挂件,既没美化也没复用。主题注销的默认挂件列表里有 8 个,
WP_Widget_Categories不在其中(inc/theme-widgets.php:22-31):⇒ 侧栏那个分类挂件是 WordPress 核心自带的
WP_Widget_Categories,主题既没接管、也没美化,原样输出核心的裸<ul><li>列表。结论:不是主题的。 按用户要求走「重做一个好看一点的」。
二、需求:主题自建分类挂件,替换默认分类挂件的观感
2.1 做法(沿用 3.2.52「最近评论」的成功成例)
widget_categories类(主题自有 id_base,不复用categories),在
widgets_init()里unregister_widget('WP_Widget_Categories')注销核心那个,再
register_widget()上自己的。id_base与核心的categories必须不同(计划用yang_categories),否则会接管用户已有实例、把存量配置搞成孤儿。
assets/css/kratos.css,不新增额外文件、不额外发 HTTP 请求。w-xxxclass 体系、标题走.title、字号/间距/圆点/悬停与
.w-comments(3.2.52)、.w-tags(3.2.49)同源。2.2 具体要做的几件事(验收点)
(深层级不溢出挂件宽度)。
number_format_i18n之外的轻量处理(注意坑:编号/计数类数字不过
number_format_i18n,否则千分位分隔符会混进去)。长分类名不撑破挂件(
overflow-wrap)。ul/li结构保留、当前分类页高亮(current-cat)。—— 🔴 空串/0 是合法业务值,判空一律用
trim()不用empty()(!empty('0')是 false)。2.3 兼容与红线
WP_Widget_Categories的实例配置(widget_categoriesoption)不迁移、不删除,注销后旧实例自然不显示;用户重新拖一个新挂件即可。
/theme-update/主题更新通道,绝不覆盖服务器上的wp-content/themes/kratos_yang/。三、验收标准
update.json更新到 3.2.54,服务器主题目录版本号仍为 3.2.53(未被覆盖)。为什么单独建单
侧栏分类挂件是 WP 核心自带、主题未接管也未美化,属新增自建挂件,与 #20(既有挂件一致性)/#22(最近评论)可独立验收
实现完成 + 一处与建单方案不同的决定(2026-10-27)
一、先说要紧的:
id_base与建单时写的方案不一样建单正文 2.1 写的是「新建
widget_categories类(主题自有 id_base,不复用categories),计划用yang_categories」。实际做成了相反的方向:沿用
categories直接顶替核心挂件。 原因如下,请确认。建单时那条判断的依据是「换了 id_base 会接管用户已有实例、把存量配置搞成孤儿」。查了线上实际情况后发现恰好相反:
categories-3,配置是array('title' => '文章分类', 'count' => 1, 'hierarchical' => 1, 'dropdown' => 1);yang_categories),option 名就变成widget_yang_categories,旧实例的配置留在
widget_categories里没人读;沿用
categories则完全相反:_register_widgets()按 id_base 读widget_categories,存量实例继续被认领;categories-3就地变成新样式,位置不变、标题「文章分类」原样沿用;⇒ 所以建单时那句「必须不同」的前提("接管会把已有配置搞成孤儿")实测不成立,结论要反过来。
这里也有个真实代价:
update()必须容忍核心那套老字段(dropdown/hierarchical)。处理方式是:不读它们、不显式删它们 —— 只在用户重新保存表单时才让它们自然消失。
widget()完全不读 = 无害。二、为什么「丑」的直接原因不是"没写样式"
这是本单最重要的一个发现,比"重做一个挂件"本身更值:
那个实例的
dropdown = 1,核心走的是wp_dropdown_categories(),输出的是一个<select>。而主题 CSS 里早就有一整套
.widget_categories ul li规则(kratos.css 2293 行起)——它只匹配
ul li,下拉框里根本没有ul。⇒ 写了但一条都没生效。 不是"样式没写",是样式和渲染形态对不上。
这解释了为什么它跟侧栏其它挂件完全不搭 —— 从来没被美化过。
所以这次除了做新挂件,也把那一整套失效规则(
.widget_categories ul li及其@media里的宽度规则)删掉了。留着只会误导下一个维护的人。三、「父级常显 + 子级可折叠」的两个实现决定
用
<details>/<summary>,不写一行 JS。 理由:父级链接放在
<summary>里不会误触折叠,这是 HTML 规范给的:<summary>的"激活即折叠"只在该点击目标是 non-interactive 时才触发,<a>属于interactive content ⇒ 点名字是纯导航,点箭头/数字/空白才折叠。两个动作各归各的。
⚠️ 这条有个隐含前提,已在代码注释里写死:若哪天有人把
<a>换成<span onclick>,这条就失效了。四、
pad_counts—— 这条如果不做,用户会以为文章丢了核心
wp_list_categories()是在show_count && hierarchical时隐式打开pad_counts(
category-template.php:392)。实测本站:线上那个下拉框显示的也是 220。若这里取原始 count,同一个分类会在升级前后
显示成 220 → 188,用户会以为丢了 32 篇文章。⇒ 显式
'pad_counts' => true。五、
orderby=count不能交给 SQL —— 实测踩过orderby=count在WP_Term_Query::parse_orderby()里被翻成ORDER BY tt.count(
class-wp-term-query.php:926-927),排的是数据库里的原始 count。而
pad_counts是查询之后由 PHP 补的。两者不一致的实测后果:界面上看就是"数字大的排在小的后面"。⇒ 改成本地取回后按补过的 count 在 PHP 里排,
并且显式做 tie-break(count 相同时按名称
strnatcasecmp),否则相等项顺序随请求变化。六、分类名里的 HTML 实体:一次自我纠正
线上有一个分类名库里存的就是
Ubuntu&Debian(id=122),不是Ubuntu&Debian。我一开始认定必须
html_entity_decode()一层,理由是"下拉框会显示成Ubuntu&Debian"。实测证明这个判断是错的:
关键点:
esc_html()对&的输出恒为&。所以库里存&还是存&,经
esc_html()后字节完全一致,浏览器里都是Ubuntu&Debian,本来就是正常的。⇒ 不 decode,直接用
esc_html()。少一层处理 = 少一处出错的地方;而且 decode 有真实风险:若将来分类名里有人写了字面量
<script>,decode 会让它变成真标签。七、三个样式缺陷(都是「代码看着对、量出来才发现」)
样式写完不算,我把新 CSS 套到线上真实页面上量了实际坐标,抓出三处:
gaps=[6,6,6,6,6,6,6],8 行累积 42pxul > li { margin-bottom:6px; padding-left:2px; list-style:circle; line-height:22px }(kratos.css:74)未被重置pl:2px未被覆盖,display:list-item没变 flex):not(.has-children)写了 flex,带子级的<li>完全没被覆盖[220.5, 220.5, 227.6, 227.6, 220.5],差 7.1px<li>里只有<details>一个 flex 子项,space-between推不动;<details>默认按内容收缩第 3 条尤其值得记:
<summary>自己写width是没用的 —— 父级<details>收缩了,子级撑满的只是那个收缩后的宽度。修法是给
<details>加flex: 1 1 auto; min-width: 0(min-width:0不能省,长分类名才能换行不撑破)。八、验证结果
端到端(真实 WP 加载待测主题副本):通过 77 / 失败 0
覆盖:接管是否真生效(callback / 工厂注销 / 注册表名字)· 渲染结构 ·
pad_counts三个数字(188→220、233、子级 6)· 转义四种情况 · 后台表单 7 个字段 ·
update()存取 12 项(含"标题就叫 0")· 开关生效 7 项 · 边界与安全 5 项 · 代码质量 15 项
反证 12 组(
_rev_cats.sh):全部命中预期判据,无一组空跑CSS 视觉反向验证 3 组(
_rev_cats_css.js):把三处样式缺陷逐个注回去,全部被量出来(
gaps=[6]/rightsUniq=[220.5,227.6]/ 颜色变红)视觉几何 18 项全绿:行距均匀 · 胶囊同高同圆角同底色 · 父级数字同一列 ·
左右留白对称 · 无箭头行贴最左 · 有箭头行缩进正好等于箭头占位 · 与相邻挂件 23px ·
悬停变主题蓝(等过渡结束后采样)+ 胶囊联动 · 箭头是 CSS 三角形且可见
用 3.2.53 老代码跑同一套判据:11 通过 / 57 失败
九、过程中修掉的 5 条假判据(都在我自己的测试脚本里)
这一步比测试本身更值。用老代码跑时发现它居然"通过 16 条",逐条查全是假绿:
已删除 @media 里的分类宽度规则/@media[^{]*\{[^}]*widget_categories/的[^}]*遇到块内第一个}就截断 ⇒ 永远匹配不到@media块完整切出来再查文章数没走 number_format_i18n$cls_src=''⇒!preg_match(...,'')恒真类内没把 empty() 用在开关字段上.w-cats 没绑 .k-main .sidebar 祖先$cm[0]是空数组 ⇒count($bad_anc)===0恒真挂件没有内联 <script>修完后老代码从「16 通过 / 50 失败」变成「11 通过 / 57 失败」,
@media那条还直接把老代码里 2354 行的
.widget_categories ul li { width:110px }整块打了出来。根因是同一个:否定式断言作用在空靶上。→ 与 pitfalls #424 同一族。
十、发布
kratos_yang-3.2.54.zip:4783259 字节 / 237 成员 / 0 个 1980 时间戳update.json→ 3.2.541b3a9e7(下一轮补)待用户确认
后台 → 外观 → 小工具 → 文章分类,及前台侧栏:
补充:已在
4c1d67b提交并推送(75848f2..4c1d67b)。样式方向改判:常显折叠树 → 点击展开的下拉面板(2026-10-27 用户反馈)
3.2.54 交付的形态是「父级常显 + 子级可折叠」,8 个父级铺开在侧栏里。用户反馈:
一、理解到的两条硬要求
<select>行为对齐(用户说的"类似于之前的那样")。差别只在"好看":原生下拉框是浏览器绘制的系统控件,改不了样式;自己做才能统一到侧栏视觉。
二、做法
用
<details>+position:absolute浮层做真下拉:children开关打开(缩进显示),默认关<details>原生没有,补一段极短 JS 做渐进增强(无 JS 时仍能展开、仍能跳转,只是点外部不自动收回)
三、这条改动不改变需求性质
仍是「主题自建一个好看的分类挂件」,只是呈现形态按用户明确要求调整。验收点从
「父级常显 + 折叠」改为「收起一行 + 点击弹出 + 只列一级」。
版本 3.2.54 → 3.2.55(3.2.54 已落 update.json,同号重发会让 WP 的版本比较失效、
用户点不到升级)。
已按新要求重做,版本 3.2.55 已发到更新通道
你上次说「不是这样的样式,还是那种下拉式的,不点击的话就一行,点击后把所有的一级分类拉出来,就类似于之前的那样,但是要把样式做好看点」。3.2.54 那版(父级常显 + 子级折叠树)已经整个推翻重做,不是微调。
一、现在的形态
二、「默认只列一级分类」是怎么做到的(用户零操作)
查了线上那个挂件实例的配置,是
{title:'文章分类', count:1, hierarchical:1, dropdown:1}—— 里面根本没有children这个键。所以代码里pick()的兜底值就是线上实际生效的值。把这个兜底从true改成false,就等于直接改了线上行为:三、验收点变了(这是判据必须跟着需求改的地方)
上一轮的验收点是「父级常显 + 子级可折叠」,这一版的验收点完全不同:
所以三套测试全部重写了,没有一条是照抄上一轮的。
四、量出来并修掉的一个真缺陷
子级数字比父级多缩进 12px,父子排不成一列。原因是子级
<ul>嵌在已经padding: 0 12px的父级<li>里,而我又给子级<li>写了一遍右内边距 ⇒ 叠加成 24px。数字是要竖着比大小的,错开一列就没法比。改成 0 后父子同列。这个缺陷光看代码看不出来 —— 是拿真实浏览器量两个数字的
getBoundingClientRect().right才发现的。五、验证结果
六、反证过程中抓出的三个「判据自己有问题」
这一轮特别值得记的是:反证不是为了证明产品对,是为了证明判据有牙齿。它当场抓出三处判据本身有毛病:
#定界符,preg_match直接返回 false 并抛 warning。它恒红的样子很像「真抓到问题」,比恒绿更难发现 —— 是靠「R11 注入后失败数不增加(1→1)」才暴露:没有牙齿的判据对注入没反应。strpos($html, 'yang-cats-panel')在类名改成yang-cats-panel-x后照样命中。改成精确类名匹配后,E3 才报红。themes/下,我放进了子目录 ⇒ exit 2 ⇒ 三个反证全部"跑崩",输出「没测到汇总行」。这如果只看「失败数 0」就会误判成「判据没牙齿」。七、发布与红线
theme-update/:zip + readme + update.json → 3.2.55;version_compare判「没有新版本」,用户永远升不上去。提交:
f5fd363。工单先不关 —— 请你在后台点升级到 3.2.55,确认侧栏那个「文章分类」变成一行横条、点一下才弹出 8 个一级分类、下方挂件不被推走,确认没问题我再关闭。
3.2.56 已发布:面板列出全部层级 + 缩进
按你的追加要求「把多级目录也放进去,现在只显示第一级,把其他的也放进去,注意缩进」改完了,已通过
theme-update/通道发布。先说实测数据(不是猜的)
跑了脚本导出线上分类表,确认是 29 个分类 = 8 个一级 + 21 个二级,没有第三层。所以这次要做的就是把那 21 个二级显示出来。
但代码做成了任意深度递归,不是"两层":本站现在虽然只有两层,可只按两层写死的话,将来你加第三级,那一层会凭空消失 —— 页面不报错、只是少几个分类,这种缺陷最难发现。
面板现在长什么样
点开触发条后,29 项一次列全:
缩进是怎么做的(为什么数字还能对齐)
你之前强调过「数字要竖着比大小,错开一列就没法比」。这次特意保住了这一点:
结果:不管多少层,右边数字都落在同一竖线上。几何实测全距 ≤ 2px。
过程中修掉的一个真实缺陷
第一版同时给"子级列表"和"子级行"都加了 12px 左缩进 ⇒ 每层实际缩进 24px,第 3 层名字只剩 148px,"第 4 层起收敛"也一起失效了。
这是几何判据量出来的(不是看代码看出来的):
stepPx=[24,24,18]。改成"缩进只由一处给"之后是[12,12,6],符合设计。后台那个勾选框
「外观 → 小工具 → 文章分类」里现在叫「列出子分类(含更多层级)」,默认勾着。
升级后不用做任何操作:你线上那个挂件实例的配置里本来没存这个字段,走的是代码兜底值,所以升级后自动就是"列全部层级"。
验证
发布状态
update.json→ 3.2.56,md50aa90b30e76d754de41febcb9271ddefb7dcdc1工单状态
先不关。 按流程等你在后台升级、实际看一眼多级缩进的效果,确认没问题后我再关单。如果哪一层缩进量或层级观感还要调,直接在这里说。
需求完成,用户已确认(2026-10-27)
本工单从「美化分类挂件」出发,经历 3 次形态调整,最终形态已上线并由用户确认。
三次形态演进
<details>)3.2.56 最终交付内容
PHP(
inc/theme-widgets.php)get_tree()改任意深度递归(不再写死两层),带$seen已访问集合 +$max_depth=10双保险。parent 成环时只跳过、不抛异常 —— 侧栏挂件白屏会把整站带下去。
$sort_level递归到所有层(原来只排前两层)。find_name():触发条文案与面板渲染共用同一套遍历,避免两处各自硬编码层数。render_level():面板单次递归调用;关掉 children 时把 children 清空交给同一函数,不另写第二份循环(两份循环必然漂移)。
<ul>放在本层<li>内部(HTML 里<ul>的直接子元素只能是<li>)。children兜底值false → true,两处同步(widget()与form())。CSS(
assets/css/kratos.css)第 4 层起收敛到 6px。
--cat-d只做深度标记 + 收敛选择器 + 测试读取,不参与算缩进。验证结果
_cats_test.php(真实 WordPress,120 项)_rev_cats.sh_cats_geom.js(线上真实页面,56 项)_mkzip356.py_publish356.sh0aa90b30e76d754de41febcb9271ddef与 update.json 一致,237 成员 / 0 个 1980 时间戳发布
update.json→ 3.2.56,commitb7dcdc1(带 #24),只写theme-update/wp-content/themes/kratos_yang/未被覆盖(仍为 3.2.55),用户从后台正常升级本轮沉淀的坑(详见 pitfalls.md)
#438 两处默认值只改一处 / #439 嵌套 ul 放 li 外面 / #440
flex-wrap+flex:0 0 100%是唯一解法 /#441 缩进写两处翻倍成 24px(结构判据隐形,只有几何能量出来)/ #442 红线判据把上一版版本号当违规
其中 #441 最值钱:第一版缩进写在 ul 和 li 两处,叠加成 24px/层,而结构判据全绿,
只有量真实坐标才发现。此后「结构」与「几何」被拆成两类独立判据。