Files
tamabox/docs/theme-new-sakura.md
T
tamakyi 5c0de805c6 new-sakura:花枝朝向与分叉修正,素材包一键导出上传到对象存储
- gen_new_sakura.py:花朵加各自旋转角(原来一律朝上,像贴纸);分叉从垂直下垂改成向右上斜;
  新增 --asset-base / --out —— 填前缀就引用对象存储位图,留空仍是内联 SVG。
- export_assets.py(新增):从 gen_new_sakura 的同一份 SVG 出图(直接 import,不另画一套),
  用无头 Chrome 透明底渲染后转 WebP;--upload 一次传 CSS + 5 张 webp + 生成的 INSTALL.txt,
  公开地址按 data/r2.txt 的 image_bucket_cdn_host(与 app.ini 同名)拼。
- serve.upload_files 支持逐文件 cacheControl。原来硬编码 immutable,而 INSTALL.txt 与镜像
  CSS 的键不带版本 —— 重新生成后浏览器/CDN 一年内都还是旧内容。现在位图 1 天、CSS 5 分钟、
  README 60 秒;selftest 补一条覆盖断言。
- 文档更正两处事实:①自定义域 r2.337845818.best 实测可匿名读(S3 endpoint 仍 403);
  ②服务端有 gzip,内联版 51KB 实际传输 3.9KB,比位图版(2.7KB + 5 图 48.9KB)小得多,
  所以「换位图省 CSS 体积」不成立,位图只在素材要复用到非 CSS 场景时才有意义。
  另记:曾以 immutable 传过的键必须去控制台清缓存,重传不会让边缘失效。
2026-10-22 11:06:13 +08:00

160 lines
7.6 KiB
Markdown
Raw 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 有 51KB(服务端有 gzip,实际传输约 3.9KB)。主题 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`:卡片占满宽度后,留白只剩顶栏上下两条,枝条会直接糊在文字上。
樱花改由底纹承担 —— `@media` 里把 `--tb-page-pattern-size` 从 300px 收到 200px,密度上来才看得见。
### 深色
- 花瓣换成更亮一档的粉(`#f0a0b8`),枝条提亮到能看见;
- 底纹 `--tb-page-pattern: none`:浅粉花瓣压在深底上会变成一层噪点;
- 整页背景改深色渐变(不用纯色,避免大面积死黑)。
深色块必须写成 `html[data-theme="dark"][data-theme-style="new-sakura"]`(三重选择器),
只写浅色块会被同特异性的作用域块盖掉。
## 配色与对比度
| 令牌 | 亮色 | 深色 | 说明 |
| --- | --- | --- | --- |
| `--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(默认) | 51.2KB | **3.9KB** | 0 | 矢量,任意 DPR |
| 位图 | 10.6KB | 2.7KB | 5 个 webp,共 48.9KB | 2 倍图 |
服务端挂了 gzip(`internal/route/gzip.go`),所以内联版**总传输 3.9KB**,比位图版的
2.7 + 48.9 = 51.6KB 小一个数量级 —— 换位图省不了流量,只是把开销从 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. 箱主设置页 → 账号 → 外观,选择「新·樱花」。