Files
tamabox/docs/theme-new-sakura.md
tamakyi 8941744621 new-sakura:顶栏改半透明磨砂,并立规矩禁止全透明
线上(box.shiroko.one/_/tamakyi)两个现象来自同一处:`--tb-navbar` 是实色,
顶栏那条带(y=20..80)把从右上角长出来的花枝齐刷刷切掉一段。

第一版顺手改成 transparent —— 花枝完整了,但顶栏"消失":导航失去依托,
文字直接压在花纹上。用户当场否掉,要求半透明。

- 顶栏:`rgba(253,246,248,0.72)` / 深色 `rgba(34,26,30,0.72)`,配
  `.uk-navbar-container{backdrop-filter:blur(12px)}`(令牌只能是颜色,模糊写在规则里)。
  0.72 有依据:导航文字 #6d555d 纯底 6.34:1,最坏情况(深花瓣在顶栏后面)压平后 5.08:1。
- 花枝 top -16px → 26px:顶栏半透明后,700~760px 宽度下枝头正好压在「登录」上(截图确认)。
- 手机底栏 #tb-mobile-utils 在 base.css 写死 rgba(255,255,255,.92),粉底上是一条白杠,
  主题里改成同色系 0.92。
- 新增 TestNavbarNotFullyTransparent:拦 `transparent` / `rgba(...,0)` / `#rrggbb00`,
  并说明"这不是审美偏好,是踩过之后立的规矩"。变异验证过(改回 transparent 立即 FAIL)。
  约定同时写进 static/css/themes/_scaffold.css 的令牌说明。
- StaticVersion 20261022b → 20261022c:css 改了不提版本号,浏览器拿着旧样式,
  现象就是"改了但线上没变"。
2026-10-22 16:05:37 +08:00

205 lines
10 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# new-sakura(新·樱花):纯 CSS 樱花主题
和已有的 `sakura` 的区别:`sakura` 是「粉底 + 网点」的静态配色;`new-sakura` 把樱花本身
做成了**可摆放的装饰层** —— 整朵花、带缺口的花瓣、带枝的花朵树,全部是内联 SVG 的
`data:image/svg+xml` URI,写在主题 CSS 里。
因此这个主题:
- **不需要任何图片素材**,也不依赖对象存储(`static/images/themes/new-sakura/` 是空的);
- 任意视口缩放都不糊(矢量),DPI 无关;
- 换花色只改 `dev/theme-tools/gen_new_sakura.py` 里的一个色值,重跑即得;
- 没有额外 HTTP 请求 —— SVG 就在 CSS 里。
代价是 CSS 有 54KB(服务端有 gzip,实际传输约 4KB)。主题 CSS 走 `StaticVersion` 缓存,一次性开销。
## 结构
```
static/css/themes/new-sakura.css ← 生成产物,别手改
dev/theme-tools/gen_new_sakura.py ← 改这里,重跑
```
```bash
python dev/theme-tools/gen_new_sakura.py
```
生成器脚本里有三样东西:花形(`blossom` / `petal` / `branch` / `pattern`)、
每个页面作用域的位置表(`SCOPES`)、以及配色(`LIGHT` / `DARK`)。
## 每个页面的樱花位置
`--tb-deco` / `--tb-deco-size` / `--tb-deco-position` 三组各 10 槽,必须等长
(`TestDecoSlotCount` 会逐文件逐声明检查)。
| 作用域 | 选择器 | 构图 |
| --- | --- | --- |
| global | `html[data-theme-style="new-sakura"]` | 右下花枝 + 左上大花 + 两片落瓣 |
| box | `…[data-page-scope="box"]` | 右上花枝垂下(细端在外、粗端朝内)+ 左下花与落瓣 |
| question | `…[data-page-scope="question"]` | 左下花枝斜穿 + 右上大花 + 落瓣 |
| admin | `…[data-page-scope="admin"]` | 左上花枝 + 左侧底部小花(卡片占满宽度,右侧会被盖住) |
| profile | `…[data-page-scope="profile"]` | 右下花枝 + 左侧竖排小花与落瓣 |
| vote | `…[data-page-scope="vote"]` | 左上花枝 + 右侧花与落瓣 |
位置一律「关键字 + 偏移」两值写法(`right -24px top -16px`)。三值语法里混用
`left` / `bottom` 关键字是非法的,整条声明会作废、花瓣静默回到左上角。
### 窄屏(≤640px)
装饰层全部置 `none`:卡片占满宽度,左右只剩 ~16px 边距,枝条放哪儿都会糊在字上。
樱花改由底纹承担 —— `@media` 里换成**加强版 pattern**(透明度 ×1.9)并把格子收到 180px;
桌面那套 0.18~0.34 的透明度在 390px 上实测接近一片纯粉。
试过在「顶栏与卡片之间」那条 35px 的带子里放两朵小樱花:box 页卡片从 y=122 起还行,
question / admin 页卡片从 y=110 起就切掉半个,结果是黏在卡片边上的粉色碎块。手机上没有
可靠的留白,就不放装饰层 —— 观感交给配色与底纹。
⚠️ **窄屏覆盖必须重新声明每个作用域选择器**:`@media` 不提升特异性。
作用域块是 `html[data-theme-style][data-page-scope="box"]`(0,2,1),而媒体查询里常见的
`html[data-theme-style]`(0,1,1)根本压不住它 —— 表现是「手机上装饰层没关掉,桌面版照样铺开」。
深色作用域块更是 0,3,1,还得再补一条 `[data-theme="dark"][data-theme-style][data-page-scope]`。
`TestNarrowMediaOverridesScopedDeco` 会在这里把关:外层每一处非 none 的 `--tb-deco`,
媒体查询里都要求有同属性名、属性数不少于它的选择器再声明一次。
### 深色
- 花瓣换成更亮一档的粉(`#f0a0b8`),枝条提亮到能看见;
- 底纹 `--tb-page-pattern: none`:浅粉花瓣压在深底上会变成一层噪点;
- 整页背景改深色渐变(不用纯色,避免大面积死黑)。
深色块必须写成 `html[data-theme="dark"][data-theme-style="new-sakura"]`(三重选择器),
只写浅色块会被同特异性的作用域块盖掉。
## 顶栏与手机底栏
顶栏走**磨砂玻璃**,不是实色也不是全透明:
```css
--tb-navbar: rgba(253, 246, 248, 0.72); /* 亮色;深色 rgba(34, 26, 30, 0.72) */
html[data-theme-style="new-sakura"] .uk-navbar-container:not(.uk-navbar-transparent) {
-webkit-backdrop-filter: blur(12px);
backdrop-filter: blur(12px);
}
```
三种写法的实测结论:
| 写法 | 现象 |
| --- | --- |
| 实色 `#fdf6f8` | 花枝经过顶栏时被齐刷刷切掉一段,像被裁过 |
| `transparent` | 花枝完整了,但顶栏"消失":导航没有依托,文字直接压在花纹上 |
| `rgba(...,0.72)` + `blur(12px)` | 花枝透过来但被模糊成柔光,顶栏仍是一块可辨认的面 |
0.72 是怎么定的:导航文字 `#6d555d` 压在顶栏上,纯底色 6.34:1;最坏情况(深花瓣正好在顶栏后面)
压平后 5.08:1,仍过 AA。再往下调透明度就会掉到 4.5 以下。
`TestNavbarNotFullyTransparent` 会拦住 `--tb-navbar: transparent`(以及 `rgba(...,0)`、
`#rrggbb00`)—— 这条不是审美偏好,是把上面第二种写法踩过之后立的规矩。
手机底栏(`#tb-mobile-utils`)在 `base.css` 里写死 `rgba(255,255,255,.92)`,压在粉底上是
一条白杠;主题里按同一思路改成 `rgba(253,246,248,.92)` / `rgba(34,26,30,.92)`。
另外 box 作用域的花枝从 `top -16px` 下移到 `top 26px`:顶栏半透明后,700~760px 宽度下
枝头正好压在「登录」上(实测截图确认)。顶栏高 60 + 外边距 20,26px 起笔即落在顶栏下沿之上,
宽屏仍是"从右上角长出来"的观感。
## 配色与对比度
| 令牌 | 亮色 | 深色 | 说明 |
| --- | --- | --- | --- |
| `--tb-bg` | `#fdf6f8` | `#221a1e` | 纯色,不用渐变(整页色感一致) |
| `--tb-surface` | `#ffffff` | `#2c2227` | 卡片 |
| `--tb-text` | `#3a2c31` (12.4:1) | `#f3e6ea` (14.0:1) | |
| `--tb-text-secondary` | `#6d555d` (6.3:1) | `#c9aeb8` (8.3:1) | |
| `--tb-text-muted` | `#77626a` (5.3:1) | `#ab9099` (5.8:1) | 踩 AA 下限 |
| `--tb-primary` | `#b8436a` (4.9:1) | `#ff93b4` (8.2:1) | 原候选 `#c14a72` 只有 4.39:1,压深 |
| `--tb-on-primary` | `#ffffff` (5.2:1) | `#1b1014` (8.9:1) | 主色按钮上的字 |
## 素材包(可选,已传到对象存储)
主题线上**不需要**任何素材:樱花是内联矢量。但同一个花形也导出成了位图,
放在对象存储上备用(`themes/new-sakura/`,共 7 个对象):
| 键 | 内容 |
| --- | --- |
| `new-sakura.css` | 主题本体(备份,便于分发) |
| `branch-right.webp` / `branch-left.webp` | 花枝 840×640 透明底(互为镜像) |
| `blossom.webp` | 单朵樱花 400×400 透明底 |
| `petal.webp` | 带缺口的花瓣 240×336 透明底 |
| `pattern.webp` | 平铺底纹 600×600 透明底 |
| `INSTALL.txt` | 包内说明 |
重新导出 / 上传(`--upload` 会一起传 CSS、位图和生成的 `INSTALL.txt`):
```bash
python dev/theme-tools/export_assets.py # 只导出到 outputs/sakura-assets
python dev/theme-tools/export_assets.py --upload # 导出并上传,读 data/r2.txt
```
`data/r2.txt` 是部署凭据(`image_endpoint` / `image_access_id` / `image_access_secret` /
`image_bucket` / `image_bucket_cdn_host`),**不进版本库**(`.gitignore` 里的 `/data/`)。
`image_bucket_cdn_host` 与 `conf/app.ini` 同名同义:只写主机名,不带 `https://` 和路径;
没有它时 `INSTALL.txt` 里的公开地址会写成占位符。
缓存策略:位图 `max-age=86400`、CSS 镜像 `300`、`INSTALL.txt` `60`。
这些键**都不带内容版本**,所以刻意不用 `immutable` —— 否则重新导出后,浏览器与 CDN
一年内拿到的都还是旧图。想要一年长缓存,得把内容哈希写进文件名(CSS 里的地址跟着换)。
⚠️ 若某个键曾经以 `immutable` 传过(本主题第一版就是这样),CDN 会一直给旧副本,
改完必须去控制台按 URL 清一次缓存,光重传没用。
### 位图版(引用对象存储)
桶绑了自定义域 `r2.337845818.best`(实测匿名 GET 200、`image/webp`、大小一致),
所以可以把 CSS 从内联切到位图:
```bash
python dev/theme-tools/gen_new_sakura.py \
--asset-base https://r2.337845818.best/themes/new-sakura \
--out static/css/themes/new-sakura.css
```
| 版本 | CSS | gzip 后 | 额外请求 | 清晰度 |
| --- | --- | --- | --- | --- |
| 内联 SVG(默认) | 53.8KB | **4.0KB** | 0 | 矢量,任意 DPR |
| 位图 | 11.8KB | 2.8KB | 5 个 webp,共 48.9KB | 2 倍图 |
服务端挂了 gzip(`internal/route/gzip.go`),所以内联版**总传输 4.0KB**,比位图版的
2.8 + 48.9 = 51.7KB 小一个数量级 —— 换位图省不了流量,只是把开销从 CSS 挪到图片。
位图版唯一正当的理由是**素材要单独复用**(分享卡、海报、其它系统)。
⚠️ 直接连桶的 S3 endpoint(`*.r2.cloudflarestorage.com`)会 403,**只有自定义域/r2.dev 可匿名读**。
切之前先 `curl -I` 验一下前缀;切完 `go build` 部署,否则 embed 里还是旧的。
## 为什么不用主题生成器出图
生成器的装饰层只支持「图片素材」(导出时打进 zip 或传对象存储),没有内联 SVG 图层。
要把樱花做成可摆放的图层,只能在设计上二选一:
1. 生成图片素材(花瓣 PNG/WebP)→ 可以用生成器、可以放 S3,但会有位图缩放、额外请求;
2. 内联 SVG 写进 CSS → 零素材、任意缩放不糊,但生成器管不到。
`new-sakura` 选 2,因为「用 CSS 做樱花」本身就是这个主题的诉求。
生成器仍然可以用来**预览**它:`?demo=1&fresh=1` 起画板,把 `new-sakura.css` 注入即可。
## 怎么验证
```bash
go test ./internal/theme # 10 槽等长、深浅块成对、必需令牌齐全、窄屏覆盖特异性
```
视觉自检用项目真实页面快照:
```bash
TEST_PREVIEW=1 go test ./templates -run TestGenPreview
python dev/theme-builder/serve.py
# 打开 outputs/preview-box-light.html,DevTools 里给 <html> 加
# data-theme-style="new-sakura" data-page-scope="box"
```
## 上线
1. `conf/app.ini` 的 `[theme] available` 加上 `new-sakura`(不改就「设置页看不到」,现象和部署没生效一样);
2. 重新 `go build` —— 主题 CSS 是编译期 embed,只重启不重编不会变;
3. 箱主设置页 → 账号 → 外观,选择「新·樱花」。