From pretext
Guides integration of @chenglou/pretext for measuring text dimensions without DOM reflows, covering API usage, auto-fit fonts, obstacle-aware layouts, masonry, and gotchas.
How this skill is triggered — by the user, by Claude, or both
Slash command
/pretext:pretextThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
You are helping a developer use **@chenglou/pretext** — a 15KB TypeScript library by Cheng Lou that computes exact text metrics using pure math (no DOM reflows). It uses `CanvasRenderingContext2D.measureText` internally, segments text, measures once, caches, then does arithmetic for all subsequent layouts.
You are helping a developer use @chenglou/pretext — a 15KB TypeScript library by Cheng Lou that computes exact text metrics using pure math (no DOM reflows). It uses CanvasRenderingContext2D.measureText internally, segments text, measures once, caches, then does arithmetic for all subsequent layouts.
Use Pretext when the developer needs to:
fillTextlayout() call is ~0.0002ms after the first prepare() per fontmeasureElement correction loops cause desyncing. For <500 items, just render all and use CSS transitions. For 1000+, use Pretext estimates as seeds but rely on DOM correctionvisibility: hidden; position: absolute)npm install @chenglou/pretext
import { prepare, layout } from '@chenglou/pretext';
// 1. Prepare a text+font pair (measures & caches internally)
const prepared = prepare('Hello world', '16px Inter');
// 2. Layout at any width — returns height and line count
const result = layout(prepared, 400, 24); // maxWidth=400, lineHeight=24px
// → { lineCount: 1, height: 24 }
// Reuse for different widths (instant — pure arithmetic)
const narrow = layout(prepared, 120, 24); // → more lines, taller
These are the bugs that will waste your time if you don't know about them. Read this section before writing any Pretext code.
layout() expects lineHeight in CSS pixels, not a multiplier. This is the #1 integration bug.
// WRONG — will compute heights ~14x too small (silent error)
layout(prepared, 500, 1.5);
// CORRECT — convert multiplier to pixels
const fontSize = 14;
const lineHeightPx = fontSize * 1.5; // = 21
layout(prepared, 500, lineHeightPx);
The error is silent — Pretext happily computes with lineHeight: 1.5 pixels, producing plausible-looking lineCount values but tiny height values.
// WRONG — arguments swapped
prepare('16px Georgia', 'Hello world');
// CORRECT
prepare('Hello world', '16px Georgia');
You cannot cache a single prepare() token and reuse it for different text. The library caches segment metrics per font string internally, so repeated calls with the same font are fast.
Pretext measures using currently loaded fonts. If you measure before web fonts load, you get fallback font metrics. Either await document.fonts.ready or accept slight inaccuracy.
On macOS, Canvas resolves system-ui to a different optical variant than DOM rendering. Use explicit font names for guaranteed accuracy.
Pretext requires CanvasRenderingContext2D.measureText. It works in all browsers and OffscreenCanvas workers, but NOT in Node.js without node-canvas.
When computing DOM element heights, don't forget border-width. A 1px border adds 2px total (top + bottom). Easy to miss, causes cumulative drift in layouts.
Start here. Match the developer's goal to the right API path — this avoids the most common mistake (using prepare when prepareWithSegments is needed, or vice versa).
| Developer wants to... | prepare variant | layout function |
|---|---|---|
| Get text height/line count at a given width | prepare | layout |
| Auto-fit font size (binary search over sizes) | prepare (in a loop) | layout |
| Auto-height a textarea | prepare with { whiteSpace: 'pre-wrap' } | layout |
| Get per-line text content (render, animate) | prepareWithSegments | layoutWithLines |
| Find widest line (shrink-wrap containers) | prepareWithSegments | walkLineRanges |
| Flow text around obstacles (variable width/line) | prepareWithSegments | layoutNextLine in a loop |
The key decision is prepare vs prepareWithSegments:
prepare → only gives you layout() (height + line count). Fastest path.prepareWithSegments → gives you ALL layout functions including layout(). Use this the moment you need per-line data or variable widths. There is no reason to call both for the same text.Common API selection mistakes:
prepare then calling layoutWithLines → crashes at runtime (no .segments)layoutWithLines when only widths are needed → walkLineRanges is cheaper (no string allocation)layoutWithLines when width varies per line → must use layoutNextLine insteadprepare on container resize → just call layout again with the new width (it's pure arithmetic)For full signatures, types, and examples, see the API Reference.
For detailed code examples of each pattern, see Patterns Reference. Here's when to reach for each:
Create a thin wrapper that converts lineHeight from CSS multiplier to pixels and returns null on failure. This prevents the critical lineHeight bug and enables progressive enhancement.
Binary search for the largest font that keeps text within N lines. This is Pretext's killer feature — CSS has no equivalent. Use for hero headlines, card titles, quote displays.
Measure variable text parts with Pretext, add fixed parts (padding, border, gaps) manually. Good for simple cards. Remember: this is inherently approximate — don't use for pixel-perfect virtualization.
The creative powerhouse. Feed a different maxWidth per line based on obstacle position. This enables magazine layouts, text flowing around images, and all the impressive community demos.
Always load Pretext as enhancement — the page should work without it. Use type="module" as a natural feature gate.
If you don't use a bundler, bundle Pretext into a single ESM file with esbuild first. It ships as multiple ES modules with relative imports that won't work standalone.
The Patterns Reference also covers creative patterns from the community:
All use the same core API (prepare + layout / layoutNextLine) — the difference is how creatively you use obstacle-aware line routing and per-frame reflow.
prepare() per font: ~1-5ms (measures character segments)prepare() with same font: fast (cached segments)layout() call: ~0.0002ms (pure arithmetic)requestAnimationFrame, scroll handlers, workersnpx claudepluginhub yaniv-golan/pretext-skill --plugin pretextMeasures pixel-accurate single-line widths, multi-line wrapped layouts, and truncates text for Even Realities G2 glasses, matching LVGL rendering. Use for precise text container sizing.
Implements responsive text using rem/em units and flexible layouts to meet WCAG 2.1 AA requirements for text resize, reflow, and text spacing.
Optimizes web font loading with font-display strategies, preloading, subsetting, WOFF2 compression, and the Font Loading API to prevent invisible text and layout shifts.