feat(typography): 16 类语义色板(8 色相×2 档)+ 字体回退升级 + 面板 v2
- 颜色:c-{blue,teal,green,yellow,orange,red,pink,purple} × {标准,soft}
共 16 类;tokens 明暗各一套值,32 项浏览器实测对比度 4.50–11.73(零失败);
黄色压到 #7a5900(原色白底仅 ~1.8:1)、黄/粉 soft 压深留余量
- 使用纪律入档:彩色只作用于正文强调,站点外壳(导航/页脚/按钮/背景/边框)
保持灰阶,单页彩色强调建议 ≤3 处
- 字体:3/4 回退类升级为「自托管优先 + 系统兜底」(font-song→Noto Serif SC、
font-kai-sys→霞鹜文楷、font-hei-sys→Noto Sans SC,+0.0KB 跨设备一致);
仿宋无 OFL 自托管包(fandol/朱雀仿宋均无版本)→ 纯系统回退,缺口待决策
- webfont 候选全部按体积账否决并留档:霞鹜文楷 Light 用到页 +8605KB、站酷小薇
+648KB、得意黑/更纱/汇文无可用 npm 包;根因=@fontsource CJK 切片粒度
- 可视化面板 v2:33 项(8 字族 + 25 颜色),颜色项带 16px 色块预览、字体项带
示例字形「秋海」并标注自托管/优先/系统;桩 CMS 7 项验证全过
- 白名单放行 25 色类 + 8 字族类;docs 三表(灰阶层级/语义色板含对比度/字体
含许可证与体积账)更新
车道:op-artalk(改口径后重跑,实测表齐全);收编复验:CSS 16 色类定义与
白名单一致、暗态值在 [data-theme="dark"] 段、字体回退类 computed 生效、
构建 48 页、E2E 37/37
验证: pnpm astro build 48 页 exit 0;E2E 37/37;playwright 抽查彩色类与
字体回退 computed
Spec: none (排版色板与字体回退,车道级交付)
Co-authored-by: GLM-5.3
This commit is contained in:
1 parent
2cab243891
commit
26bbb66a72
7 files changed
+110
-44
No files matched your search
@@ -24,8 +24,6 @@ draft: false
|
||||
|
||||
<p class="ink-accent-soft">弱强调:秋天的海面像一块被风抚平的铁皮。</p>
|
||||
|
||||
<p class="font-kai-light">霞鹜文楷 Light(自托管 300):秋天的海面像一块被风抚平的铁皮。</p>
|
||||
|
||||
<p class="c-blue">蓝色标准:秋天的海面像一块被风抚平的铁皮。</p>
|
||||
<p class="c-yellow">黄色标准(已压暗):秋天的海面像一块被风抚平的铁皮。</p>
|
||||
<p class="c-red-soft">红色 soft:秋天的海面像一块被风抚平的铁皮。</p>
|
||||
+44
-12
@@ -88,7 +88,7 @@ https://astro.build/
|
||||
|
||||
正文里可用 `<span class="…">` / `<p class="…">` 做**受限**排版。构建期有白名单过滤:**只放行下表类名,其它 class 与 `style`、`on*` 属性一律被剥离**(防注入 + 保护灰阶骨架)。
|
||||
|
||||
### 颜色类(全部实测亮暗对比度 ≥4.5:1)
|
||||
### 灰阶层级类(全部实测亮暗对比度 ≥4.5:1)
|
||||
|
||||
| 类名 | 作用 |
|
||||
|---|---|
|
||||
@@ -100,18 +100,36 @@ https://astro.build/
|
||||
| `ink-accent` | 强调(链接色,少量点缀) |
|
||||
| `ink-accent-soft` | 弱强调(比强调淡一档) |
|
||||
|
||||
### 语义色板(8 色相 × 标准/soft;亮暗各自取值,实测全部 ≥4.5:1)
|
||||
|
||||
| 色相 | 标准类 | soft 类 | 亮态对比度(标准 / soft) | 暗态(标准 / soft) |
|
||||
|---|---|---|---|---|
|
||||
| 蓝 | `c-blue` | `c-blue-soft` | 6.46 / 4.58 | 7.70 / 4.61 |
|
||||
| 青 | `c-teal` | `c-teal-soft` | 5.00 / 4.57 | 9.77 / 4.52 |
|
||||
| 绿 | `c-green` | `c-green-soft` | 5.38 / 4.67 | 10.38 / 4.67 |
|
||||
| 黄 | `c-yellow` | `c-yellow-soft` | 6.45 / 4.57 | 11.73 / 4.52 |
|
||||
| 橙 | `c-orange` | `c-orange-soft` | 6.26 / 4.65 | 9.44 / 4.58 |
|
||||
| 红 | `c-red` | `c-red-soft` | 6.68 / 4.55 | 7.51 / 4.50 |
|
||||
| 粉 | `c-pink` | `c-pink-soft` | 6.96 / 4.73 | 8.87 / 4.52 |
|
||||
| 紫 | `c-purple` | `c-purple-soft` | 7.46 / 4.53 | 8.25 / 4.64 |
|
||||
|
||||
- 黄色在亮态被压暗到 `#7a5900`(原色在白底只有 ~1.8:1),这是**必须**的:彩色文字要能读
|
||||
- `soft` 档是"阈值内最淡值",适合次要强调;标准档用于关键强调
|
||||
|
||||
> **使用纪律(重要)**:彩色**只作用于文章正文的强调文字**。站点外壳(导航、页脚、按钮、背景、卡片边框)一律保持灰阶——整站不因正文用色而花掉。写作时一个页面里的彩色强调建议不超过 3 处。
|
||||
|
||||
### 字体类
|
||||
|
||||
| 类名 | 作用 | 备注 |
|
||||
|---|---|---|
|
||||
| `font-serif` | 思源宋(书评正文族) | 自托管 |
|
||||
| `font-kai` | 霞鹜文楷(金句族) | 自托管 |
|
||||
| `font-sans` | 思源黑(UI/技术族) | 自托管 |
|
||||
| `font-mono` | JetBrains Mono(标签/数值) | 自托管 |
|
||||
| `font-song` | 宋体族 | ⚠️ 系统字体,**依赖访客设备** |
|
||||
| `font-kai-sys` | 楷体族 | ⚠️ 系统字体 |
|
||||
| `font-hei-sys` | 黑体族 | ⚠️ 系统字体 |
|
||||
| `font-fangsong` | 仿宋族 | ⚠️ 系统字体 |
|
||||
| 类名 | 作用 | 字体 | 许可证 | 加载 |
|
||||
|---|---|---|---|---|
|
||||
| `font-serif` | 书评正文族 | Noto Serif SC | OFL-1.1 | 自托管 |
|
||||
| `font-kai` | 金句族 | 霞鹜文楷 Screen | OFL-1.1 | 自托管 |
|
||||
| `font-sans` | UI/技术族 | Noto Sans SC | OFL-1.1 | 自托管 |
|
||||
| `font-mono` | 标签/数值 | JetBrains Mono | OFL-1.1 | 自托管 |
|
||||
| `font-song` | 宋体族 | Noto Serif SC 优先,系统宋体兜底 | OFL-1.1 | **自托管优先** |
|
||||
| `font-kai-sys` | 楷体族 | 霞鹜文楷优先,系统楷体兜底 | OFL-1.1 | **自托管优先** |
|
||||
| `font-hei-sys` | 黑体族 | Noto Sans SC 优先,系统黑体兜底 | OFL-1.1 | **自托管优先** |
|
||||
| `font-fangsong` | 仿宋族 | FangSong/STFangsong | — | ⚠️ **纯系统回退**(暂无 OFL 自托管仿宋包) |
|
||||
|
||||
### 文学排版类(段落级)
|
||||
|
||||
@@ -121,7 +139,21 @@ https://astro.build/
|
||||
| `quote-line` | 金句块(左侧竖线 + 文楷) |
|
||||
| `fig-cap` | 图注(居中小字) |
|
||||
|
||||
> **系统字体类的前提**:`-sys` / `font-song` / `font-fangsong` 不下载任何字体文件,命中访客本机字体(macOS/iOS 有宋楷黑、Windows 有宋黑仿宋,Android 常缺楷仿宋)。**跨设备不保证一致**——要稳定请用自托管四族。
|
||||
**字体体积账(2026-11-02 实测,本地无 gzip 的相对值)**:
|
||||
|
||||
| 候选 | 许可证 | 引入后实测 | 结论 |
|
||||
|---|---|---|---|
|
||||
| 霞鹜文楷 Light/300(`@fontsource/lxgw-wenkai`) | OFL-1.1 | 用到它的页面 **+8605KB** | ❌ 远超 150KB/页 阈值,未采用 |
|
||||
| 站酷小薇(`@fontsource/zcool-xiaowei`) | OFL-1.1 | 用到它的页面 +648KB | ❌ 同上 |
|
||||
| 得意黑 Smiley Sans | OFL-1.1 | npm 无可用包(仅 GitHub release 整包,体积更大) | ⏸ 未接入 |
|
||||
| Sarasa Gothic(含 Mono) | OFL-1.1 | npm 包为空壳(需 cli 自生成切片) | ⏸ 未接入 |
|
||||
| 汇文明朝体 | 免费商用(非 OFL) | 无 npm 包 | ⏸ 未接入 |
|
||||
| 现有四族(思源宋/黑、文楷、JetBrains) | OFL-1.1 | 基线:首页 869KB / 书评 3512KB / 技术 875KB | ✅ 已是最优 |
|
||||
|
||||
- 中文 webfont 的代价在 **CJK 切片粒度**:@fontsource 的 CJK 切片刻度是"用几个汉字就拉几 MB",所以新增中文 webfont 单页增量普遍远超 150KB
|
||||
- 现有四族已覆盖"宋/黑/楷/等宽"四种气质,且全部自托管 → 跨设备一致;`font-song`/`font-kai-sys`/`font-hei-sys` 因**自托管优先**,实际渲染也走站内字体(系统字族只兜底)
|
||||
- `font-fangsong` 是唯一纯系统回退类:m6 未找到 OFL 自托管仿宋包(Fandol/朱雀等均无 npm 可用包),命中访客本机仿宋字体,**跨设备不保证一致**
|
||||
- 未用到的字体类在构建期被按页子集化裁掉:**未使用该字体的页面字体字节增量 = 0**
|
||||
|
||||
```markdown
|
||||
<span class="ink-muted">次要说明</span>
|
||||
|
||||
@@ -1,28 +1,47 @@
|
||||
/**
|
||||
* 排版类清单 —— 单一事实源(后台编辑器面板与文档共用)。
|
||||
* 与 src/styles/global.css 的「受限排版类」段、src/lib/rehype-class-whitelist.ts 的白名单三处保持一致:
|
||||
* 与 src/styles/global.css 的「受限排版类/语义色板」段、src/lib/rehype-class-whitelist.ts 的白名单三处保持一致:
|
||||
* 加类时三处同改,否则构建期白名单会把新类剥掉。
|
||||
*
|
||||
* swatch / family 仅供**后台面板预览**使用(后台无站点 CSS 变量,故给具体值):
|
||||
* swatch 取亮态值(后台是浅色 UI),family 为该类的真实字体栈。
|
||||
*/
|
||||
window.TYPOGRAPHY_CLASSES = {
|
||||
font: [
|
||||
{ cls: 'font-serif', label: '思源宋(书评正文)' },
|
||||
{ cls: 'font-kai', label: '霞鹜文楷(金句)' },
|
||||
{ cls: 'font-sans', label: '思源黑(UI/技术)' },
|
||||
{ cls: 'font-mono', label: 'JetBrains Mono(等宽)' },
|
||||
{ cls: 'font-song', label: '宋体(系统)' },
|
||||
{ cls: 'font-kai-sys', label: '楷体(系统)' },
|
||||
{ cls: 'font-hei-sys', label: '黑体(系统)' },
|
||||
{ cls: 'font-fangsong', label: '仿宋(系统)' },
|
||||
{ cls: 'font-serif', label: '思源宋(书评正文)', family: '"Noto Serif SC", Georgia, serif', kind: '自托管' },
|
||||
{ cls: 'font-kai', label: '霞鹜文楷(金句)', family: '"LXGW WenKai Screen", "Songti SC", serif', kind: '自托管' },
|
||||
{ cls: 'font-sans', label: '思源黑(UI/技术)', family: '"Noto Sans SC", system-ui, sans-serif', kind: '自托管' },
|
||||
{ cls: 'font-mono', label: 'JetBrains Mono(等宽)', family: '"JetBrains Mono", ui-monospace, monospace', kind: '自托管' },
|
||||
{ cls: 'font-song', label: '宋体族', family: '"Noto Serif SC", "Songti SC", SimSun, serif', kind: '自托管优先' },
|
||||
{ cls: 'font-kai-sys', label: '楷体族', family: '"LXGW WenKai Screen", "Kaiti SC", STKaiti, serif', kind: '自托管优先' },
|
||||
{ cls: 'font-hei-sys', label: '黑体族', family: '"Noto Sans SC", "PingFang SC", "Microsoft YaHei", sans-serif', kind: '自托管优先' },
|
||||
{ cls: 'font-fangsong', label: '仿宋族', family: 'FangSong, STFangsong, serif', kind: '系统回退' },
|
||||
],
|
||||
color: [
|
||||
{ cls: 'ink-1', label: '最重(标题级)' },
|
||||
{ cls: 'ink-2', label: '正文' },
|
||||
{ cls: 'ink-3', label: '次要(日期/元信息)' },
|
||||
{ cls: 'ink-muted', label: '最淡(注释感)' },
|
||||
{ cls: 'ink-accent', label: '强调(链接色)' },
|
||||
{ cls: 'ink-accent-soft', label: '弱强调' },
|
||||
{ cls: 'ink-warm', label: '暖调正文' },
|
||||
{ cls: 'ink-cool', label: '冷调正文' },
|
||||
{ cls: 'ink-faint', label: '最淡可读档' },
|
||||
{ cls: 'ink-1', label: '最重(标题级)', swatch: '#111113' },
|
||||
{ cls: 'ink-2', label: '正文', swatch: '#33343a' },
|
||||
{ cls: 'ink-3', label: '次要(日期/元信息)', swatch: '#6b6e78' },
|
||||
{ cls: 'ink-muted', label: '最淡(注释感)', swatch: '#70747c' },
|
||||
{ cls: 'ink-warm', label: '暖调正文', swatch: '#3f362e' },
|
||||
{ cls: 'ink-cool', label: '冷调正文', swatch: '#2f3742' },
|
||||
{ cls: 'ink-faint', label: '最淡可读档', swatch: '#70747c' },
|
||||
{ cls: 'ink-accent', label: '强调(链接色)', swatch: '#3454d1' },
|
||||
{ cls: 'ink-accent-soft', label: '弱强调', swatch: '#4f66c2' },
|
||||
{ cls: 'c-blue', label: '蓝', swatch: '#2456c9' },
|
||||
{ cls: 'c-blue-soft', label: '蓝(soft)', swatch: '#4170dd' },
|
||||
{ cls: 'c-teal', label: '青', swatch: '#0d7d74' },
|
||||
{ cls: 'c-teal-soft', label: '青(soft)', swatch: '#0e847a' },
|
||||
{ cls: 'c-green', label: '绿', swatch: '#1f7a3a' },
|
||||
{ cls: 'c-green-soft', label: '绿(soft)', swatch: '#22853f' },
|
||||
{ cls: 'c-yellow', label: '黄(已压暗)', swatch: '#7a5900' },
|
||||
{ cls: 'c-yellow-soft', label: '黄(soft)', swatch: '#926a00' },
|
||||
{ cls: 'c-orange', label: '橙', swatch: '#9a4a00' },
|
||||
{ cls: 'c-orange-soft', label: '橙(soft)', swatch: '#ba5900' },
|
||||
{ cls: 'c-red', label: '红', swatch: '#b32020' },
|
||||
{ cls: 'c-red-soft', label: '红(soft)', swatch: '#db3737' },
|
||||
{ cls: 'c-pink', label: '粉', swatch: '#a3246a' },
|
||||
{ cls: 'c-pink-soft', label: '粉(soft)', swatch: '#ce3386' },
|
||||
{ cls: 'c-purple', label: '紫', swatch: '#6a35b8' },
|
||||
{ cls: 'c-purple-soft', label: '紫(soft)', swatch: '#8c5ed1' },
|
||||
],
|
||||
};
|
||||
@@ -37,7 +37,7 @@
|
||||
card.appendChild(title);
|
||||
|
||||
const buttons = [];
|
||||
const addGroup = (name, list) => {
|
||||
const addGroup = (name, list, withSwatch) => {
|
||||
const h = document.createElement('p');
|
||||
h.textContent = name;
|
||||
Object.assign(h.style, { margin: '12px 0 6px', fontSize: '12px', color: '#6b6e78' });
|
||||
@@ -47,22 +47,45 @@
|
||||
for (const item of list) {
|
||||
const b = document.createElement('button');
|
||||
b.type = 'button';
|
||||
b.textContent = item.label;
|
||||
b.dataset.cls = item.cls;
|
||||
b.className = item.cls === current ? 'active' : '';
|
||||
Object.assign(b.style, {
|
||||
display: 'inline-flex', alignItems: 'center', gap: '8px',
|
||||
padding: '6px 12px', borderRadius: '8px', cursor: 'pointer',
|
||||
border: '1px solid ' + (item.cls === current ? '#3454d1' : '#e6e7eb'),
|
||||
background: item.cls === current ? '#eef1fd' : '#fff',
|
||||
color: '#111113', fontSize: '13px',
|
||||
});
|
||||
if (withSwatch) {
|
||||
// 色块预览:直接展示该类在亮态下的实际颜色
|
||||
const chip = document.createElement('span');
|
||||
Object.assign(chip.style, {
|
||||
width: '16px', height: '16px', borderRadius: '4px', flex: 'none',
|
||||
background: item.swatch || '#ccc',
|
||||
border: '1px solid rgba(0,0,0,.12)',
|
||||
});
|
||||
chip.setAttribute('aria-hidden', 'true');
|
||||
b.appendChild(chip);
|
||||
} else {
|
||||
// 字体项:名称 + 用该字体栈渲染的示例字形
|
||||
const sample = document.createElement('span');
|
||||
sample.textContent = '秋海';
|
||||
Object.assign(sample.style, {
|
||||
fontFamily: item.family || 'inherit', fontSize: '15px', lineHeight: '1',
|
||||
padding: '0 4px', borderLeft: '1px solid #e6e7eb', color: '#33343a',
|
||||
});
|
||||
b.appendChild(sample);
|
||||
}
|
||||
const label = document.createElement('span');
|
||||
label.textContent = item.label + (item.kind ? `(${item.kind})` : '');
|
||||
b.appendChild(label);
|
||||
row.appendChild(b);
|
||||
buttons.push(b);
|
||||
}
|
||||
card.appendChild(row);
|
||||
};
|
||||
addGroup('字体', CLASSES.font);
|
||||
addGroup('颜色', CLASSES.color);
|
||||
addGroup('字体', CLASSES.font, false);
|
||||
addGroup('颜色', CLASSES.color, true);
|
||||
|
||||
const foot = document.createElement('div');
|
||||
Object.assign(foot.style, { marginTop: '16px', display: 'flex', justifyContent: 'flex-end', gap: '8px' });
|
||||
|
||||
@@ -44,7 +44,6 @@ const ALLOWED = new Set([
|
||||
'font-kai-sys',
|
||||
'font-hei-sys',
|
||||
'font-fangsong',
|
||||
'font-kai-light',
|
||||
// 文学排版
|
||||
'drop-cap',
|
||||
'quote-line',
|
||||
|
||||
@@ -213,11 +213,6 @@ pre code { background: none; border: none; padding: 0; font-size: inherit; }
|
||||
.prose-reading .font-fangsong, .prose-tech .font-fangsong {
|
||||
font-family: FangSong, STFangsong, "FangSong_GB2312", serif;
|
||||
}
|
||||
/* 霞鹜文楷 Light(自托管 @fontsource,OFL-1.1):更轻的金句字重 */
|
||||
.prose-reading .font-kai-light, .prose-tech .font-kai-light {
|
||||
font-family: 'LXGW WenKai', var(--font-kai);
|
||||
font-weight: 300;
|
||||
}
|
||||
/* 文学常用:首字下沉(书评开篇)与金句块(引文式的独立强调段) */
|
||||
.prose-reading p.drop-cap::first-letter, .prose-tech p.drop-cap::first-letter {
|
||||
float: left;
|
||||
|
||||
@@ -32,10 +32,10 @@
|
||||
--c-blue-soft: #4170dd;
|
||||
--c-teal-soft: #0e847a;
|
||||
--c-green-soft: #22853f;
|
||||
--c-yellow-soft: #997000;
|
||||
--c-yellow-soft: #926a00;
|
||||
--c-orange-soft: #ba5900;
|
||||
--c-red-soft: #db3737;
|
||||
--c-pink-soft: #d3368c;
|
||||
--c-pink-soft: #ce3386;
|
||||
--c-purple-soft: #8c5ed1;
|
||||
--line: #e6e7eb; /* 分隔线 */
|
||||
|
||||
|
||||
Reference in new issue
Block a user