From 91b06b67a302ebfa210e888fe9d03d27fb291b31 Mon Sep 17 00:00:00 2001 From: Divyansh Singh <40380293+brc-dd@users.noreply.github.com> Date: Sat, 25 Jul 2026 03:10:16 +0530 Subject: [PATCH] fix(theme)!: route cjk punctuation to matching system fonts The Inter Core faces (previously Inter4CJK) leave East Asian ambiguous punctuation and symbols uncovered so that the document's CJK font can render them, but the next family in the stack was a Latin system font, which claimed most of those characters before the language-aware system fallback could pick the right CJK font - and characters it lacked could land in an unrelated one (like Apple SD Gothic Neo on Chinese pages in Chrome on macOS). The zh/ja/ko stacks now name the platform defaults explicitly (PingFang SC / Microsoft YaHei / Noto Sans CJK SC, Hiragino Sans / Meiryo / Yu Gothic / Noto Sans CJK JP, Apple SD Gothic Neo / Malgun Gothic / Noto Sans CJK KR), so per-glyph matching lands there deterministically, and sites can splice a CJK webfont between 'Inter Core' and the system families. Traditional Chinese currently shares the zh (Hans) rule; the TC variant is documented in the script. BREAKING CHANGE: the Inter4CJK font family has been renamed to 'Inter Core'. Custom --vp-font-family-base overrides referencing Inter4CJK must be updated. Co-Authored-By: Claude Fable 5 --- scripts/subsetFonts.py | 47 ++++++++++++++++--- src/client/theme-default/styles/fonts.css | 55 ++++++++++++++--------- 2 files changed, 75 insertions(+), 27 deletions(-) diff --git a/scripts/subsetFonts.py b/scripts/subsetFonts.py index f3be549e0..422b6b334 100755 --- a/scripts/subsetFonts.py +++ b/scripts/subsetFonts.py @@ -50,17 +50,50 @@ BASE_VAR = """\ } """ +# The 'Inter Core' faces leave the `cjkExclusions` characters uncovered so that +# they fall through to the next family in the stack, which therefore must be +# an explicit CJK font: a Latin system font (or `sans-serif`) would claim most +# of the excluded punctuation before the language-aware system fallback ever +# got a chance to pick the right CJK font. The names below cover the defaults +# of macOS / Windows / Linux (fonts-noto-cjk); families that are not installed +# are simply skipped, and sites can splice a CJK webfont between 'Inter Core' +# and the system families. Bare zh means Hans per BCP 47 likely subtags, and +# with no Traditional Chinese rule it also covers zh-Hant/zh-TW/zh-HK/zh-MO +# pages. If Hant handling is ever requested, add after the zh rule (same +# specificity, so the later rule wins; the region tags must be enumerated +# because `:lang(zh-Hant)` cannot match e.g. `lang="zh-TW"`): +# [lang]:where(:lang(zh-Hant), :lang(zh-TW), :lang(zh-HK), :lang(zh-MO)) { +# --vp-font-family-base: +# 'Inter Core', 'PingFang TC', 'Microsoft JhengHei', 'Noto Sans CJK TC', +# ...; +# } +# (Hong Kong variants also exist: 'PingFang HK' / 'Noto Sans CJK HK'.) CJK_BASE_VAR = """\ -[lang]:where(:lang(zh), :lang(ja), :lang(ko)) { +[lang]:where(:lang(zh)) { --vp-font-family-base: - 'Inter4CJK', -apple-system, BlinkMacSystemFont, sans-serif, - 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji'; + 'Inter Core', 'PingFang SC', 'Microsoft YaHei', 'Noto Sans CJK SC', + -apple-system, BlinkMacSystemFont, sans-serif, 'Apple Color Emoji', + 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji'; +} + +[lang]:where(:lang(ja)) { + --vp-font-family-base: + 'Inter Core', 'Hiragino Sans', 'Meiryo', 'Yu Gothic', 'Noto Sans CJK JP', + -apple-system, BlinkMacSystemFont, sans-serif, 'Apple Color Emoji', + 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji'; +} + +[lang]:where(:lang(ko)) { + --vp-font-family-base: + 'Inter Core', 'Apple SD Gothic Neo', 'Malgun Gothic', 'Noto Sans CJK KR', + -apple-system, BlinkMacSystemFont, sans-serif, 'Apple Color Emoji', + 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji'; } """ # used instead of everything else in this file when `useWebFonts` is enabled # (only the content between the markers survives) - see -# src/node/plugins/webFontsPlugin.ts. There is no Inter4CJK on Google Fonts, +# src/node/plugins/webFontsPlugin.ts. There is no 'Inter Core' on Google Fonts, # so CJK documents use the regular Inter there. WEBFONT_IMPORT = f"""\ /* webfont-marker-begin */ @@ -73,7 +106,7 @@ WEBFONT_IMPORT = f"""\ # The `cjkExclusions` key of the json lists characters that have both a # Western (proportional) and an East Asian (em-square) form with no # encoding-level distinction - mostly East Asian Ambiguous punctuation and -# symbols (UAX #11). The generated Inter4CJK faces reuse the same font files +# symbols (UAX #11). The generated 'Inter Core' faces reuse the same font files # but leave these out of their unicode-ranges so that CJK fonts render them # in CJK documents. References: # https://www.unicode.org/L2/L2014/14006-sv-western-vs-cjk.pdf @@ -84,7 +117,7 @@ WEBFONT_IMPORT = f"""\ # & U+2015 (used like U+2014 in Japanese), U+203B (Japanese reference mark), # U+007E (zh's request; both forms are fine in ja because it is unused there) CJK_COMMENT = """\ -/* Inter4CJK reuses the files above, but leaves out characters that should +/* 'Inter Core' reuses the files above, but leaves out characters that should be rendered by CJK fonts in CJK documents - see scripts/subsetFonts.py */ """ @@ -187,7 +220,7 @@ def write_css(subsets: dict[str, str], cjk_exclusions: set[int]) -> None: cjk_cps = parse_ranges(value) - cjk_exclusions if cjk_cps: cjk.append( - face("Inter4CJK", css_style, file, format_ranges(cjk_cps)) + face("'Inter Core'", css_style, file, format_ranges(cjk_cps)) ) FONTS_CSS.write_text( diff --git a/src/client/theme-default/styles/fonts.css b/src/client/theme-default/styles/fonts.css index c0ae2ae64..ed6d01df4 100644 --- a/src/client/theme-default/styles/fonts.css +++ b/src/client/theme-default/styles/fonts.css @@ -154,11 +154,11 @@ unicode-range: U+02CD, U+02D8-02D9, U+02DB, U+0302, U+0306-0307, U+030A, U+030C, U+030F, U+0313, U+0315, U+031B, U+0326-0328, U+032C, U+0337-0338, U+0342-0343, U+0346-036F, U+0E3F, U+1DC0-1DF5, U+1DFC-1DFF, U+2070-2071, U+2074-208E, U+2090-209C, U+20DB-20DE, U+20E8, U+20F0, U+2100-2101, U+2103, U+2105-2106, U+2109, U+2117, U+211E-2121, U+2126, U+212A-212B, U+212E, U+2132, U+213B, U+214D, U+2150-217F, U+2183-2186, U+2189, U+2190, U+2192, U+2194-2199, U+21AA, U+21B0-21B1, U+21B3-21B5, U+21BA-21BB, U+21D0, U+21D2, U+21D4, U+21DE-21DF, U+21E4-21E5, U+21E7, U+21EA, U+2202, U+2205-2206, U+220F, U+2211, U+221A, U+221E, U+222B, U+2236, U+2248, U+2260, U+2264-2265, U+2295-2298, U+2303-2305, U+2325-2327, U+232B, U+2380, U+2387, U+238B, U+23CE-23CF, U+2423, U+2460-2468, U+24B6-24CF, U+24EA, U+25A0-25A2, U+25AA, U+25B2-25B3, U+25B6-25B7, U+25BA-25BD, U+25C0-25C1, U+25C4-25C7, U+25CA-25CB, U+25CF, U+25E6, U+25EF, U+2600, U+2605-2606, U+263C, U+2661, U+2665, U+266A-266B, U+26A0, U+2713, U+2717, U+2756, U+2764, U+2780-2788, U+27EF, U+27F5-27FA, U+2913, U+2A38, U+2B06, U+2B12-2B13, U+2B1C, U+2B24, U+2E18, U+A92E, U+1F12F-1F149, U+1F16A-1F16B, U+1F850, U+1F852; } -/* Inter4CJK reuses the files above, but leaves out characters that should +/* 'Inter Core' reuses the files above, but leaves out characters that should be rendered by CJK fonts in CJK documents - see scripts/subsetFonts.py */ @font-face { - font-family: Inter4CJK; + font-family: 'Inter Core'; font-style: italic; font-weight: 100 900; font-display: swap; @@ -167,7 +167,7 @@ } @font-face { - font-family: Inter4CJK; + font-family: 'Inter Core'; font-style: italic; font-weight: 100 900; font-display: swap; @@ -176,7 +176,7 @@ } @font-face { - font-family: Inter4CJK; + font-family: 'Inter Core'; font-style: italic; font-weight: 100 900; font-display: swap; @@ -185,7 +185,7 @@ } @font-face { - font-family: Inter4CJK; + font-family: 'Inter Core'; font-style: italic; font-weight: 100 900; font-display: swap; @@ -194,7 +194,7 @@ } @font-face { - font-family: Inter4CJK; + font-family: 'Inter Core'; font-style: italic; font-weight: 100 900; font-display: swap; @@ -203,7 +203,7 @@ } @font-face { - font-family: Inter4CJK; + font-family: 'Inter Core'; font-style: italic; font-weight: 100 900; font-display: swap; @@ -212,7 +212,7 @@ } @font-face { - font-family: Inter4CJK; + font-family: 'Inter Core'; font-style: italic; font-weight: 100 900; font-display: swap; @@ -221,7 +221,7 @@ } @font-face { - font-family: Inter4CJK; + font-family: 'Inter Core'; font-style: italic; font-weight: 100 900; font-display: swap; @@ -230,7 +230,7 @@ } @font-face { - font-family: Inter4CJK; + font-family: 'Inter Core'; font-style: normal; font-weight: 100 900; font-display: swap; @@ -239,7 +239,7 @@ } @font-face { - font-family: Inter4CJK; + font-family: 'Inter Core'; font-style: normal; font-weight: 100 900; font-display: swap; @@ -248,7 +248,7 @@ } @font-face { - font-family: Inter4CJK; + font-family: 'Inter Core'; font-style: normal; font-weight: 100 900; font-display: swap; @@ -257,7 +257,7 @@ } @font-face { - font-family: Inter4CJK; + font-family: 'Inter Core'; font-style: normal; font-weight: 100 900; font-display: swap; @@ -266,7 +266,7 @@ } @font-face { - font-family: Inter4CJK; + font-family: 'Inter Core'; font-style: normal; font-weight: 100 900; font-display: swap; @@ -275,7 +275,7 @@ } @font-face { - font-family: Inter4CJK; + font-family: 'Inter Core'; font-style: normal; font-weight: 100 900; font-display: swap; @@ -284,7 +284,7 @@ } @font-face { - font-family: Inter4CJK; + font-family: 'Inter Core'; font-style: normal; font-weight: 100 900; font-display: swap; @@ -293,7 +293,7 @@ } @font-face { - font-family: Inter4CJK; + font-family: 'Inter Core'; font-style: normal; font-weight: 100 900; font-display: swap; @@ -307,8 +307,23 @@ 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji'; } -[lang]:where(:lang(zh), :lang(ja), :lang(ko)) { +[lang]:where(:lang(zh)) { --vp-font-family-base: - 'Inter4CJK', -apple-system, BlinkMacSystemFont, sans-serif, - 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji'; + 'Inter Core', 'PingFang SC', 'Microsoft YaHei', 'Noto Sans CJK SC', + -apple-system, BlinkMacSystemFont, sans-serif, 'Apple Color Emoji', + 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji'; +} + +[lang]:where(:lang(ja)) { + --vp-font-family-base: + 'Inter Core', 'Hiragino Sans', 'Meiryo', 'Yu Gothic', 'Noto Sans CJK JP', + -apple-system, BlinkMacSystemFont, sans-serif, 'Apple Color Emoji', + 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji'; +} + +[lang]:where(:lang(ko)) { + --vp-font-family-base: + 'Inter Core', 'Apple SD Gothic Neo', 'Malgun Gothic', 'Noto Sans CJK KR', + -apple-system, BlinkMacSystemFont, sans-serif, 'Apple Color Emoji', + 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji'; }