DebugSwift

Fallback Font Check · Open source

Stop your text re-wrapping when the webfont loads.

A small command-line tool that measures how closely your fallback font matches your webfont, using the real text on your real pages, one weight band at a time. It tells you which band is off and prints the size-adjust that fixes it.

View the code on GitHubMIT licence. Runs on your machine.

Needs Node 18.3 or later. The first run also needs a Chromium build for Playwright:

Once
npx playwright install chromium
Then, on your site
npx github:DebugSwiftHQ/fallback-font-check --font "Your Font" --fallback "Your Font Fallback" https://your-site.com/

Give it several pages. Your homepage alone probably does not contain every size and weight you use.

The problem

A fallback that is the wrong width moves the whole page.

Until a webfont loads, the browser draws text in a fallback font. If the fallback is wider or narrower, lines break in different places. When the webfont arrives, every paragraph re-wraps and everything below it moves. That movement is Cumulative Layout Shift, one of Google's Core Web Vitals.

  1. Opens each page in headless Chromium

    and waits for the fonts to load.

  2. Reads your fallback's @font-face rules

    straight from the page's stylesheets, including each one's font-weight range and current size-adjust. Those ranges become the bands.

  3. Measures every piece of text twice

    rendering the same string at the same size, weight and letter-spacing in both fonts, and taking the width ratio.

  4. Prints the fix

    the mean ratio per band, a flag on any band out of tolerance, and current size-adjust × mean: the value that brings it to 1.000.

This is the open-source version of the check that keeps debugswift.com honest. It is what found that our automatic fallback ran about 11% wide on body text.

Options

Six flags, two of them required.

OptionDefaultWhat it does
--font <family>requiredThe webfont family
--fallback <family>requiredThe fallback family your @font-face rules declare
--tolerance <n>0.03How far a band's mean may drift, as a fraction
--width <px>412Viewport width. Phone-sized by default, where re-wraps hurt most
--min-length <n>15Shortest string worth measuring
--jsonoffMachine-readable output

Limits

What it can't do.

  • It measures the fallback on the machine it runs on. A visitor whose device lacks that local font falls through to something else, and no size-adjust helps there. font-display: block is the other half of the fix.
  • Width only. Vertical metrics still need setting from the font file.
  • Chromium only. Text shaping differs slightly between engines.
  • Stylesheets the page can read. Cross-origin stylesheets are skipped, so your fallback faces need to be in a same-origin or inline stylesheet.

Common questions

Why not just use the size-adjust next/font or Fontaine gives me?

Those compute one number from the font file's average glyph width, and that average can be well off for your actual copy. On debugswift.com the automatic value ran about 11% wide on 18px body text. Many variable fonts also widen as the weight goes up while Arial barely changes, so one value cannot fit body text and headings at once. This measures your real text, one weight band at a time.

Does it change my site?

No. It opens your pages in headless Chromium, measures, and prints numbers. You decide whether to paste the suggested size-adjust into your CSS.

Can I run it in CI?

Yes. It exits 0 when every band is within tolerance, 1 when at least one band is out, and 2 for bad arguments or no fallback face found. Point it at a preview deployment and a font change or a big copy rewrite cannot quietly bring layout shift back. Add --json for machine-readable output.

Is it free to use commercially?

Yes. It is MIT licensed. We built it for our own sites and shared it because the problem is not ours alone.

After the numbers

Layout shift is rarely the only thing slowing a page.

If the fonts are fixed and the page still drags, something further in is the cause. Finding it is the job.

Book a free diagnosisTwenty minutes. No pitch, no obligation.

Fixed price agreed before work starts. No hourly billing, no surprises.