axisrhythm
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
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.
It aids reading
Alternating axis values create a subtle visual banding across the paragraph — like column highlighting in a spreadsheet, but for text. The eye uses the variation as a landmark: each line has a slightly different texture, so you always know which line you're on and where the next one begins.
Line length preservation
The linePreservation option prevents reflow when the axis changes character widths. 'spacing' compensates with letter-spacing per line — exact widths, no glyph distortion. 'scale' uses a GPU scaleX transform — faster, minor horizontal compression at large ranges.
Usage
TypeScript + React · Vanilla JS
Drop-in component
import { AxisRhythmText } from '@liiift-studio/axisrhythm'
<AxisRhythmText axis="wdth" values={[100, 88]} period={2}>
Your paragraph text here...
</AxisRhythmText>Hook — attach to any element
import { useAxisRhythm } from '@liiift-studio/axisrhythm'
const ref = useAxisRhythm({ axis: 'wdth', values: [100, 88], period: 2 })
<p ref={ref}>{children}</p>Vanilla JS
import { applyAxisRhythm, getCleanHTML } from '@liiift-studio/axisrhythm'
const el = document.querySelector('p')
const original = getCleanHTML(el)
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. |
| lineDetection | 'bcr' | 'bcr' reads actual browser layout — ground truth, works with any font and inline HTML. 'canvas' uses @chenglou/pretext for arithmetic line breaking with no forced reflow on resize. Install pretext separately. |
| linePreservation | 'none' | 'none' — no compensation. 'spacing' — adjusts letter-spacing per line to match natural line widths; prevents reflow. 'scale' — applies a CSS scaleX transform per line; GPU-composited, no letter-spacing change. |
| as | 'p' | HTML element to render, e.g. 'h1', 'div', 'li'. Accepts any valid React element type. (AxisRhythmText only) |