ragtooth

A Sawtooth Rag,
on the web.

npm ↗
GitHub ↗
TypeScript·Zero dependencies·React + Vanilla JS·~2.7kb gzipped

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

Depth160
Period2
Phase1
Tracking0.7
Align— period counts from last line upResize

Typography traces its formal origins to Gutenberg's press of 1455, where justified setting and careful letter-spacing were discipline before they were decoration. The difference between fine and ordinary typesetting has always been about rhythm — how the eye moves, and where it rests. A ragged-right setting has a bad reputation, most of it unearned. The trouble is never the rag itself but the shape it falls into — accidental, without rhythm. Set in a typeface with strong descenders and a generous x-height, a sawtooth rag can feel as considered as full justification. The difference is simply that the decision is yours rather than the browser's.

These three controls — depth, period, and tracking — are enough for nearly any paragraph. Start with depth around 15–20%of your line length, keep the period at 2 for the classic sawtooth, and hold tracking just above zero. The fine-tuning is yours to find.

Yes, we used small-caps, bold, italic, and a number in the same paragraph. We wanted to make sure the tool doesn't break. On e-readers and e-ink displays, a deliberate sawtooth rag also prevents the harsh reflow artefacts that appear when text redraws line by line on a slow-refresh screen.

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

OptionDefaultDescription
sawDepth80How much shorter the short lines are. Higher = more pronounced sawtooth. Accepts px, %, em, rem, ch.
sawPeriod2Lines per cycle. 2 = every other line short, 3 = two full then one short.
sawPhasesawPeriodWhich 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.
maxTracking0.7Max letter-spacing (px, em, rem). Keeps lines from being stretched into oblivion.
resizetrueRe-runs the algorithm on container resize via ResizeObserver. Set false for static layouts.