fitFlush

Fit text to
any container.

npm ↗
GitHub ↗
TypeScriptZero dependenciesReact + Vanilla JS

CSS can’t scale a font to fill a container — font-size doesn’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

computed font-size
Container

Binary Search

Text stays on one line and scales to fill the width

Minimum 50% — equal breathing room on each side

Resize the container or adjust fill to see the font-size adapt. The size recalculates whenever the container is resized.

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.

Converges in 15–20 steps

Fit Flush probes a hidden clone of the element — try a size, measure, compare to target, narrow the range. In height and both modes it binary-searches to within 0.5 px in under 20 iterations; width mode uses an analytical fast path. 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/react'

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

Hook — attach to any element

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

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, whiteSpace, and --ff-size

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.
onFitCallback fired after each fit calculation, receiving the resolved font-size in px.

no-code

Use it in Webflow, Framer & Figma

The same effect, no build step — drop it straight into your design tool.

Webflow

One script tag, then mark any element with data-fitflush. Configure it with data-* attributes.

<!-- Site SettingsCustom CodeFooter, or an Embed element -->
<script src="https://cdn.jsdelivr.net/npm/@liiift-studio/fit-flush/dist/fitflush.webflow.min.js"></script>

<!-- Then add data-fitflush to any text element -->
<h1 data-fitflush>Your headline</h1>

Framer

Insert → Code → New Component, then paste FitFlush.tsx ↗. It imports the core from esm.sh and exposes every option in the property panel — no build step.

import { /* core */ } from "https://esm.sh/@liiift-studio/fit-flush"

Figma · beta

Part of the Type Tools Figma plugin ↗ — Plugins → Development → Import plugin from manifest, run Type Tools, and pick this tool. Here it works with compromises — tracking or named-instance swaps (Figma can't set variable axes).