fitFlush

Fit text to
any container.

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

CSS can't scale a font to fill a container — font-sizedoesn't know where to stop. Fit Flush binary-searches the right size to within half a pixel, with variable-font safety built in.

Live demo — drag the sliders

Container

Binary Search

Mode

Single line — scales to fill the exact width

Container width — 60%
Padding — 0px
Text

Drag the sliders to resize the container. The text recalculates its font-size every frame via ResizeObserver — no rerenders, no style recalculations outside the target element.

How it works

CSS can't fit a font size

There's no CSS property that says “make this text as large as it can be while staying inside its container.” clamp() just rescales, and vw units don't know about your layout. You need measurement.

Binary search in 15–20 steps

Fit Flush probes a hidden clone of the element — try a size, measure, compare to target, narrow the range. It converges to within 0.5 px in under 20 iterations with no visible reflow.

Variable-font safe

Pass vfSettings with your axis ranges and Fit Flush measures at the widest/heaviest axis values. The computed size stays correct even when a subsequent animation drives the axis to its maximum.

Resize-aware, font-load-aware

The live API wraps a ResizeObserver on the container and waits for document.fonts.ready before the first measurement. Widths from before the web font loaded are never committed.

Usage

TypeScript + React · Vanilla JS

Drop-in component

import { FitFlushText } from '@liiift-studio/fit-flush'

<FitFlushText mode="width">
  Display Headline
</FitFlushText>

Hook — attach to any element

import { useFitFlush } from '@liiift-studio/fit-flush'

const ref = useFitFlush({ mode: 'both' })
<h1 ref={ref}>Display Headline</h1>

Vanilla JS — one-shot

import { fitFlush } from '@liiift-studio/fit-flush'

const el = document.querySelector('h1')
fitFlush(el, { mode: 'width', min: 12, max: 400 })

Vanilla JS — live (ResizeObserver + fonts.ready)

import { fitFlushLive } from '@liiift-studio/fit-flush'

const handle = fitFlushLive(el, {
  mode: 'both',
  // Variable font safety — measure at widest axis
  vfSettings: { wdth: { max: 125 }, wght: { max: 900 } },
})

// Later:
handle.refit()  // force re-measurement
handle.dispose() // restore original fontSize

Options

OptionDefaultDescription
mode'both'Which dimension to fill: 'width', 'height', or 'both'.
min8Minimum font-size in px.
max400Maximum font-size in px.
precision0.5Convergence tolerance in px — binary search stops within this gap.
padding0Inset from container edges in px. Number = all sides; { x, y } = per-axis.
vfSettingsVariable-font axis ranges. Measurement uses each axis at its max for worst-case safety.
containerparentElementOverride the container element used for dimension measurement.