| name | pretext |
| description | Create text effects impossible with CSS alone โ kinetic typography, flowing text around animated obstacles, calligrams, shrinkwrap chat bubbles, typographic ASCII art, glyph path animation, variable font waves, glyph morphing, illuminated manuscripts. Single-file HTML output, zero build step. Powered by @chenglou/pretext + opentype.js. Triggers on text effects, kinetic typography, text animation, calligram, glyph art, typography art. |
| argument-hint | ["effect description or category"] |
Pretext Text Effects
Generate browser pages powered by Pretext (@chenglou/pretext)โa pure-arithmetic text measurement library that bypasses DOM layout reflow entirely. Pretext measures text with proportional font precision using canvas measureText, enabling effects impossible with CSS alone.
Companion library: opentype.js (opentype.js@1.3.4) โ direct font binary parsing for per-glyph SVG path rendering, Bezier control point access, kerning tables, and variable font axes. Pretext handles line breaking; opentype.js handles what happens inside each glyph. Together they unlock effects neither can achieve alone.
Output is always a single self-contained HTML file. No build step, no framework, runs in any modern browser.
Quick Start
Describe a text effect. Claude picks the right Pretext pattern and generates a complete HTML file.
Examples:
/pretext fluid smoke ASCII art with gold characters on black
/pretext chat bubbles that shrinkwrap tighter than CSS
/pretext calligram โ the word "ocean" shaped like a wave
/pretext editorial layout with text flowing around draggable circles
/pretext masonry grid of shower thoughts with instant height prediction
/pretext glyph path art โ SVG letterforms with stroke animation
/pretext animated dragon cursor that pushes text aside as it moves
/pretext illuminated manuscript with live vine growth reflow
/pretext variable font wave โ per-character weight animation
/pretext glyph morph โ letterform interpolation from A to Z
/pretext letterbox gallery of "BEACON" in Playfair Display italic on dark background
/pretext glyph-mask calligram โ letter R filled with lorem ipsum in Georgia
Do NOT Use This Skill When
- Simple CSS
text-shadow, text-stroke, or gradient text effects
- CSS Shapes level 1 (
shape-outside on floated elements) for static layouts
- Basic
@keyframes text animation (fade, slide, typewriter)
- Monospace ASCII art (no proportional measurement needed)
- SVG
<text> without per-glyph control
- Rich text editing (
contenteditable, ProseMirror, TipTap)
- PDF generation or markdown rendering
These are all achievable without Pretext or opentype.js.
Effect Categories
Pretext-Only Effects
| Category | Pretext APIs Used | When to Use |
|---|
| Height Prediction | prepare + layout | Accordion, masonry, virtualized lists โ anywhere you need text height without DOM reads |
| Shrinkwrap | walkLineRanges + binary search | Chat bubbles, tooltips, labels โ finding the exact tightest width for multiline text |
| Obstacle Routing | layoutNextLine (variable width) | Text flowing around images, logos, draggable orbs โ editorial layouts |
| Animated Obstacles | layoutNextLine + carveTextLineSlots | Moving creatures/orbs that displace text at 60fps โ slot-carving fills BOTH sides of obstacle |
| Typographic ASCII | prepareWithSegments (char measurement) | Fluid simulations, 3D wireframes, particle systems rendered as proportional characters |
| Calligrams | prepareWithSegments + SDF | Words rendered as shapes using their own letters โ hearts, stars, spirals |
| Glyph-Mask Calligrams | Canvas measureText + getImageData pixel mask | Any font glyph as calligram shape โ fill a large letter with small text using pixel-mask technique (no SDF needed, no opentype.js) |
| Letterbox Gallery | Glyph-mask + per-letter <canvas> grid | Each character in a string gets its own canvas with text fill, cursor displacement, and independent interaction |
| Multi-column Editorial | All rich APIs combined | Magazine-style layouts with headline fitting, pull quotes, drop caps, column flow |
Pretext + opentype.js Effects
| Category | APIs Used | When to Use |
|---|
| Glyph Path Art | opentype glyph.getPath() | SVG letterforms with fill/stroke/control point modes, stroke-draw animation |
| Text on Path | opentype getPath() + arc-length sampling | Per-glyph placement along Bezier curves with tangent rotation |
| Variable Font Animation | opentype font.tables.fvar.axes + CSS font-variation-settings | Per-character weight/width waves, breathe, ripple, cascade effects |
| Glyph Morphing | opentype paths + flubber interpolate() | Letterform interpolation between glyphs with contour-aware morphing |
| Outline Calligrams | Pretext layoutNextLine + opentype glyph mask | Text fills the interior of a large glyph's actual contour (not SDF approximation) |
| Illuminated Manuscript | All Pretext + opentype combined | Living medieval pages: wet ink, vine reflow, capital inflation, aging, erasure poetry |
Concept-to-Effect
Every design decision derives from the concept. Do not defaultโderive.
Choose the API tier from the effect complexity:
- Static height only โ
prepare() + layout() (fastest, opaque)
- Need line text/positions โ
prepareWithSegments() + layoutWithLines()
- Need per-line width variation โ
layoutNextLine() (iterator, variable width per line)
- Need aggregate geometry without strings โ
walkLineRanges() (no string materialization)
- Need individual character widths โ
prepareWithSegments() on single chars
Choose the rendering target from the output type:
- DOM elements (
.line { position: absolute }) โ editorial, accordion, masonry
- Canvas 2D (
ctx.fillText) โ calligrams, some ASCII art
- HTML spans with inline styles โ typographic ASCII (weight/style/opacity per character)
Architecture
Pretext CDN โ prepare/prepareWithSegments โ layout/layoutNextLine โ line positions
โ
opentype.js CDN โ font.parse(buffer) โ glyph.getPath() โโโโโโโโโโ> SVG <path> elements
โ
requestAnimationFrame (if animated)
resize handler (always)
Two-library split: Pretext decides WHERE text goes (line breaking, obstacle routing). opentype.js decides HOW each glyph looks (SVG paths, Bezier curves, contour data). Use Pretext alone for DOM-positioned text. Add opentype.js when you need per-glyph SVG rendering, path animation, or glyph contour access.
CDN Imports
<script type="module">
import { prepare, layout, prepareWithSegments, layoutWithLines, walkLineRanges, layoutNextLine, clearCache } from 'https://esm.sh/@chenglou/pretext@0.0.2'
</script>
<script src="https://cdn.jsdelivr.net/npm/opentype.js@1.3.4/dist/opentype.min.js"></script>
Import only the functions you need. Pin the version. opentype.js cannot parse .woff2 โ use .woff or .ttf only.
Composition Parameters
All Effects
| Parameter | Default | Notes |
|---|
| Font | '18px Georgia, Palatino, serif' | Never use system-ui โ unreliable with Pretext |
| Line height | 28 (px) | Must match CSS line-height for height prediction |
| Background | #08080e or #f5f1ea | Dark or light โ never pure black #000 |
Typographic ASCII
| Parameter | Range | Default |
|---|
| Font size | 10โ18px | 14 |
| Font family | serif preferred | Georgia, Palatino, "Times New Roman", serif |
| Charset | printable ASCII | .,:;!+-=*#@%&a-zA-Z0-9 |
| Weights | 1โ3 | [300, 500, 800] |
| Styles | normal, italic | both |
| Opacity levels | 6โ10 | 10 CSS classes .a1โ.a10 |
Calligrams
| Parameter | Range | Default |
|---|
| Canvas size | 200โ800px | 400 |
| Char density | 6โ24px | 14 |
| Shapes | heart, circle, star, wave, spiral | heart |
| Animation | spring entrance | springK: 0.08, damping: 0.75 |
Editorial / Obstacle Routing
| Parameter | Range | Default |
|---|
| Columns | 1โ4 | 2 |
| Column gap | 20โ60px | 40 |
| Gutter | 30โ80px | 48 |
| Orb count | 1โ6 | 5 |
| Orb radius | 30โ120px | 65โ110 |
| hPad (horizontal padding around obstacle) | 8โ20px | 14 |
| vPad (vertical padding) | 2โ8px | 4 |
| MIN_SLOT_WIDTH | 30โ80px | 50 |
opentype.js โ Glyph Path Effects
| Parameter | Range | Default |
|---|
| Font URL | .woff or .ttf only | @fontsource/inter@5.0.8 |
| Font size | 10โ200px | 14 (body), 42 (display) |
| Stroke-dashoffset draw speed | 30โ150ms per glyph | 80ms |
| Wet ink amplitude | 0.5โ3px | 1.8 |
| Wet ink decay (tau) | 800โ3000ms | 1500 |
| Glyph morph easing | linear, ease-in-out, spring | ease-in-out |
Glyph-Mask Calligrams / Letterbox Gallery
| Parameter | Range | Default |
|---|
| Canvas size (per letter) | 200โ1000px | 500 |
| Fill font size | 8โ24px | 11 |
| Fill model | self, lorem, alphabet, custom | lorem |
| Fill case | upper, lower, mixed | upper |
| Fill columns | 1โ4 | 1 |
| Grid columns (gallery) | 2โ6 | 3 |
| Cursor displacement radius | 50โ250px | 100 |
| Cursor displacement force | 10โ50 | 35 |
| Displacement damping | 0.85โ0.98 | 0.94 |
opentype.js + flubber โ Glyph Morphing
| Parameter | Range | Default |
|---|
| Morph duration | 300โ2000ms | 800 |
| Flubber maxSegmentLength | 5โ20 | 10 |
| Contour strategy | equal, 1-to-many, many-to-1 | auto-detect |
References
| Working on... | Load |
|---|
| Full API surface, types, caveats | references/api-reference.md |
| Typographic palette, brightness, ASCII grid | references/typographic-ascii.md |
| Column flow, obstacles, headline fitting, slot-carving, animated physics | references/obstacle-routing.md |
| SDF shapes, proportional spacing, animation | references/calligram-shapes.md |
| Pixel-mask technique, glyph-mask calligrams, cursor displacement, letterbox gallery | references/calligram-shapes.md (Pixel-Mask section) |
| opentype.js + Pretext integration patterns | references/opentype-integration.md |
| Working accordion template | assets/templates/height-prediction.html |
| Working bubbles template | assets/templates/shrinkwrap-bubbles.html |
| Working editorial template | assets/templates/editorial-engine.html |
| Working ASCII art template | assets/templates/typographic-ascii.html |
| Working calligram template | assets/templates/calligram.html |
| Working letterbox gallery template | assets/templates/letterbox-gallery.html |
| GlyphKit demos (6 working demos, local) | ~/Desktop/Programming/glyphkit/demos/ (machine-specific) |
Anti-Patterns
Pretext
- Never use
system-ui as font โ canvas and DOM can resolve different fonts on macOS
- Never use React/Vue/framework โ vanilla JS + HTML only
- Never omit
<meta name="viewport"> โ proportional measurement depends on correct device pixels
- Never use
setInterval for animation โ always requestAnimationFrame
- Never skip the resize handler โ text layout depends on container width
- Never call
prepare() inside the animation loop โ it's the expensive one-time pass. Cache it.
- Never omit
document.fonts.ready.then(...) โ measure after fonts load, not before
- Never use pure black (
#000000) โ use rich off-blacks (#06060a, #08080e, #0a0a0c)
- Never build line strings when you only need geometry โ use
walkLineRanges instead of layoutWithLines
- Never position text with CSS flow โ use
position: absolute and place lines from Pretext coordinates
- Never pick one side of an obstacle when the obstacle is in the middle โ use
carveTextLineSlots to fill both sides
- Never create/destroy DOM elements per frame โ use element pooling (
syncPool)
opentype.js
- Never use
.woff2 files โ opentype.js cannot parse them. Use .woff or .ttf only
- Never use
opentype.load() โ use fetch().then(r => r.arrayBuffer()).then(opentype.parse) for better error handling
- Never render glyphs at absolute positions then try to move them โ render at
(0,0) and position via <g transform="translate(x,y)">
- Never forget
font.unitsPerEm โ the scale factor is fontSize / font.unitsPerEm
- Never call
glyph.getPath() inside a tight loop without caching โ cache the pathData string, regenerate only when position changes
- Never skip kerning โ always check
font.getKerningValue(glyph, nextGlyph) * scale between adjacent glyphs
- Never use Google Fonts TTF CDN URLs (they return 404 for programmatic access) โ use
@fontsource via jsdelivr
Post-Generation QA
python3 ~/.claude/skills/pretext/scripts/validate_pretext.py output.html
Checklist:
- ESM import from
esm.sh/@chenglou/pretext present
<script type="module"> tag
- Named font declaration (no
system-ui)
<meta name="viewport"> present
- Window resize handler
document.fonts.ready awaited before measurement
requestAnimationFrame loop (for animated effects)
- Touch event handlers (for interactive effects)
- No
setInterval for animation
- No framework imports