axis-rhythm

Per-line axis
alternation.

npm ↗
GitHub ↗
TypeScript·Zero dependencies·React + Vanilla JS

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

Axis High100
Axis Low88
Period2
AxisAlign

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

OptionDefaultDescription
axis'wdth'Variable font axis tag, e.g. 'wdth', 'wght', 'opsz'.
values[100, 96]Axis values to cycle through across lines.
period2Lines per cycle.
align'top''top' counts from first line, 'bottom' from last.