ragtooth
A Sawtooth Rag,
on the web.
Most tools fight your rag. Ragtooth works with it — shaping text into a sawtooth pattern of alternating long and short lines. The kind of rhythm that reads as design, not accident. A technique from editorial typesetting, now in one fully-typed npm package.
Live demo — drag the sliders
What is sawtooth rag?
The problem with smooth rag
When text is set ragged-right, natural line endings create an unpredictable right edge — notches, peninsulas, near-rivers. It can look accidental. Most tools patch this with soft hyphens or non-breaking spaces: a lot of effort for a result that's still a mess.
The case for sawtooth rag
A sawtooth pattern — long line, short line, long line — gives the rag a rhythm. Structured, not random. The eye reads it as a choice. Book typographers and editorial designers have used it for decades. Now it takes thirty seconds.
Usage
TypeScript + React · Vanilla JS
Drop-in component
import { RagText } from '@liiift-studio/ragtooth'
<RagText sawDepth={120} sawPeriod={2}>
Your paragraph text here...
</RagText>Hook — attach to any element
import { useRag } from '@liiift-studio/ragtooth'
const { ref } = useRag({ sawDepth: 120, sawPeriod: 2 })
<p ref={ref}>{children}</p>Vanilla JS
import { applyRag, removeRag } from '@liiift-studio/ragtooth'
const el = document.querySelector('p')
const original = el.innerHTML
applyRag(el, original, { sawDepth: 120, sawPeriod: 2 })Options
| Option | Default | Description |
|---|---|---|
| sawDepth | 80 | How much shorter the short lines are. Higher = more pronounced sawtooth. Accepts px, %, em, rem, ch. |
| sawPeriod | 2 | Lines per cycle. 2 = every other line short, 3 = two full then one short. |
| sawPhase | sawPeriod | Which line within each cycle is shortened (1-indexed). Default is the last line of each cycle. Use with sawPeriod to place the short line exactly where you want it. |
| sawAlign | 'top' | Anchor the cycle to the top or bottom of the block. 'bottom' with sawPeriod 3 keeps the last two lines full — no awkward short penultimate line. |
| maxTracking | 0.7 | Max letter-spacing (px, em, rem). Keeps lines from being stretched into oblivion. |
| resize | true | Re-runs the algorithm on container resize via ResizeObserver. Set false for static layouts. |