Fit Flush

text-to-fit sizing


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.

Variable fonts — animate the weight after the fit

Without vfSettings{ mode: 'width' }

Variable Headline

— · fits

With vfSettings{ mode: 'width', vfSettings: { wght: { max: 900 } } }

Variable Headline

— · measured at wght 900 · fits

Both lines were fitted once. The first was measured at the weight it had; the second at the heaviest weight it will reach, so it is a little smaller at rest and never overflows.

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.

About a dozen measurements

Fit Flush probes a hidden clone of the element — try a size, measure, narrow the range. In height and both modes it binary-searches to within 0.5 px in about 12 measurements; a single line in width mode is predicted from one measurement and checked, usually 3 in all. Nothing visible moves until the final size is written.

Variable-font safe

Pass vfSettings with your axis ranges and Fit Flush measures with every axis at its max, so the size still fits when an animation drives the axis there. An optical-size axis is widest at its minimum: give min too and both ends are checked.

Resize-aware, font-load-aware

The live API fits straight away, then refits when the container resizes (a ResizeObserver), when the text changes, and when web fonts finish loading (document.fonts.ready), so a size measured with the fallback font is replaced.

Usage

TypeScript + React · Vanilla JS

Drop-in component

import { FitFlushText } from '@overpunch/fit-flush/react'

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

Hook — attach to any element

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

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

Vanilla JS — one-shot

import { fitFlush } from '@overpunch/fit-flush'

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

// Undo it: restores the original inline styles
// removeFitFlush(el)

Vanilla JS — live (refits on resize, text change and font load)

import { fitFlushLive } from '@overpunch/fit-flush'

const handle = fitFlushLive(el, {
  mode: 'both',
  // Variable font safety — measure at each axis' widest end
  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' (one line), 'height', or 'both'. Height and both need a container with a height of its own.
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.
vfSettings—Variable-font axis ranges, e.g. { wght: { max: 900 } }. Measurement uses each axis at its max (and at its min, where given) for worst-case safety.
containerparentElementOverride the container element used for dimension measurement.
onFit—Callback fired after each fit that wrote a size, 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 Settings → Custom Code → Footer, or an Embed element -->
<script src="https://cdn.jsdelivr.net/npm/@overpunch/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/@overpunch/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).