Height from width
The simplest question you can ask a text engine is the one every card, every chat bubble, every virtualized row depends on: given this text and this width, how tall? In Pretext that question is a single function call, and the answer arrives before the text is painted.
Drag the width below. The box settles exactly to the content — no reflow flash, no overshoot. The height in the corner is Pretext's answer; the box uses it as its own min-height.
I went to the woods because I wished to live deliberately, to front only the essential facts of life, and see if I could not learn what it had to teach, and not, when I came to die, discover that I had not lived.
Mechanism
Once at boot, we call prepare(text, font). Pretext segments the paragraph, measures every segment's width against the browser's canvas font engine, and caches the result inside an opaque handle.
On every width change we call layout(handle, width, lineHeightPx) and read the returned { height, lineCount }. No DOM is touched. The returned height is the exact pixel height the browser would have produced at that width — not a rounded estimate, not a probe reflow. We assign it to the container as minHeight before the next paint, so the box knows its size before the text is placed.
This one call — layout() — is the entire primitive for content-aware containers. Everything Arc 2 builds leans on it.
Application
The whole family of "jumpy on mount" patterns collapses into a one-liner:
- A card that knows its height before insertion — no sibling nudge when it lands.
- A chat bubble sized to its message at maximum bubble width, then animated open.
- A virtualized list whose row positions are arithmetic, not cached from mount heights.
- A resize handler that animates the container's height toward the next
layout()value instead of snapping after reflow.
Each of these used to require inserting the element hidden, reading its offsetHeight, removing it, and trusting the read. Now the answer comes back from arithmetic, in the same frame you asked.
"I went to the woods because I wished to live deliberately, to front only the essential facts of life, and see if I could not learn what it had to teach, and not, when I came to die, discover that I had not lived."
Henry David Thoreau, Walden (1854)Direct Claude
height returned by layout() as the container's height
"snap to content — no reflow flash"
→
set minHeight from layout() before inserting the text
"card should know its size before mount"
→
prepare() the body, layout() at the target width, render pre-sized
"animate the box opening to the message"
→
transition height toward the value layout() just returned
import { prepare, layout } from '@chenglou/pretext';
// Once, at boot. Keep the font string identical to the CSS you'll render.
const FONT = "400 20px 'EB Garamond', serif";
const LINE_HEIGHT = 20 * 1.55;
const handle = prepare(TEXT, FONT);
// On every width change. No DOM reads.
widthSlider.addEventListener('input', (e) => {
const width = parseFloat(e.target.value);
const { height, lineCount } = layout(handle, width, LINE_HEIGHT);
textRegion.style.width = width + 'px';
textRegion.style.minHeight = height + 'px'; // settles before paint
heightBadge.textContent = `height · ${Math.round(height)}px`;
});