rhythm-gpui
Print-inspired vertical rhythm for gpui — baseline offsets computed from real font metrics. Ported from rhythm-sass, it lands every text baseline exactly on a vertical rhythm grid, giving you pixel-perfect control over typographic layout in gpui apps.
Unlike the Sass original, no hand-measured baseline-ratio is required:
metrics are read from the actual font file through gpui's text system.

cargo run --example demo — switch the typeface and every baseline keeps its
appointment with the grid: the drop cap spans three lines, and four runs
(serif, monospace, CJK) share a single alphabetic baseline computed from their
respective metrics.
How it works
gpui (like CSS) vertically centers a font's ascent + descent box inside the
line height and places the baseline at:
baseline_from_top = (line_height − ascent − descent) / 2 + ascent
Given a line height that is a whole number of rhythm units, this library solves
that equation backwards: it hands you the padding or margin that puts the
baseline on the next grid line. Plumber's famous baseline-ratio turns out to be
a closed-form function of the same metrics — (em + descent − ascent) / (2·em) —
which is why looking it up per font is no longer necessary.
Installation
[]
= "0.1"
The crate has two layers behind one package:
gpuifeature (default) — the gpui integration: metric resolution throughTextSystem,Pixels-typed spacing, drop caps, theRhythmStyledextension, and the debug overlay.default-features = false— only the dependency-free rhythm math (Rhythm,FontRhythm,DropCapRhythm,snap). Any renderer that centersascent + descentinside the line height can feed it metrics — gpui is not compiled at all.
macOS needs the Metal toolchain to build gpui:
xcodebuild -downloadComponent MetalToolchain.
The MSRV is Rust 1.85 for the math-only build and is checked in CI; with the
default gpui feature the effective minimum follows gpui itself, which does
not declare one.
Quick start
use ;
use ;
// 1. Pick a grid unit (the analog of rhythm-sass `$rhythm-size`).
let grid = new;
// 2. Bind fonts to the grid. Line height is given in whole rhythm units:
// 3 × 8px = 24px. Metrics are resolved from the font file at this size.
let body = resolve;
let heading = resolve;
// 3. Use the spacing functions wherever gpui expects a length.
div
.pt
.child
.child
Everything else — the spacing functions, RhythmFont resolution and accessors,
the RhythmDropCap / DropCapRhythm solvers, the RhythmStyled extension, the
rhythm_overlay debug grid, and the gpui-free math layer — is documented on
docs.rs.
Demo & recipes
cargo run --example demo # font picker, drop cap, heading anchor toggle,
# mixed-font baseline row, grid overlay;
# downloads Google Fonts on demand
The demo doubles as a recipe collection:
- Page scaffold — open with
baseline_top, chain blocks withbaseline_between, close withbaseline_bottom. - Optical heading —
cap_toplands the capitals' ink, not the baseline, on a grid line; the pairedcap_bottomreturns the trimmed space so the block still spans whole rows and everything below stays in rhythm — closing withbaseline_bottominstead would not. Flip the demo's heading toggle to compare the two openings live: with the cap anchor, switching typefaces keeps the ink pinned while the baseline wobbles, and vice versa. - Drop cap with true wrap-around —
RhythmDropCap::resolve(ts, font, &body, 3)solves the cap size and baseline anchor from metrics;.rhythm_drop_cap(&cap)applies the anchor as a relative inset, because for cap-heavy faces (cap height > ascent − descent, e.g. Merriweather) the anchor is a downward shift, and a margin would stretch the flex row and push everything below off the grid. Wrap-around text splitting:drop_cap_paragraphin the demo. - Mixed fonts on one baseline — different sizes, families, and scripts aligned on one grid-seated alphabetic baseline. CJK fonts publish the same metrics, but ideographs are drawn on the em square, so their ink dips slightly below the shared line — standard mixed-script behavior.
- Debug overlay — chain
.rhythm_debug_overlay(grid, show)on the page container to toggle the stripes while developing; every other grid row is painted so you can verify baselines land on them. The underlyingrhythm_overlay(grid, color)element takes a custom color. - Device-pixel snapping — the functions are exact to float precision
(no whole-pixel rounding, unlike the Sass version); use
snapto round a final value to whole device pixels. gpui rounds text line heights to whole logical pixels and snaps Taffy layout edges to device-pixel boundaries. Keepgrid × line_rhythmswhole in logical pixels so gpui does not alter the line height; each remaining fractional layout edge can differ from the exact math by up to half a device pixel.
Tip: as with rhythm-sass, make every text block occupy a whole number of
rhythm units. Line heights are whole units by construction; close a
baseline-anchored block with baseline_bottom, or a cap-anchored block with
the paired cap_bottom. Blocks then compose freely without breaking the page
rhythm.
Development
git config core.hooksPath .githooks # once per clone
The pre-commit hook runs rustfmt over the staged .rs files and re-stages
them, so what you commit already satisfies CI's cargo fmt --check. Markdown,
YAML and JSON are formatted too when Prettier is on PATH.
Credits
Ported from rhythm-sass, which builds on concepts introduced by Plumber.