點 · 点 · 점 — /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.
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.
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.
Install:
npm install @hanzi.pro/diantenjeomLoad 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.
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 aunicode-range-segmented face is never the line's first available font — so it always takes that path. Left at Noto CJK's own1151 / −286the 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: normallike 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.
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.
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.
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.
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 carriestext-emphasis, never onbody, 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.500emfrom the baseline, and that outset is what the line box has to absorb. The ink sits aty ∈ [130, 630], centred in that channel rather than on the baseline: the browser hangs the mark|descent|from the channel's inner edge in bothoverandunder, so ink centred on the baseline overlaps the character outright in theovercase. - Needs
line-height≥ 2. Half-leading has to cover the outset on both sides, so the threshold is2 × (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.
Early. APIs, file names, and the punctuation codepoint set are all subject to change. More docs to come.
See docs/TODO.md.
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.