优化了一些问题

This commit is contained in:
2026-08-29 15:50:04 +08:00
parent f9e411dda8
commit e54228d509
6 changed files with 89 additions and 3 deletions
+1
View File
@@ -38,6 +38,7 @@
!/tests/widget-system/front-submit-editor-contract.test.cjs
!/tests/widget-system/comment-smilies-removal-contract.test.cjs
!/tests/widget-system/pay-card-contract.test.cjs
!/tests/widget-system/circular-button-icon-margin-contract.test.cjs
!/tests/asset-ledger/
!/tests/asset-ledger/*.php
!/tests/visual/docker-compose.zibll.yml
@@ -0,0 +1,31 @@
修复文章详情页分割线右侧三点图标不居中的问题
## 根因
三点按钮(`inc/helpers/aksrt-single-components.php:131-146`)为 `<button class="but cir post-drop-meta__toggle"><i class="fa fas fa-ellipsis-h"></i></button>`,位于 `templates/parts/single-author-bar.php` 中 `.single-meta-row` 分割线右端。`.but.cir` 是 `inline-flex; justify-content: center`,flex 居中的是子元素的 margin box。`assets/css/core/buttons.css` 中存在特异性冲突:
- 第 183 行 `.but > i[class*="fa-"]:not(.fa-angle-right) { margin-right: 0.25em; }` 特异性 (0,3,1)
- 第 225 行 `.but.cir > .fa { margin: 0; }`(本意的重置规则)特异性 (0,3,0),被压掉
导致图标右侧残留 0.25em 外边距(15px 字号下约 3.75px),三点整体左偏约 1.9px。这是全局 bug,所有 `.but.cir` 图标按钮都受影响。
## 修改内容
1. **`assets/css/core/buttons.css:224-225`** — 加强重置规则的特异性到 (0,4,0),稳定压过 (0,3,1) 的 margin 规则(不依赖源码顺序):
```css
.but.cir svg.icon,
.but.cir > .fa,
.but.cir > .fa:not(.fa-angle-right),
.but.cir > i[class*="fa-"]:not(.fa-angle-right) { margin: 0; }
```
附一行注释说明 `:not()` 变体是为压过 `.but > i[class*="fa-"]:not(.fa-angle-right)` 的 margin 规则。保留原 `.but.cir > .fa` 选择器以继续覆盖 fa-angle-right 图标。不改 main.css(旧的手工捆绑副本):public 捆绑包中 core/buttons.css 副本排在 main.min.css 之后,高特异性重置必然胜出。
2. **重建捆绑包** — 运行 `node tools/build-public-assets.mjs`(esbuild 已就绪),重新生成 `assets/css/build/public.css/.min.css` 等捆绑文件。
3. **新增契约测试** `tests/widget-system/circular-button-icon-margin-contract.test.cjs`:断言源文件 buttons.css 与构建产物 public.min.css 中包含加强后的重置选择器(防止特异性回归),沿用现有 `*.test.cjs` 契约测试模式。
## 验证
- `node --test tests/widget-system/circular-button-icon-margin-contract.test.cjs` 通过
- 在构建后的 `public.min.css` 中确认重置规则存在且位于 margin 规则之后
- 刷新文章详情页目视确认三点图标在圆形按钮内水平居中(同时全站其他 `.but.cir` 图标按钮居中恢复正常)
+4 -1
View File
@@ -1027,8 +1027,11 @@ strong, b {
.circular.radius4,
.badg.cir.radius4,
.but.cir.radius4 { border-radius: 4px; }
/* :not() variants outrank the .but > i[class*="fa-"]:not(.fa-angle-right) margin rule */
.but.cir svg.icon,
.but.cir > .fa { margin: 0; }
.but.cir > .fa,
.but.cir > .fa:not(.fa-angle-right),
.but.cir > i[class*="fa-"]:not(.fa-angle-right) { margin: 0; }
/* button group (joined) */
.but-group { display: inline-flex; }
+1 -1
View File
File diff suppressed because one or more lines are too long
+4 -1
View File
@@ -221,8 +221,11 @@
.circular.radius4,
.badg.cir.radius4,
.but.cir.radius4 { border-radius: 4px; }
/* :not() variants outrank the .but > i[class*="fa-"]:not(.fa-angle-right) margin rule */
.but.cir svg.icon,
.but.cir > .fa { margin: 0; }
.but.cir > .fa,
.but.cir > .fa:not(.fa-angle-right),
.but.cir > i[class*="fa-"]:not(.fa-angle-right) { margin: 0; }
/* button group (joined) */
.but-group { display: inline-flex; }
@@ -0,0 +1,48 @@
'use strict';
const assert = require('node:assert/strict');
const fs = require('node:fs');
const path = require('node:path');
const test = require('node:test');
const root = path.resolve(__dirname, '..', '..');
const read = (file) => fs.readFileSync(path.join(root, file), 'utf8');
// .but.cir icon buttons center via inline-flex; the 0.25em icon margin rule
// (.but > i[class*="fa-"]:not(.fa-angle-right), specificity 0,3,1) must be
// outranked by the reset, or glyphs like fa-ellipsis-h sit left of center.
const resetSelectors = [
'.but.cir svg.icon',
'.but.cir > .fa,',
'.but.cir > .fa:not(.fa-angle-right),',
'.but.cir > i[class*="fa-"]:not(.fa-angle-right)',
];
test('buttons.css resets direct-child icon margins on circular buttons', () => {
const buttons = read('assets/css/core/buttons.css');
const resetStart = buttons.indexOf('.but.cir svg.icon,');
assert.notEqual(resetStart, -1, 'missing .but.cir icon margin reset');
const resetBlock = buttons.slice(resetStart, buttons.indexOf('}', resetStart));
for (const selector of resetSelectors) {
assert.ok(resetBlock.includes(selector), `reset is missing selector: ${selector}`);
}
assert.match(resetBlock, /margin:\s*0\s*;/);
const marginStart = buttons.indexOf('.but .icon,');
assert.notEqual(marginStart, -1);
assert.ok(marginStart < resetStart, 'margin rule must precede the reset');
});
test('the built public bundle carries the reset after the margin rule', () => {
const bundle = read('assets/css/build/public.min.css');
const minifiedReset = bundle.indexOf('.but.cir>i[class*=fa-]:not(.fa-angle-right){margin:0}');
assert.notEqual(minifiedReset, -1, 'missing minified .but.cir icon margin reset');
const marginRule = /\.but>i\[class\*=fa-\]:not\(\.fa-angle-right\)\{margin-right:\.25em\}/g;
let lastMargin = -1;
for (const match of bundle.matchAll(marginRule)) {
lastMargin = match.index;
}
assert.notEqual(lastMargin, -1, 'expected the icon margin rule to still exist');
assert.ok(minifiedReset > lastMargin, 'reset must appear after the last margin rule copy');
});