Docs Reference
Rules
The engine has 33 rules, 22 on by default (the rest are off until named in enable). typographic runs them in the order below, each on the output of the one before. An id can be switched off with disable (see api.md); a group name switches off every rule that starts with it (space is every space.* rule, dash every dash.*, symbol every symbol.*).
This file is generated by bun run docs:rules from RULES in src/core/rules.ts and from the examples in scripts/gen-rules-doc.ts. Do not edit it by hand: src/docs.test.ts fails when it is out of date.
Each example runs the engine with every rule except the one shown switched off, so it shows what that rule does and nothing else. In a full run the other rules also act (an English " is turned into a quote by quote before anything else sees it). The text of each example is plain; a no-break space is shown as ~ (the character U+00A0 in the real output; an ordinary space stays a space). Text in a code span, a web address and an email are never changed by any rule.
Categories
Each rule has a category that says what it is for; the id names the mechanism and is what disable and enable take. A category is a label only: it is not a group name for disable.
tradition: the conventions of setting prose: quotation marks, the apostrophe, dashes, the ellipsis, punctuation and the spaces around it, and what must not be split at a line endsymbols: signs typed as ASCII stand-ins that have a character of their own: arrows, ©, ®, ™math: signs of arithmetic and logic: minus, ×, ±, fractions, ⇒data: numbers with what they measure: digit groups, ranges, units, percentages and currencies, dates
Order
| # | Rule | Category | Default |
|---|---|---|---|
| 1 | quote |
tradition | on |
| 2 | apostrophe |
tradition | on |
| 3 | ellipsis |
tradition | on |
| 4 | space.before-punctuation |
tradition | off |
| 5 | space.after-comma |
tradition | off |
| 6 | punctuation.double |
tradition | off |
| 7 | arrow |
symbols | on |
| 8 | symbol.implies |
math | off |
| 9 | symbol.legal |
symbols | on |
| 10 | dash.dialogue |
tradition | on |
| 11 | phone.bind |
data | off |
| 12 | number.group |
data | off |
| 13 | dash.range |
data | on |
| 14 | dash.sentence |
tradition | on |
| 15 | symbol.plusminus |
math | off |
| 16 | symbol.minus |
math | on |
| 17 | symbol.multiply |
math | on |
| 18 | symbol.fraction |
math | off |
| 19 | space.collapse |
tradition | on |
| 20 | space.title |
tradition | on |
| 21 | space.initial |
tradition | on |
| 22 | space.abbreviation |
tradition | on |
| 23 | space.number |
data | on |
| 24 | space.percent |
data | on |
| 25 | symbol.degree |
data | off |
| 26 | space.section |
symbols | on |
| 27 | space.association |
tradition | on |
| 28 | space.copyright |
symbols | on |
| 29 | space.operator |
symbols | on |
| 30 | space.short-word |
tradition | off |
| 31 | space.orphan |
tradition | on |
| 32 | space.arrow |
symbols | on |
| 33 | space.trailing |
tradition | off |
quote
Quotation marks by language, register and nesting level, from any source style
Category: tradition.
Why: sources.md, disputed.
Characters it can insert, in any language: « » „ “ ‚ ‘ ’ ” › ‹ ~. A font that lacks one of them is handled by avoid (see api.md).
- Before
- He said
"stone"and left. - After
- He said «stone» and left.
Display register, the default.
- Before
- He said
"we said'no'twice"and left. - After
- He said «we said „no“ twice» and left.
Nesting level two. Source marks may be straight, curly or guillemets.
- Before
- He said
"we said'no'twice"and left. - After
- He said ‘we said “no” twice’ and left.
British English in the literary register.
- Before
- Er sagte
"Sie rief'Haus'laut"und ging. - After
- Er sagte »Sie rief ›Haus‹ laut« und ging.
apostrophe
A straight ' in a word or before a year becomes ’
Category: tradition.
Why: sources.md, verified.
Characters it can insert, in any language: ’. A font that lacks one of them is handled by avoid (see api.md).
- Before
- It
's Maria's turn, don't wait. - After
- It’s Maria’s turn, don’t wait.
Inside a word.
- Before
- The
'90s were loud. - After
- The ’90s were loud.
Before a year.
ellipsis
… becomes …
Category: tradition.
Why: sources.md, verified.
Characters it can insert, in any language: …. A font that lacks one of them is handled by avoid (see api.md).
- Before
- Wait
...what? - After
- Wait… what?
- Before
- Wait... what?
- After
- Wait... what?
With the ellipsis in avoid (a font that cannot draw it), the text stays as typed.
space.before-punctuation
Removes a space before , . ; : ! ? ) (French keeps the space before : ; ! ?)
Category: tradition.
Why: sources.md, general-knowledge.
Off by default: switch it on with enable (--enable on the command line).
- Before
- Hello
, world. Is it ready? (yes) - After
- Hello, world. Is it ready? (yes)
Off by default.
- Before
- Oui : non
, bon ! - After
- Oui : non, bon !
French keeps its space before : ; ! ?, the rest is tidied.
space.after-comma
A comma followed directly by a letter gets a space (a,b becomes a, b)
Category: tradition.
Why: sources.md, general-knowledge.
Off by default: switch it on with enable (--enable on the command line).
- Before
- apples,pears,plums
- After
- apples, pears, plums
Digits are left alone: 1,5 stays.
- Before
- Hallo,Welt und 3,14
- After
- Hallo, Welt und 3,14
punctuation.double
A run of ! or ? becomes one mark (Wow!!! becomes Wow!); ?! and !? stay
Category: tradition.
Why: sources.md, general-knowledge.
Off by default: switch it on with enable (--enable on the command line).
- Before
- Wow!
!!Really?? - After
- Wow! Really?
- Before
- What?!! No!
!!? Stop!!! - After
- What?!! No!? Stop!
?! and !? stay as a pair; a run beside one collapses.
arrow
→ and ← become → and ←
Category: symbols.
Why: sources.md, general-knowledge.
Characters it can insert, in any language: → ←. A font that lacks one of them is handled by avoid (see api.md).
- Before
- Read more
-> - After
- Read more →
- Before
<-Back- After
- ← Back
symbol.implies
=> becomes ⇒
Category: math.
Why: sources.md, general-knowledge.
Off by default: switch it on with enable (--enable on the command line).
Characters it can insert, in any language: ⇒. A font that lacks one of them is handled by avoid (see api.md).
- Before
- A
=>B - After
- A ⇒ B
- Before
- if x
=>y then z - After
- if x⇒y then z
Not =>>, ==>, =>= or <=>.
symbol.legal
(c) before a year, ©, ® and ™ become © ® ™
Category: symbols.
Why: sources.md, verified.
Characters it can insert, in any language: © ® ™. A font that lacks one of them is handled by avoid (see api.md).
- Before
(c)2026 Studio(R)(TM)- After
- © 2026 Studio ® ™
(c) needs a year after it; © does not.
dash.dialogue
A paragraph opening with a dash is a line of dialogue, in languages that write dialogue that way: their dash and a no-break space (Ukrainian — Привіт)
Category: tradition.
Why: sources.md, verified.
Characters it can insert, in any language: — ~ –. A font that lacks one of them is handled by avoid (see api.md).
- Before
-Привіт, - сказав він.- After
- — Привіт, - сказав він.
Only the dash that opens the paragraph; the second one belongs to dash.sentence. Both registers.
- Before
-¿Esperamos? - preguntó.- After
- —¿Esperamos? - preguntó.
Spanish writes the raya attached to the turn.
- Before
-Odotetaanko? kysyi hän.- After
- – Odotetaanko? kysyi hän.
An en dash where the language uses one. A paragraph whose later line also opens with a dash is a list and is left alone.
phone.bind
An international phone number (+ or 00, at least seven digits) is kept whole: no-break spaces between its groups and non-breaking hyphens inside it, with the digits as typed
Category: data.
Why: sources.md, general-knowledge.
Off by default: switch it on with enable (--enable on the command line).
Characters it can insert, in any language: ~ ‑. A font that lacks one of them is handled by avoid (see api.md).
- Before
- Call +380
441234567 or +1(555)123-4567 - After
- Call +380 44 123 45 67 or +1 (555) 123‑4567
Off by default. The digits are not reformatted; the hyphen becomes U+2011, which does not break.
- Before
- Not 555-1234, not 2026-10-20, not DE89 3704 0044 0532 0130 00
- After
- Not 555-1234, not 2026-10-20, not DE89 3704 0044 0532 0130 00
A local number, a date and an IBAN are left alone: the number must open with + or 00 and hold seven to fifteen digits.
number.group
Digits of an integer of five or more are grouped in threes with the language separator (1,000,000; 1 000 000)
Category: data.
Why: sources.md, general-knowledge.
Off by default: switch it on with enable (--enable on the command line).
Characters it can insert, in any language: , ~. A font that lacks one of them is handled by avoid (see api.md).
- Before
- We sold
1234567copies in 2026. - After
- We sold 1,234,567 copies in 2026.
A run of five or more digits; years are left.
- Before
- Wir verkauften
1234567Exemplare, Pi ist 3,14159265. - After
- Wir verkauften 1 234 567 Exemplare, Pi ist 3,14159265.
A no-break space; the fraction is left alone.
- Before
- Продано
12345примірників, код v1000000. - After
- Продано 12 345 примірників, код v1000000.
A name glued to digits is left.
dash.range
A hyphen between two numbers (21–26, 10:00–12:00) becomes an unspaced en dash
Category: data.
Why: sources.md, general-knowledge.
Characters it can insert, in any language: –. A font that lacks one of them is handled by avoid (see api.md).
- Before
- Open 21
-26 September, 10:00-12:00. - After
- Open 21–26 September, 10:00–12:00.
- Before
- Pages 5
-9 - After
- Pages 5–9
Spaced, too.
dash.sentence
A spaced hyphen, dash or double hyphen becomes a spaced dash (em; en in German and literary British English), no-break before it
Category: tradition.
Why: sources.md, verified.
Characters it can insert, in any language: — ~ –. A font that lacks one of them is handled by avoid (see api.md).
- Before
- Wait
-what happened--really? - After
- Wait — what happened — really?
An em dash in English.
- Before
- Warte
-was ist passiert? - After
- Warte – was ist passiert?
A spaced en dash in German.
- Before
- Wait
-what happened? - After
- Wait – what happened?
A spaced en dash in British English, literary register.
symbol.plusminus
+- before a digit becomes ± (+-5 becomes ±5)
Category: math.
Why: sources.md, general-knowledge.
Off by default: switch it on with enable (--enable on the command line).
Characters it can insert, in any language: ±. A font that lacks one of them is handled by avoid (see api.md).
- Before
- Tolerance
+-5 mm - After
- Tolerance ±5 mm
- Before
- (
+-0.2) and+-10% - After
- (±0.2) and ±10%
Not a+-b or 1+-5.
symbol.minus
A hyphen before a digit (−5) becomes a minus sign (−5)
Category: math.
Why: sources.md, general-knowledge.
Characters it can insert, in any language: −. A font that lacks one of them is handled by avoid (see api.md).
- Before
- It was
-5 degrees (-3 inside). - After
- It was −5 degrees (−3 inside).
symbol.multiply
The letter x between numbers (1920×1080, 3 × 4) becomes ×
Category: math.
Why: sources.md, verified.
Characters it can insert, in any language: ×. A font that lacks one of them is handled by avoid (see api.md).
- Before
- A
1920x1080screen, a 3x4 grid. - After
- A 1920×1080 screen, a 3 × 4 grid.
symbol.fraction
1/2, 1/4 and 3/4 become ½ ¼ ¾, as whole numbers only (not 11/2, 1/20 or a date)
Category: math.
Why: sources.md, general-knowledge.
Off by default: switch it on with enable (--enable on the command line).
Characters it can insert, in any language: ½ ¼ ¾. A font that lacks one of them is handled by avoid (see api.md).
- Before
- Add
1/2cup and3/4of the milk, then1/4. - After
- Add ½ cup and ¾ of the milk, then ¼.
Only whole numbers: 11/2, 1/20, a1/2 and a date such as 1/2/2026 stay. A short word after the fraction gets the no-break space.
- Before
- Take 2
1/2hours - After
- Take 2 ½ hours
A mixed number keeps its integer: 2 ½.
space.collapse
A run of spaces becomes one
Category: tradition.
Why: sources.md, verified.
- Before
- Two
spaces,four. - After
- Two spaces, four.
space.title
No-break space after Prof., Dr., Mr. …
Category: tradition.
Why: sources.md, verified.
Characters it can insert, in any language: ~. A font that lacks one of them is handled by avoid (see api.md).
- Before
- Prof.
Dr.Oksana opens. - After
- Prof. Dr. Oksana opens.
space.initial
No-break space after an initial (K. Słoboda)
Category: tradition.
Why: sources.md, general-knowledge.
Characters it can insert, in any language: ~. A font that lacks one of them is handled by avoid (see api.md).
- Before
- Written by K.
Słoboda. - After
- Written by K. Słoboda.
space.abbreviation
Parts of an abbreviation stay together (z. B.), and Nr., ul., S. stay with what follows
Category: tradition.
Why: sources.md, verified.
Characters it can insert, in any language: ~. A font that lacks one of them is handled by avoid (see api.md).
- Before
- z.
B. Nr.5 auf S.12 - After
- z. B. Nr. 5 auf S. 12
- Before
- вул.
Хрещатик, т.д. - After
- вул. Хрещатик, т. д.
- Before
- See Fig.
2 and p.14. - After
- See Fig. 2 and p. 14.
space.number
No-break space between a number and a short word or unit after it, up to nine letters (21 September, 5 %); money stays whole: the sign or code before the amount (USD 5 000, € 49.99) and the digit groups of an amount written in threes (1 000 грн)
Category: data.
Why: sources.md, verified.
Characters it can insert, in any language: ~. A font that lacks one of them is handled by avoid (see api.md).
- Before
- On 21
September, 15people paid 5%. - After
- On 21 September, 15 people paid 5 %.
space.percent
German, French, Czech, Spanish, Italian, Portuguese, Irish, Maltese, Bulgarian, Croatian, Slovenian, Lithuanian, Latvian, Estonian, Danish, Finnish and Greek: a no-break space between a number and % (5 %); German, French, Czech, Spanish, Italian, Bulgarian, Croatian, Slovenian, Danish, Finnish and Greek: also before € after the number (49 €)
Category: data.
Why: sources.md, verified.
Characters it can insert, in any language: ~. A font that lacks one of them is handled by avoid (see api.md).
- Before
- 5% and 49€
- After
- 5 % and 49 €
The rule does nothing in other languages.
- Before
- 5% and 49€
- After
- 5 % and 49 €
symbol.degree
20 C, 20°C and 20 °C become 20 °C, with a no-break space before the unit
Category: data.
Why: sources.md, verified.
Off by default: switch it on with enable (--enable on the command line).
Characters it can insert, in any language: ° ~. A font that lacks one of them is handled by avoid (see api.md).
- Before
- It was 20°C, then 68
F and 20C. - After
- It was 20 °C, then 68 °F and 20 °C.
Every spelling becomes number, no-break space, °C or °F.
- Before
- Vitamin C 5 C and 5
°C - After
- Vitamin C 5 C and 5 °C
A bare C or F after a label or a single letter is not a temperature.
space.section
No-break space after § and ¶, doubled or not, before a number
Category: symbols.
Why: sources.md, verified.
Characters it can insert, in any language: ~. A font that lacks one of them is handled by avoid (see api.md).
- Before
- See §
5 and ¶¶3, 4 - After
- See § 5 and ¶¶ 3, 4
space.association
No-break space before e.V.
Category: tradition.
Why: sources.md, general-knowledge.
Characters it can insert, in any language: ~. A font that lacks one of them is handled by avoid (see api.md).
- Before
- Kulturverein
e.V. - After
- Kulturverein e.V.
space.copyright
No-break space after ©
Category: symbols.
Why: sources.md, verified.
Characters it can insert, in any language: ~. A font that lacks one of them is handled by avoid (see api.md).
- Before
- ©
2026 Studio - After
- © 2026 Studio
space.operator
No-break spaces around & + = and ’n’ standing alone between two words (Tom & Jerry, rock ’n’ roll)
Category: symbols.
Why: sources.md, general-knowledge.
Characters it can insert, in any language: ~. A font that lacks one of them is handled by avoid (see api.md).
- Before
- Tom
&Jerry, rock+roll - After
- Tom & Jerry, rock + roll
- Before
- rock
’n’roll - After
- rock ’n’ roll
The ’n’ of rock ’n’ roll is an “and” with its letters dropped, so it is bound like one; quote sets the two marks curly before this rule runs.
space.short-word
English, Ukrainian, Polish, Czech and Slovak: a short word of two or three letters stays with the word after it (to~the shop), for titles and headings; never more than two words in a row
Category: tradition.
Why: sources.md, general-knowledge.
Off by default: switch it on with enable (--enable on the command line).
Characters it can insert, in any language: ~. A font that lacks one of them is handled by avoid (see api.md).
- Before
- Welcome to
the city oflight - After
- Welcome to the city of light
For titles and headings; off by default. Never more than two words in a row, so a chain cannot grow into a line.
- Before
- Zapraszamy do
Gdańska - After
- Zapraszamy do Gdańska
- Before
- Ласкаво просимо до
міста - After
- Ласкаво просимо до міста
space.orphan
No-break space after a one-letter word the language must not strand at a line end
Category: tradition.
Why: sources.md, verified.
Characters it can insert, in any language: ~. A font that lacks one of them is handled by avoid (see api.md).
- Before
- A
cat and I - After
- A cat and I
- Before
- Він і
я вдомі - After
- Він і я в домі
- Before
- Idę w
las iznim - After
- Idę w las i z nim
space.arrow
No-break space before → and after ←
Category: symbols.
Why: sources.md, general-knowledge.
Characters it can insert, in any language: ~. A font that lacks one of them is handled by avoid (see api.md).
- Before
- Read more
→ - After
- Read more →
- Before
- ←
Back - After
- ← Back
space.trailing
Spaces and tabs at the end of a line are removed
Category: tradition.
Why: sources.md, general-knowledge.
Off by default: switch it on with enable (--enable on the command line).
- Before
- First line
Second lineThird - After
- First line Second line Third
Spaces and tabs at the end of a line; the lines stay.
- Before
- One trailing space
- After
- One trailing space
Something wrong or unclear here? Report an issue.