A downloaded font can remain unused because CSS selects another face, the text falls outside its coverage, or its display window has passed. On a Mac, start by inspecting the font actually rendering one label, then trace backward through selection and loading.

This guide is for someone maintaining a webpage who can see a successful font request but still sees fallback text. Use Chrome on macOS for the inspection steps. The synthetic label Panel 204 and family name Sample Sans below illustrate the diagnosis; they describe no particular website.

What does a successful download actually prove?

Treat the diagnosis as four separate questions: Was the resource fetched? Was a usable face registered? Did CSS select that face for these characters? Did the renderer draw it? A font preload starts an early fetch; its presence does not establish which font supplies the visible text. MDN: rel="preload"

Font matching operates on text, not simply on downloaded files. The browser considers the requested families and font properties, then checks character support. Consequently, one label can use several fonts, and another part of the page can cause a font download that tells you little about your target label. W3C: CSS Fonts Module Level 4, font matching

Use this decision table to choose the next check. It is a diagnostic aid derived from the documented loading and matching boundaries, rather than a record of measured browser results.

Observation Next check What it helps distinguish
Computed family differs from the intended family Follow the winning CSS declaration Cascade or targeting problem
Computed family is correct, rendered font differs Inspect face descriptors and character coverage Selection or fallback problem
JavaScript reports a loaded face, but it is absent from the document’s font set Inspect registration code Loading without registration
Font appears after a reload but misses the first visit Inspect font-display and loading timing Display-window behavior
Ordinary text works, canvas text stays unchanged Inspect drawing order and canvas font settings Separate drawing operation

Which font is actually drawing the target label?

On the affected page, press Command + Shift + C and select the text. In Elements, select the smallest element containing the affected label. Open Computed, then scroll to Rendered Fonts. Chrome documents this view as the place to inspect the fonts supplying rendered text. Chrome DevTools: CSS features reference, Chrome DevTools: Apply other effects

Record the rendered face and whether its source is local or a network resource. Also record the computed font-family, font-weight, and font-style. The computed family is the requested stack; the rendered-font result identifies what supplied the glyphs. A familiar appearance alone is weak evidence because two faces can look similar. Chrome DevTools: What font is that?

If the intended face appears for the affected text, move the investigation toward the particular appearance difference—such as weight or features. If it is absent, continue below. Keep the original text and viewport unchanged while testing so each observation remains comparable.

Does the effective CSS request the intended face?

In Computed, inspect the effective font properties, then use Styles to find their declarations. Chrome distinguishes these views: Styles includes overridden declarations, while Computed shows the values actually applied. Inspect the text-bearing child when a card contains several labels; a parent’s font declaration is insufficient evidence for the child. Chrome DevTools: CSS features reference

Check any winning font shorthand as well as individual properties. For example, font: 16px sans-serif selects a generic family and resets omitted shorthand components, including weight and style. It also resets properties such as font-feature-settings and font-variation-settings. Repair the declaration responsible for the unexpected value, then inspect again. MDN: font

Next, compare the requested family with the font-family descriptor in the relevant @font-face rule. That descriptor defines the CSS family name used to reference the face; the filename is not the name you must put in the element’s font stack. Check the rule delivered to the browser, including its src, weight, style, and any declared ranges. MDN: @font-face

A variable font’s supported weight range can be expressed with two values, whereas a static face can declare one weight. Make those descriptors describe the supplied resource accurately. A mismatch deserves investigation, but it does not automatically explain fallback: font matching includes alternatives when an exact style is unavailable. MDN: @font-face, W3C: Matching font styles

Could a local font be taking precedence?

Inspect src for a local() alternative. On a Mac with that font installed, the browser may use the local copy while a separately preloaded webfont remains unused. To isolate this condition in Chrome, open the Rendering panel, enable Disable local fonts, and reload. Compare Rendered Fonts before and after, then restore the setting. This test targets local() sources in @font-face rules. Chrome DevTools: Apply other effects

Was the face loaded and registered in the correct document?

A face created with new FontFace() can finish loading before it is added to a font set. For document rendering, it must be added to document.fonts. Inspect the application’s loading path for both operations; resolving the face’s load promise alone does not complete registration. MDN: CSS Font Loading API

The following complete console diagnostic lists the faces registered in the current document. Run it in the affected page’s console context. It reads the set without initiating loads or changing styles; it does not identify rendered glyphs. Its browser-dependent behavior has not been executed here.

(() => {
  const faces = Array.from(document.fonts, face => ({
    family: face.family,
    weight: face.weight,
    style: face.style,
    stretch: face.stretch,
    unicodeRange: face.unicodeRange,
    status: face.status
  }));

  console.table(faces);
  console.log("Registered face count:", faces.length);
})();

Use the inventory to separate a missing registration from a registered face that remains unused. For an entry whose status is error, investigate the loading failure before adjusting typography. A loaded entry establishes loading state, while face selection still depends on the element and text. MDN: CSS Font Loading API

Do not treat document.fonts.check() as proof of rendering. It can return true even for a nonexistent family. Its optional text argument narrows matching by declared Unicode ranges and does not test individual glyph coverage. Likewise, document.fonts.ready resolves after loading and layout for used fonts; it does not certify that every declared face rendered. MDN: FontFaceSet.check(), MDN: Document.fonts

Does the font cover the characters you are testing?

Check both the CSS unicode-range and the actual font resource. The descriptor controls which characters may use a face. Extending the descriptor cannot manufacture glyphs missing from the file: the font’s character map remains part of matching. MDN: unicode-range, W3C: Matching font styles

Consider an illustrative rule limited to U+0030-0039. In Panel 204, only 2, 0, and 4 fall within that range. The letters and space do not. A download triggered by the digits therefore does not establish that the font can render the whole label. MDN documents that encountering an eligible character can cause the whole font resource to download. MDN: unicode-range

This standalone JavaScript example exposes that distinction. It runs in a browser console or Node.js without setup. The deterministic range calculation was executed for the sample shown.

const text = "Panel 204";
const rows = Array.from(text, character => {
  const value = character.codePointAt(0);
  return {
    character,
    codePoint: "U+" + value.toString(16).toUpperCase().padStart(4, "0"),
    inDeclaredRange: value >= 0x30 && value <= 0x39
  };
});

console.table(rows);

If the range excludes needed characters, correct an inaccurate descriptor or supply a subset containing them. Preserve the original failing text when checking the result. Do not assume a string iteration is a shaping test: combining sequences require cluster handling, and the standard considers shaping information where required. Keep sequences intact when comparing rendered output. W3C: Font matching and cluster matching

Did the font miss its display window?

Inspect font-display when the family and coverage look correct. optional has an extremely short block period and no swap period. fallback has a short swap period. A resource arriving after the applicable window can therefore leave fallback text visible for that page load. swap permits an unlimited swap period after its initial block period. MDN: font-display

For a controlled diagnosis, temporarily change the affected rule to font-display: swap in the served stylesheet and repeat the initial load conditions. If the face now appears after loading, timing is a supported explanation. Decide whether that later visual change suits the page before retaining the setting. Repeat both an initial load and a cached reload; a cached result alone does not reproduce the original arrival timing.

What changes when a library or canvas draws the text?

If the page already uses Font Face Observer, inspect the observed family and its weight, style, and stretch options. Its documentation describes a loader and monitor, requires the usual @font-face rules, and requires a custom test string when the font lacks its default test characters. Its default timeout is three seconds. An observer timeout and a browser’s font-display window are separate conditions. Font Face Observer documentation

As a diagnostic inference, an observer’s success message should be followed by inspection of the target element. Monitoring one face’s availability does not establish that the element’s winning CSS requests it. Installing another loader adds little when the unanswered question is which face rendered.

Canvas introduces a separate drawing boundary. Its drawing context has its own font setting, initially 10px sans-serif; an invalid assignment is ignored. Setting a CSS family on the canvas element is therefore insufficient to establish the context’s font. Inspect the value assigned to the drawing context and the order of loading and drawing operations. WHATWG: Canvas text styles

For canvas text, wait for the intended registered face to load, set the context’s font, then redraw the affected scene using the application’s normal drawing routine. Previously painted pixels do not acquire a new typeface when a font arrives. Canvas text preparation also uses a hypothetical infinitely wide line box, so a successful canvas test does not establish that a constrained webpage label wraps identically. WHATWG: Text preparation and drawing text

How do you confirm the repair?

Return to the original label and repeat the observation that exposed the problem. Verify its computed request, rendered face, and relevant characters under the original load conditions. For canvas, redraw before comparing. Keep a compact record of the element, requested properties, actual rendered face, resource, and successful change; this makes the diagnosis reproducible.

Finish when you can explain why the font was unused and show the intended result on the affected text. A corrected family declaration, accurate subset, registered face, suitable display policy, or properly timed redraw each addresses a different boundary. Choose the repair supported by your observations.

Sources