refactor: 字型移出聚珍管理(移除 _lang.css),交消費端;半形顯式保留 locl

下游回饋之字型樹需求。決策:字型屬消費端領域,聚珍不應內建字型堆疊(原 _lang.css
之 Noto 後備係越權,且曾蓋掉消費端字型釀 bug)。採「完全移除、交消費端 CSS」(A 案),
否決「以參數管字型」(B 案——JS 管字型須注入 CSS、違反 JS 注入結構/CSS 給視覺之分工;
而缺 halt 字型須逐字型微調 margin 之指引,由既有 jiya.halfWidth/jiya.bias +文檔承擔)。

移除:
- 刪 src/css/_lang.css,juzhen.css 移除其 @import。dist/juzhen.css 現零 font-family
  規則(3.1→2.5kb)。

聚珍保留之三項非管理性本分(確保不礙消費端字型與 locl):
- _normalize.css `jz-* { font: inherit }`:原子繼承消費端字型、不強加。
- _jiya.css 半形 font-feature-settings 顯式改 "locl" 1, "halt" 1(預設+halt-island 兩
  處):確保泛 CJK 字型(思源/Noto CJK)之地域標點字形不因設 halt 而失。
- render 只讀不改 lang。body 文本之 locl 全由消費端字型+正確 lang 決定。

驗證(真實 Chromium):
- halt 模式:消費端逐 lang 字型生效並繼承至 jz-inner,無強制 Noto。
- margin 後備:半形盒 0.5em、字身依墨色偏側位移正確、jz-inner 繼承消費端字型、
  feature normal(locl 既定開),皆不受字型移除影響。

- demo/index.html、resize-check.html 改示範「消費端逐 lang 設 font-family」之推薦範式;
  fallback.html 更新註解。
- README 新增「字型與 locl」節+改寫 CSS 基礎設定字型項;ARCHITECTURE §6.9 改寫
  (A 案決策、否決 B 案、三項本分)+檔案樹。
- test:新增 CSS 回歸(不內建 font-family、半形保留 locl)。57 測試通過。
- dist/ 重建(僅 juzhen.css 變動,JS 不變)。
This commit is contained in:
2026-06-03 01:59:45 +08:00
parent 79fee8481f
commit 5b9c6b5fa1
10 changed files with 91 additions and 99 deletions
+17 -7
View File
@@ -473,12 +473,22 @@ class 約定:`bd`(標點)、`bd-open/close/cop/stop/sep/liga`、`cjk`、`j
- **我們**:基於原生 `<ruby>` + `jz-ruby/jz-rt` 處理注音符號直排與聲調定位。 - **我們**:基於原生 `<ruby>` + `jz-ruby/jz-rt` 處理注音符號直排與聲調定位。
- **預留**:獨立 pass`order=80`;不影響其餘。 - **預留**:獨立 pass`order=80`;不影響其餘。
### 6.9 樣式正常化/字體堆疊 — 貫穿(CSS ### 6.9 樣式正常化/字型歸屬 — 貫穿(CSS
- **我們**`juzhen.css` 內以 `:where()`(零特異度,易被消費端覆寫)提供 CJK - **normalize**`juzhen.css` 內以 `@supports``:where()` 提供 CJK normalize(關閉
normalize 與依 `:lang` 之 Noto SansSerif CJK SCTCHKJP 字體堆疊。非破壞、 與託管擠壓重複作用之原生 `text-spacing-trim``text-autospace`、間隙 `user-select`
opt-in(掛在 `.juzhen` 容器或 `[data-juzhen]` 下) 等),限 `.juzhen` 域、非破壞
- **跨瀏覽器**`:where()``:lang()` 普遍支援。 - **字型不歸聚珍管(設計決策,下游回饋)**:曾內建依 `:lang` 之 Noto 字型堆疊作「後備」,
但字型屬消費端領域、內建即越權(且曾蓋掉消費端字型釀 bug)。故**移除 `_lang.css`**
逐語言 `font-family` 完全交消費端 CSS。聚珍僅守三項非管理性本分:① `_normalize.css`
之 `jz-* { font: inherit }` 使原子繼承消費端字型、不強加;② `_jiya.css` 半形之
`font-feature-settings` 顯式併入 `"locl" 1`(連同 `halt`),確保泛 CJK 字型之地域標點
字形不因設 halt 而失;③ render 只讀不改 `lang`。body 文本之 `locl` 本就不受聚珍影響
(未設任何 feature),由消費端字型+正確 `lang` 決定。消費端範式見 README「字型與 locl」。
- **B 案(字型參數)之否決**:以 JS 參數管字型須注入 CSS、違反「JS 注入結構/CSS 給視覺」
分工;而「缺 halt 字型須逐字型微調 margin」之指引由既有 `jiya.halfWidth``jiya.bias`
參數+文檔承擔,無需新參數。
- **跨瀏覽器**`:where()``:lang()``font-feature-settings` 普遍支援。
### 6.10 計數器/緊排(counterwell-knit)— 低優先 ### 6.10 計數器/緊排(counterwell-knit)— 低優先
@@ -626,8 +636,8 @@ src/
ruby.ts 注音/ruby [後續] ruby.ts 注音/ruby [後續]
css/ css/
juzhen.css 入口(@import 各 partial juzhen.css 入口(@import 各 partial
_normalize.css _lang.css _spacing.css _jiya.css _jinze.css _normalize.css _spacing.css _jiya.css _jinze.css
_hanging.css _emphasis.css _ruby.css _hanging.css _emphasis.css _ruby.css (字型不內建,交消費端)
test/ jsdom DOM 正確性測試 test/ jsdom DOM 正確性測試
demo/ 瀏覽器 layoutjustify/簡繁 目視驗證頁 demo/ 瀏覽器 layoutjustify/簡繁 目視驗證頁
build.mjs esbuildESM + IIFE + CSStscd.ts build.mjs esbuildESM + IIFE + CSStscd.ts
+32 -9
View File
@@ -65,7 +65,7 @@ jz.render(document.querySelector("main"));
| 功能 | 說明 | | 功能 | 說明 |
|---|---| |---|---|
| **中西間隙** `spacing` | CJK↔西文邊界插入 copy-clean 之 `jz-hws``user-select:none`,剪貼板跳過)。含跨樣式行內邊界與 `code``kbd` pill 邊界(pill 緊鄰標點時穿越 `jz-jinze` 仍補間隙)。贅餘作者空格一律正規化:CJK↔西文吸收為受管間隙、CJK↔CJK(含跨 inline 標籤)剝除不留隙。 | | **中西間隙** `spacing` | CJK↔西文邊界插入 copy-clean 之 `jz-hws``user-select:none`,剪貼板跳過)。「西文」涵蓋拉丁(含擴充 A/B,拼音聲調 `ā ǎ ē`)、希臘、西里爾、數字、貨幣(`€ ₩ ₿`)、數學運算符等;CJK 標點(`。,「」()……` 等)不誤判為西文、不生隙。含跨樣式行內邊界與 `code``kbd` pill 邊界(pill 緊鄰標點時穿越 `jz-jinze` 仍補間隙;pill 跨行右隙、間隙落行末之自我應驗皆已修)。贅餘作者空格一律正規化:CJK↔西文吸收為受管間隙、CJK↔CJK(含跨 inline 標籤)剝除不留隙。 |
| **長英文詞** `longWord` | `[A-Za-z]{6,}` 包入 `<span lang="en">`,配合 CSS `hyphens:auto` 原生斷詞(DOM 無連字符 → 複製不污染)。 | | **長英文詞** `longWord` | `[A-Za-z]{6,}` 包入 `<span lang="en">`,配合 CSS `hyphens:auto` 原生斷詞(DOM 無連字符 → 複製不污染)。 |
| **標點擠壓** `jiya` | 標點 charify 為 `jz-char``bd-*` class。三機制:①baseline 政策(全角式不擠/開明式擠句內點號+括號引號/半角式全擠);②行內連續擠壓(相鄰標點對向側收);③行端擠壓(`lineedge` layout pass)。半形預設以字型 `halt` 渲染(按字形正確定位,居中字形如 `!?`/繁體 `。,` 不重疊;不依賴 `text-spacing-trim`)。**字型缺 `halt` 時可切 `margin` 後備模式**(見下節)。 | | **標點擠壓** `jiya` | 標點 charify 為 `jz-char``bd-*` class。三機制:①baseline 政策(全角式不擠/開明式擠句內點號+括號引號/半角式全擠);②行內連續擠壓(相鄰標點對向側收);③行端擠壓(`lineedge` layout pass)。半形預設以字型 `halt` 渲染(按字形正確定位,居中字形如 `!?`/繁體 `。,` 不重疊;不依賴 `text-spacing-trim`)。**字型缺 `halt` 時可切 `margin` 後備模式**(見下節)。 |
| **禁則** `jinze` | 行首行末避頭尾:閉類/點號綁前一字(不置行首)、開類綁後一字(不置行末),只隔離邊界字故長引文中段仍可斷。亦為 `justify` 綁定載體。 | | **禁則** `jinze` | 行首行末避頭尾:閉類/點號綁前一字(不置行首)、開類綁後一字(不置行末),只隔離邊界字故長引文中段仍可斷。亦為 `justify` 綁定載體。 |
@@ -216,18 +216,18 @@ JS 只注入 `jz-*` 結構,**所有視覺尺寸在 `juzhen.css`**——故 CSS
2. **設定 `lang`(強烈建議)**:在內容容器標 `lang="zh-Hans"``"zh-Hant"``"ja"` 2. **設定 `lang`(強烈建議)**:在內容容器標 `lang="zh-Hans"``"zh-Hant"``"ja"`
驅動全角/開明政策與字形 `locl`。無 `lang` 時回退 `options.lang.default` 驅動全角/開明政策與字形 `locl`。無 `lang` 時回退 `options.lang.default`
3. **作用域類(用 `scope.include` 時)**:若設 `scope.include: ".juzhen"`,須給內容 3. **作用域類(用 `scope.include` 時)**:若設 `scope.include: ".juzhen"`,須給內容
容器加對應 class(如 `class="juzhen"`);`juzhen.css` 之 normalize/字型堆疊亦 容器加對應 class(如 `class="juzhen"`);`juzhen.css` 之 normalize 亦收斂於此域,
收斂於此域,不外溢。 不外溢。
4. **長英文詞斷字**`longWord` 包出 `<span lang="en">` 後,須由消費端 CSS 提供 4. **長英文詞斷字**`longWord` 包出 `<span lang="en">` 後,須由消費端 CSS 提供
`hyphens: auto`(含 `-webkit-hyphens`)才會原生斷詞顯示連字符。 `hyphens: auto`(含 `-webkit-hyphens`)才會原生斷詞顯示連字符。
5. **`justify`(選用)**:欲兩端對齊則自行設 `text-align: justify`;句末標點單份間距 5. **`justify`(選用)**:欲兩端對齊則自行設 `text-align: justify`;句末標點單份間距
與行末去隙由聚珍處理,無需額外設定。 與行末去隙由聚珍處理,無需額外設定。
6. **字型(影響擠壓)**:開明/半角式半形預設依字型 `halt`。若用含 `halt` 之字型 6. **字型由消費端負責(聚珍不管字型)**:聚珍**不**內建任何 `font-family`/字型堆疊
(Noto CJK/思源),預設即可;若用缺 `halt` 之字型,改用 `margin` 後備模式(見 ——逐語言字型完全由消費端 CSS 決定。聚珍只確保原子(`jz-*`)以 `font: inherit`
上節)。`juzhen.css` 內建依 `:lang` 之 Noto SansSerif CJK 字型堆疊,但**僅作 繼承消費端字型、絕不強加,並顯式保留 `locl`(見下「字型與 locl」一節)。半形擠壓
後備**(全包於 `:where()`、零特異度,且只命中帶 `lang` 屬性之元素、不直接覆寫標點 依字型 OpenType `halt`:含 `halt` 之字型(Noto CJK/思源)預設即可;**缺 `halt`
字面容器 `jz-inner`)——**消費端在 `.juzhen` 或任一祖先設 `font-family` 即恆優先** 之字型**(如方正書宋 GBK)須改用 `jiya.halfWidth:"margin"` 後備,並按字型以
標點亦隨之以消費端字型渲染(故缺 `halt` 字型務必配 `margin` 後備,否則標點不擠)。 `jiya.bias` 逐字微調墨色偏側(見「字型適配工作流」)。
7. **自動處理、無需消費端介入**`juzhen.css` 已在 `.juzhen` 範圍內以 `@supports` 7. **自動處理、無需消費端介入**`juzhen.css` 已在 `.juzhen` 範圍內以 `@supports`
關閉 Blink 原生 `text-spacing-trim``text-autospace`(避免與託管擠壓重複作用), 關閉 Blink 原生 `text-spacing-trim``text-autospace`(避免與託管擠壓重複作用),
並令注入間隙 `user-select: none`(複製乾淨)。 並令注入間隙 `user-select: none`(複製乾淨)。
@@ -239,6 +239,29 @@ JS 只注入 `jz-*` 結構,**所有視覺尺寸在 `juzhen.css`**——故 CSS
--- ---
## 字型與 locl(消費端設定)
聚珍**不管字型**;逐語言字型由你以 CSS 設定。推薦範式——逐 `lang``font-family`
掛在 `.juzhen` 或帶 `lang` 之容器(**勿**直接設 `jz-inner``jz-char`,它們靠繼承取字型):
```css
.juzhen:lang(zh-Hans), .juzhen [lang]:lang(zh-Hans) { font-family: "Noto Serif SC", "Source Han Serif SC", serif; }
.juzhen:lang(zh-Hant), .juzhen [lang]:lang(zh-Hant) { font-family: "Noto Serif TC", "Source Han Serif TC", serif; }
.juzhen:lang(ja), .juzhen [lang]:lang(ja) { font-family: "Noto Serif JP", serif; }
```
要點:
- **字型須含 OpenType `halt`** 才能半形擠壓;否則用 `jiya.halfWidth:"margin"` 按字型
`jiya.bias` 逐字微調墨色偏側(見「字型適配工作流」)。
- **locl(地域字形)**:泛 CJK 字型(思源/Noto CJK)以 `locl``lang` 選地域字形
(如「直/海/骨」之簡繁日差異)。聚珍**不改寫 `lang`、不關閉 locl**,故只要你 ①
正確標 `lang`、② 用支援 locl 之字型,locl 即正常。body 文本之 locl 全由你的字型+
`lang` 決定;標點(`jz-inner`)之 `halt` 已顯式併入 `locl`,地域標點字形不致失。
- 若只想全語言同一字型,直接 `.juzhen { font-family: … }` 即可(無需逐 `lang`)。
---
## v1 向下相容 ## v1 向下相容
```js ```js
+3 -2
View File
@@ -18,8 +18,9 @@
/* 字型類獨立於 .juzhen:A 用裸 .cn(瀏覽器原生排版),B/C 才掛 .juzhen /* 字型類獨立於 .juzhen:A 用裸 .cn(瀏覽器原生排版),B/C 才掛 .juzhen
(含 normalize 之 text-spacing-trim:space-all)。否則 A 之原生標點被 space-all (含 normalize 之 text-spacing-trim:space-all)。否則 A 之原生標點被 space-all
撐開、顯得比 B 鬆,造成「B 似乎有效」之假象(實則 B 全形未擠、與真原文等寬)。 */ 撐開、顯得比 B 鬆,造成「B 似乎有效」之假象(實則 B 全形未擠、與真原文等寬)。 */
/* 字型設於容器即可——_lang.css 修正後不再直接強制 jz-inner 之字型,故消費端 /* 字型設於容器即可——聚珍不管字型(無 _lang.css)、亦不強制 jz-inner`font:
字型經繼承正確套到標點字面(標點亦以 FZShuSong 渲染,halt 確實缺席)。 */ inherit`),故消費端字型經繼承正確套到標點字面(標點亦以 FZShuSong 渲染,halt
確實缺席)。此即「字型由消費端負責」之示例。 */
.cn { font-family: "方正书宋_GBK", "FZShuSong-Z01", serif; font-size: 46px; line-height: 1.5; } .cn { font-family: "方正书宋_GBK", "FZShuSong-Z01", serif; font-size: 46px; line-height: 1.5; }
.w { font-weight: 400; opacity: .85; } .w { font-weight: 400; opacity: .85; }
h1 { font-size: 1rem; } h1 { font-size: 1rem; }
+7 -1
View File
@@ -6,13 +6,19 @@
<title>聚珍(Juzhen)切片 1 — 目视验证</title> <title>聚珍(Juzhen)切片 1 — 目视验证</title>
<!-- 明确载入 Noto 系列 webfont 作参考:擠壓依赖字型 OpenType halt <!-- 明确载入 Noto 系列 webfont 作参考:擠壓依赖字型 OpenType halt
系统 fallback 字型(如 PingFang)可能无 halt,Safari 下尤其表现为开明式无效。 系统 fallback 字型(如 PingFang)可能无 halt,Safari 下尤其表现为开明式无效。
Noto Serif/Sans SC/TC 含 halt,确保跨浏览器一致参考。 --> Noto Serif/Sans SC/TC 含 halt,确保跨浏览器一致参考。
注意:字型由**消费端**自行设定——聚珍不再内建 _lang.css 字体堆叠。下方 <style>
之 `.juzhen:lang(...)` 即推荐范式(逐 lang 设 font-family,须含 halt)。 -->
<link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+SC&family=Noto+Sans+TC&family=Noto+Serif+SC&family=Noto+Serif+TC&display=swap" rel="stylesheet"> <link href="https://fonts.googleapis.com/css2?family=Noto+Sans+SC&family=Noto+Sans+TC&family=Noto+Serif+SC&family=Noto+Serif+TC&display=swap" rel="stylesheet">
<link rel="stylesheet" href="../dist/juzhen.css"> <link rel="stylesheet" href="../dist/juzhen.css">
<style> <style>
/* demo 自身样式(非套件之一)。 */ /* demo 自身样式(非套件之一)。 */
/* 消费端字型设定(推荐范式):逐 lang 设 font-family,置于 .juzhen 或带 lang 之
元素;字型须含 OpenType haltNoto CJK 具备),否则用 jiya.halfWidth:"margin"。 */
.juzhen:lang(zh-Hans), .juzhen [lang]:lang(zh-Hans) { font-family: "Noto Serif SC", "Noto Serif CJK SC", serif; }
.juzhen:lang(zh-Hant), .juzhen [lang]:lang(zh-Hant) { font-family: "Noto Serif TC", "Noto Serif CJK TC", serif; }
body { margin: 0; padding: 2rem; font-size: 18px; line-height: 2; background: #fafafa; color: #1a1a1a; } body { margin: 0; padding: 2rem; font-size: 18px; line-height: 2; background: #fafafa; color: #1a1a1a; }
h1 { font-size: 1.4rem; } h1 { font-size: 1.4rem; }
.toolbar { position: sticky; top: 0; background: #fff; border: 1px solid #ddd; padding: .75rem 1rem; margin-bottom: 1.5rem; display: flex; gap: 1rem; flex-wrap: wrap; align-items: center; z-index: 10; } .toolbar { position: sticky; top: 0; background: #fff; border: 1px solid #ddd; padding: .75rem 1rem; margin-bottom: 1.5rem; display: flex; gap: 1rem; flex-wrap: wrap; align-items: center; z-index: 10; }
+3
View File
@@ -7,6 +7,9 @@
<link rel="stylesheet" href="../dist/juzhen.css"> <link rel="stylesheet" href="../dist/juzhen.css">
<style> <style>
body { font: 16px/1.9 serif; max-width: 60em; margin: 2rem auto; padding: 0 1rem; color: #1a1a1a; } body { font: 16px/1.9 serif; max-width: 60em; margin: 2rem auto; padding: 0 1rem; color: #1a1a1a; }
/* 消費端字型(推薦範式:逐 lang 設,須含 halt)——聚珍不再內建字體堆疊。 */
.juzhen:lang(zh-Hant), .juzhen [lang]:lang(zh-Hant) { font-family: "Noto Serif TC", "Noto Serif CJK TC", "Source Han Serif TC", serif; }
.juzhen:lang(zh-Hans), .juzhen [lang]:lang(zh-Hans) { font-family: "Noto Serif SC", "Noto Serif CJK SC", "Source Han Serif SC", serif; }
h1 { font-size: 1.4rem; } h1 { font-size: 1.4rem; }
h2 { font-size: 1.05rem; margin: 1.6rem 0 .4rem; color: #036; } h2 { font-size: 1.05rem; margin: 1.6rem 0 .4rem; color: #036; }
.hint { color: #666; font-size: .85rem; margin: .2rem 0 .6rem; } .hint { color: #666; font-size: .85rem; margin: .2rem 0 .6rem; }
+4 -37
View File
@@ -20,39 +20,6 @@ jz-jinze {
text-decoration: inherit; text-decoration: inherit;
} }
/* src/css/_lang.css */
:where(.juzhen:lang(zh-Hans)),
:where(.juzhen [lang]:lang(zh-Hans)) {
font-family:
"Noto Serif SC",
"Noto Serif CJK SC",
"Source Han Serif SC",
serif;
}
:where(.juzhen:lang(zh-Hant)),
:where(.juzhen [lang]:lang(zh-Hant)) {
font-family:
"Noto Serif TC",
"Noto Serif CJK TC",
"Source Han Serif TC",
serif;
}
:where(.juzhen:lang(zh-HK)),
:where(.juzhen [lang]:lang(zh-HK)) {
font-family:
"Noto Serif HK",
"Noto Serif CJK HK",
"Noto Serif TC",
serif;
}
:where(.juzhen:lang(ja)),
:where(.juzhen [lang]:lang(ja)) {
font-family:
"Noto Serif JP",
"Noto Serif CJK JP",
serif;
}
/* src/css/_spacing.css */ /* src/css/_spacing.css */
jz-hws { jz-hws {
margin-right: 0.25em; margin-right: 0.25em;
@@ -74,8 +41,8 @@ jz-inner {
} }
jz-char.jz-half jz-inner, jz-char.jz-half jz-inner,
jz-char.jz-half-le jz-inner { jz-char.jz-half-le jz-inner {
font-feature-settings: "halt" 1; font-feature-settings: "locl" 1, "halt" 1;
-webkit-font-feature-settings: "halt" 1; -webkit-font-feature-settings: "locl" 1, "halt" 1;
} }
[data-jz-halfwidth=margin] jz-char.jz-half jz-inner, [data-jz-halfwidth=margin] jz-char.jz-half jz-inner,
[data-jz-halfwidth=margin] jz-char.jz-half-le jz-inner { [data-jz-halfwidth=margin] jz-char.jz-half-le jz-inner {
@@ -110,8 +77,8 @@ jz-char.jz-half-le jz-inner {
} }
[data-jz-halfwidth=halt] jz-char.jz-half jz-inner, [data-jz-halfwidth=halt] jz-char.jz-half jz-inner,
[data-jz-halfwidth=halt] jz-char.jz-half-le jz-inner { [data-jz-halfwidth=halt] jz-char.jz-half-le jz-inner {
font-feature-settings: "halt" 1; font-feature-settings: "locl" 1, "halt" 1;
-webkit-font-feature-settings: "halt" 1; -webkit-font-feature-settings: "locl" 1, "halt" 1;
margin-left: 0; margin-left: 0;
} }
[data-jz-halfwidth=halt] jz-char.jz-half, [data-jz-halfwidth=halt] jz-char.jz-half,
+9 -5
View File
@@ -32,12 +32,16 @@ jz-inner
具備);缺 halt 之字型則維持全形(優雅降級,不重疊)。三機制(baseline 政策 具備);缺 halt 之字型則維持全形(優雅降級,不重疊)。三機制(baseline 政策
C/行內鄰接 A/行端 B)皆以「加半形 class」落地,並集疊加、不重壓。 C/行內鄰接 A/行端 B)皆以「加半形 class」落地,並集疊加、不重壓。
jz-half 來自 baseline+鄰接;jz-half-le 來自行端 layout pass(分用不同 class jz-half 來自 baseline+鄰接;jz-half-le 來自行端 layout pass(分用不同 class
以便重算時僅清除行端結果,效果相同)。 */ 以便重算時僅清除行端結果,效果相同)。
**顯式保留 locl**`font-feature-settings` 為低階屬性,雖按規範不應關閉未列之預設
特性(locl 依 lang 選地域字形,泛 CJK 字型如思源/Noto CJK 賴之定位標點),但瀏覽器
實作有別;故同時列 `"locl" 1` 確保地域標點字形不因設 halt 而失(對無 locl 之字型亦
無害)。body 文本之 locl 不受聚珍影響(未設任何 feature),由消費端字型+正確 lang 決定。 */
jz-char.jz-half jz-inner, jz-char.jz-half jz-inner,
jz-char.jz-half-le jz-inner jz-char.jz-half-le jz-inner
{ {
font-feature-settings: "halt" 1; font-feature-settings: "locl" 1, "halt" 1;
-webkit-font-feature-settings: "halt" 1; -webkit-font-feature-settings: "locl" 1, "halt" 1;
} }
/* margin 後備模式(.feedback):消費端字型缺 halt 時,render() 於 root 設 /* margin 後備模式(.feedback):消費端字型缺 halt 時,render() 於 root 設
@@ -106,8 +110,8 @@ jz-char.jz-half-le jz-inner
[data-jz-halfwidth="halt"] jz-char.jz-half jz-inner, [data-jz-halfwidth="halt"] jz-char.jz-half jz-inner,
[data-jz-halfwidth="halt"] jz-char.jz-half-le jz-inner [data-jz-halfwidth="halt"] jz-char.jz-half-le jz-inner
{ {
font-feature-settings: "halt" 1; font-feature-settings: "locl" 1, "halt" 1;
-webkit-font-feature-settings: "halt" 1; -webkit-font-feature-settings: "locl" 1, "halt" 1;
margin-left: 0; margin-left: 0;
} }
-35
View File
@@ -1,35 +0,0 @@
/* 依 lang 之字體堆疊(§6.9)。**僅作預設後備**,消費端自訂字型恆優先。
兩項關鍵:
1. 整個選擇器包進 :where() → 零特異度,消費端任何 `.juzhen { font-family }`
(特異度 ≥ 0,1,0)即可覆寫。
2. 只命中**帶 `lang` 屬性之元素**(容器或內層區塊),**不**直接命中 jz-inner
jz-char(它們無 lang 屬性,靠繼承取字型)——否則會在標點字面容器上強制 Noto,
蓋掉消費端字型(曾致 demo 失真:消費端設 FZShuSong 卻仍以 Noto 之 halt 擠壓)。
簡繁日各取對應 Noto CJK,使 locl 字形正確。名稱涵蓋 webfont(「Noto Serif SC」)
與本機(「Noto Serif CJK SC」「Source Han Serif SC」)。擠壓半形依字型 OpenType
`halt`:消費端若用缺 halt 之字型(如方正書宋 GBK),請改用 margin 後備模式
(見 README「haltmargin 後備」)。 */
:where(.juzhen:lang(zh-Hans)),
:where(.juzhen [lang]:lang(zh-Hans))
{
font-family: "Noto Serif SC", "Noto Serif CJK SC", "Source Han Serif SC", serif;
}
:where(.juzhen:lang(zh-Hant)),
:where(.juzhen [lang]:lang(zh-Hant))
{
font-family: "Noto Serif TC", "Noto Serif CJK TC", "Source Han Serif TC", serif;
}
:where(.juzhen:lang(zh-HK)),
:where(.juzhen [lang]:lang(zh-HK))
{
font-family: "Noto Serif HK", "Noto Serif CJK HK", "Noto Serif TC", serif;
}
:where(.juzhen:lang(ja)),
:where(.juzhen [lang]:lang(ja))
{
font-family: "Noto Serif JP", "Noto Serif CJK JP", serif;
}
+5 -3
View File
@@ -1,9 +1,11 @@
/* 聚珍(Juzhen)樣式入口。視覺尺寸一律在此(I2):JS 只注入 jz-* 結構, /* 聚珍(Juzhen)樣式入口。視覺尺寸一律在此(I2):JS 只注入 jz-* 結構,
間隙寬度/擠壓量/原子化/字體堆疊皆由 CSS 給定。 間隙寬度/擠壓量/原子化皆由 CSS 給定。
切片 1normalize(禁用層)+ lang(字體)+ spacing + jiya + jinze。 */ **字型不歸聚珍管**:逐語言 font-family 完全交由消費端 CSS(見 README「字型由消費端
負責」)。聚珍僅守三項非管理性本分:_normalize 之 `jz-* { font: inherit }` 使原子
繼承消費端字型、不強加;_jiya 之半形 feature 顯式保留 `locl`render 不改寫 `lang`。
切片 1normalize(禁用層)+ spacing + jiya + jinze。 */
@import "./_normalize.css"; @import "./_normalize.css";
@import "./_lang.css";
@import "./_spacing.css"; @import "./_spacing.css";
@import "./_jiya.css"; @import "./_jiya.css";
@import "./_jinze.css"; @import "./_jinze.css";
+11
View File
@@ -497,3 +497,14 @@ test("CSSjz-* 原子 text-decoration:inherit,補裝飾盒跳過原子致下
const css = readFileSync(cssPath, "utf8"); const css = readFileSync(cssPath, "utf8");
assert.match(css, /text-decoration\s*:\s*inherit/, "構建 CSS 應含 text-decoration:inherit"); assert.match(css, /text-decoration\s*:\s*inherit/, "構建 CSS 應含 text-decoration:inherit");
}); });
test("CSS:聚珍不內建 font-family(字型交消費端),半形顯式保留 locl(下游回饋)", () =>
{
// 字型屬消費端領域,聚珍不內建任何字型堆疊(移除 _lang.css);僅以 _normalize 之
// `font: inherit` 使原子繼承消費端字型、不強加,並於半形 feature 顯式併入 locl
// 確保泛 CJK 字型之地域標點字形不因設 halt 而失。body locl 由消費端字型+lang 決定。
const cssPath = fileURLToPath(new URL("../dist/juzhen.css", import.meta.url));
const css = readFileSync(cssPath, "utf8");
assert.equal(/font-family/.test(css), false, "聚珍 CSS 不應內建任何 font-family");
assert.match(css, /font-feature-settings:\s*"locl"\s+1,\s*"halt"\s+1/, "半形 feature 應顯式保留 locl");
});