FlowingDev

Font Fallbacks, erklärt: Wie man dem CLS-Layout-Erdbeben ausweicht

Lerne, wie CSS-Font-Metriken wie size-adjust und ascent-override den Cumulative Layout Shift verhindern, indem sie System-Fonts perfekt an deine Web-Fonts anpassen.

Tool ausprobieren: Font Fallback Generator (CLS)

In einem Satz

Font Fallback Metriken sind ein Set von CSS-Superkräften, die einen System-Font künstlich so strecken und stauchen, dass er exakt denselben mathematischen Raum einnimmt wie dein Custom Web Font. Das verhindert, dass das Seiten-Layout brutal verrutscht, wenn der Custom Font endlich geladen ist.

Das Problem, das es löst

Um das Problem zu verstehen, musst du erst den ältesten Typografie-Krieg des Webs verstehen: Was zeigen wir dem User, während wir darauf warten, dass ein 150KB schwerer Custom Web Font über eine wackelige 3G-Verbindung herunterlädt?

Historisch gesehen haben Browser einen von zwei Ansätzen gewählt, und beide waren furchtbar. Der erste war FOIT (Flash of Invisible Text), bei dem der Browser den Text einfach versteckte, bis der Font fertig geladen war. User starrten auf einen leeren Screen und fragten sich, ob deine Seite kaputt ist. Der zweite war FOUT (Flash of Unstyled Text). Hier hat der Browser den Text sofort mit einem lokalen System-Font (wie Arial oder Times New Roman) gerendert und ihn dann hektisch gegen deinen wunderschönen Custom Font ausgetauscht, in der Millisekunde, in der er ankam.

Die Web-Dev-Community war sich größtenteils einig, dass FOUT besser für Accessibility und die gefühlte Performance ist. Hässlichen Text zu lesen, ist schließlich immer noch besser, als gar keinen Text zu lesen. Wir haben das mit der CSS-Regel font-display: swap offiziell gemacht.

Aber font-display: swap hat ein neues Monster erschaffen: den Cumulative Layout Shift (CLS).

Jeder Font hat ein einzigartiges Skelett. Ein "x" in Roboto ist physisch breiter als ein "x" in Arial. Die Höhe der Großbuchstaben variiert. Das Spacing, das in den Font-Files steckt, ist komplett anders. Wenn der Browser Arial gegen Roboto austauscht, nimmt der Text plötzlich unterschiedlich viel horizontalen und vertikalen Platz ein. Absätze fließen neu (Reflow). Text bricht in eine neue Zeile um.

Wenn das passiert, während der User schon liest oder gerade einen Button klicken will, springt die komplette Seite, als hätte ein Erdbeben das DOM getroffen. Buttons flüchten unter dem Cursor des Users weg. Bilder werden auf der Seite nach unten geschoben. Google hat die Core Web Vitals eingeführt, um exakt diesen Frust zu messen, und straft die SEO von Seiten mit hohem CLS hart ab. Wir brauchten einen Weg, um den hässlichen, temporären System-Font dazu zu bringen, den physischen Fußabdruck des schönen Custom Fonts perfekt zu imitieren.

Wie es unter der Haube funktioniert

Um den Layout Shift zu stoppen, fassen wir den Custom Web Font gar nicht an. Stattdessen erschaffen wir eine "Frankenstein"-Version eines lokalen System-Fonts mithilfe der @font-face-Regel. Wir nutzen spezielle CSS-Deskriptoren, um die interne Geometrie des System-Fonts zu überschreiben (Overriding).

Die Anatomie einer Font Box

Um einen Font zu manipulieren, musst du verstehen, wie ein Browser die Box um eine Textzeile berechnet. Da geht es nicht nur um die font-size. Es hängt von drei internen Metriken ab, die fest in das Font-File selbst eingebacken sind:

  1. Ascent (Oberlänge): Die Distanz von der unsichtbaren Baseline (auf der die Buchstaben sitzen) bis zur Spitze der höchsten Zeichen (wie das obere Ende eines 'h' oder 'T').
  2. Descent (Unterlänge): Die Distanz von der Baseline bis zum unteren Ende der am tiefsten hängenden Zeichen (wie der Schwanz eines 'y', 'g' oder 'p').
  3. Line Gap (Durchschuss): Der empfohlene Leerraum zwischen dem Descent der einen Zeile und dem Ascent der nächsten Zeile (oft auch Leading genannt).

In CSS ist die Gesamthöhe einer Textzeile grob gesagt ascent + descent + line gap. Wenn dein Custom Font und dein System-Font unterschiedliche Werte für diese drei Metriken haben, wird dein Layout vertikal springen. Wenn die Buchstaben unterschiedlich breit sind, springt dein Layout horizontal.

Auftritt der Overrides

CSS Fonts Module Level 4 hat vier magische Deskriptoren eingeführt, die innerhalb eines @font-face-Blocks angewendet werden können. Diese Deskriptoren klinken sich ein, wenn der Browser die internen Metriken des Font-Files liest, und überschreiben sie on-the-fly.

  • size-adjust: Wirkt wie ein Multiplikator für den kompletten Font. Wenn dein Custom Font generell breiter als Arial ist, könntest du size-adjust: 110% bei Arial setzen, um ihn horizontal zu strecken.
  • ascent-override: Überschreibt die interne Ascent-Metrik des Fonts.
  • descent-override: Überschreibt die interne Descent-Metrik des Fonts.
  • line-gap-override: Überschreibt die interne Line-Gap-Metrik des Fonts.

Die Mathematik zusammenfügen

Der Trick (und die Falle) ist, dass size-adjust alles skaliert – inklusive der horizontalen Breiten, des Ascents und des Descents. Wenn du also einen System-Font um 110% hochskalierst, damit die Zeilenbreiten zu deinem Web Font passen, hast du unabsichtlich auch die vertikale Höhe des System-Fonts um 110% vergrößert. Das verursacht wiederum einen vertikalen Layout Shift!

Um das zu fixen, werden die Overrides (ascent-override, descent-override, line-gap-override) als Prozentsätze des size-adjust-Wertes berechnet, nicht anhand der originalen Font-Size. Du nutzt size-adjust, um die horizontale Breite perfekt anzupassen, und dann nutzt du die Override-Metriken, um die vertikale Höhe quasi wieder zusammenzuquetschen (oder in die Länge zu ziehen), damit sie exakt der Bounding Box deines Custom Fonts entspricht.

So sieht der resultierende Code aus. Achte darauf, wie wir eine brandneue, imaginäre Font-Family namens Roboto-Fallback erstellen, die den lokalen Arial-Font mit mathematisch präzisen Overrides wrappt:

/* 1. Lade deinen eigentlichen Web Font */
@font-face {
  font-family: 'Roboto';
  src: url('/fonts/roboto.woff2') format('woff2');
  font-display: swap;
}

/* 2. Erschaffe den Frankenstein-Fallback */
@font-face {
  font-family: 'Roboto-Fallback';
  src: local('Arial');
  /* Strecke Arial horizontal, passend zu Roboto */
  size-adjust: 104.5%; 
  /* Stauche Arials vertikale Metriken, passend zu Roboto */
  ascent-override: 89.92%;
  descent-override: 23.44%;
  line-gap-override: 0%;
}

/* 3. Nutze sie gemeinsam in deinem CSS */
body {
  font-family: 'Roboto', 'Roboto-Fallback', sans-serif;
}

Wenn die Seite lädt, malt (paints) der Browser sofort Roboto-Fallback. Das ist einfach nur lokales Arial, aber horizontal gestreckt und vertikal gestaucht, um dem exakten mathematischen Fußabdruck von Roboto zu entsprechen. Wenn das echte Roboto-File fertig runtergeladen ist, passiert der Swap: Die Buchstabenformen ändern ihr Design, aber das Layout bewegt sich keinen einzigen Pixel.

Storys aus der echten Welt

Die "Abbrechen"-Button-Falle

Eine große E-Commerce-Plattform kämpfte mit Warenkorb-Abbrüchen und wütenden Customer-Support-Tickets. User beschwerten sich, dass sie aus Versehen ihre Warenkörbe leerten. Das Engineering-Team nahm User-Sessions auf und fand den Schuldigen. Die Seite lud einen schweren Branded Font. Ein User sah den "Checkout"-Button direkt neben dem "Warenkorb leeren"-Button. Genau in der Millisekunde, in der die Maus auf "Checkout" klickte, lud der Branded Font. Dieser Font war deutlich breiter als der System-Standard. Der Text reflowte, schob den "Checkout"-Button nach rechts und positionierte den "Warenkorb leeren"-Button exakt unter den herabsausenden Mausklick des Users. Die Implementierung von size-adjust und Metric Overrides nagelte die Buttons während des Font Swaps fest, woraufhin die versehentliche Abbruchrate sofort in den Keller ging.

Das News-Artikel-Erdbeben

Ein beliebter Tech-Blog machte ein massives Redesign, inklusive eines wunderschönen, modernen Serif-Fonts für Long-Form-Artikel. Um Speed zu priorisieren, nutzten sie font-display: swap. Leser mit langsameren Verbindungen erlebten jedoch ein extrem nerviges Phänomen: Sie luden einen Artikel, scrollten drei Absätze runter und fingen an zu lesen. Zwei Sekunden später lud der Serif-Font. Weil der Serif-Font einen größeren ascent und descent hatte als der System-Standard, wuchs jede einzelne Textzeile um den Bruchteil eines Millimeters. Über drei Absätze hinweg summierte sich das zu einem massiven vertikalen Shift, der den Satz, den der User gerade las, komplett vom Screen katapultierte. Durch das Hinzufügen von ascent-override und descent-override zu einem lokalen Fallback-Font swappte der Text perfekt auf der Stelle und hielt das Auge des Lesers genau da, wo es hingehörte.

Der Lighthouse-Score-Heartbreak

Ein Frontend-Dev verbrachte drei Wochen damit, eine Web-App für Googles Core Web Vitals zu optimieren. Bilder wurden in WebP komprimiert, unkritisches JavaScript wurde deferrt und das CSS minifiziert. Er ließ Google Lighthouse laufen und erwartete siegessicher einen perfekten 100er Score. Stattdessen gab es eine 75. Der Report flaggte eine massive Cumulative-Layout-Shift-Strafe. Der komplette Shift wurde von einer einzigen <H1>-Headline verursacht, die beim Laden eines Custom Web Fonts von einer auf zwei Zeilen umbrach. Anstatt den Header neu zu designen oder auf unsichtbaren Text zurückzugreifen, generierte der Dev exakte Fallback-Metriken für den Heading-Font. Der Layout Shift droppte auf null, der Lighthouse-Score knackte die 100 und der Developer konnte endlich schlafen.

Häufige Fehler und Fallen

  • Die Prozentzahlen raten: Du kannst diese Zahlen nicht über den Daumen peilen. Die Berechnungen erfordern es, die exakten Daten für Unique Units Per Em (UPM), Ascent, Descent und Line Gap direkt aus den binären Tabellen im Web-Font- und im System-Font-File zu extrahieren und sie über Kreuz zu multiplizieren (Cross-Multiplying). Nutze immer einen programmatischen Generator, um die Mathematik fehlerfrei hinzubekommen.
  • Overrides auf den Web Font anwenden: Eine klassische Falle ist es, size-adjust direkt in die @font-face-Deklaration des Custom Web Fonts zu ballern. Das bringt gar nichts. Die Overrides müssen auf eine komplett separate @font-face-Deklaration angewendet werden, die einen local() System-Font lädt, den du dann an zweiter Stelle in deinem font-family-Stack platzierst.
  • Strukturell komplett gegensätzliche Fallbacks wählen: Font-Metriken können auch keine Wunder vollbringen. Wenn dein Custom Font eine ultra-condensed Sans-Serif ist und du als lokale Fallback-Basis einen breiten, ausladenden Serif-Font wählst, wird die size-adjust-Mathematik den Serif-Font komplett verzerrt und unleserlich aussehen lassen, während er auf den Swap wartet. Wähle immer einen lokalen System-Font, der eine grob ähnliche Skelettstruktur hat wie dein Web Font.
  • Das line-gap-override vergessen: Devs fokussieren sich oft komplett auf Breite und Höhe und nehmen an, dass es reicht, den Line Gap auf normal zu setzen oder ihn wegzulassen. Wenn der Browser aber beschließt, dem Fallback-Font ein Default-Leading zu verpassen, das nicht perfekt zum internen Leading des Web Fonts passt, verschiebt sich dein vertikaler Rhythmus. Definiere es immer explizit.

Warum du das auf dem Radar haben solltest

Du solltest in dem Moment an Font Fallback Metriken denken, in dem du dich entscheidest, einen Custom Web Font in einem Projekt zu nutzen, bei dem UX und SEO wichtig sind.

Historisch gesehen war Typografie im Web ein Kompromiss zwischen Ästhetik (schöne Fonts nutzen) und Performance (springende Layouts vermeiden). Mit der Einführung von CSS-Font-Metriken ist dieser Kompromiss Geschichte. Wann immer du die Core Web Vitals einer Seite auditierst, mit einem miesen CLS-Score kämpfst oder wiederverwendbare UI-Komponenten baust, die es sich nicht leisten können, auf dem Screen herumzuzittern (Jitter), sind Font Fallback Overrides die definitive Engineering-Lösung. Sie verwandeln Web Fonts von einem Loading-Risiko in eine nahtlose, nicht wahrnehmbare Transition.

Tauch tiefer ein

Theorie erledigt. Zeit, loszulegen — 100 % in deinem Browser.

Tool ausprobieren: Font Fallback Generator (CLS)