Web Performance

FOUT vs FOIT:理解无样式和不可见文字闪烁

Updated 二月 24, 2026
FOUT显示备用文字,FOIT什么都不显示。两者都是字体加载的副作用——了解原因及解决方法。

FOUT vs FOIT: Understanding Flash of Unstyled and Invisible Text

When you load a custom web font, the browser faces a decision during the moment between when the page first renders and when the font file finishes downloading: what should it show in place of text that should appear in that font?

There are two possibilities. The browser can show text in a fallback system font — readable but styled differently than intended. Or it can show nothing — no text at all — until the custom font arrives. These two behaviors have names, and understanding the trade-offs between them is fundamental to managing typography performance.

FOUT (Flash of Unstyled Text) is when the fallback font appears first, then visibly switches to the custom font.

FOIT (Flash of Invisible Text) is when the text is hidden until the custom font loads.

Neither is ideal. Both are avoidable. But if you must choose one, FOUT is almost always the lesser evil.


What Is FOUT (Flash of Unstyled Text)?

FOUT occurs when a browser renders text using a fallback font — typically Arial, Helvetica, Times New Roman, or another system font — and then swaps to the custom typeface once it finishes downloading. Users see the text immediately in the fallback font, then notice a visual "flash" as the custom font replaces it.

The term "unstyled" is somewhat misleading. The text isn't unstyled — it's styled with the fallback font, which is a valid, readable typeface. What "unstyled" means in this context is "not styled with the intended custom font."

FOUT is the historical behavior of browsers before they introduced font-blocking. Firefox in particular used FOUT as its default behavior for many years. Web designers hated it because the font swap was visually jarring, especially for display fonts where the metrics difference between the custom and fallback font is dramatic.

In modern CSS, FOUT is produced by:

@font-face {
  font-family: 'Inter';
  src: url('/fonts/inter-regular.woff2') format('woff2');
  font-display: swap;  /* Causes FOUT */
}

What Users Experience During FOUT

The FOUT experience depends on how different the custom font is from the fallback. If you're loading a sans-serif body font and the fallback is Arial, the FOUT is subtle — fonts are similar enough that the swap is barely noticeable. If you're loading a narrow display font and the fallback is a wide system serif, the FOUT is dramatic: text changes shape, size, and weight, often pushing page content up or down as line breaks change.

FOUT also causes Cumulative Layout Shift. When the custom font loads and text reflows, CLS scores rise. A severe FOUT on a text-heavy page can push CLS above 0.25 — into "Poor" territory.


What Is FOIT (Flash of Invisible Text)?

FOIT occurs when the browser hides text while waiting for the custom font to load. Users see blank spaces where paragraphs should be. When the font arrives, the text appears — sometimes described as "popping in."

FOIT is produced by the browser default behavior (equivalent to font-display: block):

@font-face {
  font-family: 'Inter';
  src: url('/fonts/inter-regular.woff2') format('woff2');
  /* No font-display = browser default = FOIT behavior */
}

Or explicitly:

@font-face {
  font-family: 'Inter';
  src: url('/fonts/inter-regular.woff2') format('woff2');
  font-display: block;  /* Explicitly causes FOIT */
}

Modern browsers implement a 3-second FOIT timeout. During the first 3 seconds of font loading, text is invisible. If the font loads within 3 seconds, it appears cleanly with no FOUT. If the font doesn't load within 3 seconds, the browser falls back to the system font (displaying FOUT), and then swaps to the custom font when it eventually arrives.

The Safari Variation

Safari historically had slightly different behavior: it would wait indefinitely for a font before showing any text, with no 3-second fallback. This meant that on slow connections, entire pages could display completely blank text for 10, 20, or even 30 seconds. This extreme FOIT was the primary motivation for the browser industry converging on font-display as a developer-controlled solution.

Modern Safari now uses font-display: auto by default, which is closer to the 3-second block behavior, but historical Safari behavior explains why early font-display recommendations were so emphatically against the block/FOIT approach.


Why FOUT Is Better Than FOIT

Given the choice between invisible text and text in the wrong font, invisible text seems worse. Yet the web performance community firmly recommends FOUT (via font-display: swap) over FOIT. Here's why.

Readability Over Aesthetics

Text that users can read — even in a fallback font — is text that serves its purpose. Text that users cannot see serves no purpose at all. A user who encounters FOIT on a slow connection reads nothing while they wait. A user who encounters FOUT reads in the fallback font, which may be slightly less attractive but is functionally equivalent.

For content-driven sites — blogs, news, documentation, e-commerce product descriptions — content accessibility is the primary concern. Invisible text is inaccessible text.

Core Web Vitals Implications

FOIT directly harms Largest Contentful Paint. If your headline text is the largest content element on the page (the LCP element), and FOIT hides it for 2 seconds, your LCP score is 2 seconds worse than it could be. Pushing LCP above 2.5 seconds moves you from the "Good" range to "Needs Improvement."

Google's page experience ranking signals use field LCP data from real users. FOIT, by hiding text, inflates real-world LCP times across your entire user base.

FOIT Also Causes Layout Shift

A common misconception: because FOIT makes text invisible, there's no layout shift. In fact, browsers still allocate space for invisible text during FOIT — the layout is computed with the text present but rendered as transparent pixels. When the font loads and the text becomes visible, small metric differences can still cause minor layout shift.

More significantly, the transition from FOIT to visible text can sometimes cause perceptible visual disruption, even if it technically scores lower on CLS than a visible font swap. Users notice sudden appearance of large text blocks.

User Research Evidence

Eye-tracking and user experience studies consistently show that users tolerate momentary FOUT better than extended blank areas. The cognitive expectation when visiting a website is that content will be present. A blank area suggests the page is broken or still loading — increasing anxiety and decreasing trust. Text in the wrong font, by contrast, reads as intentional content, just not yet polished to final form.


Fixing FOUT and FOIT with font-display

The font-display CSS descriptor is the primary tool for controlling FOUT and FOIT behavior. Adding it to every @font-face rule gives you explicit control over what users see during font loading.

Eliminating FOIT Completely

To ensure text is never invisible (eliminating FOIT), use font-display: swap or font-display: fallback:

@font-face {
  font-family: 'Roboto';
  src: url('/fonts/roboto-regular.woff2') format('woff2');
  font-weight: 400;
  font-display: swap;
}

font-display: swap provides immediate fallback text with no invisible period. The trade-off is potential late-loading FOUT — if Roboto takes 5 seconds to load, the swap happens 5 seconds in, potentially causing layout shift during active reading.

Reducing FOUT Visibility

To minimize the visual impact of FOUT, match fallback font metrics to the custom font using size-adjust and override descriptors:

/* Fallback that approximates Roboto's metrics */
@font-face {
  font-family: 'Roboto-Fallback';
  src: local('Arial');
  size-adjust: 104.7%;
  ascent-override: 92.7%;
  descent-override: 24.4%;
  line-gap-override: 0%;
}

body {
  font-family: 'Roboto', 'Roboto-Fallback', Arial, sans-serif;
}

When Arial has been scaled to match Roboto's proportions, the visual transition during the swap is barely perceptible. Text occupies nearly the same space in both fonts, so layout barely shifts.

Eliminating Both FOUT and FOIT

The only way to eliminate both FOUT and FOIT is to ensure the font loads before any text is painted. This requires:

  1. Preloading the font so it downloads before CSS is parsed
  2. Using font-display: optional so the font is used immediately if available, and never swapped if it isn't
<link rel="preload" href="/fonts/inter-regular.woff2" as="font" type="font/woff2" crossorigin>
@font-face {
  font-family: 'Inter';
  src: url('/fonts/inter-regular.woff2') format('woff2');
  font-display: optional;
}

On first visit, if the preloaded font arrives before the initial paint, users see Inter from the first moment — no FOUT, no FOIT. On subsequent visits, the font is cached and loads instantly. The downside: on very slow connections where the font doesn't arrive before the initial paint, the fallback font is used for the entire session.


The Font Loading API Approach

The CSS Font Loading API provides JavaScript-level control over font loading behavior, enabling sophisticated strategies that aren't possible with CSS alone.

Detecting Font Load Completion

The FontFace API and document.fonts interface let you detect when specific fonts have loaded:

// Check if a specific font is loaded
document.fonts.load('400 1em Inter').then(() => {
  // Inter Regular is now loaded and available
  document.documentElement.classList.add('fonts-loaded');
});

This pattern works with a "fonts-loaded" CSS class to control FOUT visibility:

/* Without custom font loaded — use a similar system font */
body {
  font-family: 'Arial', sans-serif;
  letter-spacing: 0.01em; /* Minor adjustment to match Inter's spacing */
}

/* Once custom font is confirmed loaded — apply it */
.fonts-loaded body {
  font-family: 'Inter', 'Arial', sans-serif;
  letter-spacing: 0;
}

This approach is sometimes called the "FOUT with class" technique. It produces FOUT — text shows in Arial, then transitions to Inter — but the transition is controlled entirely in JavaScript/CSS, giving you more flexibility than font-display alone.

Font Loading API for Staged Loading

The API enables staged font loading — loading critical fonts first and deferring less important fonts:

// Load body font immediately
const bodyFont = new FontFace('Inter', 'url(/fonts/inter-regular.woff2)', {
  weight: '400',
  display: 'swap'
});

document.fonts.add(bodyFont);
bodyFont.load();

// Load display font only after body text is ready
bodyFont.loaded.then(() => {
  const displayFont = new FontFace('Playfair Display', 'url(/fonts/playfair-bold.woff2)', {
    weight: '700',
    display: 'swap'
  });
  document.fonts.add(displayFont);
  displayFont.load();
});

This ensures body text (the most critical content) loads first, then heading fonts load without competing for bandwidth during the critical loading window.

SessionStorage for Repeat Visits

A common pattern stores a flag in sessionStorage when fonts have loaded, then skips the FOUT on subsequent page views within the same session:

if (sessionStorage.getItem('fonts-loaded')) {
  document.documentElement.classList.add('fonts-loaded');
} else {
  document.fonts.ready.then(() => {
    sessionStorage.setItem('fonts-loaded', '1');
    document.documentElement.classList.add('fonts-loaded');
  });
}

On the first page view, FOUT occurs as fonts load. The flag is set. On every subsequent page view in the session, the fonts-loaded class is added immediately via JavaScript, so text renders with the custom font from the first paint — no FOUT on any page after the first.


Understanding FOUT and FOIT as distinct phenomena — with different causes, different user impacts, and different solutions — is the conceptual foundation for all font performance work. Both problems are solvable. FOUT is generally preferable to FOIT, but the ideal outcome is a font that's available before the first paint, making the question moot entirely.


Accessibility Implications

FOIT creates a specific accessibility problem that goes beyond visual aesthetics. During the invisible text period, assistive technologies like screen readers may attempt to read content that isn't visually rendered. The interaction between screen reader behavior and font loading is browser-dependent and can lead to confusing experiences for users with visual impairments who rely on both visual cues and audio output.

More significantly, FOIT violates the implicit contract that web content makes with users: that readable content will be available as quickly as possible. For users with cognitive disabilities who rely on consistent, predictable page behavior, unexpected periods of blank text areas are particularly disorienting.

WCAG (Web Content Accessibility Guidelines) doesn't have a specific criterion for FOIT, but WCAG 1.3.1 (Info and Relationships) and 2.4.3 (Focus Order) are implicated when content appears and disappears unpredictably. From a strict accessibility perspective, font-display: swap (or any value that prevents extended invisible text) is the more accessible choice.


Diagnosing FOUT and FOIT in Production

Synthetic testing in DevTools gives you control but doesn't capture what real users experience on diverse devices and connections. Real-user monitoring (RUM) can measure font-related timing in production.

Measuring Font Load Time with User Timing API

// Measure how long body text font takes to load
performance.mark('font-load-start');

document.fonts.load('400 1em Inter').then(() => {
  performance.mark('font-load-end');
  performance.measure('inter-load-time', 'font-load-start', 'font-load-end');

  const measure = performance.getEntriesByName('inter-load-time')[0];
  const loadTimeMs = Math.round(measure.duration);

  // Send to analytics
  if (window.gtag) {
    gtag('event', 'font_load_time', {
      font_family: 'Inter',
      load_time_ms: loadTimeMs,
      connection_type: navigator.connection?.effectiveType || 'unknown',
    });
  }
});

This records font load time per user, segmented by connection type. The data reveals what percentage of your users experience FOUT (font loads after initial paint) and how long the FOUT window lasts. With this information, you can make data-driven decisions about whether your font loading strategy needs improvement for your specific audience.

Typography Terms

Try These Tools

Fonts Mentioned

Related Articles