diff --git a/.workbuddy/memory/2026-10-21.md b/.workbuddy/memory/2026-10-21.md index ae69ac2..514e9dc 100644 --- a/.workbuddy/memory/2026-10-21.md +++ b/.workbuddy/memory/2026-10-21.md @@ -466,3 +466,33 @@ CLI 端到端手动验证:`honeypot hits` / `--ip` / `--show-password` 输出 explore `.explore-name` 1.2rem→1rem、`.hot-vote-title` 0.98→0.875、首页 `.tb-stat-num` 手机 1.25→1rem、 `.hot-vote-action` 0.82→0.875(变大)。头像 50% / 胶囊 999px 有意保留。 下一轮若要彻底统一:`templates/user/profile.html` 16 处 + `base.css` 自身 11 处;`mail/*` 不要动(邮件客户端不保证 var())。 + +## 18:3x 收尾:字号全令牌化 + 主色补到 AA(新增 --tb-on-primary) + +**字号**:`templates/user/profile.html` 20 处、`base.css` 9 处全部换成 `--tb-fs-*`。 +base.css 里那 4 档导航字号(0.95/0.88/0.82/0.78)合并成「桌面 md → ≤1000 xs」,logo 三档统一 lg。 +**保留未改**(有理由):`.tb-pin-flag` / `.md-body code` 的 `0.9em`(相对父级,换成 rem 会破坏继承)、 +验证码的 `1.4rem/1.2rem`(等宽字形尺寸,不是正文阶梯)、chat 关闭按钮 `40px`、`password.html` 的 `3em`、mail/*(邮件客户端不保证 var())。 +行内 `12px` / `0.75rem` 这几处恰好等值,顺手换了;chat 里还剩一个 `13px`(13→12 会缩,没动)。 + +**主色**(同色相压亮度,目标 4.7 而非 4.5,留余量):base/screw `#1e87f0`(3.26~3.42) → **`#0e6fd0`**(4.70), +shiroko `#4a7fc1`(3.69) → **`#3b6dac`**(4.74),sakura `#e5678f`(2.80) → **`#ba3e66`**(4.70,饱和度压到 .5 保持柔和粉)。 +`--tb-primary-weak` 与 shiroko 的两条 box-shadow 一并换 RGB;`TestSwatch` 的断言改成 `#ba3e66`。 + +⚠️ **主色有两个互相矛盾的角色**:当链接色要「深」,当按钮底色配白字却要「更深的底」—— +但深色模式的主色是亮强调(`#5c8dff`/`#ff8ab0`/`#6fa3e0`),白字只有 **2.2~3.1:1**。 +所以新增 **`--tb-on-primary`**(亮 `#ffffff` / 暗 `#0b1016`),`.uk-button-primary` 与 +`.uk-label:not(danger|warning|success|secondary)` 都吃它 → 深色模式按钮是「浅底深字」。实测 6.10/8.65/7.27。 + +其它泄漏点:header 验证码 focus 的 `#1e87f0`/`rgba(30,135,240)` → 令牌;profile `.feat-day.is-on` 写死的 +`rgba(30,135,240,.14)` → `var(--tb-primary-weak)`;explore 随机投票的 JS outline、vote-results 的行内色 → 令牌。 + +**深色 muted 之前算漏了**:只对 `--tb-bg` 算(4.7~4.8 以为过了),对**压平后的卡片**才是真实背景 +(sakura 4.31 / shiroko 4.17)。已提亮到 `#a18790` / `#7f8ea1`(4.62),base 深色 4.77 本来就过。 + +**生成器**:`paletteVars` 增加 `--tb-on-primary`(白字/深字里挑更优);`?dump=palette` 增加 +`onPrimaryCr` / `onPrimaryPickedBest` / `warnings` —— 主色卡在中间调时**不断言失败**(那是主色要调, +不是挑错),列进 warnings;配色面板给橙色提示「压深配白字 / 提亮配深字」。 +`_scaffold.css` 令牌数 29 → 30。`StaticVersion` → **`20261021c`**。 +最终审计 8 种组合(base 亮暗 + 三主题亮暗)全部 ≥4.5。⚠️ 写 CSS 用 python 改时注意:`git checkout` 会把 +base.css 还原成 CRLF,插入行要用同一行尾,否则变 MIXED。 diff --git a/.workbuddy/memory/topics/builder.md b/.workbuddy/memory/topics/builder.md index ed3bd7d..ee05641 100644 --- a/.workbuddy/memory/topics/builder.md +++ b/.workbuddy/memory/topics/builder.md @@ -27,6 +27,9 @@ - 参考背景取「页面底色 + 压平后的卡片」两者更不利的那个; - 手填正文本身不达标时**原样返回**(宁可不够淡,不能产出看不见的字)。 - 配色面板有读数(正文/次要/淡文字/主色),<4.5 变橙;默认 primary 已压到 `#3d6fac`(4.63)。 + - 还要导出 **`--tb-on-primary`**(白字 `#ffffff` / 深字 `#0b1016` 里挑对比度更高的): + 深色模式主色偏亮,白字只有 2.2~3.1:1。主色卡中间调时两种字都不够 → 面板橙色提示 + + `?dump=palette` 的 `warnings`,**不判失败**(是主色要调,不是工具挑错)。 - 自检钩子:`?dump=css|zip|validate|install|fx|fxpipe|fxkey|layers|fit|quality|palette`,`?demo=1&fresh=1&rows=N&scope=&loadurl=` (`?demo=1` 已含 fit 层 + 竖版背景,`@media` 才会生成)。用法: `chrome --headless=old --virtual-time-budget=12000 --dump-dom "http://127.0.0.1:8777/dev/theme-builder/index.html?demo=1&fresh=1&dump=fit"` diff --git a/.workbuddy/memory/topics/style.md b/.workbuddy/memory/topics/style.md index ece1175..881ff78 100644 --- a/.workbuddy/memory/topics/style.md +++ b/.workbuddy/memory/topics/style.md @@ -26,6 +26,12 @@ 基座修了但主题页纹丝不动(sakura/shiroko 的 muted 就曾是 2.8:1,是这次才补到 4.6)。 - 顶栏链接:亮色用 `--tb-text-secondary`、hover/当前页 `--tb-primary`;深色块原本 `!important` 定成 `--tb-text`,会让同一顶栏在深浅两模式权重跳一档 → 已加 dark secondary 覆盖。 +- **主色要达到 AA 4.5:1**(它同时是 14px 链接/按钮文字色)。现值:base `#0e6fd0`、shiroko `#3b6dac`、 + sakura `#ba3e66`(都是同色相压亮度)。**改主色必须三处一起**:`base.css :root` + `themes/default.css` + 该主题。 +- ⚠️ **`--tb-on-primary`**(亮 `#ffffff` / 暗 `#0b1016`)是「主色作背景时的文字色」。 + 深色模式的强调色偏亮(`#5c8dff`),白字只有 2.2~3.1:1 —— 用一个令牌同时伺候「链接色要深」和 + 「按钮底要深」是矛盾的,所以拆成两个。`.uk-button-primary`、`.uk-label:not(语义色)` 都吃它。 +- 字号阶梯已全部落地(模板 + base.css);`0.9em`(相对父级)、验证码等宽字形、`mail/*` 是**有意例外**。 - 视觉自检:`TEST_PREVIEW=1 go test ./templates -run TestGenPreview` → `outputs/preview-*.html`; 截图必须 `chrome --headless=old --force-device-scale-factor=1`(新版 headless 本机挂住)。 ⚠️ `--window-size` ≠ CSS 视口;要精确视口就用 iframe 指定 width/height。验证 CSS 靠**像素比对** diff --git a/.workbuddy/memory/topics/theme.md b/.workbuddy/memory/topics/theme.md index 2ca12c6..d891ad1 100644 --- a/.workbuddy/memory/topics/theme.md +++ b/.workbuddy/memory/topics/theme.md @@ -44,7 +44,8 @@ - ⚠️ `ruleBody` 取令牌前要剥 `/* */`:注释会和后面的令牌粘成一个键,色块预览静默失效。 - ⚠️ **主题会覆盖 base.css 的同名令牌 —— 基座改配色必须同步改主题**。改 `--tb-text-muted` 时 sakura `#a08c94`(2.8:1)、shiroko `#8794a7`(2.76:1) 都还没跟上,"修了但主题页没变"就是这么来的。 - 现已调到 `#78696e`(4.61) / `#656e7a`(4.63),同色相只压深。主色仍不达标(见 topics/style.md)。 + 现已调到 `#78696e`(4.61) / `#656e7a`(4.63),深色块 `#a18790` / `#7f8ea1`(对压平后的卡片 4.62)。 + 主色也已修(`#0e6fd0` / `#3b6dac` / `#ba3e66`)+ 新增 `--tb-on-primary`,见 topics/style.md。 量对比度时半透明 `--tb-surface` 要**压平到 --tb-bg** 再算,别当纯白。 - 主题选择页的色块由浅色块的 `--tb-bg` + `--tb-primary` 生成(`swatchOf`),注释里别写令牌名。 - `test01` 是用户 2026-10-21 自建又删掉的验证主题(css + 2 张 webp 已移除,未提交)。 diff --git a/dev/theme-builder/README.md b/dev/theme-builder/README.md index 8992b88..32411a8 100644 --- a/dev/theme-builder/README.md +++ b/dev/theme-builder/README.md @@ -145,7 +145,13 @@ http://127.0.0.1:8777/dev/theme-builder/ - 卡片是半透明(`rgba(255,255,255,0.86)`)时先**压平到页面底色**再算,否则白卡会被当成纯白、对比度偏低; - 同时检查页面底色与卡片两个背景,取更不利的那个; - 手填的正文本身就达不到目标时**原样返回**(不再往淡里调)——宁可不够淡,不能产出看不见的字; -- 面板底部有读数 `正文 12.5:1 · 次要 5.5:1 · 淡文字 4.6:1 · 主色 3.4:1`,低于 4.5 变橙(手填色只提醒不拦)。 +- 面板底部有读数 `正文 12.5:1 · 次要 5.5:1 · 淡文字 4.6:1 · 主色 4.6:1`,低于 4.5 变橙(手填色只提醒不拦)。 + +主色除了当链接色,还当按钮/标签的**背景**,所以还要交代「压在上面的字」: +导出时写 `--tb-on-primary`,在白字和深字(`#0b1016`)里挑对比度更高的那个。 +浅色主题主色深 → 白字;深色主题主色普遍偏亮(`#5c8dff` 之流)→ 白字只有 2.2~3.1:1,必须换深字。 +主色卡在**中间调**时两种字都不够 —— 面板会给橙色提示「压深一点配白字,或提亮一点配深字」, +`?dump=palette` 里也会列进 `warnings`(不判失败:那是主色要调,不是工具挑错)。 顺带:这套字号阶梯(`--tb-fs-xs…hero`)来自 `base.css`,生成器**不碰字号** —— 主题的职责是配色与背景,改字号会连带挤坏所有页面排版。 diff --git a/dev/theme-builder/index.html b/dev/theme-builder/index.html index 45cd2bc..c764e7d 100644 --- a/dev/theme-builder/index.html +++ b/dev/theme-builder/index.html @@ -685,6 +685,13 @@ function textBackdrops(p){ return [p.bg, flatten(p.surface, p.bg)]; } +/** 主色作背景时的文字色:白字和深字里挑对比度更高的那个。 + 浅色模式主色深 → 白字;深色模式主色普遍偏亮(#5c8dff 之流),白字只有 2~3:1, + 必须换成深字。不写这个令牌就只能吃 base.css 的默认值,深浅两套必然错一套。 */ +function onPrimary(primary){ + return contrastRatio('#ffffff', primary) >= contrastRatio('#0b1016', primary) ? '#ffffff' : '#0b1016'; +} + function paletteVars(p){ const back = textBackdrops(p); return [ @@ -699,6 +706,7 @@ function paletteVars(p){ ` --tb-text-muted: ${mixToContrast(p.text, back, 4.5)};`, ` --tb-primary: ${p.primary};`, ` --tb-primary-weak: ${withAlpha(p.primary, 0.12)};`, + ` --tb-on-primary: ${onPrimary(p.primary)};`, ].join('\n'); } @@ -1610,6 +1618,19 @@ function renderPalette(){ ).join(' · ') + '
低于 4.5:1(WCAG AA 正文)会变橙。次要/淡文字由正文自动派生并保底。'; wrap.appendChild(crL); + // 主色卡在中间调时,白字和深字都不到 4.5 —— 这不是文字色挑错,是主色本身要动。 + const bestOn = Math.max(contrastRatio('#ffffff', S.palette.primary), + contrastRatio('#0b1016', S.palette.primary)); + if (bestOn < 4.5){ + const warn = document.createElement('p'); + warn.className = 'hint'; + warn.style.color = '#e0a33e'; + warn.textContent = `⚠ 主色偏中间调:白字 ${contrastRatio('#ffffff', S.palette.primary).toFixed(2)}:1、` + + `深字 ${contrastRatio('#0b1016', S.palette.primary).toFixed(2)}:1,按钮上的字都不够清楚。` + + '把主色压深一点(配白字)或提亮一点(配深字)就能过。'; + wrap.appendChild(warn); + } + const note = document.createElement('p'); note.className = 'hint'; note.textContent = '深色配色写在生成结果里(深色不加载图片);要改深色请直接改 CSS。'; @@ -3038,7 +3059,7 @@ function boot(){ const r = { cases: {} }; try { const cases = { - '默认亮色': { bg:'#eef3f9', surface:'rgba(255,255,255,0.86)', navbar:'rgba(255,255,255,0.52)', text:'#222d3b', primary:'#4a7fc1', border:'#d6e0ec' }, + '默认亮色': { bg:'#eef3f9', surface:'rgba(255,255,255,0.86)', navbar:'rgba(255,255,255,0.52)', text:'#222d3b', primary:'#3d6fac', border:'#d6e0ec' }, '默认深色': { bg:'#141a22', surface:'#1d2530', navbar:'#141a22', text:'#e3eaf4', primary:'#6fa3e0', border:'#334052' }, // 极端:文字色本身就不达标 → 必须原样返回,不能继续往底色淡 '低对比手填': { bg:'#f4f4f4', surface:'#ffffff', navbar:'#ffffff', text:'#c8ccd2', primary:'#dddddd', border:'#e5e5e5' }, @@ -3046,6 +3067,7 @@ function boot(){ '白卡彩底': { bg:'#f6d9e4', surface:'#ffffff', navbar:'#f6d9e4', text:'#3b2b33', primary:'#c2557d', border:'#efd3de' }, }; let allOk = true; + const warns = []; for (const name in cases){ const p = cases[name]; const back = textBackdrops(p); @@ -3057,9 +3079,21 @@ function boot(){ muted: +worst(mut).toFixed(2), rawText: +worst(p.text).toFixed(2), mutedDarkerThanSecondary: worst(mut) <= worst(sec), // 层级:淡文字必须比次要更淡 + // 主色作底的文字:白字 vs 深字里必须挑更优的那个。 + // 注意这里不断言「一定 ≥4.5」:主色卡在中间调时,两种文字色都不够, + // 这不是挑错了,而是主色本身要调 —— 列进 warnings 提醒,不判失败。 + onPrimary: onPrimary(p.primary), + onPrimaryCr: +contrastRatio(onPrimary(p.primary), p.primary).toFixed(2), + onPrimaryPickedBest: Math.abs( + contrastRatio(onPrimary(p.primary), p.primary) - + Math.max(contrastRatio('#ffffff', p.primary), contrastRatio('#0b1016', p.primary)) + ) < 0.01, // 关键断言:只要有 4.5 的空间就必须达标;本来就不达标的不算错 - ok: (worst(p.text) < 4.5) ? (mut === p.text) : (worst(mut) >= 4.5 && worst(sec) >= 4.5), + ok: ((worst(p.text) < 4.5) ? (mut === p.text) : (worst(mut) >= 4.5 && worst(sec) >= 4.5)) + && Math.abs(contrastRatio(onPrimary(p.primary), p.primary) - + Math.max(contrastRatio('#ffffff', p.primary), contrastRatio('#0b1016', p.primary))) < 0.01, }; + if (row.onPrimaryCr < 4.5) warns.push(name + '(主色 ' + p.primary + ' 中间调,白字/深字都不到 4.5)'); // 卡片半透明时必须按压平后的颜色算,否则白卡 86% 会被当成 #fff row.surfaceFlat = flatten(p.surface, p.bg); r.cases[name] = row; @@ -3069,6 +3103,14 @@ function boot(){ for (const name in r.cases){ if (!r.cases[name].mutedDarkerThanSecondary) allOk = false; } + // demo 的两套配色也要过:导出后的 CSS 直接吃这两个值 + r.demoLight = { primary: S.palette.primary, on: onPrimary(S.palette.primary), + cr: +contrastRatio(onPrimary(S.palette.primary), S.palette.primary).toFixed(2) }; + r.demoDark = { primary: S.dark.primary, on: onPrimary(S.dark.primary), + cr: +contrastRatio(onPrimary(S.dark.primary), S.dark.primary).toFixed(2) }; + if (r.demoLight.cr < 4.5) warns.push('demo 亮色(可改 palette.primary)'); + if (r.demoDark.cr < 4.5) warns.push('demo 深色(可改 dark.primary)'); + r.warnings = warns; // 把生成结果里的派生值也打出来,方便和 base.css 的取值对照 const css = buildCSS(false); r.mutedLine = (css.match(/--tb-text-muted:[^;]+;/) || [''])[0]; diff --git a/internal/conf/static.go b/internal/conf/static.go index 68de277..f9823fb 100644 --- a/internal/conf/static.go +++ b/internal/conf/static.go @@ -10,7 +10,7 @@ var ( BuildCommit = "dev" // StaticVersion 静态资源缓存版本号:base.css 与主题 css 共用,改样式只改这一处。 - StaticVersion = "20261021b" + StaticVersion = "20261021c" ) // 文本审核通道取值([security] text_censor_source)。定义在 conf 侧, diff --git a/internal/theme/theme_test.go b/internal/theme/theme_test.go index 71a953b..b14f56a 100644 --- a/internal/theme/theme_test.go +++ b/internal/theme/theme_test.go @@ -96,7 +96,7 @@ func TestSwatch(t *testing.T) { require.NoError(t, err) sw := swatchOf(string(raw), "sakura") assert.True(t, strings.HasPrefix(sw, "linear-gradient("), "swatch 应为渐变串,实际 %q", sw) - assert.Contains(t, sw, "#e5678f") + assert.Contains(t, sw, "#ba3e66") assert.Equal(t, "", swatchOf("/* 空文件 */", "sakura"), "取不到颜色时返回空串") assert.Equal(t, "", swatchOf(`html[data-theme-style="x"] { --tb-bg: url(evil); --tb-primary: #fff; }`, "x"), diff --git a/static/css/base.css b/static/css/base.css index f68eb04..b3fb585 100644 --- a/static/css/base.css +++ b/static/css/base.css @@ -20,8 +20,14 @@ 旧的 #999 只有 2.85:1,都低于 WCAG AA 正文要求的 4.5:1; 这个值在 #fff 上 4.83:1、在 --tb-surface-2 上 4.6:1,是能兼顾「淡」和「看得清」的下限。 */ --tb-text-muted: #6b7280; - --tb-primary: #1e87f0; - --tb-primary-weak: rgba(30, 135, 240, 0.10); + /* 主色同时也是 14px 链接/按钮文字的颜色,必须满足 AA 4.5:1。 + #1e87f0 white 底上只有 3.42:1(大号字够 3:1,正文不够),压深到 #0e6fd0 = 4.70:1, + 白字压在按钮上反而更清楚(5.00:1)。色相不变,只降亮度。 */ + --tb-primary: #0e6fd0; + --tb-primary-weak: rgba(14, 111, 208, 0.10); + /* 主色作背景时的文字色。浅色模式主色深 → 白字;深色模式主色浅 → 深字。 + 深色块里主色是「亮强调」(#5c8dff 之流),白字压上去只有 2.2~3.1:1。 */ + --tb-on-primary: #ffffff; --tb-radius: 12px; --tb-radius-sm: 8px; --tb-radius-xs: 6px; @@ -64,6 +70,7 @@ --tb-text-muted: #8b93a1; --tb-primary: #5c8dff; --tb-primary-weak: rgba(92, 141, 255, 0.14); + --tb-on-primary: #0b1016; --tb-shadow: 0 1px 2px rgba(0, 0, 0, 0.30), 0 12px 28px -18px rgba(0, 0, 0, 0.55); --tb-shadow-hover: 0 1px 2px rgba(0, 0, 0, 0.35), 0 18px 40px -20px rgba(0, 0, 0, 0.65); } @@ -117,14 +124,14 @@ /* 移动端优化:缩小字体和间距,避免右侧空白 */ @media (max-width: 640px) { .uk-logo { - font-size: 1rem; + font-size: var(--tb-fs-lg); } .uk-navbar-nav a { - font-size: 0.78rem; + font-size: var(--tb-fs-xs); padding: 0 4px; } .emoji-placeholder { - font-size: 1rem; + font-size: var(--tb-fs-lg); } /* 移动端用户名限宽省略,避免长英文用户名撑爆顶栏 */ .tb-nav-username { @@ -429,9 +436,9 @@ color: var(--tb-primary); } -/* UIkit 组件跟随主题色:UIkit 3.3.3 的主色是编译期常量(#1e87f0), - CSS 变量注入不进去,只能按组件覆盖。基础主题的 --tb-primary 恰好就是 #1e87f0, - 所以这段对默认外观零改动,只是让箱主主题能贯穿到按钮/链接/勾选。 */ +/* UIkit 组件跟随主题色:UIkit 3.3.3 的主色是编译期常量(写死在 uikit.min.css 里), + CSS 变量注入不进去,只能按组件逐个覆盖成 var(--tb-primary)。 + 少了这段,换主题后会出现「页面变粉、按钮还蓝」。 */ a, .uk-link { color: var(--tb-primary); } @@ -440,6 +447,7 @@ a:hover, .uk-link:hover { } .uk-button-primary { background-color: var(--tb-primary); + color: var(--tb-on-primary); } .uk-button-primary:hover, .uk-button-primary:focus { @@ -457,6 +465,7 @@ a:hover, .uk-link:hover { 同特异性下 base.css 在 UIkit 之后加载,不排除会把它们一并刷成主色。 */ .uk-label:not(.uk-label-danger):not(.uk-label-warning):not(.uk-label-success):not(.uk-label-secondary) { background-color: var(--tb-primary); + color: var(--tb-on-primary); } .uk-input:focus, .uk-select:focus, @@ -554,7 +563,7 @@ a.tb-chip:hover { /* 顶栏自适应:导航全部直接显示,窄屏时分级缩小字号/间距,保证英文/日文等长文字不溢出 */ .uk-navbar-nav a { - font-size: 0.95rem; + font-size: var(--tb-fs-md); padding: 0 10px; } /* 未登录移动端占位:桌面端隐藏,仅 ≤640px 显示(见下方 media 查询)。 @@ -566,17 +575,17 @@ a.tb-chip:hover { } @media (max-width: 1200px) { .uk-navbar-nav a { - font-size: 0.88rem; + font-size: var(--tb-fs-md); padding: 0 8px; } } @media (max-width: 1000px) { .uk-navbar-nav a { - font-size: 0.82rem; + font-size: var(--tb-fs-xs); padding: 0 6px; } .uk-logo { - font-size: 1.1rem; + font-size: var(--tb-fs-lg); } /* 登录用户名限宽,过长省略号截断 */ .tb-nav-username { @@ -590,11 +599,11 @@ a.tb-chip:hover { } @media (max-width: 800px) { .uk-navbar-nav a { - font-size: 0.78rem; + font-size: var(--tb-fs-xs); padding: 0 4px; } .uk-logo { - font-size: 1rem; + font-size: var(--tb-fs-lg); } .tb-nav-username { max-width: 80px; diff --git a/static/css/themes/_scaffold.css b/static/css/themes/_scaffold.css index 277f906..b8ef075 100644 --- a/static/css/themes/_scaffold.css +++ b/static/css/themes/_scaffold.css @@ -16,7 +16,7 @@ display / flex / grid、position 一律不动 —— 主题不是换皮模板。 3. 本文件不参与构建,仅作参考;真正生效的是同目录下的 .css。 - ── 可用的 29 个令牌 ────────────────────────────────────────────── + ── 可用的 30 个令牌 ────────────────────────────────────────────── 底色层 --tb-bg 页面底色(html/body) --tb-surface 卡片、输入框等前景面板 --tb-surface-2 次级面(标签底、进度条轨道、代码块) @@ -31,6 +31,9 @@ 往浅调很容易掉到 3:1 以下(旧的 #8a919c 是 3.18:1),肉眼"淡一点"其实已经看不清了。 强调 --tb-primary 主色:链接、主按钮、进度条 --tb-primary-weak 主色 10% 左右的浅底(选中态、标签) + --tb-on-primary 主色作背景时的文字色(默认白字;深色块一般是深字) + 主色同时是 14px 链接色,要 ≥4.5:1;但主色越深, + 白字压在按钮上越清楚 —— 所以深色模式反而不该用白字。 形状 --tb-radius 卡片 12px / --tb-radius-sm 8px / --tb-radius-xs 6px 阴影 --tb-shadow 卡片默认 / --tb-shadow-hover 悬停 字体 --tb-font 字体栈(一般不改) @@ -83,8 +86,9 @@ html[data-theme-style="SLUG"] { --tb-text-secondary: #5c6370; --tb-text-muted: #6b7280; /* AA 下限:白底 4.83:1。别再往浅调 */ - --tb-primary: #1e87f0; - --tb-primary-weak: rgba(30, 135, 240, 0.10); + --tb-primary: #0e6fd0; /* AA:白底 4.70:1。再浅就只够大号字了 */ + --tb-primary-weak: rgba(14, 111, 208, 0.10); + --tb-on-primary: #ffffff; --tb-radius: 12px; --tb-radius-sm: 8px; @@ -129,6 +133,7 @@ html[data-theme="dark"][data-theme-style="SLUG"] { --tb-primary: #5c8dff; --tb-primary-weak: rgba(92, 141, 255, 0.14); + --tb-on-primary: #0b1016; /* 深色主色偏亮,白字只有 3.13:1 */ --tb-shadow: 0 1px 2px rgba(0, 0, 0, 0.30), 0 12px 28px -18px rgba(0, 0, 0, 0.55); --tb-shadow-hover: 0 1px 2px rgba(0, 0, 0, 0.35), 0 18px 40px -20px rgba(0, 0, 0, 0.65); diff --git a/static/css/themes/default.css b/static/css/themes/default.css index de06166..9d01002 100644 --- a/static/css/themes/default.css +++ b/static/css/themes/default.css @@ -4,7 +4,7 @@ 省掉一次静态请求;下方令牌仅用于设置页色块预览。 */ html[data-theme-style="default"] { --tb-bg: #f7f8fa; - --tb-primary: #1e87f0; + --tb-primary: #0e6fd0; } html[data-theme="dark"][data-theme-style="default"] { diff --git a/static/css/themes/sakura.css b/static/css/themes/sakura.css index a3134f5..6032ed2 100644 --- a/static/css/themes/sakura.css +++ b/static/css/themes/sakura.css @@ -17,8 +17,9 @@ html[data-theme-style="sakura"] { /* 弱化文字:旧值 #a08c94 只有 2.8:1(看不清),#78696e 是 4.6:1,踩住 AA 下限 */ --tb-text-muted: #78696e; - --tb-primary: #e5678f; - --tb-primary-weak: rgba(229, 103, 143, 0.12); + /* 原来的 #e5678f 在粉底上只有 2.80:1(链接几乎看不清);同色相压深到 #ba3e66 = 4.70:1 */ + --tb-primary: #ba3e66; + --tb-primary-weak: rgba(186, 62, 102, 0.12); --tb-shadow: 0 1px 2px rgba(120, 60, 80, 0.05), 0 12px 28px -18px rgba(120, 60, 80, 0.22); --tb-shadow-hover: 0 1px 2px rgba(120, 60, 80, 0.07), 0 18px 40px -20px rgba(120, 60, 80, 0.30); @@ -38,7 +39,7 @@ html[data-theme="dark"][data-theme-style="sakura"] { --tb-text: #f2e4e9; --tb-text-secondary: #c6aab4; - --tb-text-muted: #9d818b; + --tb-text-muted: #a18790; --tb-primary: #ff8ab0; --tb-primary-weak: rgba(255, 138, 176, 0.16); diff --git a/static/css/themes/shiroko.css b/static/css/themes/shiroko.css index 8222a8d..f1ab31b 100644 --- a/static/css/themes/shiroko.css +++ b/static/css/themes/shiroko.css @@ -24,8 +24,9 @@ html[data-theme-style="shiroko"] { /* 弱化文字:旧值 #8794a7 只有 2.76:1(看不清),#656e7a 是 4.63:1,踩住 AA 下限 */ --tb-text-muted: #656e7a; - --tb-primary: #4a7fc1; - --tb-primary-weak: rgba(74, 127, 193, 0.11); + /* 原来的 #4a7fc1 在 #eef3f9 上只有 3.69:1;同色相压深到 #3b6dac = 4.74:1 */ + --tb-primary: #3b6dac; + --tb-primary-weak: rgba(59, 109, 172, 0.11); --tb-shadow: 0 1px 2px rgba(38, 56, 82, 0.05), 0 18px 44px -22px rgba(38, 56, 82, 0.30); --tb-shadow-hover: 0 1px 2px rgba(38, 56, 82, 0.07), 0 24px 56px -24px rgba(38, 56, 82, 0.38); @@ -69,7 +70,7 @@ html[data-theme="dark"][data-theme-style="shiroko"] { --tb-text: #e3eaf4; --tb-text-secondary: #a6b4c8; - --tb-text-muted: #77869b; + --tb-text-muted: #7f8ea1; --tb-primary: #6fa3e0; --tb-primary-weak: rgba(111, 163, 224, 0.16); @@ -105,10 +106,10 @@ html[data-theme="dark"][data-theme-style="shiroko"] { html[data-theme-style="shiroko"] .uk-card-default { backdrop-filter: blur(16px) saturate(1.05); -webkit-backdrop-filter: blur(16px) saturate(1.05); - box-shadow: 0 0 0 1px rgba(74, 127, 193, 0.12), var(--tb-shadow); + box-shadow: 0 0 0 1px rgba(59, 109, 172, 0.12), var(--tb-shadow); } html[data-theme-style="shiroko"] .uk-card-default.uk-card-hover:hover { - box-shadow: 0 0 0 1px rgba(74, 127, 193, 0.18), var(--tb-shadow-hover); + box-shadow: 0 0 0 1px rgba(59, 109, 172, 0.18), var(--tb-shadow-hover); } html[data-theme-style="shiroko"] .uk-navbar-container:not(.uk-navbar-transparent) { background-color: var(--tb-navbar); diff --git a/templates/base/header.html b/templates/base/header.html index 9ad6e41..27e0d94 100644 --- a/templates/base/header.html +++ b/templates/base/header.html @@ -51,8 +51,8 @@ transition: border-color 0.2s; } .captcha-equation-box:focus-within { - border-color: #1e87f0; - box-shadow: 0 0 0 3px rgba(30,135,240,0.12); + border-color: var(--tb-primary); + box-shadow: 0 0 0 3px var(--tb-primary-weak); } .math-captcha-modal-question { font-size: 1.4rem; @@ -76,8 +76,8 @@ background: #fff; } #captcha-modal-answer:focus { - border-color: #1e87f0; - box-shadow: 0 0 0 2px rgba(30,135,240,0.2); + border-color: var(--tb-primary); + box-shadow: 0 0 0 2px var(--tb-primary-weak); } /* 隐藏数字输入框的上下箭头 */ #captcha-modal-answer::-webkit-inner-spin-button, diff --git a/templates/chat/conversation.html b/templates/chat/conversation.html index 49b0439..58cc3ca 100644 --- a/templates/chat/conversation.html +++ b/templates/chat/conversation.html @@ -65,7 +65,7 @@ {{ if $isMyMessage }}{{t $.LangCode "chat.me"}}{{ else }}{{t $.LangCode "chat.other"}}{{ end }} - + {{ .CreatedAt.Format "01-02 15:04" }} diff --git a/templates/question/item.html b/templates/question/item.html index 0367600..9b1a967 100644 --- a/templates/question/item.html +++ b/templates/question/item.html @@ -221,7 +221,7 @@
- +