FOIT, FOUT, FOFT und der richtige Umgang mit font-display
Web-Fonts sind einer der häufigsten Auslöser für unsichtbaren Text und ruckelnde Layout-Shifts. Dieser Artikel erklärt die Rendering-Mechanik hinter FOIT, FOUT und FOFT, zeigt, wie font-display, Preloading, Variable Fonts und size-adjust zusammenspielen, und macht Font-Loading in Magento- und Hyvä-Shops technisch sauber und messbar stabil.
Inhaltsverzeichnis
- 1. Warum Font-Loading eine Rendering-Entscheidung ist
- 2. FOIT, FOUT und FOFT im Detail
- 3. font-display: auto, block, swap, fallback, optional
- 4. Kritische Fonts gezielt preloaden
- 5. Variable Fonts zur Reduktion der Dateianzahl
- 6. size-adjust, ascent-override und descent-override
- 7. Self-Hosting vs. Font-CDN
- 8. Die Font Loading API und document.fonts.ready
- 9. font-display-Werte im direkten Vergleich
- 10. Zusammenfassung
- 11. FAQ
1. Warum Font-Loading eine Rendering-Entscheidung ist
Web-Fonts werden nicht automatisch mit dem HTML ausgeliefert, sondern als separate Ressource nachgeladen - und der Browser muss in der Zwischenzeit entscheiden, was er mit Text tun soll, dessen Zielschriftart noch nicht verfügbar ist. Diese Entscheidung ist keine Nebensächlichkeit, sondern eine der wenigen Stellen, an denen das Rendering-Verhalten des Browsers direkt konfigurierbar ist. Wer die Optionen nicht kennt, überlässt die Entscheidung dem jeweiligen Browser-Default, und die fallen je nach Engine unterschiedlich aus.
Für Magento- und Hyvä-Shops ist das besonders relevant, weil Produktseiten oft mehrere Schriftschnitte gleichzeitig laden: Regular für Fließtext, Bold für Preise und Headlines, teils eine separate Icon-Schriftart. Jede dieser Dateien durchläuft denselben Lebenszyklus aus Anfrage, Download und Rendering-Freigabe. Ohne bewusste Steuerung über font-display und Preloading entstehen daraus in Summe spürbare Verzögerungen bei der Textsichtbarkeit und unerwartete Sprünge im Layout, sobald die echte Schriftart die Fallback-Schrift ersetzt.
2. FOIT, FOUT und FOFT im Detail
FOIT (Flash of Invisible Text) beschreibt das Verhalten, bei dem der Browser Text so lange unsichtbar hält, bis die Web-Font-Datei vollständig geladen ist. Das war lange der Chrome-Standard und führt bei langsamen Verbindungen zu einem leeren Viewport, obwohl der Text technisch längst im DOM steht. FOUT (Flash of Unstyled Text) ist das Gegenteil: Der Browser rendert sofort mit einer Fallback-Schrift und tauscht sie aus, sobald die Zielschrift verfügbar ist. Text ist immer lesbar, aber es kann zu einem sichtbaren Sprung kommen, wenn Fallback- und Zielschrift unterschiedliche Laufweiten haben.
FOFT (Flash of Faux Text) ist eine dritte, seltener genutzte Strategie: Zunächst wird eine reduzierte Version der Zielschrift geladen, etwa nur der Regular-Schnitt, während Bold oder Italic clientseitig synthetisiert werden. Sobald die vollständigen Schnitte eintreffen, ersetzt der Browser die synthetisierten Varianten durch die echten. FOFT reduziert die initiale Downloadgröße, erkauft sich das aber mit potenziell zwei sichtbaren Wechseln statt einem. In der Praxis ist FOUT mit sauber abgestimmten Fallback-Metriken für die meisten Magento-Shops der robusteste Kompromiss zwischen Sichtbarkeit und visueller Stabilität.
3. font-display: auto, block, swap, fallback, optional
Die CSS-Eigenschaft font-display in einer @font-face-Deklaration steuert genau dieses Verhalten deklarativ, ohne JavaScript. Sie definiert zwei Zeitfenster: den Block-Zeitraum, in dem Text unsichtbar bleibt, falls die Font noch nicht geladen ist, und den Swap-Zeitraum, in dem mit Fallback-Schrift gerendert und bei Fertigstellung ausgetauscht wird. Nach Ablauf beider Fenster wird endgültig entweder die Zielschrift oder dauerhaft die Fallback-Schrift verwendet, je nach Wert.
auto überlässt die Entscheidung dem Browser und ist damit unvorhersehbar. block erzwingt einen kurzen Block-Zeitraum (typisch 3 Sekunden) gefolgt von unbegrenztem Swap, was FOIT auf kurze Zeit begrenzt. swap setzt den Block-Zeitraum auf praktisch null und rendert sofort mit Fallback, was FOUT erzeugt und für die meisten Content-Seiten die richtige Wahl ist. fallback erlaubt nur ein sehr kurzes Block-Fenster (ca. 100ms) und ein kurzes Swap-Fenster (ca. 3s), danach bleibt dauerhaft die Fallback-Schrift aktiv, wenn die Zielschrift zu spät kommt. optional geht noch weiter: Kein Block-Fenster, minimales Swap-Fenster, und der Browser darf den Download bei langsamer Verbindung sogar ganz abbrechen und dauerhaft die Fallback-Schrift nutzen, ideal für Ladezeiten unter Netzwerkdruck ohne jeden CLS-Beitrag.
/* font-display steuert Block- und Swap-Zeitfenster deklarativ */
@font-face {
font-family: "Inter";
src: url("/fonts/inter-regular.woff2") format("woff2");
font-weight: 400;
font-display: swap; /* immediate fallback render, swap when ready */
}
@font-face {
font-family: "Inter";
src: url("/fonts/inter-bold.woff2") format("woff2");
font-weight: 700;
font-display: swap;
}
/* Non-critical icon font: no layout impact if it never loads */
@font-face {
font-family: "ShopIcons";
src: url("/fonts/shop-icons.woff2") format("woff2");
font-display: optional; /* skip download entirely on slow connections */
}
body {
font-family: "Inter", system-ui, -apple-system, sans-serif;
}
4. Kritische Fonts gezielt preloaden
Der Browser entdeckt @font-face-Deklarationen erst, wenn das CSS geparst ist, und startet den Font-Download meist erst danach, wenn er auf ein Element trifft, das die Schrift tatsächlich braucht. Dieser späte Entdeckungszeitpunkt ist der Hauptgrund für sichtbares FOUT selbst bei schnellen Verbindungen. Ein <link rel="preload" as="font"> im Dokumenten-Head durchbricht diese Kette: Der Browser lädt die Datei parallel zum CSS, ohne auf dessen Parsing zu warten, und reduziert die Zeit bis zur Zielschrift oft um mehrere hundert Millisekunden.
Wichtig ist, das Preloading auf die tatsächlich above-the-fold sichtbaren Schriftschnitte zu beschränken - Preloading einer Bold-Variante, die erst im Footer verwendet wird, verschwendet Bandbreite, die für kritischere Ressourcen fehlt. Das Attribut crossorigin ist bei Font-Preloads zwingend erforderlich, auch bei same-origin Requests, da Fonts laut Spezifikation immer im CORS-Modus geladen werden; fehlt es, lädt der Browser die Datei beim tatsächlichen Einsatz ein zweites Mal.
<!-- Preload only the above-the-fold font weights, crossorigin is mandatory -->
<link rel="preload" as="font" type="font/woff2"
href="/fonts/inter-regular.woff2" crossorigin>
<link rel="preload" as="font" type="font/woff2"
href="/fonts/inter-bold.woff2" crossorigin>
<!-- Hyvä phtml: preload the variable font used for hero and product title -->
<link rel="preload" as="font" type="font/woff2"
href="{{$block->getViewFileUrl('fonts/inter-var.woff2')}}" crossorigin>
<style>
/* CSS declared after preload so the browser already has the bytes cached */
@font-face {
font-family: "Inter";
src: url("/fonts/inter-var.woff2") format("woff2-variations");
font-weight: 400 700;
font-display: swap;
}
</style>
5. Variable Fonts zur Reduktion der Dateianzahl
Klassische Web-Font-Setups laden pro Schriftschnitt eine eigene Datei: Regular, Bold, Italic, Bold Italic, oft in mehreren Gewichten. Für einen Shop mit vier bis sechs Schnitten summiert sich das schnell auf 300-500 KB an Font-Daten, die alle den kritischen Rendering-Pfad belasten. Variable Fonts kodieren den gesamten Gewichtsbereich, teils auch Breite und Kursivität, in einer einzigen Datei mit interpolierbaren Achsen. Statt fünf separater 60-KB-Dateien lädt der Browser eine einzige Datei von oft nur 80-120 KB, die jedes Gewicht zwischen 100 und 900 abdeckt.
Der Effekt auf Font-Loading ist doppelt positiv: Weniger HTTP-Requests bedeuten weniger Verbindungsaufbau-Overhead, und weil nur eine Datei geladen werden muss, ist auch nur ein Preload-Link nötig statt mehrerer. CSS-seitig wird der Gewichtsbereich über font-weight: 400 700 als Range deklariert, und im laufenden Stylesheet kann dann jeder beliebige Zwischenwert wie font-weight: 550 genutzt werden, was mit statischen Fonts unmöglich wäre. Der Kompromiss: Variable Fonts sind pro Datei größer als ein einzelner statischer Schnitt, daher lohnt sich der Umstieg erst, wenn tatsächlich mehrere Gewichte im Einsatz sind.
/* One variable font file covers the entire weight axis 100-900 */
@font-face {
font-family: "Inter Var";
src: url("/fonts/inter-var.woff2") format("woff2-variations");
font-weight: 100 900;
font-stretch: 75% 125%;
font-display: swap;
}
/* Any intermediate weight is now usable without extra files */
.product-price {
font-family: "Inter Var", system-ui, sans-serif;
font-weight: 650;
}
.hero-headline {
font-family: "Inter Var", system-ui, sans-serif;
font-weight: 780;
}
6. size-adjust, ascent-override und descent-override
Selbst mit perfektem font-display: swap bleibt ein Restproblem: Fallback- und Zielschrift haben fast nie identische Metriken. Unterschiedliche x-Höhe, Laufweite und Zeilenabstand führen dazu, dass Text beim Font-Swap seine Zeilenumbrüche verschiebt und dadurch nachfolgende Elemente nach oben oder unten rutschen, gemessen als Cumulative Layout Shift. Die CSS-Deskriptoren size-adjust, ascent-override, descent-override und line-gap-override in @font-face lösen genau dieses Problem, indem sie die Metriken einer Fallback-Schrift so anpassen, dass sie der Zielschrift möglichst nahekommen.
Der praktische Workflow: Für die Zielschrift, etwa Inter, wird eine passende System-Fallback-Schrift wie Arial gewählt und über ein Tool wie fontaine oder den Capsize-Metrikrechner analysiert, wie stark Ascent, Descent und Zeichenbreite abweichen. Die berechneten Prozentwerte werden dann als separate @font-face-Regel für die Fallback-Schrift definiert und im font-family-Stack direkt vor der Zielschrift platziert. Das Ergebnis: Der Fallback-Text während des Swap-Fensters nimmt bereits nahezu denselben Platz ein wie der finale Text, wodurch der Layout-Sprung beim Font-Austausch praktisch verschwindet.
/* Target font */
@font-face {
font-family: "Inter";
src: url("/fonts/inter-var.woff2") format("woff2-variations");
font-weight: 100 900;
font-display: swap;
}
/* Metric-matched fallback: Arial adjusted to mimic Inter's box model */
@font-face {
font-family: "Inter Fallback";
src: local("Arial");
size-adjust: 107.4%;
ascent-override: 90.2%;
descent-override: 22.8%;
line-gap-override: 0%;
}
body {
/* Fallback renders at near-identical width during the swap window */
font-family: "Inter", "Inter Fallback", system-ui, sans-serif;
}
7. Self-Hosting vs. Font-CDN
Google Fonts und ähnliche Font-CDNs bieten bequeme <link>-Einbindung, erzwingen aber einen zusätzlichen DNS-Lookup, TCP-Handshake und TLS-Handshake zu einer fremden Domain, bevor der eigentliche Font-Download überhaupt startet - typischerweise 100-300ms zusätzliche Latenz auf mobilen Verbindungen. Seit dem Wegfall des gemeinsamen Browser-Caches über Domains hinweg (Cache-Partitionierung) entfällt zudem der frühere Vorteil, dass ein Nutzer die Google-Fonts-Datei bereits von einem anderen Besuch im Cache hatte. Für DSGVO-relevante Shops in der EU kommt hinzu, dass das direkte Einbinden von Google-Fonts-URLs als problematisch für den Datenschutz gilt, da dabei die IP-Adresse an Google übertragen wird.
Self-Hosting löst beide Probleme: Die Font-Dateien liegen auf derselben Domain oder demselben CDN wie der restliche Shop, wodurch Verbindungsaufbau entfällt und Preload-Hints zuverlässig funktionieren, weil rel="preconnect" für eine fremde Font-Domain nicht mehr nötig ist. In Magento lassen sich Fonts einfach im Theme-Verzeichnis unter web/fonts ablegen und über getViewFileUrl() referenzieren, sodass sie automatisch durch setup:static-content:deploy versioniert und über den CDN des Shops mit langen Cache-Headern ausgeliefert werden. Der einzige Nachteil von Self-Hosting ist der manuelle Pflegeaufwand bei Font-Updates, was sich durch ein einfaches Build-Skript im Theme-Repository fast vollständig automatisieren lässt.
8. Die Font Loading API und document.fonts.ready
Für Fälle, in denen CSS allein nicht ausreicht, etwa um eine Ladeanimation exakt bis zum Font-Rendering zu synchronisieren oder um FOUT-Übergänge sanft einzublenden, bietet der Browser die CSS Font Loading API über das globale document.fonts-Objekt. Sie erlaubt es, den Ladezustand einzelner Font-Faces programmatisch abzufragen und auf das vollständige Laden aller referenzierten Fonts zu warten, ohne auf unzuverlässige Timeout-Heuristiken zurückzugreifen.
Ein gängiges Pattern ist, dem <html>-Element vor dem Laden eine Klasse wie fonts-loading zu geben, die per CSS eine unauffällige Fallback-Darstellung erzwingt, und diese Klasse erst nach document.fonts.ready gegen fonts-loaded auszutauschen. So lässt sich ein sanfter Opacity-Übergang für den Font-Swap realisieren, der optisch weniger abrupt wirkt als ein harter Sprung, ohne dabei den nativen font-display: swap-Mechanismus zu ersetzen. Wichtig ist, dieses Pattern nur additiv zu nutzen, denn ohne aktiviertes JavaScript muss der native Browser-Fallback trotzdem greifen.
// Wait for all referenced fonts to finish loading, then swap a CSS class
document.documentElement.classList.add('fonts-loading');
document.fonts.ready.then(() => {
document.documentElement.classList.remove('fonts-loading');
document.documentElement.classList.add('fonts-loaded');
});
// Check a specific font-face without waiting for the whole document
document.fonts.load('700 1em "Inter"').then((loadedFaces) => {
if (loadedFaces.length > 0) {
console.log('Bold Inter is available for rendering');
}
});
// React to individual font load events for fine-grained control
document.fonts.addEventListener('loadingdone', (event) => {
event.fontfaces.forEach((face) => {
console.log(`Loaded: ${face.family} ${face.weight}`);
});
});
9. font-display-Werte im direkten Vergleich
Jeder font-display-Wert trifft eine andere Abwägung zwischen Textsichtbarkeit, visueller Stabilität und Kontrolle über die Markenschrift. Die folgende Übersicht fasst zusammen, wann welcher Wert die richtige Wahl ist.
| Wert | Block-Fenster | Verhalten | Empfehlung |
|---|---|---|---|
| auto | Browserabhängig | Unvorhersehbar, oft FOIT in Chrome | Vermeiden |
| block | ~3 s | Kurzes FOIT, dann unbegrenztes Swap | Nur bei Icon-Fonts vertretbar |
| swap | ~0 ms | Sofortiges FOUT, Text immer sichtbar | Standard für Fließtext |
| fallback | ~100 ms | Kurzes Swap-Fenster, danach Fallback fix | Guter Kompromiss bei Markenschriften |
| optional | 0 ms | Download kann abgebrochen werden, kein CLS | Ideal für Nebensächliches |
In der Praxis lassen sich die Werte kombinieren: swap für die Hauptschrift, die die Markenidentität trägt, und optional für dekorative oder sekundäre Schriftschnitte, die keine kritische Information tragen. Zusammen mit Preloading, Variable Fonts und metrikangepassten Fallbacks entsteht daraus eine Font-Loading-Strategie, die weder unsichtbaren Text noch störende Layout-Shifts produziert.
Mironsoft
Font-Loading-Engineering und Rendering-Performance für Magento- und Hyvä-Shops
Font-Loading ohne Layout-Shift umsetzen?
Wir analysieren eure aktuelle Font-Ladestrategie, migrieren zu Self-Hosting mit Variable Fonts und richten metrikangepasste Fallbacks ein, damit Text sofort sichtbar ist und beim Font-Swap kein CLS entsteht.
Font-Audit
Analyse aller geladenen Schriftschnitte, Dateigrößen und font-display-Werte
Self-Hosting-Migration
Umzug von Font-CDNs auf performantes, DSGVO-konformes Self-Hosting
Variable-Font-Integration
Konsolidierung mehrerer Schnitte in eine einzige, optimierte Font-Datei
10. Zusammenfassung
Font-Loading ohne Layout-Shift beginnt mit dem Verständnis der drei Grundmuster FOIT, FOUT und FOFT und der Entscheidung, welches davon für welchen Schriftschnitt sinnvoll ist. font-display: swap ist für die meisten Textinhalte die richtige Basis, optional eignet sich für nicht kritische Schriften, und ein bewusst gesetztes <link rel="preload"> verkürzt die Zeit bis zur Zielschrift spürbar, weil der Download nicht erst auf das Parsen des CSS wartet.
Variable Fonts reduzieren die Anzahl der zu ladenden Dateien drastisch und vereinfachen damit auch das Preloading, während size-adjust, ascent-override und descent-override den eigentlichen Layout-Shift beim Font-Swap eliminieren, indem sie die Fallback-Schrift metrisch an die Zielschrift angleichen. Self-Hosting entfernt zusätzliche Latenz durch fremde Domains und reduziert Datenschutzrisiken gegenüber Font-CDNs. In Kombination ergibt sich eine Ladestrategie, bei der Text von der ersten Sekunde an sichtbar ist und beim Font-Wechsel kein spürbarer Sprung mehr entsteht.
Font-Loading ohne Layout-Shift - Das Wichtigste auf einen Blick
font-display richtig wählen
swap für Fließtext, optional für Nebensächliches, auto vermeiden.
Kritische Fonts preloaden
<link rel="preload" as="font"> mit crossorigin, nur above-the-fold.
Variable Fonts nutzen
Eine Datei statt vieler Schnitte, weniger Requests und einfacheres Preloading.
Fallback-Metriken angleichen
size-adjust, ascent-override, descent-override gegen CLS beim Font-Swap.