Typography
Style text, control how it wraps, truncates, and fits, and set its direction and language in Takumi.
Style text with color, stroke, decoration, shadow, and spacing, then control how it wraps, truncates, and fits its container. This page also covers right-to-left text and language-aware shaping. Takumi draws text with the fonts you load, so load your fonts first.
Styling text
Color, stroke, and fill
color takes modern CSS color spaces, such as oklch(), lab(), and color(display-p3 ...), as well as rgb() and hsl(). Outline glyphs with -webkit-text-stroke. Set a separate fill with -webkit-text-fill-color. The stroke uses color when you leave its color out.
<div
style={{
color: "oklch(0.7 0.15 200)",
WebkitTextStroke: "2px black",
WebkitTextFillColor: "white",
}}
>
Outlined
</div>Decoration & transform
text-decoration draws underline, line-through, and overline. The shorthand also takes a style, color, and thickness, in any order. text-transform changes the case.
<div
style={{
textDecoration: "underline overline 2px red",
textTransform: "uppercase",
}}
>
Marked up
</div>Shadow
text-shadow takes offsets, a blur radius, and a color. Separate layers with commas.
<div style={{ textShadow: "2px 2px 4px rgba(0,0,0,0.5), 0 0 8px blue" }}>Glow</div>Spacing & alignment
letter-spacing, word-spacing, line-height, text-indent, and text-align work as in a browser, justify included.
<div
style={{
letterSpacing: "0.05em",
lineHeight: 1.4,
textAlign: "justify",
textIndent: "2em",
}}
>
Body copy
</div>Flowing text
Wrapping
text-wrap: balance evens out line lengths. text-wrap: pretty keeps the last line from holding a single word.
<div style={{ textWrap: "balance" }}>Super Long Text</div>word-break and overflow-wrap decide where a long word splits. Use word-break: keep-all to keep CJK (Chinese, Japanese, Korean) words whole, and break-all or overflow-wrap: anywhere to split long URLs.
Truncation
line-clamp cuts text after a number of lines and ends the last line with an ellipsis, as Chrome does. line-clamp: 3 none cuts without one. The legacy -webkit-line-clamp works only with display: -webkit-box and -webkit-box-orient: vertical, as in browsers.
<div
style={{
lineClamp: 3,
}}
>
Super Long Text
</div>text-overflow: ellipsis works only on a box that clips its overflow, as in Chrome, so set overflow: hidden with it. It ends a line that is wider than the box, such as white-space: nowrap text or a word too long to break. Text cut off by the box's height gets no ellipsis, so use line-clamp for that.
Fit to container
text-fit scales text to fit its line box instead of wrapping or overflowing. The value starts with a mode: grow, shrink, or none. An optional target and a percentage cap follow.
<div style={{ textFit: "shrink" }}>Headline that always fits</div>
<div style={{ textFit: "grow per-line 150%" }}>Per-line scaled, capped at 150%</div>The target decides which lines share a scale:
| Target | Effect |
|---|---|
consistent (default) | One scale for the whole block. |
per-line | Scales each line except the last. |
per-line-all | Scales every line. |
The percentage caps how far the scale can go. With text-overflow: ellipsis, Takumi fits the text first. Only a line that still overflows at its fitted size gets an ellipsis, such as a line the cap holds back. The line-clamp count still applies.
RTL & bidirectional text
Takumi runs the Unicode Bidirectional Algorithm, so Arabic and Hebrew text, mixed with left-to-right text, orders correctly. Set the base direction with the dir attribute or direction: rtl. unicode-bidi controls embedding, isolation, and overrides, as in a browser.
<p dir="rtl">مرحبا بالعالم, Hello</p>Language-aware text
The lang attribute sets a node's BCP-47 language. Descendants inherit it, and a nested lang overrides it. Takumi uses it in three places.
- Shaping, where one code point can draw a different glyph for
zh-Hans,zh-Hant,ja, orko(Han unification). - Case mapping, where
text-transformfollows Turkish, Azerbaijani, and Greek rules for those languages. - Selectors, where
:lang()matches it.
<div>
<p lang="ja">日本語</p>
<p lang="zh-Hant">繁體中文</p>
</div>Pass lang to set a default for the whole render. A node's own lang still overrides it.
const image = await render(node, { width: 1200, height: 630, fonts, lang: "ja" });Glyphs change only when the font has per-language variants, such as a pan-CJK font. A font made for one region draws the same glyph for every language. Emoji have their own section in Image loading & emoji.
Last updated on