全局美化 v2

This commit is contained in:
tamakyi
2026-10-19 11:22:01 +08:00
parent d47329d505
commit b165995a1c
17 changed files with 3104 additions and 63 deletions
+29
View File
@@ -173,3 +173,32 @@ P2 = 图标统一(UIkit icons 取代 emoji/位图 png)、标签减量、view
`outputs/preview-{home,box,explore}-{light,dark}.html`,用无头 Chrome 截图核对过亮暗两套。 `outputs/preview-{home,box,explore}-{light,dark}.html`,用无头 Chrome 截图核对过亮暗两套。
⚠️ 本机 **Chrome 新版 headless 会挂住**,必须 `--headless=old`,且页面外部资源要本地化 ⚠️ 本机 **Chrome 新版 headless 会挂住**,必须 `--headless=old`,且页面外部资源要本地化
(`outputs/preview-assets/`,从 cdnjs.snrat.com 取 UIkit 3.3.3;cloudflare/jsdelivr 在本机不通)。 (`outputs/preview-assets/`,从 cdnjs.snrat.com 取 UIkit 3.3.3;cloudflare/jsdelivr 在本机不通)。
## 封面遮罩 P0 缺陷修复(重要)
**症状**:`.tb-cover::after` 遮罩不渲染,且**完全不报错**——截图对比像素只差 ±1(只看着像"没效果")。
**根因**:UIkit 3.3.3 自带
`.uk-flex::after,.uk-flex::before{display:none}`(优先级 0,2,0),
而封面元素是 `uk-flex`,我写的 `.tb-cover::after`(0,1,1)优先级不够 → 被压成 display:none。
定位方法:`getComputedStyle(el,'::after').display` 得到 `none`;再逐个 `classList.remove` 试 →
去掉 `uk-flex` 立刻变 `block`。最后在 uikit.min.css 里 grep 到那条规则。
**修法**:选择器写成 `.tb-cover::after, .tb-cover.uk-flex::after` 并显式 `display:block`。
**实测效果**(无头 Chrome 截图 + 像素测量,非目测):
- 深色渐变背景 + 白字:封面底部亮度 0.229→0.102,白字对比度 **3.77→6.89**(过 WCAG AA 4.5)
- 线上默认背景(`box_background.png` 2048×1152,接近全白)+ 黑字:0.912→0.971,对比度 19.25→20.42,
即**对默认背景几乎没有可见影响**
- 亮度判定 JS 已验证:`#fff` 会加 `tb-cover-dark`,`#000` 不加。
**教训**:CSS 改动不能只看"规则写上了/类加上了",必须量像素。伪元素 + UIkit 工具类组合要在计算样式层验证。
## 账号背景图的真实链路(回答用户提问时查证)
- 注册时把 `[upload] default_background` **一次性复制**进 `users.background`(`route/auth/register.go:51`),
不是每次渲染兜底 → 之后改配置**不影响存量用户**。
- 页面只用 `.PageUser.Background` 渲染 `data-src`;为空则无背景图、无遮罩。
- 分享卡是另一条路:`route/question/share.go:345` 用 `user.Background`,为空时回退 `conf.App.ShareBackground`。
- 线上 `box.shiroko.one/_/tamakyi` 实际值 = `https://r2.337845818.best/tamabox/box_background.png`。
- 预览里那张"渐变图"是 `zz_preview_test.go` 造的测试数据(data: URI 渐变),**不是产品行为**;
顺带发现 Go 模板会把 `data:` URI 在属性上下文里替换成 `#ZgotmplZ`(真实 http URL 不受影响)。
Binary file not shown.

After

Width:  |  Height:  |  Size: 189 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 216 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 152 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 119 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 297 KiB

File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Binary file not shown.

Before

Width:  |  Height:  |  Size: 88 KiB

+7 -1
View File
@@ -468,8 +468,14 @@
border-radius: var(--tb-radius) var(--tb-radius) 0 0; border-radius: var(--tb-radius) var(--tb-radius) 0 0;
overflow: hidden; overflow: hidden;
} }
.tb-cover::after { /* ⚠️ 必须带 .uk-flex 复合选择器:UIkit 3.3.3 里有
.uk-flex::after,.uk-flex::before{display:none}(优先级 0,2,0),
会把这个遮罩伪元素整个干掉——不报错、只是什么都不显示(踩过一次)。
单写 .tb-cover::after 优先级不够,会被它压回 display:none。 */
.tb-cover::after,
.tb-cover.uk-flex::after {
content: ""; content: "";
display: block;
position: absolute; position: absolute;
inset: 0; inset: 0;
z-index: 0; z-index: 0;
+4 -2
View File
@@ -53,13 +53,15 @@ func TestGenPreview(t *testing.T) {
// 提问箱主页:带背景图(渐变占位)+ 浅色文字,验证遮罩方向 // 提问箱主页:带背景图(渐变占位)+ 浅色文字,验证遮罩方向
boxData := publicBaseData() boxData := publicBaseData()
boxData["VConsole"] = false boxData["VConsole"] = false
boxData["Background"] = "data:image/svg+xml;utf8," + bgURI := "data:image/svg+xml;utf8," +
strings.NewReplacer("#", "%23").Replace( strings.NewReplacer("#", "%23").Replace(
`<svg xmlns='http://www.w3.org/2000/svg' width='900' height='500'><defs><linearGradient id='g' x1='0' y1='0' x2='1' y2='1'><stop offset='0' stop-color='#3b6ea5'/><stop offset='1' stop-color='#d98b6a'/></linearGradient></defs><rect width='900' height='500' fill='url(#g)'/></svg>`) `<svg xmlns='http://www.w3.org/2000/svg' width='900' height='500'><defs><linearGradient id='g' x1='0' y1='0' x2='1' y2='1'><stop offset='0' stop-color='#3b6ea5'/><stop offset='1' stop-color='#d98b6a'/></linearGradient></defs><rect width='900' height='500' fill='url(#g)'/></svg>`)
// 注意:模板读的是 .PageUser.Background,必须写进 PageUser 里,否则 {{ if .PageUser.Background }} 为假、遮罩类不会渲染
boxData["PageUser"].(map[string]interface{})["Background"] = bgURI
boxData["PageUser"].(map[string]interface{})["Usernamecolor"] = "#ffffff" boxData["PageUser"].(map[string]interface{})["Usernamecolor"] = "#ffffff"
boxData["PageUser"].(map[string]interface{})["Introcolor"] = "#ffffff" boxData["PageUser"].(map[string]interface{})["Introcolor"] = "#ffffff"
// 无头 Chrome 里 uk-img 的懒加载不一定触发,这里直接把背景图写进 CSS 便于核对遮罩 // 无头 Chrome 里 uk-img 的懒加载不一定触发,这里直接把背景图写进 CSS 便于核对遮罩
coverCSS := `<style>.tb-profile-cover{background-image:url("` + boxData["Background"].(string) + coverCSS := `<style>.tb-profile-cover{background-image:url("` + bgURI +
`");background-size:cover;background-position:center;}</style>` `");background-size:cover;background-position:center;}</style>`
for _, theme := range []string{"light", "dark"} { for _, theme := range []string{"light", "dark"} {
box, err := renderToString(t, "question/list", boxData) box, err := renderToString(t, "question/list", boxData)