Skip to content

About

Punctuation-only CJK fonts, per locale. Drop into a font fallback chain.

Resources

Stars

1 star

Watchers

0 watching

Forks

Repository files navigation

diantenjeom

點 · 点 · 점 — /ti̯ɛn˨˩˦ ten tɕʌm/, or, as you might say in English, /ˈdiː.æn.tɛn.dʒəm/

A set of punctuation-only CJK fonts, extracted from Noto Sans CJK + Noto Serif CJK, shipped as variable fonts.

The name strings together the readings of 點 ("dot") in Mandarin (diǎn), Japanese (ten), and Korean (jeom) — the three reading traditions whose punctuation conventions the project sets out to disentangle.

Why

CJK text routinely mixes a Latin body font with a CJK fallback. That fallback also has to render the punctuation — and different conventions render the same Unicode punctuation differently (the position of 。, the shape of 「」, the width of 、, where 〕, should squeeze). Bundling punctuation with whichever CJK font happens to be loaded forces one convention onto everyone.

diantenjeom pulls just the punctuation glyphs out of Noto CJK and ships them as small variable fonts you drop into a font fallback chain — CSS font-family, InDesign composite fonts, app text styles, anywhere fallback is supported — and pick the punctuation style independently from the body face.

Variants

Each style ships as a separate variable font; pick by punctuation positioning convention, not by language tag.

Four variants, each named after the layout document whose punctuation positioning it follows (JIS / MOE / GB / KLReq). Naming is intentionally decoupled from text locale, so you can pair any variant with any CJK text font (e.g. Japanese text with MOE punctuation, TC text with JIS punctuation).

Family Style Punctuation convention
Diantenjeom Sans JIS Sans JIS X 4051 — 、,。 hug the top-left corner of the em
Diantenjeom Serif JIS Serif JIS X 4051
Diantenjeom Sans MOE Sans 教育部《重訂標點符號手冊》— 、,。 centred
Diantenjeom Serif MOE Serif 同上
Diantenjeom Sans GB Sans GB/T 15834 — 、,。:;!? side-aligned
Diantenjeom Serif GB Serif 同上
Diantenjeom Sans KLR Sans W3C KLReq — 、,。 corner-attached, :;!? upright
Diantenjeom Serif KLR Serif 同上

Each variant exposes the full wght axis (Sans 100–900, Serif 200–900) with named instances on the CSS-standard grid: Thin / ExtraLight / Light / Regular / Medium / SemiBold / Bold / ExtraBold / Black.

KLR follows W3C KLReq (Requirements for Hangul Text Layout and Typography), the most systematic Hangul layout document in existence — Korea has had no statutory vertical-punctuation spec since 한글 맞춤법 dropped its vertical chapter in 2015, so KLReq is the standing reference despite being a Working Draft. What it asks of a font:

  • cl1 / cl2 — full-width brackets and quotes ((「『〔《…) sit flush against one end of the em, with the adjustable space at the other end.
  • cl6 / cl7 — dividing marks (!?) and middle dots (· : ;) stay upright in vertical writing rather than rotating.
  • cl8 / cl9 — 。 、 are full-width in vertical writing, corner-attached. Horizontally KLReq calls for the half-width Western . , instead, which a system font already renders well — so KLR's Korean value concentrates in vertical text (book spines, obituaries, calligraphy, mixed Hangul–Hanja prose) plus the bracket family, which horizontal Korean does use.

Background and sources: docs/notes/korean-vertical-punctuation.md.

Implementation note: KLR and JIS agree everywhere except : (U+FF1A), which KLReq cl7 keeps upright and JIS X 4051 rotates. Since that difference lives in vert/vrt2, the two render identically in horizontal text — pick one, and don't ship both to the same page.

Usage

Install:

npm install @hanzi.pro/diantenjeom

Load the stylesheet — it ships the @font-face blocks:

@import "@hanzi.pro/diantenjeom/css";

or, with a bundler:

import "@hanzi.pro/diantenjeom";

The default stylesheet ships every convention (JIS / MOE / GB / KLR). If you only need one, import just that standard to skip the others' @font-face rules:

@import "@hanzi.pro/diantenjeom/moe"; /* also: /jis, /gb, /klr */

The default uses font-display: swap — the host font's punctuation shows immediately, then swaps to Diantenjeom's positioning once the (tiny) font loads. The fallback glyph is fully legible, so the only cost is a brief in-em-box shift. If you'd rather have no positional flash (at the cost of briefly-invisible punctuation on a slow fetch), or a performance-first mode that applies the font only if it loads near-instantly, import the block or optional variant instead:

@import "@hanzi.pro/diantenjeom/block";        /* block, every convention */
@import "@hanzi.pro/diantenjeom/block/moe";    /* block, MOE only         */
@import "@hanzi.pro/diantenjeom/optional/jis"; /* optional, JIS only      */

Then put a Diantenjeom family before your CJK text font in the font-family fallback chain:

body {
  font-family:
    "Helvetica Neue",        /* Latin body                              */
    "Diantenjeom Sans MOE",  /* punctuation — must precede the CJK font */
    "Noto Sans TC",          /* CJK body                                */
    sans-serif;
}

Each @font-face carries a unicode-range covering only the ~30 punctuation codepoints, so the browser pulls Diantenjeom only for those characters and falls through to the next font for everything else (Han, kana, Hangul, Latin). That's why it has to sit ahead of the CJK font — otherwise the CJK font's own punctuation wins. Pick the convention independently of your text language: pair Diantenjeom Sans MOE with Japanese text, Diantenjeom Serif JIS with Traditional Chinese, and so on.

Markup caveat. Browser pair-squeezing (text-spacing-trim) only collapses adjacent punctuation that share an inline formatting context. Splitting two glyphs across separate atomic-inline boxes — display: inline-block / inline-flex / inline-grid, as some frameworks emit when wrapping characters — silently disables the squeeze; plain inline <span>s are fine. See docs/pair-squeeze-and-markup.md.

Line metrics

Every face is built with hhea / usWin* / sTypo* normalised to 880 / −120 on a 1000 upem, and that is deliberate on two counts:

  • The difference ((A − |D|) / 2 = 0.380em) is where a browser puts the glyph's central baseline in vertical writing. Chromium and Safari synthesise it from whichever font drew the character, and a unicode-range-segmented face is never the line's first available font — so it always takes that path. Left at Noto CJK's own 1151 / −286 the punctuation would sit 0.0525em to one side of ButTaiwan-metric body text. 0.380em is the em box's own centre, and what Hiragino / YuMincho / 源起 / 芫荽 / ziano already use.
  • The sum (1.0em) keeps the face from ever growing the line. A punctuation font contributes to line-height: normal like any other; 1.0em means it never wins that max.

The stylesheets do not restate these as ascent-override — Safari ignores those descriptors on this path, so the numbers have to live in the files.

From a CDN (no install)

jsDelivr and unpkg mirror every published file straight from npm — no install, no hosting. Point an @import or <link> at the file you want:

@import "https://cdn.jsdelivr.net/npm/@hanzi.pro/diantenjeom@0.2.0/dist/diantenjeom.css";
/* one convention:    …/dist/moe.css                                   */
/* block / optional:  …/dist/block/moe.css   …/dist/optional/jis.css   */
<link rel="stylesheet"
      href="https://cdn.jsdelivr.net/npm/@hanzi.pro/diantenjeom@0.2.0/dist/diantenjeom.css">

unpkg works identically — swap the host for https://unpkg.com/. The @font-face src paths are relative, so the woff2 files resolve against the same CDN automatically; you only ever reference the CSS. Pin a version (@0.2.0) in production so the CDN can cache the response immutably — an unpinned URL resolves to the latest release and is served with a short TTL.

Without a build step

Copy dist/diantenjeom.css and dist/fonts/ to your site and link the CSS directly. The @font-face src URLs are relative to the stylesheet, so keep fonts/ next to it.

Desktop apps (InDesign, etc.)

Install the OTFs from dist/fonts/ and add a Diantenjeom family ahead of your CJK font in a composite-font / fallback list — the same ordering rule applies.

Emphasis marks (著重號)

The package also ships one non-punctuation face, Diantenjeom Emphasis, holding the four CJK emphasis marks (﹅﹆●○ and the sesame/dot pairs text-emphasis draws from). It exists because the system fonts disagree wildly about how big a mark is, how far it sits from the text, and — on some faces — whether they have one at all.

em {
  font-style: normal;
  font-family: "Diantenjeom Emphasis", "Noto Serif TC", serif; /* mark font FIRST */
  text-emphasis: filled circle;
  text-emphasis-position: under right;  /* covers vertical text too — see below */
  line-height: 2;                       /* see below */
}

Three things about it differ from the punctuation faces:

  • No unicode-range. A mark is not drawn by text-shaping the run's own characters — the browser picks the mark glyph from the first font in the element's list that has it. Restricting the face by codepoint would keep it from ever being consulted. Because it is unrestricted, list it only on the element that carries text-emphasis, never on body, or it will draw that element's spaces too.
  • Metrics 880 / −120 — the same line metrics the punctuation faces carry, so every file in the package states one. Same 0.380em centre as everything else (so an <em> doesn't jog sideways in vertical text — a bug this font caused once already), but the sum now matters for a second reason: a mark is laid out at (A + |D|) / 2 = 0.500em from the baseline, and that outset is what the line box has to absorb. The ink sits at y ∈ [130, 630], centred in that channel rather than on the baseline: the browser hangs the mark |descent| from the channel's inner edge in both over and under, so ink centred on the baseline overlaps the character outright in the over case.
  • Needs line-height ≥ 2. Half-leading has to cover the outset on both sides, so the threshold is 2 × (A + |D|) / upem — below it the marked line grows taller than its neighbours and the paragraph goes ragged. There is no CSS override for this; it is the price of a mark that clears the text instead of touching it.

One value of text-emphasis-position serves both writing modes: over | under is read only in horizontal text and right | left only in vertical, so under right means "below the line" horizontally and "right of the column" vertically — 教育部 convention both times.

Full derivation, the clearance/threshold frontier, and why vmtx tsb, vert substitution and ascent-override all have no effect on marks: docs/emphasis-marks.md.

Status

Early. APIs, file names, and the punctuation codepoint set are all subject to change. More docs to come.

TODO

See docs/TODO.md.

License

SIL Open Font License 1.1, inherited from Noto CJK. The shipped faces are derived works: subset to the punctuation set, name table rewritten off the Reserved Font Name, and line metrics normalised as described above. See LICENSE for the full text and the upstream notices.

About

Punctuation-only CJK fonts, per locale. Drop into a font fallback chain.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages