axis-rhythm
Per-line axis
alternation.
CSS applies font variation settings to the whole element. Axis Rhythm applies them line by line — cycling any axis through a sequence of values across paragraph lines. The result is a texture the eye reads as rhythm, not noise.
Live demo — drag the sliders
Typography has always been as much about texture as legibility. The even grey of a well-set paragraph — called its colour by compositors — depends on consistency: consistent spacing, consistent weight, consistent rhythm from line to line. Variable fonts crack this open. The wdth axis can compress or expand a letterform; the wght axis can lighten or darken it; the opsz axis can adjust optical weight for the point size. Applied uniformly, these give you a different typeface. Applied line by line, they give you something more interesting: a paragraph with rhythm. Each line carries a different setting but the text reads as one. The difference is a texture the eye feels before the mind names it.
Each line gets a different axis value. The paragraph reads as one.
How it works
CSS stops at the element
font-variation-settings applies a single setting to an entire element. Every line gets the same axis value. There's no way to target individual lines — they're not DOM nodes.
Axis Rhythm works line by line
The algorithm detects visual lines using glyph positions, then wraps each in a span with its own font-variation-settings. Resize, reflow, inline elements — all handled automatically.
Usage
TypeScript + React · Vanilla JS
Drop-in component
import { AxisRhythmText } from '@liiift-studio/axis-rhythm'
<AxisRhythmText axis="wdth" values={[100, 88]} period={2}>
Your paragraph text here...
</AxisRhythmText>Hook — attach to any element
import { useAxisRhythm } from '@liiift-studio/axis-rhythm'
const ref = useAxisRhythm({ axis: 'wdth', values: [100, 88], period: 2 })
<p ref={ref}>{children}</p>Vanilla JS
import { applyAxisRhythm } from '@liiift-studio/axis-rhythm'
const el = document.querySelector('p')
const original = el.innerHTML
applyAxisRhythm(el, original, { axis: 'wdth', values: [100, 88], period: 2 })Options
| Option | Default | Description |
|---|---|---|
| axis | 'wdth' | Variable font axis tag, e.g. 'wdth', 'wght', 'opsz'. |
| values | [100, 96] | Axis values to cycle through across lines. |
| period | 2 | Lines per cycle. |
| align | 'top' | 'top' counts from first line, 'bottom' from last. |