字号阶梯收尾 + 主色补到 WCAG AA(新增 --tb-on-primary)

一号事项(剩余令牌化):
- templates/user/profile.html 20 处、base.css 9 处 font-size 全部落到 --tb-fs-*
- base.css 导航四档(0.95/0.88/0.82/0.78)合并成「桌面 md → ≤1000 xs」;logo 三档统一 lg
- 有理由保留的:.tb-pin-flag / .md-body code 的 0.9em(相对父级)、验证码 1.4/1.2rem(等宽字形)、
  chat 关闭按钮 40px、password 页 3em、mail/*(邮件客户端不保证 var());行内 12px/0.75rem 等值顺手换掉

二号事项(主色对比度):
- base/default #1e87f0(3.42) → #0e6fd0(4.70);shiroko #4a7fc1(3.69) → #3b6dac(4.74);
  sakura #e5678f(2.80) → #ba3e66(4.70,饱和度压到 .5 保持柔和)。同色相只压亮度,目标 4.7 留余量
- 深色块 muted 之前只对 --tb-bg 算(4.7 以为过了),对压平后的卡片其实 4.31/4.17 → 提到 #a18790 / #7f8ea1
- 新增 --tb-on-primary(亮 #ffffff / 暗 #0b1016):深色模式主色偏亮,白字只有 2.2~3.1:1,
  用同一个令牌兼顾「链接色要深」和「按钮底要深」是矛盾的。.uk-button-primary 与
  .uk-label:not(语义色) 改吃它 → 深色按钮变成浅底深字(6.10 / 8.65 / 7.27)
- 顺手封掉几处主色泄漏:header 验证码 focus、profile .feat-day.is-on、explore 随机投票的 JS outline、
  vote-results 行内色,全部改成令牌(否则换主题时不跟着变)

生成器同步:paletteVars 导出 --tb-on-primary(白字/深字择优);?dump=palette 增加
onPrimaryCr / onPrimaryPickedBest / warnings —— 主色卡中间调时只警告不判失败;
配色面板给橙色提示。_scaffold 令牌 29 → 30。StaticVersion → 20261021c。

验证:TestSwatch 断言同步为 #ba3e66;8 种组合(base 亮暗 + 三主题亮暗)对比度全部 ≥4.5;
go test internal/{theme,templates,conf,i18n} 全绿;生成器 validate/fit/quality/palette/layers/fx/fxpipe 全过。
This commit is contained in:
tamakyi
2026-10-21 18:39:08 +08:00
parent 252b22a342
commit 27856dd4a2
20 changed files with 168 additions and 64 deletions
+4 -4
View File
@@ -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,
+1 -1
View File
@@ -65,7 +65,7 @@
<strong class="uk-margin-small-left" data-page-node-id="V7t5pBo43sNBv1a6rvp0jv">
{{ if $isMyMessage }}{{t $.LangCode "chat.me"}}{{ else }}{{t $.LangCode "chat.other"}}{{ end }}
</strong>
<span class="uk-text-meta uk-margin-small-left" style="font-size: 0.75rem;" data-page-node-id="JidfstvpABpsDBPRL3WY6I">
<span class="uk-text-meta uk-margin-small-left" style="font-size: var(--tb-fs-xs);" data-page-node-id="JidfstvpABpsDBPRL3WY6I">
{{ .CreatedAt.Format "01-02 15:04" }}
</span>
</div>
+2 -2
View File
@@ -221,7 +221,7 @@
<div x-data="{ files: null }" class="tb-img-uploader">
<div uk-form-custom>
<a href="#" class="uk-icon-link" uk-icon="image" style="margin-left: 10px"></a>
<span style="font-size: 12px" x-text="files ? files[0].name : '{{t .LangCode "ask.addImage"}}'"></span>
<span style="font-size: var(--tb-fs-xs)" x-text="files ? files[0].name : '{{t .LangCode "ask.addImage"}}'"></span>
<input id="uploader" name="images" type="file" accept="image/*" x-on:change="files = Object.values($event.target.files)">
</div>
<button x-show="files" type="button" class="uk-icon-link" uk-icon="close" style="margin-left: 10px;"
@@ -253,7 +253,7 @@
<div x-data="{ files: null }" class="tb-img-uploader">
<div uk-form-custom>
<a href="#" class="uk-icon-link" uk-icon="image" style="margin-left: 10px"></a>
<span style="font-size: 12px" x-text="files ? files[0].name : '{{t .LangCode "ask.addImage"}}'"></span>
<span style="font-size: var(--tb-fs-xs)" x-text="files ? files[0].name : '{{t .LangCode "ask.addImage"}}'"></span>
<input id="uploader" name="images" type="file" accept="image/*" x-on:change="files = Object.values($event.target.files)">
</div>
<button x-show="files" type="button" class="uk-icon-link" uk-icon="close" style="margin-left: 10px;"
@@ -100,7 +100,7 @@
<div x-data="{ files: null }" class="tb-img-uploader">
<div uk-form-custom>
<a href="#" class="uk-icon-link" uk-icon="image" style="margin-left: 10px"></a>
<span style="font-size: 12px" x-text="files ? files[0].name : '{{t .LangCode "ask.addImage"}}'"></span>
<span style="font-size: var(--tb-fs-xs)" x-text="files ? files[0].name : '{{t .LangCode "ask.addImage"}}'"></span>
<input id="uploader" name="images" type="file" accept="image/*" x-on:change="files = Object.values($event.target.files)">
</div>
<button x-show="files" type="button" class="uk-icon-link" uk-icon="close" style="margin-left: 10px;"
+1 -1
View File
@@ -508,7 +508,7 @@ document.addEventListener('DOMContentLoaded', function() {
if (!votes.length) return;
const target = votes[Math.floor(Math.random() * votes.length)];
randomVoteBtn.classList.add('is-spinning');
target.style.outline = '2px solid #1e87f0';
target.style.outline = '2px solid var(--tb-primary)';
target.style.outlineOffset = '2px';
window.setTimeout(function() {
window.location.href = target.href;
+22 -22
View File
@@ -46,7 +46,7 @@
}
.theme-btn {
margin: 2px;
font-size: 0.8rem;
font-size: var(--tb-fs-xs);
}
.social-item {
display: flex;
@@ -88,7 +88,7 @@
background: transparent;
color: var(--tb-text-muted);
cursor: pointer;
font-size: 0.9rem;
font-size: var(--tb-fs-md);
white-space: nowrap;
transition: background 0.2s ease, color 0.2s ease;
}
@@ -123,10 +123,10 @@
justify-content: space-between;
gap: 8px;
}
.feat-card-head h3 { font-size: 1.05rem; }
.feat-card-head h3 { font-size: var(--tb-fs-lg); }
.feat-status {
flex: 0 0 auto;
font-size: 0.72rem;
font-size: var(--tb-fs-xs);
line-height: 1.6;
padding: 0 8px;
border-radius: var(--tb-radius-xs);
@@ -135,7 +135,7 @@
.feat-status.is-closed { background: #f0506e; color: #fff; }
.feat-hint {
display: block;
font-size: 0.78rem;
font-size: var(--tb-fs-xs);
line-height: 1.5;
color: var(--tb-text-muted);
}
@@ -143,12 +143,12 @@
.feat-field > label > span {
display: block;
margin-bottom: 4px;
font-size: 0.875rem;
font-size: var(--tb-fs-md);
}
.feat-field-label {
display: block;
margin-bottom: 4px;
font-size: 0.875rem;
font-size: var(--tb-fs-md);
}
/* 收集模式三选一(长期开放 / 定时收集 / 循环定时),胶囊样式复用 .feat-day */
.feat-modes {
@@ -174,7 +174,7 @@
padding: 3px 8px;
border: 1px solid var(--tb-border);
border-radius: 999px;
font-size: 0.78rem;
font-size: var(--tb-fs-xs);
color: var(--tb-text-secondary);
cursor: pointer;
user-select: none;
@@ -194,7 +194,7 @@
display: flex;
align-items: center;
gap: 6px;
font-size: 0.82rem;
font-size: var(--tb-fs-md);
color: var(--tb-text-secondary);
}
.feat-schedule-times input[type="time"] {
@@ -209,7 +209,7 @@
}
html[data-theme="dark"] .feat-day.is-on {
border-color: var(--tb-primary);
background: rgba(30, 135, 240, 0.14);
background: var(--tb-primary-weak);
color: var(--tb-primary);
}
html[data-theme="dark"] .feat-schedule-times input[type="time"] {
@@ -218,7 +218,7 @@
color: var(--tb-text);
}
.feat-quota-line {
font-size: 0.82rem;
font-size: var(--tb-fs-md);
color: var(--tb-text-secondary);
text-align: center;
}
@@ -248,7 +248,7 @@
}
.chat-zone hr { margin: 14px 0; }
.chat-zone .uk-legend {
font-size: 1.05rem;
font-size: var(--tb-fs-lg);
margin: 0 0 10px;
}
@@ -272,8 +272,8 @@
padding: 2px 8px;
border-radius: var(--tb-radius-xs);
background: var(--tb-primary);
color: #fff;
font-size: 0.875rem;
color: var(--tb-on-primary);
font-size: var(--tb-fs-md);
line-height: 1.8;
}
.tb-tag-chip button {
@@ -281,7 +281,7 @@
border: 0;
background: transparent;
color: inherit;
font-size: 1rem;
font-size: var(--tb-fs-lg);
line-height: 1;
cursor: pointer;
opacity: 0.75;
@@ -294,14 +294,14 @@
outline: 0;
background: transparent;
color: inherit;
font-size: 0.875rem;
font-size: var(--tb-fs-md);
}
.tb-tag-meta {
display: flex;
justify-content: space-between;
gap: 8px;
margin-top: 4px;
font-size: 0.75rem;
font-size: var(--tb-fs-xs);
color: var(--tb-text-muted);
}
.tb-tag-error { color: #f0506e; }
@@ -323,14 +323,14 @@
}
.tb-pcard-title {
margin: 0 0 10px;
font-size: 1.02rem;
font-size: var(--tb-fs-lg);
font-weight: 600;
}
/* 全页纵向压缩:面板内表单字段间距由 UIkit 默认 20px 收紧到 12px */
.profile-panel .uk-margin { margin-bottom: 12px; }
.tb-pcard-title .tb-pcard-extra {
float: right;
font-size: 0.76rem;
font-size: var(--tb-fs-xs);
font-weight: 400;
color: var(--tb-text-muted);
line-height: 1.6;
@@ -380,7 +380,7 @@
.tb-ratio-tag {
display: inline-block;
margin: 8px 0 0;
font-size: 0.72rem;
font-size: var(--tb-fs-xs);
line-height: 1;
color: var(--tb-text-secondary);
background: #f4f4f4;
@@ -390,7 +390,7 @@
}
.tb-file-hint {
display: block;
font-size: 0.78rem;
font-size: var(--tb-fs-xs);
line-height: 1.5;
color: var(--tb-text-muted);
margin-top: 6px;
@@ -419,7 +419,7 @@
display: block;
margin: 0 0 10px;
text-align: center;
font-size: 0.78rem;
font-size: var(--tb-fs-xs);
line-height: 1.6;
color: var(--tb-text-muted);
}
+1 -1
View File
@@ -78,7 +78,7 @@
{{ $replies := index $.TextReplies $stat.Field.Label }}
{{ if $replies }}
<div class="uk-margin-small-top">
<div class="uk-text-small" style="cursor:pointer; user-select:none; color:#1e87f0;" onclick="toggleFieldReplies('{{ $stat.Field.ID }}')" title="{{t $.LangCode "vote.resultsToggleHint"}}">
<div class="uk-text-small" style="cursor:pointer; user-select:none; color:var(--tb-primary);" onclick="toggleFieldReplies('{{ $stat.Field.ID }}')" title="{{t $.LangCode "vote.resultsToggleHint"}}">
<span id="caret-{{ $stat.Field.ID }}" style="display:inline-block; transition:transform .15s; transform:rotate(-90deg);">▾</span>
{{t $.LangCode "vote.resultsViewTexts"}}({{ len $replies }})
</div>