Skip to content

chroxify/typefit

Repository files navigation

Typefit

Text that always fits perfectly.

Typefit is a browser-first text fitting library powered by @chenglou/pretext. It gives you a small React component, a React hook, a DOM API, and a pure measurement API for fitting text into real layout constraints.

It is built for cases where simple DOM scaling is not enough: balanced multiline headlines, minimum and maximum line budgets, shaped rows, snapped type scales, custom font loading, and measurable layout results.

Packages

Package Purpose
@typefit/react React component and hook. Install this in React apps.
@typefit/core Framework-agnostic DOM and measurement APIs.

Install

bun add @typefit/react
npm install @typefit/react

For non-React projects:

bun add @typefit/core

Quick Start

import { Typefit } from '@typefit/react';

export function HeroTitle() {
  return (
    <Typefit as="h1" maxSize={96} lines={{ min: 2 }} hideUntilFit>
      Text that always fits perfectly.
    </Typefit>
  );
}

Typefit reads normal CSS from the element, including custom fonts:

@font-face {
  font-family: "Open Runde";
  src: url("/fonts/OpenRunde-Semibold.woff2") format("woff2");
  font-weight: 600;
  font-style: normal;
  font-display: swap;
}

.headline {
  font-family: "Open Runde", system-ui, sans-serif;
  font-weight: 600;
  letter-spacing: -0.015em;
  line-height: 1.05;
}
<Typefit as="h1" className="headline" maxSize={112} lines={{ target: 2 }}>
  Custom fonts work through CSS.
</Typefit>

You can also pass the measured font explicitly:

const headlineFont = (size: number) =>
  `600 ${size}px "Open Runde", system-ui, -apple-system, sans-serif`;

<Typefit as="h1" font={headlineFont} maxSize={112}>
  Explicit font measurement.
</Typefit>;

Core Usage

Use fitText when you want a pure measurement result before touching the DOM:

import { fitText } from '@typefit/core';

const result = fitText({
  text: 'Balanced responsive text',
  width: 520,
  maxSize: 96,
  lines: { target: 2, max: 3 },
  balance: { mode: 'even' },
  font: { family: 'Inter', weight: 700 },
  includeLines: true,
});

console.log(result.fontSize, result.lineCount, result.lines);

Use typefit when you want Typefit to measure an element, apply styles, and keep it updated:

import { typefit } from '@typefit/core';

const fit = typefit(element, {
  maxSize: 96,
  lines: { min: 2 },
  observe: true,
});

fit.refresh();
fit.update({ shape: 'diamond' });
fit.destroy();

What Typefit Can Control

Line Budgets

Use lines to describe how many lines are acceptable.

<Typefit lines={{ min: 2, target: 3, max: 4 }}>
  Fit this headline into a predictable line budget.
</Typefit>
  • min asks Typefit to use at least this many lines.
  • target asks Typefit to prefer a specific line count.
  • max prevents Typefit from exceeding a line count.

If no max is provided, text may wrap to as many lines as needed. If a configured minSize makes the requested fit impossible, the result reports fits: false.

Strategies

<Typefit strategy="balance">Balanced wrapping.</Typefit>
<Typefit strategy="fill">Largest possible type.</Typefit>
  • balance chooses a wrap that reads evenly, then sizes.
  • fill prioritizes the largest font size that fits the container.

Balance Modes

<Typefit balance={{ mode: 'even' }}>Even lines.</Typefit>
<Typefit balance={{ mode: 'compact' }}>Packed lines.</Typefit>
<Typefit balance={{ mode: 'stable', stability: 0.45 }}>Stable resize behavior.</Typefit>

Snap Scales

Use snap to keep fitted type on a design-system scale.

<Typefit snap={[24, 32, 40, 48, 56, 64, 72, 88, 104, 120]}>
  Snap to intentional sizes.
</Typefit>

Shapes

Shape presets turn lines into strict row-width budgets.

<Typefit shape="diamond" lines={{ min: 3 }}>
  Widest in the middle.
</Typefit>

<Typefit shape="pyramid" lines={{ min: 3 }}>
  Wider every line.
</Typefit>

Available presets:

'rectangle'
'pyramid'
'inverted-pyramid'
'diamond'
'hourglass'
'oval'
'left-ramp'
'right-ramp'

Custom row shapes are supported too:

fitText({
  text: 'Custom row offsets',
  width: 360,
  lines: { min: 3 },
  shape: {
    unit: 'ratio',
    rows: [
      { width: 0.42, x: 0.29 },
      { width: 1, x: 0 },
      { width: 0.5, x: 0.25 },
    ],
  },
  font: { family: 'Inter', weight: 700 },
});

Last-Line Control

<Typefit avoidWidows lastLine={{ minRatio: 0.3 }}>
  Avoid a short final line when possible.
</Typefit>

Browser Support

Typefit works in any current browser with standard canvas text measurement. It uses OffscreenCanvas when available and falls back to a DOM canvas context, so Chromium, Firefox, WebKit/Safari, and other standards-compliant browsers are supported.

Server-only measurement is not yet a V1 target.

The DOM and React APIs also listen for:

  • ResizeObserver
  • MutationObserver
  • document.fonts.ready
  • document.fonts loadingdone

When web fonts finish loading, Typefit clears cached metrics and refits.

API

@typefit/react

Export Description
Typefit Polymorphic React component.
useTypefit Hook returning { ref, result, refresh }.
const { ref, result, refresh } = useTypefit<HTMLHeadingElement>({
  maxSize: 96,
  lines: { target: 2 },
});

return <h1 ref={ref}>Measured with a hook</h1>;

@typefit/core

Export Description
fitText(options) Pure measurement API. Requires text, width, and font.
typefit(element, options) DOM fitting API. Reads text and computed styles from the element.
isSupported() Returns whether the current browser can measure text.
clearCache() Clears Pretext measurement cache.
setLocale(locale?) Sets Pretext segmentation locale.

Common Options

Option Default Description
minSize 10 Smallest font size Typefit may choose.
maxSize 512 Largest font size Typefit may choose.
lines none { min, target, max } line-count policy.
strategy 'balance' 'balance' or 'fill'.
balance { mode: 'even' } Wrap scoring options.
shape none Shape preset or custom row configuration.
snap none Array of allowed font sizes, or { sizes, mode }.
lastLine none Final-line quality constraints.
avoidWidows false Shortcut for avoiding short final lines.
lineHeight 1.1 Line-height multiplier or size callback.
letterSpacing computed style Letter spacing in pixels.
observe true DOM/React only. Refit on resize, content changes, and font loads.
hideUntilFit false React only. Hide until the first fit completes.

Contributing

We would love to have your help in making Typefit better.

Here's how you can contribute:

License

Typefit is licensed under the MIT License.

About

Text that always fits perfectly.

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors