Webfont Performance, FOUT, FOIT und Usability

Webfonts Performancevergleich

Webfonts haben sich in den letzten knapp 6 Jahren zu einem mehr oder weniger fixen Bestandteil des modernen Webdesign entwickelt. Neben gestalterischen Fortschritten, hat sich auch sehr viel in der Art und Weise, wie wir Webfonts einbinden, getan. Die @font-face Syntax ist mehrere Entwicklungsschritte durchgegangen, es gibt einige Services, die Webfonts anbieten, und eine neue API ist im Anmarsch.

Inspiriert durch Bram Steins Talk auf der Smashing Conference 2015 in Freiburg habe ich mich die letzten Tage mit dem Laden von Webfonts und den damit verbundenen Aspekten hinsichtlich der Usability und Performance beschäftigt. Ich habe mir angesehen, wie sich Browser verhalten während Webfonts geladen werden und wie man darüber Kontrolle ergreifen kann, um ein möglichst konsistentes Verhalten in allen Browsern zu erreichen und dabei auch die Webfont Performance zu verbessern. Weiters habe ich unterschiedliche Optionen getestet Webfonts zu laden und die Ergebnisse zusammengefasst.

Webfonts: Services und Formate

Seit 2009 haben wir die Möglichkeit mit Webfonts zu arbeiten und müssen uns nicht mehr auf Systemfonts beschränken. Webfonts können wir entweder selber auf unseren Servern hosten oder eines der vielen Services (z.B. Google Fonts, Typekit, Fonts.com oder Fontdeck) verwenden.

Browser WOFF Support

Webfonts gibt es in verschiedenen Formaten (.eot, .ttf, .svg, etc.), wobei wir uns heute auf WOFF und WOFF2 (bessere Kompression als WOFF) beschränken können und sollten. Der Vorteil von WOFF und besonders WOFF2 gegenüber den anderen Formate ist die deutlich geringere Dateigröße.

Font Lademechanismus

Das alleinige Einbinden eines Fonts mit @font-face bezweckt noch keinen Request. Erst wenn ein Selektor die Schriftart mit font-family zugewiesen bekommt und auch ein passendes HTML-Element existiert wird der Font heruntergeladen. Diesen Mechanismus nennt man Lazy Loading und grundsätzlich ist das auch eine gute Sache, weil so unnötige Requests verhindert werden.

Lazyloading is bad for webfonts

-Bram Stein

Lazy Loading kann aber zur Folge haben, dass der Font noch geladen wird, die Seite aber schon fertig aufgebaut ist. Ist der Font schon im Browsercache, gibt es kein Problem, weil er quasi sofort abgerufen und angewendet werden kann. Ist das nicht der Fall, muss zwischenzeitlich die Darstellung des Textes irgendwie bewerkstelligt werden. Das kann sich sehr deutlich auf die Usability und Accessibility auswirken, wie man im folgenden gif sieht.

FOIT und FOUT

Aufbau auf einem Smartphone mit 3G Verbindung. Obwohl schon die ersten Elemente, sogar Bilder und Icons, zu sehen sind, wird der Text erst sehr spät angezeigt.

Darstellung von Webfonts während dem Laden

Lange wurde darüber diskutiert, wie Text dargestellt werden soll während die Webfonts noch geladen werden. So richtig einig ist man sich darüber immer noch nicht und Browserhersteller ändern auch gerne ihre Meinung. Es gibt zwei verschiedene Herangehensweisen, die zwei unterschiedliche Effekte zur Folge haben.

FOUT

Die erste Herangehensweise zeigt zuerst den Fallback Font, der im Font-Stack definiert worden ist, und tauscht diesen durch den Webfont aus, sobald dieser fertig geladen hat. Beispielsweise würde bei font-family: 'Francois One', Times, sans-serif Times angezeigt werden und dann, nach erfolgreichem Download, Francois One. Diesen Wechsel von einem zum anderen Font nennt man Flash of unstyled Text (FOUT).

Flash of unstyled Text

Firefox and Opera haben mit diesem Verhalten angefangen und schließlich aber zur zweiten Variante gewechselt. Das gilt auch für alle anderen großen Browserhersteller. Der einzige aktuelle Browser mit FOUT ist Internet Explorer.

Nachteile von FOUT

Das Problem mit FOUT ist, dass sich das Layout durch das Austauschen der Fonts verschieben kann, besonders dann wenn Fallback Font und Webfont sehr unterschiedlich sind. Zudem sieht dieser Flash of unstyled Text ungewollt und wie ein Fehler aus.

FOIT

Auf Grund der Nachteile von FOUT haben die meisten Browser begonnen Text zu verstecken während der Webfont geladen wird. Anstatt des Fallback Fonts ist unsichtbarer Text zu sehen (oder eben nicht zu sehen). Das führt dazu, dass das Layout nicht mehr zuckt. Das nennt man dann Flash of invisible Text (FOIT).

Flash of invisible Text

Nachteile von FOIT

FOIT ist zwar optisch vielleicht schöner, aber das große Problem daran ist, dass es bei langsamen Internetverbindungen (z.B.: Smartphone oder öffentliches WLAN) passieren kann, dass lange Zeit unsichtbarerer Text dargestellt wird, obwohl der Rest des Layouts bereits aufgebaut und bedienbar ist. Um dem entgegenzuwirken zeigen Firefox, Chrome und Opera nach 3 Sekunden unsichtbarem Text erst recht wieder den Fallback Font. Das ist zwar eine Lösung, aber richtig ideal ist das nicht, weil 3 Sekunden nicht gerade kurz sind (siehe transfermarkt.at gif oben). In Safari, Safari (iOS) und Android Webkit ist es überhaupt so, dass ein Fallback Font erst nach 30 Sekunden oder gar nicht angezeigt wird.

FOUT optimieren

Auf Grund der eben beschriebenen Nachteile ist FOIT für viele nicht die ideale Lösung. Wenn man für sich akzeptiert hat, dass es zu FOUT kommen kann, kann man verschiedene Dinge machen, um diesen Wechsel von einer Schrift zu anderen unscheinbarer zu machen.

Font Stacks

Eine der einfachsten Optimierungen ist das Aufbauen des Font Stacks so, dass man Fallbackfonts verwendet, die dem Webfont ähnlich sind. Wenn der Fallback Font ansatzweise ähnliche Eigenschaften aufweist wie der Webfont, ist der Sprung nicht mehr so extrem.

Zum Beispiel ist font-family: Arial, Times, monospace; keine gute Wahl, weil alle drei Schriften sehr unterschiedlich sind.

Font Stacks testen

Auf fontfamily.io kann man Font Stacks testen. Man sieht welche Fallback Fonts auf welchem Betriebssystem installiert sind und ob Fallback Fonts überhaupt zum Einsatz kommen könnten. Beispielsweise bei font-family: Arial, "Helvetica Neue", Helvetica, sans-serif; würden "Helvetica Neue" und Helvetica nie verwendet werden, weil Arial überall installiert ist.

Mehr über dieses Thema erfährt man in einem Artikel von Zach Leatherman.

font-size-adjust

Es gibt viele Charakteristika, die eine Schrift ausmachen. Eine davon ist die x-Höhe, die Höhe der Kleinbuchstaben. Ein Grund warum der FOUT sehr deutlich zu sehen sein kann, ist, dass die x-Höhe des Fallback Fonts stark von der x-Höhe des Webfonts abweicht. Jeder Font hat einen Formfaktor, der das Verhältnis von x-Höhe zu Schrifthöhe darstellt. Dieses Verhältnis kann man berechnen, wenn man die x-Höhe durch die Schriftgröße rechnet.

Mit der Eigenschaft font-size-adjust kann man diesen Wert auf alle Schriften anwenden. Dadurch passt sich dann die Höhe der Kleinbuchstaben der Fallbacks Fonts an die Höhe der Kleinbuchstaben des Webfonts an.

In folgendem Beispiel ist der Text zuerst in Arial 50px zu sehen, dann Verdana 50px und schließlich Verdana 50px mit font-size-adjust.

See the Pen font-size-adjust Test by Manuel Matuzovic (@matuzo) on CodePen.

Die Eigenschaft font-size-adjust funktioniert aktuell leider nur in Firefox.

Herausfinden ob Fonts installiert sind

Sollte man herausfinden wollen, ob ein bestimmter Font auf dem System installiert ist oder nicht, kann man dafür Font Face Observer von Bram Stein verwenden und mögliche Anpassungen mit Javascript machen.

var observer = new FontFaceObserver('My Family', {
  weight: 400
});

observer.check().then(function () {
  console.log('Font is available');
}, function () {
  console.log('Font is not available');
});

Webfonts laden

Die Art und Weise mit der man Fonts lädt spielt eine sehr wichtige Rolle, da es zwischen den verschiedenen Techniken massive Unterschiede gibt. Ich habe mir 6 Herangehensweisen angesehen und in punkto Performance und Kontrollmöglichkeiten verglichen.

Lokal auf dem Server

Die Fonts liegen auf dem eigenen Server und man bindet sie mit @font-face ein.

@font-face{
    font-family:Cabin;
            src:url(cabin-regular-webfont.woff2) format('woff2'),
                url(cabin-regular-webfont.woff) format('woff'),
                url(cabin-regular-webfont.ttf) format('truetype');
}

Vorteile: Geringe Dateigröße, wenn man die Fonts vorher bearbeitet hat (Stichwort: Subsetting).

Nachteile: Keine Kontrolle über FOUT und FOIT. Liegt nicht auf einem CDN. Lazy-Loading.

Mit link-Element und einem externen Service

Man verwendet beispielsweise ein Service wie Google Fonts und bindet die Fonts per link-Element ein.

<link href='https://fonts.googleapis.com/css?family=Open+Sans' rel='stylesheet' type='text/css'>

Vorteile: Gute Performance beim zweiten Besuch.

Nachteile: Blockt Rendering, was sehr schlecht für Usability und Accessibility ist.

Web Font Loader

Mit dem von Google und Typekit entwickelten Web Font Loader erlangt man mehr Kontrolle über das Laden der Webfonts. Web Font Loader unterstützt Fonts von Google Fonts, Typekit, Fonts.com, and Fontdeck.

Das Besondere an WFL (Web Font Loader) ist, dass es ein Eventsystem in Javascript und CSS zu Verfügung stellt. In Javascript gibt es Events auf die man reagieren kann, bspw. fontloading oder fontinactive und in CSS werden je nach Status Klassen auf das <html>-Element angewendet, bspw. .wf-loading oder .wf-inactive. Mit Hilfe dieser Klassen hat man die Kontrolle darüber, ob Fonts mit FOUT oder FOIT geladen werden, weil man selber festlegt was bei .wf-loading bzw. .wf-active passieren soll.

Mit WFL kann man Fonts asynchron laden.

<script>
    WebFontConfig = {
      google: {
        families: ['Droid Sans', 'Droid Serif']
      }
    };

   (function(d) {
      var wf = d.createElement('script'), s = d.scripts[0];
      wf.src = 'https://ajax.googleapis.com/ajax/libs/webfont/1.6.6/webfont.js';
      s.parentNode.insertBefore(wf, s);
   })(document);
</script>

Möchte man FOUT verhindern, kann man WFL auch synchron einbinden. Das hat natürlich zur Folge, dass die Seite erst angezeigt wird, wenn der Font fertig geladen hat.

<script src="https://ajax.googleapis.com/ajax/libs/webfont/1.6.6/webfont.js"></script>
<script>
    WebFont.load({
        google: {
            families: ['Droid Sans', 'Droid Serif']
        }
    });
</script>

Vorteile: Kontrolle über FOUT und FOIT. Statusklassen in CSS und Events in Javascript. Sehr performant.

Nachteile: Javascript notwendig.

Font Face Oberserver

Font Face Observer checkt zwar nur, ob ein Font vorhanden ist oder nicht, aber er macht das indem er den Font lädt. Praktischerweise gibt es auch eine Callback-Funktion, die nach erfolgreichen Ladevorgang ausgeführt wird. Man kann also ähnliche Kontrolle wie bei Web Font Loader erlangen und noch dazu den Font asynchron laden.

var fontA = new FontFaceObserver( "Cabin" );
var fontB = new FontFaceObserver( "Francois One" );

Promise
    .all([fontA.check(), fontB.check()])
    .then(function(){
        document.documentElement.className += "fonts-loaded";
    });

Sobald beide Fonts geladen worden sind, bekommt das HTML Element die Klasse fonts-loaded.

.fonts-loaded body {
    font-family: 'Cabin', sans-serif;
}

Die Deklaration im CSS greift also erst, wenn die Klasse fonts-loaded im HTML zugewiesen worden ist.

Vorteile: Kontrolle über FOUT und FOIT. Statusklasse in CSS. Sehr performant. Geringe Größe des Scripts.

Nachteile: Javascript notwendig. Nicht so viel Kontrolle wie bei Web Font Loader.

CSS Font Loading

Es gibt auch schon eine native Lösung (CSS Font Loading) für das Laden von Webfonts mit der man ähnliche Dinge machen kann wie mit Web Font Loader oder Font Face Observer. Ein brauchbarer Browsersupport ist aber leider noch nicht gegeben. Zwischenzeitlich kann man Fontloader Polyfill nutzen.

Weiters gibt es einen Entwurf für eine Eigenschaft (font-display) mit der man steuern können wird, wie Webfonts dargestellt werden sollen, je nachdem ob sie bereits geladen sind oder nicht. font-display wird noch von keinem Browser unterstützt.

Webfont Performance Vergleich

Ich habe 6 Dokumente mit verschiedene Varianten erstellt und die Performance mit WebPageTest gemessen. Getestet habe ich auf einem Server in Prag mit Mobile 3G - Fast (1.6 Mbps/768 Kbps 150ms RTT) auf Chrome. Außerdem habe ich die .htaccess des HTML5 Boilerplate wegen der gzip- und Caching-Einstellungen kopiert.

Beim ersten Aufruf (ohne Browsercache)
Text sichtbar Webfont sichtbar Speed Index Ladezeit
Systemfonts 0,5s / 700 0,553s
Google Link-Element 2,7s 2,7s 2700 1.586s
Google Web Font Loader 0,7s 2,6s 777 1.606s
Lokal Web Font Loader 0,8s 1,6s 868 1.102s
Lokal @font-face 1,1s 1,1s 1086 0.974s
Lokal Web Font Observer 0,6s 1,4s 752 1.191s

Am schnellsten wird Text angezeigt mit der Web Font Observer Variante. Der niedrigste Speedindex geht ebenfalls an Web Font Observer. Die geringste Ladezeit insgesamt und für den Webfont erreicht man mit der lokalen @font-face Lösung. Das führe ich darauf zurück, dass ich den Font mit zopfli komprimiert und händisch das Subsetting eingestellt habe.

Beim zweiten Aufruf (mit Browsercache)
Text sichtbar Webfont sichtbar Speed Index Ladezeit
Systemfonts 0,4s / 504 0,369s
Google Link-Element 0,5s 0,5s 500 0.375s
Google Web Font Loader 0,4s 0,6s 408 0.416s
Lokal Web Font Loader 0,4s 0,6s 408 0.404s
Lokal @font-face 0,5s 0,5s 500 0.394s
Lokal Web Font Observer 0,5s 0,5s 500 0.420s

Beim zweiten Aufruf weist die Web Font Loader Variante in beiden Fälle den niedrigsten Speedindex und die schnellste Darstellung von Text auf.

Zusätzliche Performanceoptimierung

Neben der Ladetechnik gibt es noch zusätzliche Dinge, die man machen kann um die Performance zu optimieren.

Subsetting

Beim Subsetting reduziert man die Größe der Fonts indem man nur die Zeichen in den Font nimmt, die man benötigen wird. Das geht mit dem Webfont Generator auf Font Squirrel sehr einfach. Man wählt den „Expert“-Modus und wechselt bei „Subsetting“ auf „Custom Subsetting…“. Dort wählt man dann nur die Zeichentypen, Sprachen und Unicode Tabellen, die man benötigt.

WOFF Komprimierung mit zopfli

WOFF Dateien sind zwar schon komprimiert, aber mit sfnt2woff-zopfli kann man noch extra 5-8% in Dateigröße herausholen.

Fonts cachen

Man sollte Fonts möglichst lange (1 Jahr bspw.) cachen, weil sich selten etwas an Fonts ändert. Wie das geht kann man in der .htaccess Datei des HTML5 Boilerplate nachlesen.

Progressive enhancement

FOUT ist zwar nicht schön, aber am Ende ist lesbarer Inhalt meistens wichtiger als ein schöner Webfont. Deswegen sollte man Webfonts als ein progressive enhancement sehen. Die Inhalte sind so schnell wie möglich konsumierbar und werden mit einem Systemfont dargestellt. Sobald der Webfont geladen ist, wird das Leseerlebnis durch einen Webfont optimiert.

Mir persönlich ist es lieber, dass BenutzerInnen ein Mal FOUT sehen, als dass sie möglicherweise mehrere Sekunden nichts sehen. Wenn man sich ein bisschen Zeit nimmt und sich mit der Thematik beschäftigt, kann man den Flash of unstyled Text durch die richtige Ladetechnik und reduzieren der Dateigröße der Fonts optimieren.

Sonstiges

Quellen:

Manuel Matuzovic

Frontend Developer aus Wien.

3 Kommentare zu “Webfont Performance, FOUT, FOIT und Usability

  1. Ein sehr informativer Aufsatz! Einen weiteren Vorteil hat die Variante mit lokalen Font-Dateien auch noch: Es fällt (mindestens) ein DNS-Lookup auf die externen Dateien weg, das sind zwar nur einige ms, aber viele kleine Maßnahmen senken die Renderingzeit am Ende spürbar. Ich finde, dein Beitrag zeigt auch den Spagat, der bei der Nutzung von Webfonts notwendig ist um sich zwischen Pest und Cholera zu entscheiden. Ganz grob tendiert meiner Erfahrung nach der Gestalterische, Designer, etc. gegen den FOUT, der Entwickler, benutzerfreundlich Orientierte eher dafür, genau aus den von Dir im letzten Absatz genannten Gründen. Am schlimmsten finde ich persönlich den exzessiven, übertriebenen Einsatz von zehn oder mehr Fonts auf einer Seite …

  2. Update: Bei Google Webfonts kannte ich die Option bereits, subsets zu laden. Neu war mir ein beta Feature, on-the-fly nur bestimmte Zeichen eines Fonts zu verwenden (das o.g. subsetting):

    https://developers.google.com/fonts/docs/getting_started#optimizing_your_font_requests_beta

    Im Schnelltest verringert das die Dateigrößen der Fonts, allerdings habe ich ein Problem, wenn ich für zwei Fonts jeweils die Zeichen beschränken möchte. Zumindest der Standardweg funktioniert dann nicht, d.h., der zweite Font wird beim folgenden Beispiel nicht geladen, weder mit dem kompletten Zeichensatz noch reduziert auf „def“ (andere Kombinationen habe ich jetzt nicht testen können).

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind markiert *