· 3 min read
Typing typographic characters on Windows
Alt codes on the numeric keypad, the Win + . symbol panel, and Word’s own shortcuts — three ways to type every dash, quote and sign, with the full code table.
A library that fixes the typography of running text. Give it a string and a language. It gives the string back with the right quotation marks, dashes where there were hyphens, an ellipsis, and no-break spaces where a line should not break.
bun add git+ssh://git@github.com/mokhniuk/typography.git#v0.13.0
Version 0.13.0, a pre-release. Not on npm yet; it will be there at 1.0. Until then, install a release tag from git. The repository is private, so the machine needs SSH access. The package ships TypeScript sources, so it needs Bun or a bundler that reads TypeScript.
“Curiouser and curiouser!” cried Alice (she was so much surprised, that for the moment she quite forgot how to speak good English); “now I’m opening out like the largest telescope that ever was! Good-bye, feet!” — for when she looked down at her feet, they seemed to be almost out of sight…
The engine is one function over a string. Everything else is a way of getting your text into it, or a way of seeing what it did.
explain returns every change with the rule that made it and the text before and after, so a surprise is a question with an answer, not a mystery. The rulesTypographyRoot, avoidMissing); …/dom exposes the measure. The font checkerpre and code are never touched. An unknown rule name throws instead of passing quietly.Each language has its own pack: its quotation marks and how they nest, its dash, its habits with spaces. These four are not translations of one sentence; each is written the way that language is.
Curly quotes and apostrophes, a spaced dash, a real ellipsis, an en dash in ranges, and a number kept with its month.
"It's five o'clock," she said - "and we're late..." The workshop runs 21-26 September, 10:00-12:00, in room 4B.Ukrainian guillemets, the dialogue dash with its no-break space, and initials kept with the surname.
- Зачекаємо? - спитав професор К. Новак. - Сказали: "вже п'ята година"... Квитки зі знижкою 15%.German marks, nested the German way, and a range that is a range.
Sie sagte: "Er rief 'Halt' - und blieb stehen" ... Seiten 12-14, ca. 20 Min. am Tag.Polish low-and-high quotes, and one-letter words held to the next word so no line ends on them.
Powiedział: "Idę do domu" - i poszedł. W sali nr 5 jest 20 osób, a w drugiej a w trzeciej 12.22 rules run by default, and 11 more you switch on. Here are some of them on one line each. A boxed space is a no-break space.
| Rule | You write | You get |
|---|---|---|
quote |
"Hello" |
“Hello” |
apostrophe |
don't |
don’t |
ellipsis |
Wait... |
Wait… |
dash.range |
pages 21-26 |
pages 21–26 |
dash.sentence |
Wait - what? |
Wait — what? |
dash.dialogue |
- Привіт, - сказала вона. |
— Привіт, — сказала вона. |
space.number |
10 km |
10 km |
space.number |
USD 5 000 |
USD 5 000 |
space.section |
§ 5 |
§ 5 |
space.operator |
Tom & Jerry |
Tom & Jerry |
space.initial |
T. H. Shevchenko |
T. H. Shevchenko |
space.orphan |
Jadę z Anią w góry |
Jadę z Anią w góry |
symbol.legal |
(c) 2026 |
© 2026 |
symbol.multiply |
1920x1080 |
1920×1080 |
symbol.minus |
-5 C |
−5 C |
number.group opt-in |
1000000 |
1,000,000 |
phone.bind opt-in |
+380 44 123 45 67 |
+380 44 123 45 67 |
space.short-word opt-in |
Welcome to the city |
Welcome to the city |
One function takes a string and a language. The other entry points do the same for plain text, HTML, Markdown, rehype trees and React, and bring the browser side: hanging punctuation and font checks.
import { typographic, explain } from '@mokhniuk/typography';
typographic('He said "we\'re here" - and left.', 'en', { quoteStyle: 'us' });
// He said “we’re here” — and left. (a no-break space before the dash)
explain('He said "hi"... 21-26', 'en', { quoteStyle: 'us' }).changes;
// quote “ · quote ” · ellipsis … · dash.range –
| Import | For |
|---|---|
@mokhniuk/typography | A string. explain lists each change and the rule behind it. |
…/text | Plain text, a paragraph at a time. Line breaks are kept. |
…/html | An HTML string. Tags and code are untouched. |
…/markdown | A Markdown document. |
…/rehype | A unified or rehype pipeline, or any hast tree. |
…/react | Typeset and TypographyRoot, with hanging punctuation. TypographyRoot measures the page’s font and fills avoid. |
…/dom | Hanging punctuation in the browser, without a framework, and which characters the page’s font can draw. |
typo | The command line, for files and CI. Needs Bun. |
Before you depend on it, you should know where it is weak.
quoteStyle: 'us' for “”."a 5" hole") is one: the engine closes the quotation early, at the inch mark. Such cases are listed in the known limits.“Typography is what language looks like. A straight quote, a hyphen standing in for a dash, a number torn from the word it counts: each is a small lie about the text, and a reader feels all of them at once without being able to name one.”
Where each decision came from, and what a reader should know about the marks themselves. Written while building the engine.
· 3 min read
Alt codes on the numeric keypad, the Win + . symbol panel, and Word’s own shortcuts — three ways to type every dash, quote and sign, with the full code table.
· 3 min read
Replacing straight quotes with curly ones looks like a dozen regexes. Here is what the dozen regexes get wrong, and what an engine has to do instead.
· 3 min read
The most useful typographic character is invisible. Where a no-break space belongs, why CSS cannot place it for you, and the rules that set it.
Typography Engine is a solo project by Oleg Mokhniuk: the rules, the language packs, the docs and this site are one person’s work.
If it is useful and you want to give something back, skip the coffee. The best way to support this project is a donation to Come Back Alive, the Ukrainian foundation that equips the people defending Ukraine.
Or support the author directly: paypal.me/mokhniuk.