CJK Thin Type System · 中文细体排版语言
cjk-thin-type-systemv1.0.0
排版
MIT(本包 JSON / 文档 / 断言为自产)
中文 / 日文暗色单页站、作品集、个人站要求「细、干净、一套语言」时;或已有站点出现「字体乱」(同一字号挂多字重、各组件一套行高 / 字距 / 阴影)需要先普查再收敛时
不适用场景
明亮纸媒长文(走 cjk-typography 的正文档 + 衬线 article 栈)、数据密集工具面板(走系统栈)、需要粗体力量感的营销落地页
载入预览…
卡片与舞台里跑的是包内真实的预览页;WebGL 类需点“运行预览”才会建立渲染上下文。
取用方式
固定版本(CDN)
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/nakamotosai/sai-assets@pack/cjk-thin-type-system@1.0.0/packs/cjk-thin-type-system/css/default.css">raw 直取(跟随 main)
curl -O https://raw.githubusercontent.com/nakamotosai/sai-assets/main/packs/cjk-thin-type-system/SKILL.md仓库内落地
python scripts/apply.py cjk-thin-type-system --to <项目目录>整包下载
https://assets.ouuut.com/downloads/cjk-thin-type-system.zip源码与规则
pack.json{
"schema": 1,
"id": "cjk-thin-type-system",
"title": "CJK Thin Type System · 中文细体排版语言",
"kind": "fonts",
"category": "fonts",
"version": "1.0.0",
"license": "MIT(本包 JSON / 文档 / 断言为自产)",
"attribution": [
{
"name": "本地素材库 · 字号阶梯与字重配对原子卡",
"license": "自产观测(2026-10-31 全站排版普查)",
"what": "21 种样式组合 → 6 组规则的收敛样本与字重/字号/行高/影档位"
},
{
"name": "sai-frontend §4 中文排版硬闸",
"license": "internal",
"what": "阴影至多一层、移动优先、中文回退链三条硬口径"
},
{
"name": "Noto Sans SC / Fraunces(字族名引用)",
"license": "OFL-1.1(Fraunces 待核)",
"what": "只引用字族名,包内不含字体文件;字族与回退栈见 cjk-typography 包"
}
],
"origin": {
"project": "asset-source",
"path": "fonts/cjk-thin-type-system.json",
"derived_from": null
},
"entry": "SKILL.md",
"files": {
"tokens": [
"tokens/index.json",
"tokens/scale.json",
"tokens/weights.json",
"tokens/tracking.json",
"tokens/leading.json",
"tokens/shadow.json"
],
"usage": "docs/usage.md",
"assert": "scripts/assert_tokens.py",
"preview": "preview/index.html"
},
"tags": ["cjk", "typography", "thin", "type-scale", "dark", "letter-spacing", "line-height", "text-shadow"],
"when": "中文 / 日文暗色单页站、作品集、个人站要求「细、干净、一套语言」时;或已有站点出现「字体乱」(同一字号挂多字重、各组件一套行高 / 字距 / 阴影)需要先普查再收敛时",
"not_for": "明亮纸媒长文(走 cjk-typography 的正文档 + 衬线 article 栈)、数据密集工具面板(走系统栈)、需要粗体力量感的营销落地页",
"default_variant": null,
"verified": {
"date": "2026-11-03",
"how": "python packs/cjk-thin-type-system/scripts/assert_tokens.py",
"evidence": "PASS assert_tokens cjk-thin-type-system / scale 4 · weights 2 · tracking 3 · leading 3 · shadow 2 / probe: preview 2 文件 · 5 条 font-size 最小 12px · 最小行高 1.35 · vw 外推 0 · 斜体 0;负例 6/6 判失败(11px / 裸 4vw / clamp 上限 4vw / lh 1.2 / italic / min_px 15)"
},
"canonical_home": null
}tokens/index.json{
"id": "cjk-thin-type-system",
"kind": "fonts",
"title": "CJK Thin Type System · 中文细体排版语言",
"version": "1.0.0",
"summary": "暗色单页站的中文极细排版语言:字号 4 档 · 字重 2 档(另附「越大越细」阶梯)· 字距 3 档 · 行高 3 档 · 文字影 2 档。约法 = 组件只继承,不再自己写 font-size / font-weight / letter-spacing / line-height / text-shadow。",
"split": "与 cjk-typography 分工:那条管「用哪些字」(字族与回退栈),本条管「用几档、多细、多宽、多高、几层影」。本包不下发字族。",
"axes": [
{ "id": "scale", "count": 4, "file": "tokens/scale.json", "what": "字号档(giant / display / title / body)" },
{ "id": "weights", "count": 2, "file": "tokens/weights.json", "what": "字重档(text 200 / mark 600)+ 越大越细配对" },
{ "id": "tracking", "count": 3, "file": "tokens/tracking.json", "what": "字距档(0 / .02em / .12em)" },
{ "id": "leading", "count": 3, "file": "tokens/leading.json", "what": "行高档(1.4 / 1.35 / 1.6)" },
{ "id": "shadow", "count": 2, "file": "tokens/shadow.json", "what": "文字影档(单层 / 双层扩散)" }
],
"defaults": {
"scale": "body",
"weight": "text",
"tracking": "body",
"leading": "body",
"shadow": "text"
},
"floors": {
"cjk_px": 12,
"thin_weight_px": 17,
"leading": 1.35,
"display_shadow_px": 28
},
"family_pack": "cjk-typography",
"rules": [
"组件只继承,不再各自写 font-size / font-weight / letter-spacing / line-height / text-shadow —— 同一字号挂多字重、各组件一套行高,正是「字体乱」的根因",
"行高与文字影绑字号档,不绑组件",
"响应式写进 clamp 的中段,上限用绝对单位;禁用 vw 外推上限",
"同一字号只挂一种字重",
"中文禁斜体;禁 border-bottom 当前下划线",
"正文中文不加字距;.02em / .12em 只给标题与微标签"
],
"trap": "按钮不吃继承:button / input 用 UA 默认字(回落无文字影的正文字形),必须 button,input,select,textarea{font:inherit;letter-spacing:inherit;text-shadow:inherit}"
}
tokens/scale.json{
"id": "scale",
"unit": "px",
"floor_px": 12,
"note": "字号 4 档。地板 12px 是任何中文文字的最小值(微标签),细体档的更高地板见 weights.json。clamp 中段用 vw 做响应式、上限一律绝对单位。",
"tiers": [
{
"id": "giant",
"role": "字标 —— 全站唯一允许挂显示字族的元素",
"css": "clamp(44px, 8.2vw, 122px)",
"min_px": 44,
"max_px": 122,
"weight": "mark",
"leading": "display",
"shadow": "display",
"note": "源卡未给字标字距档:保持 0,不额外加"
},
{
"id": "display",
"role": "标语 / 邮箱 —— 全站最大正文",
"css": "clamp(28px, 3.2vw, 44px)",
"min_px": 28,
"max_px": 44,
"weight": "text",
"leading": "display",
"shadow": "display",
"note": "≥28px 才允许第二层扩散影"
},
{
"id": "title",
"role": "区块标题 / 卡片标题",
"css": "clamp(20px, 2vw, 22px)",
"min_px": 20,
"max_px": 22,
"weight": "text",
"tracking": "title",
"leading": "title",
"shadow": "text"
},
{
"id": "body",
"role": "正文 / 界面 / 微标签",
"css": "17px",
"min_px": 17,
"max_px": 17,
"weight": "text",
"tracking": "body",
"leading": "body",
"shadow": "text",
"note": "细体正文的地板就是 17px,不许更小;微标签要再小必须把字重抬到 400(见 weights.json)"
}
]
}
tokens/weights.json{
"id": "weights",
"pair": {
"text": 200,
"mark": 600
},
"tiers": [
{ "id": "text", "value": 200, "what": "正文档:细体,只准挂在 ≥17px" },
{ "id": "mark", "value": 600, "what": "字标 / 强调档:显示字族与小块强调" }
],
"floor_px": 12,
"thin_min_px": 17,
"ladder": [
{ "weight": 200, "min_px": 17, "max_px": null, "note": "细体只能挂大字号" },
{ "weight": 300, "min_px": 14, "max_px": 16, "note": "中号:再小开始「笔画溶解」" },
{ "weight": 400, "min_px": 12, "max_px": 13, "note": "微标签:12px 地板,不许更小,也不许更细" }
],
"ladder_note": "区间上界由相邻档与 12px 地板推出;源卡只给了下界(200 ≥17 / 300 ≥14)与「400 只准 ≤12px」两条。",
"rule": "越大越细:200 只准 ≥17px;300 只准 ≥14px;400 只准 ≤12px。全站最小档 ≥17px 时,唯一合法正文档就是 200。",
"anti_pattern": "同一字号挂两种字重(200 与 300 并存)是最常见的「乱」,合并到一档。"
}
tokens/tracking.json{
"id": "tracking",
"unit": "em",
"tiers": [
{ "id": "body", "value": "0", "what": "正文 / 界面:中文不加字距(--ls-0)" },
{ "id": "title", "value": "0.02em", "what": "标题:轻微开一点,给中文标题透气" },
{ "id": "label", "value": "0.12em", "what": "微标签 / eyebrow:拉丁小字与全大写标签" }
],
"cjk_rules": [
"中文正文不加字距(加字距会把中文行切成碎块)",
".02em / .12em 只用于标题与微标签,不下放到正文",
"中文禁两端对齐(text-align:justify 会用字距变化去填行,中文行宽因此抖动)"
],
"anti_pattern": "给中文正文加 letter-spacing 冒充「高级感」;一页里出现第四档字距值。"
}
tokens/leading.json{
"id": "leading",
"unit": "ratio",
"floor": 1.35,
"tiers": [
{ "id": "display", "value": 1.4, "what": "大字标语 / 字标" },
{ "id": "title", "value": 1.35, "what": "区块标题(全站最小行高,不许再压)" },
{ "id": "body", "value": 1.6, "what": "正文 / 界面:中文正文的舒适区" }
],
"rule": "行高绑字号档,不绑组件 —— 11 种行高的根因就是每个组件自己写 1.55 / 1.6 / 1.65 / 1.7 / 1.78 / 1.9。",
"anti_pattern": "用行高当间距工具(要留白加 margin,不要拉 line-height);正文行高压到 1.35 以下。"
}
tokens/shadow.json{
"id": "shadow",
"tiers": [
{
"id": "text",
"css": "0 1px 2px rgba(0,0,0,.9)",
"layers": 1,
"min_px": 0,
"what": "正文 / 界面 / 标题:发丝笔画只配单层影"
},
{
"id": "display",
"css": "0 3px 12px rgba(0,0,0,.55), 0 10px 44px rgba(0,0,0,.7)",
"layers": 2,
"min_px": 28,
"what": "≥28px 的标语 / 字标:第二层扩散影只给大字号"
}
],
"rule": "细体只配单层影;第二层扩散影只给 ≥28px。发丝笔画 + 双层影 = 糊成一团,肉眼看就是「字体变粗了」。",
"anti_pattern": "给 17px 正文加第二层扩散影;用彩色 glows 当文字影(暗色站上会直接吃掉笔画对比)。"
}
docs/usage.md# usage · cjk-thin-type-system 配对规则
> 核心主张:排版声明**只继承、只挂档**。组件里出现裸的 `font-size` / `line-height` / `letter-spacing` / `text-shadow`,就是这套语言破口的开始。
## 1 谁配谁(元素 → 档)
| 元素 | 字号档 | 字重 | 字距 | 行高 | 文字影 |
|---|---|---|---|---|---|
| 字标(全站唯一显示字族) | `giant` | `mark` 600 | 0 | 1.4 | `display`(双层,≥28px) |
| 标语 / 邮箱 | `display` | `text` 200 | 0 | 1.4 | `display` |
| 区块标题 / 卡片标题 | `title` | `text` 200 | `.02em` | 1.35 | `text` |
| 正文 / 界面 | `body` | `text` 200 | 0 | 1.6 | `text` |
| 微标签 / eyebrow(12px 级) | 自定义小档 | 400 | `.12em` | ≥1.35 | `text` |
- 字族不在本包:去 `cjk-typography` 取(`body-sans` 回退栈 / `sys-console` 系统栈 / 免费细体三选)。
- 12px 级的微标签是**唯一**允许低于 17px 的中文,且必须把字重抬到 400 —— 细体小字会「笔画溶解」。
## 2 何时用细体(判据)
1. **暗底 + 大字号**才成立:细体靠白字在暗底上的发光量显出笔画;17px 以下、或亮底,笔画直接变灰。
2. 一页最多两档细体字号(标语 + 正文);第三档开始观感从「高级」滑向「没力气」。
3. 需要力量感、数据密度、亮底长文时不要用:分别走粗体档、系统栈、纸媒正文档。
4. 已有站点先普查后收敛,不要凭感觉改单个元素:`audit` 探针(见 §4)先输出「字重分布 / 字号分布 / 组合表」三张表。
## 3 硬约束(改坏一条即不算这套语言)
| 约束 | 值 | 反例 |
|---|---|---|
| 中文最小字号 | 12px | `font-size:11px` 的角标 |
| 细体最小字号 | 17px | 15px 挂 200 字重 |
| 最小行高 | 1.35 | 正文行高 1.2 |
| 第二层影 | ≥28px 才准 | 给 17px 正文加扩散影 |
| `vw` | 只在 `clamp()` 中段 | `font-size:5vw`(无上限) |
| 斜体 | 中文禁 | `font-style:italic` 套中文 |
| 下划线 | 禁 `border-bottom` 当下划线 | 中文正文 `border-bottom:1px solid` |
**越大越细**(字重阶梯):`200 ≥17px` · `300 14–16px` · `400 12–13px`。同一字号只挂一种字重;全站最小档 ≥17px 时,唯一合法正文档就是 200。
## 4 审计探针(能跑起来,不是建议)
`scripts/assert_tokens.py` 把探针做成了可失败的检查,四条都是硬断言:
1. **字号地板**:`preview/` 下所有 CSS / HTML 的 `font-size` 声明,纯 `px` 值不得 < 12px;`clamp()` 的最小值同样不得 < 12px。
2. **最小行高**:`line-height` 无单位值不得 < 1.35。
3. **`vw` 外推**:`font-size` 里出现 `vw` 时,只允许出现在 `clamp()` 的中段,且上限必须是绝对单位;裸 `font-size:Xvw` 直接判失败。
4. **中文禁斜体**:样式表里不得出现 `font-style:italic`。
ps:探针只扫打包产物(`preview/`),把它接到你的项目上时,把它指向项目的样式表即可。
浏览器里做同样的普查(源卡自带的 DOM 探针,按「叶子级文字节点」取样,避免父级重复计数):
```js
const rows = [];
document.querySelectorAll('body *').forEach(el => {
const t = (el.textContent || '').trim();
if (!t) return;
if ([...el.children].some(c => (c.textContent || '').trim())) return;
const cs = getComputedStyle(el);
rows.push({ size: +parseFloat(cs.fontSize).toFixed(1), w: cs.fontWeight, ls: cs.letterSpacing, lh: cs.lineHeight, sh: cs.textShadow, fam: cs.fontFamily.split(',')[0] });
});
const g = new Map();
for (const r of rows) { const k = [r.fam, r.size, r.w, r.ls, r.lh, r.sh].join('|'); const e = g.get(k) || { ...r, n: 0 }; e.n++; g.set(k, e); }
console.log('组合数', g.size, '| 字号集合', [...new Set(rows.map(r => r.size))].sort((a, b) => b - a),
'| 字重集合', [...new Set(rows.map(r => r.w))].sort(), '| 影种类', new Set(rows.map(r => r.sh)).size);
```
## 5 反模式
- 用行高当间距工具(要留白加 `margin`,不要拉 `line-height`)。
- 同一字号挂两种字重(200 与 300 并存)—— 最常见的「乱」。
- 给中文正文加 `letter-spacing` 冒充「高级感」;一页出现第四档字距值。
- 按断点逐组件改 `font-size`(会留下 18 / 20 / 24 / 26px 等散值);响应式一律写进 `clamp()` 中段。
- 给 17px 正文加第二层扩散影,或用彩色 glow 当文字影。
- 拉丁显示字族套中文元素(会静默回落宋体,笔画与字面全变)。
- 用 `text-align: justify` 排中文(字距被拿来填空,行宽抖动)。
- 把字标字族下放给普通标题;字标是全站唯一允许出现显示字族的元素。
## 6 实测样本(源卡)
某暗底单页站 2026-10-31 全站普查:**151 个文字元素 / 21 种样式组合**(9 种字距 / 11 种行高 / 11 种文字影 / 字族里混进 Arial),按本包收敛后:组合 21 → 9、排版声明 223 → 31 条、字族 3 → 2、行高 11 → 3、文字影 11 → 2。
## 7 取用
```bash
python <sai-assets>/scripts/apply.py cjk-thin-type-system --to src/tokens
```
消费后在项目 `design-lock.json` 记 `assets[]`:`pack=cjk-thin-type-system`、`version`、`sha`(见 `docs/USAGE-INTEGRATION.md`)。
SKILL.md---
name: cjk-thin-type-system
description: Apply a thin-weight CJK type language (4 size tiers, 3 tracking tiers, 3 line-height tiers, 2 text-shadow tiers, 12px floor, bigger-is-thinner weight ladder) to dark single-page sites, portfolios and personal sites. Use when CJK type looks inconsistent — one size carrying several weights, every component writing its own line-height/letter-spacing/shadow — or when a site must read "thin, clean, one language".
---
# cjk-thin-type-system
一套中文**细体**排版语言:字号 4 档 / 字重 2 档 / 字距 3 档 / 行高 3 档 / 文字影 2 档。组件只继承,不再自己写 `font-size` / `font-weight` / `letter-spacing` / `line-height` / `text-shadow`。
## When to use
- 中文 / 日文**暗底**单页站、作品集、个人站,要求「细、干净、一套语言」
- 站点已经「字体乱」:同一字号挂多字重、每个组件一套行高 / 字距 / 阴影(先跑审计探针普查,再按本包收敛)
- 需要把响应式字号写成不可漂移的档位(而不是各断点改 `font-size`)
## When not to use
- 明亮纸媒长文 / 正文阅读 → `cjk-typography` 的 `article` 衬线栈
- 控制台、地图 HUD、内网工具 → 系统栈(`cjk-typography` 的 `sys-console` 档)
- 需要粗体力量感的营销落地页、霓虹 / 赛博风
- 只要概念、不管字族:`cjk-typography`
## Default
默认整站 = `body` 档:`17px` / 字重 `200` / 字距 `0` / 行高 `1.6` / 单层影 `0 1px 2px rgba(0,0,0,.9)`。
## Files
| path | use |
|------|-----|
| `tokens/index.json` | 五个轴的目录、默认档、地板与硬规则 |
| `tokens/scale.json` | 字号 4 档(giant / display / title / body)+ 每档绑定的字重 / 字距 / 行高 / 影 |
| `tokens/weights.json` | 字重 2 档(text 200 / mark 600)+「越大越细」配对阶梯 |
| `tokens/tracking.json` | 字距 3 档 + 中文禁字距 / 禁两端对齐 |
| `tokens/leading.json` | 行高 3 档(1.4 / 1.35 / 1.6)+ 最小行高 1.35 |
| `tokens/shadow.json` | 文字影 2 档 + 第二层只给 ≥28px |
| `docs/usage.md` | **配对规则 + 地板 + 反模式 + 审计探针** |
| `preview/index.html` | 活预览(档位阶梯 + 字号 / 行高 / 影对照) |
| `scripts/assert_tokens.py` | 断言:产物齐备 · 档位自洽 · 字号地板 · 最小行高 · 禁 `vw` 外推 |
## Agent apply steps
1. 读 `tokens/index.json`,把 `floors` 与 `rules` 当硬约束;再按页面元素读 `tokens/scale.json` 取档。
2. 字族去 `cjk-typography` 取(本包不下发字族);本包只决定档位。
3. 把六个 token 文件里的值抄进项目的 `:root`(变量名自定),**不要**在组件里写裸数值。
4. 组件的排版声明只允许「继承」或「挂某个档」两种写法;确需换档,挂 `scale.json` 里的档 id。
5. 收尾:改完跑 `python <pack>/scripts/assert_tokens.py`;若项目里有预览,顺手把审计探针那三条(字号地板 / 最小行高 / `vw` 外推)对本项目样式表跑一遍。
## Hard rules
- **地板 12px**:任何中文文字不许小于 12px;12px 起必须 400 字重。
- **越大越细**:200 只准 ≥17px;300 只准 ≥14px;400 只准 ≤12px。同一字号只挂一种字重。
- **最小行高 1.35**,且行高绑字号档、不绑组件。
- **细体只配单层影**;第二层扩散影只给 ≥28px。
- **禁用 `vw` 外推上限**:`clamp()` 的中段可以用 `vw`,上限必须是绝对单位。
- **CJK 禁斜体**、**禁 `border-bottom` 当前下划线**(中文没有斜体字形,合成斜体会糊;下划线会切掉下伸笔画)。
- 中文正文不加字距,禁两端对齐。
- 按钮不吃继承:`button,input,select,textarea{font:inherit;letter-spacing:inherit;text-shadow:inherit}`。
- 拉丁显示字族只给纯拉丁字标;给中文元素会**静默回落**宋体。