Hi! I’ve also been confused.
The cause is that an accent is composed of several small glyphs provided by the font, and Typst fails to construct some width ranges from those glyphs.
Well, I’m not sure about the details, but DeepWiki (an AI/LLM that has access to Typst’s code base) may give you a feel of it.
DeepWiki answer
What you’re observing is the normal behavior of math accent stretching in Typst, where different accent glyphs switch to larger variants at different size thresholds based on font-specific data.
Why the “trigger” sizes differ
The size thresholds you’re seeing (111%, 112%, 113%, 114%) are determined by the math font’s glyph assembly rules, not by Typst’s code. When you specify a size for an accent, Typst uses this to calculate a target width for stretching the accent glyph 1 . The actual rendering depends on how the font provides stretched variants of that particular accent character.
Different accent characters (arrow, harpoon, dash) have different stretching behaviors defined in the font’s math table:
- Some accents may have multiple size variants available
- Others may only have a few discrete sizes
- The thresholds for switching between variants are font-specific
How accent stretching works
- The
size parameter is resolved to a target width relative to the base 1
- This width is used to set stretch information with a
short_fall constant of 0.5em 2
- During layout, the accent is stretched relative to the base width and font size 3
The discrete steps you see occur because the font only provides specific glyph variants at certain sizes, rather than continuously scalable glyphs.
Notes
- This behavior is font-dependent. Different math fonts may show different threshold behaviors
- The
ACCENT_SHORT_FALL constant (0.5em) affects how much shorter an accent can be compared to its base 2
- Test cases in the codebase demonstrate various accent sizing behaviors, including sized accents in script style 4
Citations
File: crates/typst-library/src/math/ir/resolve.rs (L300-301)
let width = elem.size.resolve(styles);
accent.set_stretch(Stretch::new().with_x(StretchInfo::new(width, ACCENT_SHORT_FALL)));
File: crates/typst-library/src/math/accent.rs (L19-20)
/// How much the accent can be shorter than the base.
pub const ACCENT_SHORT_FALL: Em = Em::new(0.5);
File: crates/typst-layout/src/math/accent.rs (L30-31)
accent.set_stretch_relative_to(base.width(), Axis::X);
accent.set_stretch_font_size(size, Axis::X);
File: tests/suite/math/accent.typ (L33-39)
--- math-accent-sized paged ---
// Test accent size.
$tilde(sum), tilde(sum, size: #50%), accent(H, hat, size: #200%)$
--- math-accent-sized-script paged ---
// Test accent size in script size.
$tilde(U, size: #1.1em), x^tilde(U, size: #1.1em), sscript(tilde(U, size: #1.1em))$
Perhaps the behavior can be improved?