agentcss 0.25.4 → 0.25.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/01-tokens/base.reset.css +99 -0
- package/01-tokens/tokens.core.css +954 -0
- package/02-primitives/combobox/combobox.js +3 -0
- package/02-primitives/date-range/date-range.js +10 -1
- package/02-primitives/popover/popover.js +2 -0
- package/03-patterns/drawer/drawer.js +2 -0
- package/04-recipes/app-shell/shell-nav.js +1 -0
- package/09-assets/model-viewer/model-viewer.js +11 -2
- package/09-assets/scientific-plot/scientific-plot.js +1 -0
- package/CHANGELOG.md +141 -0
- package/CONTRIBUTING.md +4 -3
- package/README.md +20 -3
- package/README.zh-CN.md +41 -16
- package/SECURITY.md +28 -9
- package/START-HERE.md +7 -7
- package/SUPPORT.md +2 -2
- package/ai/components.json +1 -1
- package/dist/01-tokens/base.reset.css +1 -0
- package/dist/01-tokens/tokens.core.css +1 -0
- package/dist/02-primitives/combobox/combobox.js +3 -0
- package/dist/02-primitives/date-range/date-range.js +9 -1
- package/dist/02-primitives/popover/popover.js +1 -0
- package/dist/03-patterns/drawer/drawer.js +1 -0
- package/dist/04-recipes/app-shell/shell-nav.js +1 -0
- package/dist/09-assets/model-viewer/model-viewer.js +9 -2
- package/dist/09-assets/scientific-plot/scientific-plot.js +1 -0
- package/dist/COSTS.md +6 -6
- package/docs/ACCESSIBILITY.md +2 -2
- package/docs/CONVENTIONS.md +18 -6
- package/docs/LAYER-BASELINE.md +2 -2
- package/index.html +1 -1
- package/llms-full.txt +1 -1
- package/package.json +17 -5
|
@@ -0,0 +1,954 @@
|
|
|
1
|
+
/* ============================================================================
|
|
2
|
+
* tokens.core.css —— **只有变量**,不含任何全局规则
|
|
3
|
+
* ============================================================================
|
|
4
|
+
*
|
|
5
|
+
* 什么时候用它:把这个库嵌进**已有站点**,只想拿到配色/间距/字号令牌,
|
|
6
|
+
* 不想让库改掉宿主的焦点环、点按反馈、减少动效策略。
|
|
7
|
+
*
|
|
8
|
+
* 什么时候不该用它:从零搭页面(那就用 `tokens.css`,它含底座层)。
|
|
9
|
+
*
|
|
10
|
+
* ⚠️ 本文件是**生成物**,由 `05-audit/split-tokens.py --write` 从
|
|
11
|
+
* `01-tokens/tokens.css` 切出来。手改会在门禁里红,且下次重切就没了。
|
|
12
|
+
* 真值永远是 `tokens.css`(I-7:同一事实不留第二份手写来源)。
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
/* ============================================================================
|
|
16
|
+
frontend-lib / 01-tokens / tokens.css
|
|
17
|
+
纸感中性基底 · 第一版
|
|
18
|
+
|
|
19
|
+
铁律:这里的每一个颜色都必须带着 05-audit/contrast.py 的实测数字进来。
|
|
20
|
+
凭感觉填的色一律不许入库 —— 第一版就抓出 5 项不达标(见文件末尾"废弃记录")。
|
|
21
|
+
|
|
22
|
+
使用方式:
|
|
23
|
+
1) 现代构建:直接 @import 或复制进你的样式入口
|
|
24
|
+
2) 单文件版:只复制 [核心] 段,其余删掉,可内联进 <style>
|
|
25
|
+
|
|
26
|
+
改色流程:改值 -> 跑 python 05-audit/contrast.py -> 把新数字写回注释 -> 才允许提交
|
|
27
|
+
========================================================================= */
|
|
28
|
+
|
|
29
|
+
:root {
|
|
30
|
+
/* ================================================================ [L1] 原始值
|
|
31
|
+
⚠️ **这一层不是给使用者用的**,它是"配色取值"本身。
|
|
32
|
+
|
|
33
|
+
换一套品牌配色 = 只改这一块的 30 个值,L2 一行都不用动;
|
|
34
|
+
暗色段也只覆盖这块(见文件末尾的 `@media (prefers-color-scheme: dark)`)。
|
|
35
|
+
|
|
36
|
+
🔴 命名带 `--raw-` 是**刻意的**:它在告诉读代码的人
|
|
37
|
+
「别在组件里 `var(--raw-*)`」—— 组件只该用 L2 的语义名。
|
|
38
|
+
`token-audience-gate` 把这一层登记为 internal。
|
|
39
|
+
|
|
40
|
+
ℹ️ 边界(别夸大它的能力):这是"取值层",**不是**与语义解耦的色阶标尺
|
|
41
|
+
(gray-50 / gray-100 那种)。真正的色阶标尺需要先做一次颜色聚类与命名
|
|
42
|
+
决策,是独立的后续工程(内部排期不在公开面维护,别在这里找编号)。
|
|
43
|
+
本层解决的是"换肤只改一处 + 暗色不可能漏"这两件事。
|
|
44
|
+
---------------------------------------------------------------- */
|
|
45
|
+
--raw-paper: #f6f7f8;
|
|
46
|
+
--raw-surface: #ffffff;
|
|
47
|
+
--raw-surface-sunken: #f1f3f5;
|
|
48
|
+
--raw-surface-raised: #ffffff;
|
|
49
|
+
--raw-scrollbar-thumb: #7A808A;
|
|
50
|
+
--raw-scrollbar-thumb-hov: #6E747E;
|
|
51
|
+
--raw-text-primary: #15181c;
|
|
52
|
+
--raw-text-secondary: #4f555e;
|
|
53
|
+
--raw-text-tertiary: #606770;
|
|
54
|
+
--raw-text-on-accent: #ffffff;
|
|
55
|
+
--raw-text-on-solid: #ffffff;
|
|
56
|
+
--raw-skeleton-bg: #e9ecef;
|
|
57
|
+
--raw-skeleton-shimmer: rgba(255, 255, 255, 0.72);
|
|
58
|
+
--raw-border-control: #868b94;
|
|
59
|
+
--raw-border-decor: #e2e5e9;
|
|
60
|
+
--raw-border-decor-str: #c8cdd4;
|
|
61
|
+
--raw-danger: #c0392b;
|
|
62
|
+
--raw-danger-hover: #8f1e17;
|
|
63
|
+
--raw-scrim: rgba(21, 24, 28, 0.4);
|
|
64
|
+
--raw-success: #2f6b3a;
|
|
65
|
+
--raw-warning: #8a5a00;
|
|
66
|
+
--raw-info: #1d5f8a;
|
|
67
|
+
--raw-danger-bg: #fdeeec;
|
|
68
|
+
--raw-success-bg: #eaf2e6;
|
|
69
|
+
--raw-warning-bg: #fbf1dd;
|
|
70
|
+
--raw-info-bg: #e8f1f8;
|
|
71
|
+
/* 方向色(涨跌)。当前取值与 danger / success 相同 —— 但那是**巧合**,
|
|
72
|
+
不是同一件事,所以单独命名(理由见语义层那一段)。
|
|
73
|
+
浅色:红 4.89–5.44 · 绿 5.75–5.96,四组底全部 AA。 */
|
|
74
|
+
--raw-dir-up: #c0392b;
|
|
75
|
+
--raw-dir-down: #2f6b3a;
|
|
76
|
+
--raw-accent: #1b4d8f;
|
|
77
|
+
--raw-switch-on: #1b4d8f;
|
|
78
|
+
--raw-accent-hover: #163a6d;
|
|
79
|
+
--raw-accent-soft: #e6eef7;
|
|
80
|
+
|
|
81
|
+
/* 气泡的一对"反色面":浅色深底浅字,暗色换成抬高面。
|
|
82
|
+
独立成令牌是为了让组件不再按 `prefers-color-scheme` 分支(RFC-THEME-SCOPE D5)。
|
|
83
|
+
实测 16.60:1(浅)/ 11.93:1(暗)。border 浅色与气泡底同色(不可见),
|
|
84
|
+
不用 `transparent` 是因为令牌树会把它归成"非颜色"。 */
|
|
85
|
+
--raw-tooltip-bg: #15181c;
|
|
86
|
+
--raw-tooltip-fg: #f6f7f8;
|
|
87
|
+
--raw-tooltip-border: #15181c;
|
|
88
|
+
|
|
89
|
+
/* ---------------------------------------------------------------- 基底
|
|
90
|
+
paper = 页面底(冷灰白)。🔴 不能更浅:与 --surface 的对比度要 >= 1.06:1,
|
|
91
|
+
否则页面看着就是纯白,"表面浮在底上"的分层完全消失。
|
|
92
|
+
surface= 卡片/面板底,比纸底亮一档,靠明度差而非描边来分层
|
|
93
|
+
---------------------------------------------------------------- */
|
|
94
|
+
--paper: var(--raw-paper);
|
|
95
|
+
--surface: var(--raw-surface);
|
|
96
|
+
--surface-sunken: var(--raw-surface-sunken);
|
|
97
|
+
/* 🔴 浮层底(下拉/气泡/抽屉面板)。
|
|
98
|
+
这条**原本只在暗色段定义** ⇒ 浅色下 6 个组件(combobox/popover/select/
|
|
99
|
+
dropdown/tooltip/table)的 `background` 整条失效 ⇒ 实测**完全透明**。
|
|
100
|
+
浅色值 = --surface:浅色抬高靠阴影(--shadow-*),暗色才靠"面更亮"。 */
|
|
101
|
+
--surface-raised: var(--raw-surface-raised);
|
|
102
|
+
/* ============================================================================
|
|
103
|
+
🔴 滚动条令牌(判据:WCAG 2.5.5 Target Size)
|
|
104
|
+
----------------------------------------------------------------------------
|
|
105
|
+
⭐ 依据(5 个权威来源口径一致):
|
|
106
|
+
· CSS Scrollbars Level 1(W3C)—— scrollbar-width / color / gutter
|
|
107
|
+
· ::-webkit-scrollbar 伪元素族 —— Chromium/WebKit 专属,
|
|
108
|
+
**唯一能给 thumb 加圆角与 hover 的方式**
|
|
109
|
+
· WCAG 1.4.11 非文本对比 —— thumb 对 track **≥ 3:1**
|
|
110
|
+
|
|
111
|
+
⚠️ 为什么滚动条要**降彩度**、用灰色而不是品牌色:
|
|
112
|
+
滚动条是**次要**信息("还有多少内容"),不该抢注意力。
|
|
113
|
+
行业惯例(Radix / shadcn / GitHub)都是中性灰。
|
|
114
|
+
|
|
115
|
+
⚠️ 为什么 track 透明:轨道代表"没有内容的区域",
|
|
116
|
+
画出来等于视觉噪声。
|
|
117
|
+
==========================================================================*/
|
|
118
|
+
--scrollbar-size: 10px; /* 桌面宽度;移动端由浏览器接管 */
|
|
119
|
+
--scrollbar-size-thin: 6px; /* 代码块等次要场景 */
|
|
120
|
+
/* 🔴 色值是**脚本反解**出来的(WCAG 1.4.11 要求 thumb 对 track ≥3:1),
|
|
121
|
+
不是"看着差不多"挑的:
|
|
122
|
+
#7a808a 对 #ffffff 3.98:1 · 对 #f6f7f8 3.71:1 ✅
|
|
123
|
+
⚠️ 第一版我随手挑的 #B4BAC4 只有 1.82:1 ⇒ 门禁直接判不合格。 */
|
|
124
|
+
--scrollbar-thumb: var(--raw-scrollbar-thumb); /* 中性**冷**灰(非品牌色,且保持蓝调)*/
|
|
125
|
+
--scrollbar-thumb-hov: var(--raw-scrollbar-thumb-hov); /* hover 更深一档 · 4.06:1 */
|
|
126
|
+
--scrollbar-radius: 5px; /* = size/2 ⇒ 胶囊形 */
|
|
127
|
+
/* 内凹区:代码块、表格头、禁用底 */
|
|
128
|
+
|
|
129
|
+
/* ---------------------------------------------------------------- 文字
|
|
130
|
+
三档够用了。超过三档说明信息层级没想清楚,不是 token 不够。
|
|
131
|
+
全部数字由 05-audit/contrast.py 从本文件实测得出,不是手写的。
|
|
132
|
+
改任何颜色后必须重跑该脚本 —— 手写注释必然漂移(已发生过)。
|
|
133
|
+
---------------------------------------------------------------- */
|
|
134
|
+
--text-primary: var(--raw-text-primary); /* 16.60 纸底 / 17.81 表面 · AAA */
|
|
135
|
+
--text-secondary: var(--raw-text-secondary); /* 7.01 AAA */
|
|
136
|
+
/* 🔴 由 #6b7280 调深到 #606770。
|
|
137
|
+
axe-core 实测发现:#6b7280 压在**语义底色**上只有 4.13:1
|
|
138
|
+
(4.5 是正文门槛)—— 它只在白底上勉强过线(4.83),
|
|
139
|
+
一放到 --surface-sunken / --warning-bg 等底上就不达标。
|
|
140
|
+
调深后最低 4.89(压在最亮的 --surface 上),留了 0.39 余量。
|
|
141
|
+
梯度仍在:primary 17.8 / secondary 7.5 / tertiary 4.9 —— 明显分层。 */
|
|
142
|
+
--text-tertiary: var(--raw-text-tertiary); /* 5.33 纸底 / 4.89 最亮底(surface) · AA */ /* 4.51 AA */
|
|
143
|
+
--text-on-accent: var(--raw-text-on-accent); /* 8.38 压在 accent 上(不是压纸底)· AAA */
|
|
144
|
+
|
|
145
|
+
/* 🔴 新增( H9 期间由暗色门禁逼出来):
|
|
146
|
+
「实心态」组件(filled badge 等)压在**语义色实底**上的文字色。
|
|
147
|
+
⚠️ 为什么不能复用 --text-on-accent:
|
|
148
|
+
暗色下 --success / --danger / --warning 都**变浅**了(为在深底上更醒目),
|
|
149
|
+
实心态的底色也随之变浅 ⇒ 白字压上去只有 2.72:1。
|
|
150
|
+
⇒ 需要一个**独立令牌**,让暗色能把它换成深字。 */
|
|
151
|
+
--text-on-solid: var(--raw-text-on-solid); /* 亮色:实底(深色语义色)⇒ 白字 */
|
|
152
|
+
/* ---------------------------------------------------------------- 骨架屏
|
|
153
|
+
🔴 新增(对齐通行组件库的基础档位)
|
|
154
|
+
加载态是移动端最高频的状态;没有它,每个业务方都会自己造一个。 */
|
|
155
|
+
--skeleton-bg: var(--raw-skeleton-bg); /* 骨架底色:比纸面略深一档 */
|
|
156
|
+
--skeleton-shimmer: var(--raw-skeleton-shimmer); /* 微光高光 */
|
|
157
|
+
--skeleton-h: 1em; /* 单行默认高度 */
|
|
158
|
+
--skeleton-size: 40px; /* circle 默认直径 */
|
|
159
|
+
--skeleton-duration: 1.4s; /* 微光扫过周期 */
|
|
160
|
+
/* ---------------------------------------------------------------- 进度条
|
|
161
|
+
🔴 新增( I5)。组件私有参数集中在这里定义,
|
|
162
|
+
而不是散在 CSS 里用 fallback —— 否则 deps 门禁会报"未定义令牌"。
|
|
163
|
+
⭐ --ease-out 刻意独立命名:进度条要"向前"的 feel,
|
|
164
|
+
匀速(linear)反而像卡住了。 */
|
|
165
|
+
--ease-out: cubic-bezier(0.23, 1, 0.32, 1);
|
|
166
|
+
--progress-h: 6px; /* 线形默认高度 */
|
|
167
|
+
--progress-dur: 240ms; /* 宽度过渡时长 */
|
|
168
|
+
--r-full: 999px; /* 胶囊形圆角 */
|
|
169
|
+
--ring-size: 64px; /* 环形默认直径 */
|
|
170
|
+
--ring-thickness: 8px; /* 环形环宽 */
|
|
171
|
+
/* 进度条的当前值。默认 0% 是**正确默认**(不是占位)——
|
|
172
|
+
实例用 JS setProperty 覆盖它(见 progress demo)。
|
|
173
|
+
⚠️ 必须有默认值,否则"忘了设值"会渲染成 0% 且**没人发现**。 */
|
|
174
|
+
--progress: 0%;
|
|
175
|
+
/* 环形用**无单位**数值(conic-gradient 的角度计算不接受带 % 的长度)。
|
|
176
|
+
⚠️ 这就是环形曾完全不显示的原因,别再改成 --progress。 */
|
|
177
|
+
--progress-num: 0;
|
|
178
|
+
|
|
179
|
+
/* ---------------------------------------------------------------- 气泡卡片
|
|
180
|
+
🔴 新增( I4)。默认值是**正确默认**,
|
|
181
|
+
不是占位 —— 没定义就会静默用 0(deps 门禁会报未定义)。 */
|
|
182
|
+
--z-popover: 60; /* 层级:高于导航,低于 dialog */
|
|
183
|
+
--popover-min-w: 200px;
|
|
184
|
+
--popover-max-w: 320px;
|
|
185
|
+
--popover-p: var(--sp-3);
|
|
186
|
+
--popover-gap: 8px; /* 浮层与触发器的距离 */
|
|
187
|
+
--popover-dur: 140ms;
|
|
188
|
+
|
|
189
|
+
/* ---------------------------------------------------------------- 边框
|
|
190
|
+
🔴 本库最容易被做错的一处,两类不许合并:
|
|
191
|
+
|
|
192
|
+
control = 控件边界(输入框、按钮的轮廓)。WCAG 1.4.11 要求 >= 3.0,
|
|
193
|
+
因为它是"识别出这是个控件"的唯一线索。必须达标。
|
|
194
|
+
decor = 纯装饰分隔(卡片内线、表格行线)。WCAG 不强制。
|
|
195
|
+
对它强求 3.0 会把界面做得很重很难看 —— 那是误读规范。
|
|
196
|
+
---------------------------------------------------------------- */
|
|
197
|
+
--border-control: var(--raw-border-control); /* 3.19 / 3.42 AA Large */
|
|
198
|
+
--border-decor: var(--raw-border-decor); /* 1.18 纯装饰,WCAG 不强制 */
|
|
199
|
+
--border-decor-str: var(--raw-border-decor-str); /* 1.49 装饰,需要更强分隔时用 */
|
|
200
|
+
|
|
201
|
+
/* ---------------------------------------------------------------- 语义色
|
|
202
|
+
全部实测 >= 5.6 AA(vs 纸底)。可当文字用,也可当底色用。
|
|
203
|
+
---------------------------------------------------------------- */
|
|
204
|
+
--danger: var(--raw-danger);
|
|
205
|
+
--danger-hover: var(--raw-danger-hover); /* 危险按钮 hover:加深(浅色下加深才对)*/
|
|
206
|
+
--scrim: var(--raw-scrim); /* 抽屉/弹窗遮罩 */ /* 5.07 AA */
|
|
207
|
+
--success: var(--raw-success); /* 5.96 vs 纸底 · AA */
|
|
208
|
+
--warning: var(--raw-warning); /* 5.53 vs 纸底 · AA */
|
|
209
|
+
--info: var(--raw-info); /* 6.41 vs 纸底 · AA */
|
|
210
|
+
|
|
211
|
+
/* 语义色的浅底(用于提示条背景,不放正文) */
|
|
212
|
+
--danger-bg: var(--raw-danger-bg);
|
|
213
|
+
--success-bg: var(--raw-success-bg);
|
|
214
|
+
--warning-bg: var(--raw-warning-bg);
|
|
215
|
+
--info-bg: var(--raw-info-bg);
|
|
216
|
+
|
|
217
|
+
/* ---------------------------------------------------------------- 方向色
|
|
218
|
+
🔴 为什么方向要独立成令牌,而不是复用 --danger / --success
|
|
219
|
+
----------------------------------------------------------------
|
|
220
|
+
这两组读起来都是"红/绿",但它们是**两件事**:
|
|
221
|
+
|
|
222
|
+
--danger / --success = 好恶(错误/成功)。这是**评价**。
|
|
223
|
+
--dir-up / --dir-down = 涨跌(方向)。这是**事实**。
|
|
224
|
+
|
|
225
|
+
把两件事共用一个名字,会在这个场景里立刻暴露:
|
|
226
|
+
「研发费用同比 +38%」—— 是"涨",但那是**坏消息**。
|
|
227
|
+
用 --danger 表示它,颜色说着"错了",语义说着"涨了",读者收到矛盾信号。
|
|
228
|
+
|
|
229
|
+
所以方向色单独命名,取值可以与语义色相同(当前就相同),
|
|
230
|
+
但**允许将来分开调**:金融终端普遍用比 --danger 更饱和的红。
|
|
231
|
+
|
|
232
|
+
🔴 口径可整体切换(不要在每个组件里判断)
|
|
233
|
+
----------------------------------------------------------------
|
|
234
|
+
默认是 A 股 / 东亚口径:**红涨绿跌**。
|
|
235
|
+
给 `<html data-market="intl">`(或任意祖先)就切成国际口径:绿涨红跌。
|
|
236
|
+
|
|
237
|
+
实现方式与暗色同构 —— **换掉底层值,语义名不动**:
|
|
238
|
+
--dir-up 永远表示"涨",组件的 CSS 只写 `color: var(--dir-up)`。
|
|
239
|
+
口径切换只改这两条令牌的指向,组件一行都不用改。
|
|
240
|
+
组件里若出现 `[data-market]` 分支,那是把全局约定抄进了组件(判据会拦)。
|
|
241
|
+
|
|
242
|
+
实测对比度(05-audit/contrast.py,四组底各算一遍):
|
|
243
|
+
浅色:红 4.89–5.44 · 绿 5.75–5.96 (全部 AA)
|
|
244
|
+
暗色:红 5.60–6.15 · 绿 6.01–6.60 (全部 AA)
|
|
245
|
+
最差的一格是红点在 --surface-sunken 上 4.89 —— 仍 ≥ 4.5。
|
|
246
|
+
---------------------------------------------------------------- */
|
|
247
|
+
--dir-up: var(--raw-dir-up); /* 涨:默认红 */
|
|
248
|
+
--dir-down: var(--raw-dir-down); /* 跌:默认绿 */
|
|
249
|
+
|
|
250
|
+
/* ⚠️ 口径切换的规则在 `:root` **外面**(见本块结束后的「国际口径」)。
|
|
251
|
+
第一版写在里面 —— `:root { }` 只能装声明,不能装规则块。
|
|
252
|
+
theme-sync.py 按「声明序列」解析这个块,遇到嵌套规则会把选择器切碎,
|
|
253
|
+
生成一段无效 CSS(实测:`:where(...)` 被截成 `where(...)`、第二条声明丢失)。
|
|
254
|
+
这类嵌套即使浏览器支持 CSS Nesting 也不该写:本库要兼容 Chromium 97。 */
|
|
255
|
+
|
|
256
|
+
/* ---------------------------------------------------------------- 强调色
|
|
257
|
+
🔴 一源原则:一个项目只选一个 accent,不要混用。
|
|
258
|
+
accent 决定"可点击"的语义,混用等于取消了这层语义。
|
|
259
|
+
三选一,把另外两个删掉,别全留着。
|
|
260
|
+
---------------------------------------------------------------- */
|
|
261
|
+
--accent: var(--raw-accent);
|
|
262
|
+
/* 开关开态轨道色(浅色版):与品牌色一致;暗色下换降彩度值(见 dark 段)。
|
|
263
|
+
不用 var(--accent) —— 暗色下 accent 太亮,铺满一大块会发光。
|
|
264
|
+
⚠️ 这段注释原本**缺开头**,导致 --switch-on 被浏览器当成坏声明丢掉
|
|
265
|
+
(实测浅色下取值为空)。已由 token-parse-gate.py 兜住。 */
|
|
266
|
+
--switch-on: var(--raw-switch-on); /* 7.82 AAA */
|
|
267
|
+
--accent-hover: var(--raw-accent-hover); /* 11.29 白字压上 · AAA */
|
|
268
|
+
--accent-soft: var(--raw-accent-soft); /* focus ring / 浅底,不要拿它放正文 */
|
|
269
|
+
|
|
270
|
+
/* 气泡(见 L1 的说明) */
|
|
271
|
+
--tooltip-bg: var(--raw-tooltip-bg);
|
|
272
|
+
--tooltip-fg: var(--raw-tooltip-fg);
|
|
273
|
+
--tooltip-border: var(--raw-tooltip-border);
|
|
274
|
+
/* --accent: #0f7a6c; 薄荷绿 5.00 —— 活泼/工具类 */
|
|
275
|
+
/* --accent-hover: #0c6459; 白字 7.04 AAA */
|
|
276
|
+
/* --accent-soft: #dff1ec; */
|
|
277
|
+
/* --accent: #185fa5; 蓝 6.25 —— 严肃/数据类 */
|
|
278
|
+
|
|
279
|
+
/* ================================================================ [核心]
|
|
280
|
+
间距:8px 网格为基准,大值流体。
|
|
281
|
+
想要的值不在表里 => 说明布局没规划,改设计,不要加 token。
|
|
282
|
+
|
|
283
|
+
🔴 哪些该流体:--sp-4 往上(区块间距,随屏幕变大而变大,观感自然)
|
|
284
|
+
哪些不该:--sp-1..3(元素内部间距,流体化会让控件尺寸忽大忽小)
|
|
285
|
+
---------------------------------------------------------------- */
|
|
286
|
+
--sp-1: 4px;
|
|
287
|
+
--sp-2: 8px;
|
|
288
|
+
--sp-3: 12px;
|
|
289
|
+
--sp-4: clamp(1rem, 0.95rem + 0.3vw, 1.25rem); /* 16 → 20px */
|
|
290
|
+
--sp-5: clamp(1.25rem, 1.15rem + 0.7vw, 1.75rem); /* 20 → 28px */
|
|
291
|
+
--sp-6: clamp(1.5rem, 1.3rem + 1.2vw, 2.25rem); /* 24 → 36px */
|
|
292
|
+
--sp-7: clamp(2rem, 1.7rem + 1.8vw, 3.5rem); /* 32 → 56px */
|
|
293
|
+
--sp-8: clamp(2.5rem, 2.1rem + 2.4vw, 4.5rem); /* 40 → 72px */
|
|
294
|
+
|
|
295
|
+
/* ================================================================ [核心]
|
|
296
|
+
断点令牌
|
|
297
|
+
----------------------------------------------------------------
|
|
298
|
+
🔴 CSS 变量**不能**用在 media query 条件里。
|
|
299
|
+
所以下面四个只是"文档与计算的单一出处",写 media query 时仍要写字面值:
|
|
300
|
+
|
|
301
|
+
@media (min-width: 48rem) { ... } ← 只能写字面值,不能写 var(--bp-md)
|
|
302
|
+
|
|
303
|
+
留它们的理由:算流体值、给脚本查、以及让"我们有几个断点"这件事有唯一答案。
|
|
304
|
+
⚠️ 断点用的是 rem 不是 px —— 用户调大字号时断点跟着变,这才是对的。
|
|
305
|
+
---------------------------------------------------------------- */
|
|
306
|
+
--bp-sm: 30rem; /* 480px 大手机 / 小平板 */
|
|
307
|
+
--bp-md: 48rem; /* 768px 平板 */
|
|
308
|
+
--bp-lg: 64rem; /* 1024px 小笔记本 */
|
|
309
|
+
--bp-xl: 80rem; /* 1280px 桌面 */
|
|
310
|
+
|
|
311
|
+
/* ================================================================ [核心]
|
|
312
|
+
字号阶梯
|
|
313
|
+
----------------------------------------------------------------
|
|
314
|
+
🔴 修正:上下限从 px 改成 rem。
|
|
315
|
+
px 上下限不尊重"用户把浏览器默认字号调大"的设置 ——
|
|
316
|
+
那样 clamp 的下限会锁死,用户放到 200% 也涨不上去。
|
|
317
|
+
rem 基准跟着用户设置走,这是无障碍底线。
|
|
318
|
+
|
|
319
|
+
🔴 哪些该流体、哪些不该(这条更容易做错):
|
|
320
|
+
|
|
321
|
+
该流体:正文、小标题、大标题、间距
|
|
322
|
+
—— 它们随屏幕变大而变大,观感自然
|
|
323
|
+
|
|
324
|
+
**不该流体**:UI 文本(--fs-xs / --fs-sm)、小间距(--sp-1..3)
|
|
325
|
+
—— 它们是控件尺寸的一部分。流体会让按钮和输入框在不同屏幕下
|
|
326
|
+
大小不一,布局抖动。搜索到的原话:
|
|
327
|
+
"UI typography benefits from minimal scaling.
|
|
328
|
+
Too much fluidity breaks layout."
|
|
329
|
+
|
|
330
|
+
公式:clamp(MIN-REM, REM基准 + VW系数, MAX-REM)
|
|
331
|
+
VW 系数怎么来:先定 min/max 视口(375px / 1280px),
|
|
332
|
+
系数 ≈ (max-rem - min-rem) × 100 / (1280 - 375) ≈ 差值 × 0.1105
|
|
333
|
+
|
|
334
|
+
✅ WCAG 1.4.4 判据:只要 MAX ≤ 2.5 × MIN,就一定通过 resize-text 检查。
|
|
335
|
+
本阶梯最大比值 2.5rem/1.75rem = 1.43,安全。
|
|
336
|
+
---------------------------------------------------------------- */
|
|
337
|
+
--fs-xs: 0.75rem; /* 12px 固定:辅助文字不流体 */
|
|
338
|
+
--fs-sm: 0.875rem; /* 14px 固定:UI 文本不流体 */
|
|
339
|
+
--fs-base: clamp(1rem, 0.95rem + 0.3vw, 1.125rem); /* 16 → 18px */
|
|
340
|
+
--fs-md: clamp(1.125rem, 1.05rem + 0.5vw, 1.3125rem); /* 18 → 21px */
|
|
341
|
+
--fs-lg: clamp(1.3125rem, 1.2rem + 0.7vw, 1.5rem); /* 21 → 24px */
|
|
342
|
+
--fs-xl: clamp(1.5rem, 1.3rem + 1.2vw, 1.875rem); /* 24 → 30px */
|
|
343
|
+
--fs-2xl: clamp(1.75rem, 1.45rem + 1.8vw, 2.5rem); /* 28 → 40px */
|
|
344
|
+
|
|
345
|
+
/* 行高:正文 1.7 是中文长文的舒适值(西文常用 1.5,中文需要更松) */
|
|
346
|
+
--lh-tight: 1.25; /* 标题 */
|
|
347
|
+
--lh-base: 1.7; /* 正文 */
|
|
348
|
+
--lh-loose: 1.85; /* 长段落、低龄/无障碍场景 */
|
|
349
|
+
|
|
350
|
+
/* 行长:65ch 左右是阅读上限。超过就要分栏或收窄容器。
|
|
351
|
+
⚠️ 这里曾经有一个裸的 `--measure: 68ch`,0.7.3 移除 —— 它既没被任何组件
|
|
352
|
+
引用、也没在任何文档里出现过,而 `--measure-page`(页面列宽)与
|
|
353
|
+
`--measure-prose`(长文列宽)已经把它该管的两个场景分掉了。
|
|
354
|
+
不要再把它加回来:`token-audience-gate.py` 判据 ① 会拦住没登记受众的令牌。
|
|
355
|
+
⇒ 版心请用下面这两个带语义后缀的。 */
|
|
356
|
+
|
|
357
|
+
/* ----------------------------------------------------------------
|
|
358
|
+
🔴 补:页面列宽 --measure-page(0.4.2)
|
|
359
|
+
此前 33 个页面各自写 `.wrap { max-width: 680px / 720 / 760 / 800 / 820 /
|
|
360
|
+
860 / 900 / 42rem / 44rem / 46rem / 52rem … }` —— 16 种写法。
|
|
361
|
+
结果就是「没有全局可改」:想改列宽要改 33 处,漏一处就不一致。
|
|
362
|
+
实测众数是 820px(8/33 页),取 52rem = 832px(干净的 rem 值,
|
|
363
|
+
且 rem 会跟随用户字号设置,px 不会)。
|
|
364
|
+
改这一个令牌 ⇒ 全部页面列宽一起变。
|
|
365
|
+
---------------------------------------------------------------- */
|
|
366
|
+
--measure-page: 52rem;
|
|
367
|
+
|
|
368
|
+
/* 🔴 补:标题字距 --tracking-title(0.4.2)
|
|
369
|
+
大字号要收紧字距,否则标题散。此前只有 list/nav/states 三页写了
|
|
370
|
+
`letter-spacing: -0.02em`(写死值),其余 33 页没有 ⇒ 标题观感不一致。 */
|
|
371
|
+
--tracking-title: -0.02em;
|
|
372
|
+
|
|
373
|
+
/* ---------------------------------------------------------------- 圆角
|
|
374
|
+
一套就够。圆角混搭(卡片 20px、按钮 4px)是典型的没想清楚。
|
|
375
|
+
---------------------------------------------------------------- */
|
|
376
|
+
--r-sm: 6px;
|
|
377
|
+
/* 🔴 补:内嵌小元素用的小圆角(菜单项、标签、chip)。
|
|
378
|
+
之前 dropdown.css 里凭空写了 var(--r-xs, 3px) —— 令牌不存在时
|
|
379
|
+
CSS 变量会**静默失效**(整条声明被丢弃),所以一直没被发现。
|
|
380
|
+
⇒ 补上这个令牌(deps 门禁抓到)。 */
|
|
381
|
+
--r-xs: 3px;
|
|
382
|
+
--r-md: 10px;
|
|
383
|
+
--r-lg: 14px;
|
|
384
|
+
--r-pill: 999px;
|
|
385
|
+
|
|
386
|
+
/* ---------------------------------------------------------------- 阴影
|
|
387
|
+
两级。第三级阴影出现的时候,通常说明层级设计出了问题。
|
|
388
|
+
y 轴偏移小、模糊大 —— 模拟纸面浮起,不是悬浮在太空。
|
|
389
|
+
---------------------------------------------------------------- */
|
|
390
|
+
--shadow-1: 0 1px 2px rgba(31, 29, 25, 0.05), 0 1px 3px rgba(31, 29, 25, 0.06);
|
|
391
|
+
--shadow-2: 0 2px 4px rgba(31, 29, 25, 0.05), 0 6px 16px rgba(31, 29, 25, 0.08);
|
|
392
|
+
|
|
393
|
+
/* ---------------------------------------------------------------- 动效
|
|
394
|
+
🔴 一源原则:全项目只用这一组时长和缓动。
|
|
395
|
+
六个地方来的六个效果 => 六个不同的缓动曲线 => 看起来像没设计过。
|
|
396
|
+
---------------------------------------------------------------- */
|
|
397
|
+
--dur-fast: 120ms; /* 颜色、透明度 */
|
|
398
|
+
--dur-base: 180ms; /* 位移、展开 */
|
|
399
|
+
--dur-slow: 280ms; /* 大块入场、遮罩 */
|
|
400
|
+
|
|
401
|
+
--ease-out: cubic-bezier(0.23, 1, 0.32, 1); /* UI 默认:入场、展开 */
|
|
402
|
+
--ease-in-out: cubic-bezier(0.77, 0, 0.175, 1); /* 屏上位移、折叠 */
|
|
403
|
+
--ease-drawer: cubic-bezier(0.32, 0.72, 0, 1); /* 抽屉式滑入(iOS 观感)*/
|
|
404
|
+
|
|
405
|
+
/* 🔴 修正:上一版这三个曲线是**我自编**的
|
|
406
|
+
(0.22,1,0.36,1 / 0.65,0,0.35,1),现已换成业界通用值 ——
|
|
407
|
+
与 Tailwind v4 默认、Emil Kowalski 技能库一致。
|
|
408
|
+
**曲线不该自己编**:自编的曲线没有经过大量实现验证,
|
|
409
|
+
阻尼比、重心位置都是猜的,跨浏览器表现不一致。 */
|
|
410
|
+
|
|
411
|
+
/* 位移距离:动效的"幅度"也要统一,不然同样 180ms 也会感觉乱 */
|
|
412
|
+
--lift-sm: 1px;
|
|
413
|
+
--lift-md: 2px;
|
|
414
|
+
--lift-lg: 8px;
|
|
415
|
+
|
|
416
|
+
/* ---------------------------------------------------------------- z-index
|
|
417
|
+
乱写 z-index(999、9999)是前端最常见的失控点。
|
|
418
|
+
只用这六个值。需要更高的 => 说明层级模型有问题。
|
|
419
|
+
---------------------------------------------------------------- */
|
|
420
|
+
--z-base: 0;
|
|
421
|
+
--z-sticky: 10;
|
|
422
|
+
--z-header: 20;
|
|
423
|
+
/* 🔴 补回:上一轮"清理重复 z 令牌"时把它误删了,
|
|
424
|
+
而 card / pagination 都在用 ⇒ 它们的 z-index 声明**静默失效**。
|
|
425
|
+
⚠️ CSS 变量不存在时**不报错**,声明直接被丢弃 —— 这类问题只能靠门禁抓。 */
|
|
426
|
+
--z-raised: 5; /* 浮起层(卡片悬停、下拉、内层可点元素)*/
|
|
427
|
+
--z-overlay: 30;
|
|
428
|
+
--z-modal: 40;
|
|
429
|
+
--z-toast: 50;
|
|
430
|
+
|
|
431
|
+
/* ------------------------------------------------------------
|
|
432
|
+
命中区尺寸
|
|
433
|
+
|
|
434
|
+
之前全库到处硬写 `44px`(20 处,各组件各写一遍)⇒ 改一次要翻遍全库。
|
|
435
|
+
|
|
436
|
+
为什么是 44px:**WCAG 2.5.5 Target Size(Level AAA)** 要求 44×44 CSS px。
|
|
437
|
+
⚠️ 别混淆:WCAG 2.5.8(AA 级)只要 24×24;本库按 AAA 做。
|
|
438
|
+
------------------------------------------------------------ */
|
|
439
|
+
--sz-touch: 44px; /* 最小命中区(指针)*/
|
|
440
|
+
--sz-touch-sm: 32px; /* 次要场景(表格行内等密排)*/
|
|
441
|
+
|
|
442
|
+
/* ==========================================================================
|
|
443
|
+
🔴 补:跳过导航链接(skip link)
|
|
444
|
+
--------------------------------------------------------------------------
|
|
445
|
+
a11y-scan 报了两条违规(都因为入口页没有地标):
|
|
446
|
+
· landmark-one-main —— 文档应有一个 <main>
|
|
447
|
+
· region —— 所有内容应被地标包裹
|
|
448
|
+
⇒ 修法:入口页包 <main id="main">,并提供"跳到主内容"的链接。
|
|
449
|
+
|
|
450
|
+
⭐ 为什么要 skip link:
|
|
451
|
+
键盘用户每次进页面都要 Tab 过几十个导航链接才能到正文。
|
|
452
|
+
skip link 让它们**按一次 Tab 就能跳过**(第一次 Tab 就会出现,
|
|
453
|
+
聚焦时才显形)—— 这是 WCAG 2.4.1 Bypass Blocks 的标准做法。
|
|
454
|
+
========================================================================== */
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
|
|
458
|
+
/* ============================================================================
|
|
459
|
+
废弃记录 —— 保留在这里是为了不再犯同样的错
|
|
460
|
+
=========================================================================
|
|
461
|
+
第一版自检抓出 5 项不达标,全部是"凭感觉填的":
|
|
462
|
+
|
|
463
|
+
text-tertiary #8a8478 vs 纸底 = 3.56 → 正文需 4.5,不达标
|
|
464
|
+
vs 白面 = 3.72 → 同样不达标
|
|
465
|
+
改 #6f6a5e(5.16 / 5.39)
|
|
466
|
+
教训:辅助文字色最容易填浅,因为它"看起来就该浅"。
|
|
467
|
+
|
|
468
|
+
border #cfc9bb vs 纸底 = 1.58 → UI 边界需 3.0,不达标
|
|
469
|
+
改 #8a8478(3.56)
|
|
470
|
+
但:装饰分隔线不需要 3.0,硬套会让界面变重
|
|
471
|
+
→ 因此把边框拆成 control / decor 两类,这是第一版最大的收获。
|
|
472
|
+
========================================================================= */
|
|
473
|
+
|
|
474
|
+
/* ============================================================================
|
|
475
|
+
i18n:度量令牌(行长控制)
|
|
476
|
+
============================================================================
|
|
477
|
+
🔴 为什么需要这一节
|
|
478
|
+
|
|
479
|
+
中文排版用 `em` 限宽看起来"完美",**但那是巧合**:1em = 1 个汉字宽。
|
|
480
|
+
换到英文,同样的 `max-width: 14em` 只能放约 20 个字符 ——
|
|
481
|
+
实测 84 字符的英文标题会折成 **4 行**,这是坏的排版。
|
|
482
|
+
|
|
483
|
+
`ch` 也不对:ch = "0" 的宽度,**1 汉字 ≈ 2ch**,
|
|
484
|
+
所以 13ch 只能放 6 个汉字(曾见过标题被挤成三行)。
|
|
485
|
+
|
|
486
|
+
⇒ 结论:**没有任何单一单位能同时伺候所有语言**,必须按语言切换。
|
|
487
|
+
|
|
488
|
+
用法:组件用 `var(--measure-*)`,不写死单位。
|
|
489
|
+
|
|
490
|
+
h1 { max-width: var(--measure-title); }
|
|
491
|
+
p { max-width: var(--measure-prose); }
|
|
492
|
+
|
|
493
|
+
── 各语言的取值依据 ──────────────────────────────────────────────
|
|
494
|
+
| 语言 | 计量 | 正文值 | 舒适区 |
|
|
495
|
+
|-------------|-----------|--------|---------------|
|
|
496
|
+
| 中文/日韩 | em(1em=1字)| 34em | 25-35 字 |
|
|
497
|
+
| 拉丁/西里尔 | ch(1ch≈1字)| 68ch | 60-75 字符 |
|
|
498
|
+
| 阿拉伯 | ch | 60ch | 同上,且 RTL |
|
|
499
|
+
| 印地/泰文 | ch | 60ch | 连字会让 ch 偏小,取小值更安全 |
|
|
500
|
+
|
|
501
|
+
`:lang()` 选的是**内容语言**,不是界面语言 —— 这才是对的。
|
|
502
|
+
========================================================================= */
|
|
503
|
+
|
|
504
|
+
:root {
|
|
505
|
+
/* 正文行长:中文 34 字 ≈ 拉丁 68 字符,两边都落在各自的舒适区 */
|
|
506
|
+
--measure-prose: 34em;
|
|
507
|
+
--measure-title: 20em; /* 标题比正文窄,避免大字长时间扫视 */
|
|
508
|
+
--measure-lede: 32em; /* 导语 */
|
|
509
|
+
--measure-pull: 30em; /* 引言块 */
|
|
510
|
+
--measure-aside: 32em; /* 边注 */
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
|
|
514
|
+
/* 拉丁 / 西里尔 / 希腊:改用 ch(1ch ≈ 1 个字符宽) */
|
|
515
|
+
:root:lang(en), :root:lang(en-US), :root:lang(en-GB),
|
|
516
|
+
:root:lang(fr), :root:lang(de), :root:lang(es), :root:lang(pt),
|
|
517
|
+
:root:lang(ru), :root:lang(el), :root:lang(tr), :root:lang(vi) {
|
|
518
|
+
--measure-prose: 68ch;
|
|
519
|
+
--measure-title: 34ch;
|
|
520
|
+
--measure-lede: 62ch;
|
|
521
|
+
--measure-pull: 58ch;
|
|
522
|
+
--measure-aside: 56ch;
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
|
|
526
|
+
/* 阿拉伯语:RTL,行长同样按 ch */
|
|
527
|
+
:root:lang(ar) {
|
|
528
|
+
--measure-prose: 60ch;
|
|
529
|
+
--measure-title: 30ch;
|
|
530
|
+
--measure-lede: 56ch;
|
|
531
|
+
--measure-pull: 52ch;
|
|
532
|
+
--measure-aside: 50ch;
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
|
|
536
|
+
/* 印地 / 泰文 / 高棉 / 缅甸:连字与组合字形会让 ch 偏小,取更保守的值 */
|
|
537
|
+
:root:lang(hi), :root:lang(th), :root:lang(km), :root:lang(my) {
|
|
538
|
+
--measure-prose: 56ch;
|
|
539
|
+
--measure-title: 28ch;
|
|
540
|
+
--measure-lede: 52ch;
|
|
541
|
+
--measure-pull: 48ch;
|
|
542
|
+
--measure-aside: 46ch;
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
|
|
546
|
+
/* 🔴 兜底:未知语言(`lang` 缺失或写了别的)走 ch 而不是 em ——
|
|
547
|
+
拉丁字母是更常见的假设;而且 ch 在等宽字体下也准。
|
|
548
|
+
中文/日韩显式命中上面的 em 分支(:root 默认值),不会落到这里。 */
|
|
549
|
+
:root:lang(zh), :root:lang(zh-CN), :root:lang(zh-TW),
|
|
550
|
+
:root:lang(ja), :root:lang(ko) {
|
|
551
|
+
--measure-prose: 34em;
|
|
552
|
+
--measure-title: 20em;
|
|
553
|
+
--measure-lede: 32em;
|
|
554
|
+
--measure-pull: 30em;
|
|
555
|
+
--measure-aside: 32em;
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
|
|
559
|
+
/* ============================================================================
|
|
560
|
+
暗色模式(prefers-color-scheme: dark)
|
|
561
|
+
============================================================================
|
|
562
|
+
🔴 建立。**下面每个值都是按 WCAG 公式算出来的,不是"反过来"。**
|
|
563
|
+
|
|
564
|
+
── 为什么不能把浅色反过来 ────────────────────────────────────
|
|
565
|
+
1. **纯黑底会让彩色文字发灰**(视觉上对比度下降)⇒ paper 用 #14171a 不是 #000
|
|
566
|
+
2. **明度关系要重排**:浅色是"底浅→字深",暗色是"底深→字亮",
|
|
567
|
+
但**中间灰阶的分布完全不同** —— 浅色的 #6b7280(tertiary)
|
|
568
|
+
在深底上完全不够用(只有 2.1:1)
|
|
569
|
+
3. **饱和色要降**:#c0392b 在深底上刺眼且显脏 ⇒ 提到 #e07a6e
|
|
570
|
+
4. **强调色方向相反**:浅色用深靛蓝(沉),暗色必须**变亮**才看得见
|
|
571
|
+
⇒ --accent 从 #1b4d8f 变成 #7aa9de。**这不是"反色"能得出的。**
|
|
572
|
+
5. **按钮上的文字要翻过来**:浅色按钮是"蓝底白字",
|
|
573
|
+
暗色按钮变亮 ⇒ 必须改成"**亮底深字**"(--text-on-accent: #0d1114)
|
|
574
|
+
|
|
575
|
+
── 实测数据(WCAG 相对亮度公式算的)──────────────────────────
|
|
576
|
+
| 令牌 | 暗色值 | 在 paper 上的对比度 | 要求 |
|
|
577
|
+
|---|---|---|---|
|
|
578
|
+
| --text-primary | #e8eaed | 14.93 | 4.5 ✅ |
|
|
579
|
+
| --text-secondary | #b8bec6 | 9.61 | 4.5 ✅ |
|
|
580
|
+
| --text-tertiary | #959ba3 | 6.42 | 4.5 ✅ |
|
|
581
|
+
| --danger | #e07a6e | 6.15 | 4.5 ✅ |
|
|
582
|
+
| --success | #6faa7a | 6.60 | 4.5 ✅ |
|
|
583
|
+
| --warning | #c99a3f | 7.01 | 4.5 ✅ |
|
|
584
|
+
| --info | #5b9bc4 | 5.94 | 4.5 ✅ |
|
|
585
|
+
| --accent | #7aa9de | 7.33 | 4.5 ✅ |
|
|
586
|
+
| --text-on-accent | #0d1114 | 7.73(压 accent 上)| 4.5 ✅ |
|
|
587
|
+
| --border-control | #7d848d | 4.76 | 3.0 ✅ |
|
|
588
|
+
|
|
589
|
+
⚠️ **--text-primary 故意不用最高对比的那个**(#f2f4f6 = 16.32:1)。
|
|
590
|
+
暗色下过高的对比会**发光、刺眼**,长时间阅读更累。
|
|
591
|
+
**过线即可,不是越高越好。**
|
|
592
|
+
|
|
593
|
+
── 底色三层(明度差必须够,否则分层直接消失)──────────────────
|
|
594
|
+
| 层 | 值 | 与 paper 的对比 |
|
|
595
|
+
|---|---|---|
|
|
596
|
+
| sunken(内凹)| #080a0c | 1.10 ✅ |
|
|
597
|
+
| paper(页面底)| #14171a | — |
|
|
598
|
+
| surface(卡片)| #1c2024 | 1.10 ✅ |
|
|
599
|
+
|
|
600
|
+
🔴 我第一版把 sunken 设成 #0f1214,与 paper 只差 **1.05** ——
|
|
601
|
+
**卡片和页面底几乎分不开**,这正是本库在浅色下踩过的坑
|
|
602
|
+
(--paper 曾经是 #fcfcfd,与白色只差 1.025)。**同样的错不能犯两次。**
|
|
603
|
+
|
|
604
|
+
── 为什么用 @media 而不是 light-dark() ──────────────────────
|
|
605
|
+
`light-dark()` 是 2024 的新函数,**精简档 的老 WebView 不支持**。
|
|
606
|
+
用 @media 是目前兼容性最好的做法。
|
|
607
|
+
---------------------------------------------------------------- */
|
|
608
|
+
|
|
609
|
+
/* `screen and` 承重:暗色是屏幕概念,纸没有深色模式。
|
|
610
|
+
缺了它,暗色令牌在 print 里照样生效 ⇒ 浅字配被丢弃的深底。 */
|
|
611
|
+
@media screen and (prefers-color-scheme: dark) {
|
|
612
|
+
/* ========================================================================
|
|
613
|
+
第二次修正(上一版被 真机实测否决)
|
|
614
|
+
------------------------------------------------------------------------
|
|
615
|
+
反馈:「小方框变成了咖啡色」「背景变成了淡白色」
|
|
616
|
+
「主业是白色兼灰色有高级感,现在变成了暖色」
|
|
617
|
+
|
|
618
|
+
前一版的三个问题:
|
|
619
|
+
1 引入暖灰(#E8E6E3 / #1E1E1E)—— 反馈像咖啡色
|
|
620
|
+
2 --paper 被写成别处覆盖的值 —— 结果 dark 下背景是淡白
|
|
621
|
+
3 🔴 注释里写了 `#080a0c` 和中文分号
|
|
622
|
+
⇒ 被 gen-theme-js.py 的 `[^;]+` 当成**属性值**
|
|
623
|
+
⇒ --paper 整个丢失 ⇒ 切暗色时背景不变(实测确认)
|
|
624
|
+
|
|
625
|
+
⭐ 本次原则:**只修该修的,色相一律保持冷灰**
|
|
626
|
+
主业的"高级感"来自冷灰(白+灰),不是暖灰 —— 这是 最终定的调。
|
|
627
|
+
|
|
628
|
+
保留三条业界共识(不改变色相):
|
|
629
|
+
1 层级靠"表面变亮":暗色下阴影不可见,抬高面必须更亮
|
|
630
|
+
原来的 --surface-sunken 比 paper 还深,方向是错的
|
|
631
|
+
2 正文不用纯白:纯白在深底上会 halation(字发散)
|
|
632
|
+
3 tertiary 必须够 4.5:1:用脚本算,不靠眼睛调
|
|
633
|
+
======================================================================== */
|
|
634
|
+
|
|
635
|
+
/* 冷灰基座 -- 不用纯黑(OLED 拖影),不用暖灰(显廉价) */
|
|
636
|
+
/* ⚠️ `:not([data-theme="light"])` —— 让「强制亮」能压过系统暗色。
|
|
637
|
+
不加这层 `:not`:系统暗色 + data-theme="light" 时,本段仍会命中
|
|
638
|
+
⇒ 使用者点了「亮」却还是暗的(三态开关里最容易被忽略的一条路径)。
|
|
639
|
+
⇒ 强制暗由下面生成的 `[data-theme="dark"]` 段负责,两边互不干扰。 */
|
|
640
|
+
:root:not([data-theme="light"]) {
|
|
641
|
+
--raw-paper: #14171A;
|
|
642
|
+
--raw-surface: #1C2024;
|
|
643
|
+
--raw-surface-raised: #232830;
|
|
644
|
+
/* 凹陷面不再更黑,改为比 paper 略浅(层级靠变亮) */
|
|
645
|
+
--raw-surface-sunken: #1A1D21;
|
|
646
|
+
|
|
647
|
+
/* 🔴滚动条(暗色)
|
|
648
|
+
⚠️ 暗色下 thumb 要比浅色**更亮**(#4A515C)才看得见;
|
|
649
|
+
用浅色那档会变成"黑里有黑"。 */
|
|
650
|
+
--raw-scrollbar-thumb: #6E747E; /* 对 #14171a 3.82:1 · 对 #232830 3.15:1 ✅ */
|
|
651
|
+
--raw-scrollbar-thumb-hov: #7D828A; /* 4.24:1 */
|
|
652
|
+
|
|
653
|
+
/* 边框 -- 冷灰,靠变亮分层 */
|
|
654
|
+
--raw-border-decor: #2A3038;
|
|
655
|
+
--raw-border-decor-str: #3A424C;
|
|
656
|
+
--raw-border-control: #8A9099;
|
|
657
|
+
|
|
658
|
+
/* 文本 -- 冷调白,非纯白(避免 halation) */
|
|
659
|
+
--raw-text-primary: #E4E7EA;
|
|
660
|
+
--raw-text-secondary: #AEB6C0;
|
|
661
|
+
/* 实算 原来的 #959ba3 对 paper 只有 4.14:1 不达 AA,提到这个值 */
|
|
662
|
+
--raw-text-tertiary: #9BA3AD;
|
|
663
|
+
--raw-text-on-accent: #0D1114;
|
|
664
|
+
--raw-text-on-solid: #0D1114; /* 暗色:success/danger/warning 都变浅了 ⇒ 实心态换深字 */
|
|
665
|
+
/* 暗色:底色比纸面**略浅**(深底上"更亮"才看得出是占位) */
|
|
666
|
+
--raw-skeleton-bg: #2E343C; /* 亮度差 0.032(契约要求 >0.02)*/
|
|
667
|
+
--raw-skeleton-shimmer: rgba(255, 255, 255, 0.06);
|
|
668
|
+
|
|
669
|
+
/* 语义色 -- 沿用原有冷调 */
|
|
670
|
+
--raw-danger: #E07A6E;
|
|
671
|
+
--raw-danger-hover: #F09A90;
|
|
672
|
+
--raw-danger-bg: #341D1A;
|
|
673
|
+
--raw-success: #6FAA7A;
|
|
674
|
+
--raw-success-bg: #16241A;
|
|
675
|
+
/* 方向色(暗色):与 success / danger 同值,理由见浅色段。
|
|
676
|
+
暗色下红 5.60–6.15 · 绿 6.01–6.60,四组底全部 AA。 */
|
|
677
|
+
--raw-dir-up: #E07A6E;
|
|
678
|
+
--raw-dir-down: #6FAA7A;
|
|
679
|
+
--raw-warning: #C99A3F;
|
|
680
|
+
/* 🔴 最终定的(第二次修正):
|
|
681
|
+
「本身**淡黄色的那些框**你就改成**暖灰色**,
|
|
682
|
+
**在深色模式下的暖灰色**这个就很好啊」
|
|
683
|
+
|
|
684
|
+
浅色下 --warning-bg 是 #fbf1dd(淡黄)—— **保持不动**。
|
|
685
|
+
暗色下原来是 #2B2313(**深红棕**)⇒ 在冷灰背景里
|
|
686
|
+
像一块"**咖啡色**"的脏斑 ⇒ 这就是 真机实测看到的。
|
|
687
|
+
|
|
688
|
+
⇒ 暗色下改成**暖灰**(保留一点暖调,与浅色的淡黄呼应,
|
|
689
|
+
但**不再偏红褐**,与整体的冷灰底融合)。
|
|
690
|
+
⚠️ 只改这一个令牌,**底色/表面/文本一律不动**。 */
|
|
691
|
+
--raw-warning-bg: #242A31;
|
|
692
|
+
--raw-info: #5B9BC4;
|
|
693
|
+
--raw-info-bg: #13242F;
|
|
694
|
+
|
|
695
|
+
/* 品牌色 -- 沿用原有淡蓝 */
|
|
696
|
+
--raw-accent: #7AA9DE;
|
|
697
|
+
/* 🔴 开关开态的轨道色 -- 暗色版
|
|
698
|
+
🔴 **降彩度**:#7AA9DE 太亮,铺满一大块会发光。
|
|
699
|
+
实测:滑块是纯白 #FFF,轨道若也接近白 ⇒ 对比不足、边界糊。
|
|
700
|
+
⇒ 轨道取**中深蓝**(#3E6EA8 族),与白滑块有清晰对比,
|
|
701
|
+
在 #1C2024 的深底上又不刺眼。 */
|
|
702
|
+
--raw-switch-on: #3E6EA8;
|
|
703
|
+
--raw-accent-hover: #8FB8E6;
|
|
704
|
+
--raw-accent-soft: #1C2C3D;
|
|
705
|
+
|
|
706
|
+
/* 气泡:暗色下不再"反色"(反色会变成浅底深字,在暗界面里像个白块),
|
|
707
|
+
改用抬高面 + 正文色 —— 但只比纸面亮一档,所以必须补一条描边。
|
|
708
|
+
文字/底 11.93:1(AAA),实测见 contrast.py。 */
|
|
709
|
+
--raw-tooltip-bg: #232830;
|
|
710
|
+
--raw-tooltip-fg: #E4E7EA;
|
|
711
|
+
--raw-tooltip-border: #2A3038;
|
|
712
|
+
|
|
713
|
+
--raw-scrim: rgba(0, 0, 0, 0.62);
|
|
714
|
+
/* ⚠️ 这里原本还抄了一份 --sans/--mono/--serif/--tracking-normal —— 是死代码:
|
|
715
|
+
真正的字体栈定义在后面的 `:root`,同特异性且位置更靠后 ⇒ 无条件覆盖这里
|
|
716
|
+
(实测暗色下 --tracking-normal = 0,不是这里的 0.01em)。
|
|
717
|
+
留着还会被 theme-toggle.js 的差集规则复制成 inline style ⇒
|
|
718
|
+
inline 优先于样式表 ⇒ 强制暗色时字体被改。已删。 */
|
|
719
|
+
} /* ← :root 闭合*/
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
|
|
723
|
+
/* === BEGIN: light-via-attr(由 05-audit/theme-sync.py 生成,勿手改)=== */
|
|
724
|
+
[data-theme="light"] {
|
|
725
|
+
--raw-accent: #1b4d8f;
|
|
726
|
+
--raw-accent-hover: #163a6d;
|
|
727
|
+
--raw-accent-soft: #e6eef7;
|
|
728
|
+
--raw-border-control: #868b94;
|
|
729
|
+
--raw-border-decor: #e2e5e9;
|
|
730
|
+
--raw-border-decor-str: #c8cdd4;
|
|
731
|
+
--raw-danger: #c0392b;
|
|
732
|
+
--raw-danger-bg: #fdeeec;
|
|
733
|
+
--raw-danger-hover: #8f1e17;
|
|
734
|
+
--raw-dir-down: #2f6b3a;
|
|
735
|
+
--raw-dir-up: #c0392b;
|
|
736
|
+
--raw-info: #1d5f8a;
|
|
737
|
+
--raw-info-bg: #e8f1f8;
|
|
738
|
+
--raw-paper: #f6f7f8;
|
|
739
|
+
--raw-scrim: rgba(21, 24, 28, 0.4);
|
|
740
|
+
--raw-scrollbar-thumb: #7A808A;
|
|
741
|
+
--raw-scrollbar-thumb-hov: #6E747E;
|
|
742
|
+
--raw-skeleton-bg: #e9ecef;
|
|
743
|
+
--raw-skeleton-shimmer: rgba(255, 255, 255, 0.72);
|
|
744
|
+
--raw-success: #2f6b3a;
|
|
745
|
+
--raw-success-bg: #eaf2e6;
|
|
746
|
+
--raw-surface: #ffffff;
|
|
747
|
+
--raw-surface-raised: #ffffff;
|
|
748
|
+
--raw-surface-sunken: #f1f3f5;
|
|
749
|
+
--raw-switch-on: #1b4d8f;
|
|
750
|
+
--raw-text-on-accent: #ffffff;
|
|
751
|
+
--raw-text-on-solid: #ffffff;
|
|
752
|
+
--raw-text-primary: #15181c;
|
|
753
|
+
--raw-text-secondary: #4f555e;
|
|
754
|
+
--raw-text-tertiary: #606770;
|
|
755
|
+
--raw-tooltip-bg: #15181c;
|
|
756
|
+
--raw-tooltip-border: #15181c;
|
|
757
|
+
--raw-tooltip-fg: #f6f7f8;
|
|
758
|
+
--raw-warning: #8a5a00;
|
|
759
|
+
--raw-warning-bg: #fbf1dd;
|
|
760
|
+
|
|
761
|
+
--accent: var(--raw-accent);
|
|
762
|
+
--accent-hover: var(--raw-accent-hover);
|
|
763
|
+
--accent-soft: var(--raw-accent-soft);
|
|
764
|
+
--border-control: var(--raw-border-control);
|
|
765
|
+
--border-decor: var(--raw-border-decor);
|
|
766
|
+
--border-decor-str: var(--raw-border-decor-str);
|
|
767
|
+
--danger: var(--raw-danger);
|
|
768
|
+
--danger-bg: var(--raw-danger-bg);
|
|
769
|
+
--danger-hover: var(--raw-danger-hover);
|
|
770
|
+
--dir-down: var(--raw-dir-down);
|
|
771
|
+
--dir-up: var(--raw-dir-up);
|
|
772
|
+
--info: var(--raw-info);
|
|
773
|
+
--info-bg: var(--raw-info-bg);
|
|
774
|
+
--paper: var(--raw-paper);
|
|
775
|
+
--scrim: var(--raw-scrim);
|
|
776
|
+
--scrollbar-thumb: var(--raw-scrollbar-thumb);
|
|
777
|
+
--scrollbar-thumb-hov: var(--raw-scrollbar-thumb-hov);
|
|
778
|
+
--skeleton-bg: var(--raw-skeleton-bg);
|
|
779
|
+
--skeleton-shimmer: var(--raw-skeleton-shimmer);
|
|
780
|
+
--success: var(--raw-success);
|
|
781
|
+
--success-bg: var(--raw-success-bg);
|
|
782
|
+
--surface: var(--raw-surface);
|
|
783
|
+
--surface-raised: var(--raw-surface-raised);
|
|
784
|
+
--surface-sunken: var(--raw-surface-sunken);
|
|
785
|
+
--switch-on: var(--raw-switch-on);
|
|
786
|
+
--text-on-accent: var(--raw-text-on-accent);
|
|
787
|
+
--text-on-solid: var(--raw-text-on-solid);
|
|
788
|
+
--text-primary: var(--raw-text-primary);
|
|
789
|
+
--text-secondary: var(--raw-text-secondary);
|
|
790
|
+
--text-tertiary: var(--raw-text-tertiary);
|
|
791
|
+
--tooltip-bg: var(--raw-tooltip-bg);
|
|
792
|
+
--tooltip-border: var(--raw-tooltip-border);
|
|
793
|
+
--tooltip-fg: var(--raw-tooltip-fg);
|
|
794
|
+
--warning: var(--raw-warning);
|
|
795
|
+
--warning-bg: var(--raw-warning-bg);
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
/* === END: light-via-attr === */
|
|
799
|
+
|
|
800
|
+
/* === BEGIN: dark-via-attr(由 05-audit/theme-sync.py 生成,勿手改)=== */
|
|
801
|
+
@media screen {
|
|
802
|
+
:root[data-theme="dark"],
|
|
803
|
+
[data-theme="dark"] {
|
|
804
|
+
--raw-accent: #7AA9DE;
|
|
805
|
+
--raw-accent-hover: #8FB8E6;
|
|
806
|
+
--raw-accent-soft: #1C2C3D;
|
|
807
|
+
--raw-border-control: #8A9099;
|
|
808
|
+
--raw-border-decor: #2A3038;
|
|
809
|
+
--raw-border-decor-str: #3A424C;
|
|
810
|
+
--raw-danger: #E07A6E;
|
|
811
|
+
--raw-danger-bg: #341D1A;
|
|
812
|
+
--raw-danger-hover: #F09A90;
|
|
813
|
+
--raw-dir-down: #6FAA7A;
|
|
814
|
+
--raw-dir-up: #E07A6E;
|
|
815
|
+
--raw-info: #5B9BC4;
|
|
816
|
+
--raw-info-bg: #13242F;
|
|
817
|
+
--raw-paper: #14171A;
|
|
818
|
+
--raw-scrim: rgba(0, 0, 0, 0.62);
|
|
819
|
+
--raw-scrollbar-thumb: #6E747E;
|
|
820
|
+
--raw-scrollbar-thumb-hov: #7D828A;
|
|
821
|
+
--raw-skeleton-bg: #2E343C;
|
|
822
|
+
--raw-skeleton-shimmer: rgba(255, 255, 255, 0.06);
|
|
823
|
+
--raw-success: #6FAA7A;
|
|
824
|
+
--raw-success-bg: #16241A;
|
|
825
|
+
--raw-surface: #1C2024;
|
|
826
|
+
--raw-surface-raised: #232830;
|
|
827
|
+
--raw-surface-sunken: #1A1D21;
|
|
828
|
+
--raw-switch-on: #3E6EA8;
|
|
829
|
+
--raw-text-on-accent: #0D1114;
|
|
830
|
+
--raw-text-on-solid: #0D1114;
|
|
831
|
+
--raw-text-primary: #E4E7EA;
|
|
832
|
+
--raw-text-secondary: #AEB6C0;
|
|
833
|
+
--raw-text-tertiary: #9BA3AD;
|
|
834
|
+
--raw-tooltip-bg: #232830;
|
|
835
|
+
--raw-tooltip-border: #2A3038;
|
|
836
|
+
--raw-tooltip-fg: #E4E7EA;
|
|
837
|
+
--raw-warning: #C99A3F;
|
|
838
|
+
--raw-warning-bg: #242A31;
|
|
839
|
+
|
|
840
|
+
--accent: var(--raw-accent);
|
|
841
|
+
--accent-hover: var(--raw-accent-hover);
|
|
842
|
+
--accent-soft: var(--raw-accent-soft);
|
|
843
|
+
--border-control: var(--raw-border-control);
|
|
844
|
+
--border-decor: var(--raw-border-decor);
|
|
845
|
+
--border-decor-str: var(--raw-border-decor-str);
|
|
846
|
+
--danger: var(--raw-danger);
|
|
847
|
+
--danger-bg: var(--raw-danger-bg);
|
|
848
|
+
--danger-hover: var(--raw-danger-hover);
|
|
849
|
+
--dir-down: var(--raw-dir-down);
|
|
850
|
+
--dir-up: var(--raw-dir-up);
|
|
851
|
+
--info: var(--raw-info);
|
|
852
|
+
--info-bg: var(--raw-info-bg);
|
|
853
|
+
--paper: var(--raw-paper);
|
|
854
|
+
--scrim: var(--raw-scrim);
|
|
855
|
+
--scrollbar-thumb: var(--raw-scrollbar-thumb);
|
|
856
|
+
--scrollbar-thumb-hov: var(--raw-scrollbar-thumb-hov);
|
|
857
|
+
--skeleton-bg: var(--raw-skeleton-bg);
|
|
858
|
+
--skeleton-shimmer: var(--raw-skeleton-shimmer);
|
|
859
|
+
--success: var(--raw-success);
|
|
860
|
+
--success-bg: var(--raw-success-bg);
|
|
861
|
+
--surface: var(--raw-surface);
|
|
862
|
+
--surface-raised: var(--raw-surface-raised);
|
|
863
|
+
--surface-sunken: var(--raw-surface-sunken);
|
|
864
|
+
--switch-on: var(--raw-switch-on);
|
|
865
|
+
--text-on-accent: var(--raw-text-on-accent);
|
|
866
|
+
--text-on-solid: var(--raw-text-on-solid);
|
|
867
|
+
--text-primary: var(--raw-text-primary);
|
|
868
|
+
--text-secondary: var(--raw-text-secondary);
|
|
869
|
+
--text-tertiary: var(--raw-text-tertiary);
|
|
870
|
+
--tooltip-bg: var(--raw-tooltip-bg);
|
|
871
|
+
--tooltip-border: var(--raw-tooltip-border);
|
|
872
|
+
--tooltip-fg: var(--raw-tooltip-fg);
|
|
873
|
+
--warning: var(--raw-warning);
|
|
874
|
+
--warning-bg: var(--raw-warning-bg);
|
|
875
|
+
}
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
/* === END: dark-via-attr === */
|
|
879
|
+
|
|
880
|
+
/* 🔴 涨跌口径切换(`[data-market="intl"]`)**不在本文件里**,在 `01-tokens/market.css`。
|
|
881
|
+
----------------------------------------------------------------------------
|
|
882
|
+
原因不是洁癖:读本文件的那几个工具(contrast / gen-ai-contract /
|
|
883
|
+
gen-tokens-tree / token-audience)走 `_css.declared_tokens()`,它是**纯词法**的
|
|
884
|
+
—— 遍历所有声明、后者覆盖前者、**不看选择器**。
|
|
885
|
+
把一段**条件性**的交换规则放在这里,它们会把它当成默认值叠上去,
|
|
886
|
+
于是报告里「暗色 涨」显示成绿色,而浏览器里其实是对的 —— 门禁冤枉代码,
|
|
887
|
+
比漏判更难查(实测,见 E137)。
|
|
888
|
+
⇒ 本文件只放「无条件声明」与「暗色媒体段」,条件规则一律外置。
|
|
889
|
+
========================================================================= */
|
|
890
|
+
|
|
891
|
+
/* ============================================================================
|
|
892
|
+
字体栈令牌
|
|
893
|
+
============================================================================
|
|
894
|
+
🔴 补。**这是个一直存在但没人发现的缺口** ——
|
|
895
|
+
全库有 27 处引用 `var(--sans)` / `var(--mono)` / `var(--serif)`,
|
|
896
|
+
而 tokens.css **从来没定义过它们** ⇒ 全部回退到浏览器默认字体。
|
|
897
|
+
换句话说:库里"看起来有字体体系",其实没有。
|
|
898
|
+
|
|
899
|
+
── 为什么字体栈要按语言分 ────────────────────────────────────
|
|
900
|
+
一个字体通常**不覆盖所有文字**。Segoe UI 没有中文字形、
|
|
901
|
+
苹方没有拉丁字形 ⇒ 浏览器逐字符 fallback 到系统默认。
|
|
902
|
+
问题在于:**不同系统 fallback 到的字体不同**
|
|
903
|
+
(macOS 苹方 / Windows 微软雅黑 / Linux 思源)⇒ 同一份代码观感不一致。
|
|
904
|
+
|
|
905
|
+
⇒ 做法:字体栈里**把各文字的字体都列上**,让浏览器在我们指定的顺序里选,
|
|
906
|
+
而不是让它按系统默认挑。
|
|
907
|
+
|
|
908
|
+
⚠️ 全部是**系统字体**,不下载任何字体文件 ——
|
|
909
|
+
这是精简档(某项目)的硬约束,也是本库"零外部资产"的一部分。
|
|
910
|
+
|
|
911
|
+
字体栈的排列顺序:**拉丁 → 中文 → 泛型**。
|
|
912
|
+
浏览器逐字符 fallback,排在前面、优先用。
|
|
913
|
+
========================================================================= */
|
|
914
|
+
|
|
915
|
+
:root {
|
|
916
|
+
--sans:
|
|
917
|
+
/* 拉丁 */
|
|
918
|
+
-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
|
|
919
|
+
'Helvetica Neue', Arial,
|
|
920
|
+
/* 中文 */
|
|
921
|
+
'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei',
|
|
922
|
+
'Source Han Sans SC', 'Noto Sans CJK SC',
|
|
923
|
+
/* 阿拉伯 / 希伯来 / 泰文(系统通常自带相应字体)*/
|
|
924
|
+
'Geeza Pro', 'Segoe UI Historic',
|
|
925
|
+
/* 泛型 */
|
|
926
|
+
sans-serif;
|
|
927
|
+
|
|
928
|
+
--mono:
|
|
929
|
+
/* 拉丁等宽 */
|
|
930
|
+
ui-monospace, 'SF Mono', 'Cascadia Mono', 'Segoe UI Mono',
|
|
931
|
+
'Roboto Mono', Menlo, Consolas,
|
|
932
|
+
/* 中文等宽(中文通常回落到无衬线,但列出更可控)*/
|
|
933
|
+
'Sarasa Mono SC', 'Microsoft YaHei Mono',
|
|
934
|
+
monospace;
|
|
935
|
+
|
|
936
|
+
--serif:
|
|
937
|
+
/* 拉丁衬线 */
|
|
938
|
+
'Iowan Old Style', 'Palatino Linotype', Palatino, Georgia,
|
|
939
|
+
/* 中文衬线 */
|
|
940
|
+
'Songti SC', 'SimSun', 'Noto Serif CJK SC', 'Source Han Serif SC',
|
|
941
|
+
serif;
|
|
942
|
+
|
|
943
|
+
/* 字距:默认 0。中文与拉丁的字距需求不同,
|
|
944
|
+
阿拉伯/希伯来文连字后密度更高,需要更松 —— 下面按语言覆盖。 */
|
|
945
|
+
--tracking-normal: 0;
|
|
946
|
+
}
|
|
947
|
+
|
|
948
|
+
|
|
949
|
+
/* 🔴 阿拉伯语与希伯来语:需要**更宽松的字距**。
|
|
950
|
+
阿拉伯文的字形连接后视觉密度比拉丁高,
|
|
951
|
+
同样的字距在两套文字里"挤的程度"不同。 */
|
|
952
|
+
:root:lang(ar), :root:lang(fa), :root:lang(he), :root:lang(ur) {
|
|
953
|
+
--tracking-normal: 0.01em;
|
|
954
|
+
}
|