typography tutorial \u00b7 intermediate · 25 min
Thai + Latin: Bilingual Typography Guide

What you’ll learn
Bilingual Thai-Latin typography fails and succeeds on six specific technical decisions: line-height ratio, x-height matching, font pairing, character spacing, numeral handling, and web font delivery. This guide covers all six with working specifications. It is for designers producing bilingual work — Thai products with English touchpoints, international products launching in Thailand, or any editorial context with both scripts — who want the complete checklist rather than the intuitive approximation.
The vertical problem
Thai occupies four vertical tiers (below-baseline descender, consonant baseline, above-consonant vowel, tone mark) while Latin occupies two (descender, baseline/x-height). This single structural difference drives every bilingual line-height and spacing decision. When a Latin designer sets Thai at Latin line-height (1.2–1.4), tone marks on one line collide with descenders on the line above. The fix is simple and rigid: Thai line-height is always 10–15% taller than the equivalent Latin line-height (Google Fonts Thai Typography Primer, 2024).
Working numbers: if your Latin body is 16 px / 24 px (1.5), your Thai body is 16 px / 26 px (1.625). If your Latin display is 48 px / 56 px (1.17), your Thai display is 48 px / 72 px (1.5). The display ratio difference is larger because display type’s tone marks are proportionally larger and require more headroom. These numbers are minimums; pushing Thai line-height further (1.7, 1.8) is acceptable for long-form reading.
In CSS, the recommended pattern is per-language selectors:
html[lang="th"] body { line-height: 1.625; font-size: 17px; }
html[lang="en"] body { line-height: 1.5; font-size: 16px; }
The size increase (17 px Thai vs 16 px Latin) handles the optical-size problem discussed below.
The optical-size problem
Thai glyphs look optically smaller than Latin glyphs at the same point size because the consonant body occupies only about 50% of the em-square, with the remaining 50% split between above-vowel and below-vowel tiers. At 16 px Latin, the Latin cap-height is roughly 11 px and x-height 8 px. At 16 px Thai, the consonant body is roughly 8 px — visibly smaller than Latin lowercase at the same setting. Cadson Demak’s bilingual specifications (Typotheque Journal, 2022) recommend increasing Thai body text 5–10% to reach optical parity.
Practically: Latin 14 px becomes Thai 15 px. Latin 16 px becomes Thai 17 px or 18 px. Latin 48 px display can stay 48 px in Thai if the specific typeface has a taller consonant body; most display fonts (Kanit Bold, Prompt Black) run large enough that no adjustment is needed at display sizes.
The x-height matching rule
When pairing a Latin typeface with a Thai typeface, the critical measurement is x-height match within 5%: the Latin x-height and the Thai consonant body height must visually align at the same point size. This is Winitchaikul’s (2018) working rule. Mismatched x-heights produce pairings that look off even when everything else is correct — the eye picks up the vertical inconsistency immediately.
The current canonical bilingual pairings all pass the x-height test:
- Sarabun + Inter — both 500-em body heights, matched perfectly. Default recommendation for UI body.
- Kanit + Roboto — both geometric, both ~510-em. Consumer product default.
- Prompt + Inter — modern loopless Thai + Inter. SaaS default.
- IBM Plex Thai + IBM Plex Sans — designed as a family, perfect match. Corporate default.
- Noto Sans Thai + Noto Sans — Google’s matched pair, the web default.
- Sarabun + Source Serif (for serif needs) — 500-em matched.
- Charmonman + Dancing Script — handwritten pairing, both informal.
Pairings to avoid: any Latin serif with x-height below 450-em (Garamond, Baskerville) paired with any modern loopless Thai. The x-height mismatch is visible and unfixable.
Spacing and tracking
Thai running text is set with zero letter-spacing; positive tracking on Thai body breaks vowel-consonant binding and produces effective gibberish. The Latin convention of adding slight positive tracking for readability at small sizes does not apply to Thai. The vowels and tone marks need their exact designed proximity to consonants or the syllable structure breaks visually.
For all-caps-equivalent Thai (all-consonant headlines without vowels — a stylistic display treatment), positive tracking is acceptable at 20–50 units. For any Thai text containing vowels, tracking stays at 0.
Word spacing works differently: Thai has no interword spaces in the source text, but it does have word boundaries that readers parse implicitly. Use a Thai-aware word-break library (ICU BreakIterator or libthai) or insert zero-width spaces (U+200B) at known word boundaries in source content. Default CSS word-break behaviour breaks mid-word in Thai; set word-break: normal with overflow-wrap: break-word and let a segmentation library handle real line breaking.
Numerals — Thai versus Arabic
Thai numerals (๐ ๑ ๒ ๓ ๔ ๕ ๖ ๗ ๘ ๙) and Arabic numerals (0-9) are both in active use, but in different contexts. Ceremonial, royal, religious, and formal documents use Thai numerals; commercial, technical, and everyday text uses Arabic numerals. The Royal Institute of Thailand’s usage guidelines (2011) are explicit on this distinction.
For design decisions:
- Wedding invitations, temple signage, royal announcements, Buddhist calendar dates — Thai numerals.
- Receipts, prices, product dimensions, phone numbers, URLs, dates on product packaging — Arabic numerals.
- Mixed contexts (a wedding invitation with an RSVP phone number) — Thai numerals for the ceremony date, Arabic for the phone number.
- UI contexts — always Arabic numerals. Every Thai consumer app ships with Arabic numerals.
The rule is never mix Thai and Arabic numerals within the same logical element. พ.ศ. ๒๕๖๙ / CE 2026 is correct; พ.ศ. 2569 / CE 2026 reads as a copy-paste error.
Web implementation
The production web pattern for bilingual Thai-Latin sites uses CSS unicode-range subsetting to serve Thai-specific font files for Thai codepoints and Latin-specific files for Latin codepoints, preventing users from downloading unused glyphs. MDN’s unicode-range documentation (2024) covers the syntax; the working Thai pattern is:
@font-face {
font-family: 'Sarabun';
src: url('/fonts/sarabun-thai.woff2') format('woff2');
font-weight: 400;
font-display: swap;
unicode-range: U+0E01-0E5B, U+200C-200D, U+25CC;
}
@font-face {
font-family: 'Sarabun';
src: url('/fonts/inter-latin.woff2') format('woff2');
font-weight: 400;
font-display: swap;
unicode-range: U+0000-007F, U+2000-206F;
}
body {
font-family: 'Sarabun', system-ui, sans-serif;
font-size: 17px;
line-height: 1.625;
}
The browser fetches only the Thai file when rendering Thai characters, only the Latin file when rendering Latin characters. For Thai-primary sites preload the Thai file explicitly:
<link rel="preload" as="font" type="font/woff2"
href="/fonts/sarabun-thai.woff2" crossorigin>
Skip the preload on Latin-primary sites — the Thai file fetches lazily when needed.
Bilingual layout patterns
Three layout patterns handle most bilingual Thai-Latin content: parallel columns, vertically stacked, and inline dominant-language. The right choice depends on whether the two scripts carry the same meaning and which audience is primary.
- Parallel columns — Thai left, Latin right, equal visual weight. Used for formal invitations, government documents, bilingual brochures where both languages serve distinct audiences.
- Vertically stacked — Thai first, Latin below (smaller and lighter). Used for Thai-primary products where English is accommodation, not peer. Bangkok street signage, Thai restaurant menus in Thailand, Thai branding for local products.
- Inline dominant-language — one script is primary, the other appears only for proper nouns or specific terms. Used for editorial content where the audience reads one language and the other appears as quotation or transliteration.
The layout pattern is the first decision in bilingual design — every subsequent typography choice flows from it. Parallel columns require x-height matching above all. Stacked layouts allow the secondary language to use a different typeface entirely. Inline mixed content requires font-family fallback chains that handle both scripts in one string.
Keep reading
The Thai + Latin font pairings page lists specific recommended pairings with x-height data. The pillar Complete Guide to Thai Typography covers the writing system itself. The Loopless Revolution explains why loopless Thai pairs more cleanly with Latin than looped Thai does. Individual fonts most relevant here: Sarabun, Kanit, IBM Plex Thai, Noto Sans Thai.
Information verified as of April 2026
Sources
- Thai text requires a line-height 10–15% taller than equivalent Latin text because Thai combines up to four vertical tiers of glyphs: descender, consonant, above-vowel, and tone-mark.—Google Fonts — Thai Typography Primer (2024) (accessed Apr 5, 2026)
- Optimal bilingual pairing matches x-heights within 5% — Sarabun at 500-em x-height pairs cleanly with Inter at 510-em but clashes with Garamond at 420-em.—Winitchaikul, P. (2018). Thai Letterform Construction. Silpakorn University Press. (accessed Apr 4, 2026)
- Thai characters are optically smaller than Latin at the same point size; designers should increase Thai body text by 5–10% to reach optical parity.—Cadson Demak — Bilingual Typography Specifications, Typotheque Journal, 2022 (accessed Apr 2, 2026)
- CSS unicode-range subsetting allows a single @font-face rule to serve Thai-specific font files for Thai codepoints (U+0E01-0E5B) and Latin-specific files for Latin codepoints, optimising load performance.—MDN Web Docs — CSS @font-face unicode-range (2024) (accessed Apr 6, 2026)
- Thai numerals (๐-๙) are retained for ceremonial, royal, and religious contexts while Arabic numerals (0-9) are the standard for commercial, technical, and everyday text.—Royal Institute of Thailand — Thai Language Usage Guidelines, 2011 edition (accessed Apr 7, 2026)