CSS optimieren: Ladezeit und Seitenaufbau

CSS optimieren und komprimieren

CSS-Dateien gehen im Handumdrehen auf wie Hefeteig. Dabei wird CSS immer komplexer und übernimmt mehr Aufgaben von Javascript. Wie sorgt man dafür, dass sich die Seite trotz großer CSS-Dateien schnell aufbaut und die Ladezeit im Rahmen bleibt?

23-02-02 SITEMAP CSS HTML JS Basis JS Web Tutorial SVG

Wenn die CSS-Datei immer länger wird

Wenn die CSS-Datei erst einmal 50, 100 KB erreicht, lassen sich scheinbar überflüssige Stile kaum noch sicher entsorgen. Die Gefahr, eine lang nicht erblickte Seite damit in den Abgrund zu ziehen, ist groß. Ein Neu-Aufbau der CSS macht mehr Arbeit, als abzusehen ist, und unterliegt derselben Gefahr: Alle Seiten müssen sorgfältig geprüft werden, ob alle Elemente auf großen und kleinen Monitoren noch dort sind, wo sie sein sollten.

Eine klassische Ordnung im CSS wird durch die Webseite selbst bestimmt: Zuerst die Stile für den Seitenkopf, dann die Stile für den Hauptinhalt, am Ende für den Fuß der Seite. Aber wohin mit den Stilen für Typografie, Buttons und Formulare?

Knäul von Abhängigkeiten und gegenseitigen Wirkungen
Nicht nur der Entwicklung, sondern auch der Browser muss vor- und zurückspringen, um die Elemente der Seite aufzubauen.

Die Reihenfolge, in der CSS-Regeln geschrieben werden, birgt ein Dilemma: Die nachfolgenden Regeln überschrieben zuvor angelegte Regeln und immer wieder müssen Stile zurückgesetzt werden. Die Spezifität ist ein weiterer Hemmschuh. Egal wie gut die Regeln aufeinander aufbauen, wie gut die Kaskade genutzt wird, wie treffend die Namenskonventionen durchdacht sind – am Ende schlägt die Spezifität durch und die Regeln werden länger und länger.

Effiziente Selektoren

Selektoren sind unterschiedlich effizient. Ein id-Selektor greift schneller als ein class-Selektor. Am Ende der Skala rangieren Pseudo-Selektoren, bei denen die Browser am längsten brauchen, bis das Element identifiziert und aufgehübscht ist.

CSS Effizienz

In der Praxis ist der Geschwindigkeitsunterschied zwischen id- und class-Selektoren gering. Viele Experten raten zum Verzicht auf id-Selektoren zugunsten von Klassen-Selektoren, denn Klassen-Selektoren können wiederverwendet werden. id-Selektoren dürfen nur einmal innerhalb der HTML-Datei benutzt werden. Also braucht ein einzelnes Element der Seite einen Packen CSS-Eigenschaften für sich allein. Das kann den Geschwindigkeitsvorteil unterwandern. Na ja …

Das heißt nun nicht, dass alle id-Selektoren aus dem HTML herausgeworfen werden sollten, denn Javascript profitiert von id-Attributen.

Bevor CSS-Selektoren komplexer und komplexer werden, ist es einfacher, das HTML um eine CSS-Klasse zu erweitern.

Browser-Präfix

Der Browser-Prefix stirbt langsam aus. Selbst IE11 ist nur noch eine Randerscheinung. Gleichzeitig blähen die vorangestellten Kürzel ms-, moz-, webkit- die CSS-Datei auf.

/** Beispiele **/
-moz-border-radius
-moz-box-shadow 
-moz-transition
@-moz-keyframes
-o-transition
-webkit-transition
-webkit-sticky

Der Browser-Präfix tauchte so um 2005 auf und erreichte zwischen 2012 und 2017 seinen Höhepunkt. In dieser Zeit entstanden die Prefixer-Tools. Mozilla stellte den Präfix so um 2013 ein, Webkit im Jahr 2016, trotzdem gibt es noch CSS-Eigenschaften, für die ein Präfix Sinn macht oder sogar erforderlich ist. Übrig blieb mit wenigen Ausnahmen nur der webkit-Prefix für alle Browser (auch für firefox):

/** Beispiele **/
-webkit-hyphens
-webkit-line-clamp 

Bei aller Liebe zu kurzen Ladezeit: Pflege und Änderungen

CSS-Variablen (Custom Properties) sind schon seit vielen Jahren in allen modernen Browsern implementiert. Wenn sie die Ladezeit und die Transformation der Seite verlängern, dann in einem homöopathischen Ausmaß. Auf jeden Fall erleichtern sie die Pflege der CSS-Datei.

/* ❌ wichtige Werte nicht über die gesamte CSS-Datei verstreuen */
h2 {
	color: firebrick;
	margin-top: 2rem;
}

.card {
	color: firebrick;
	background-color: seashell;
	padding: 1rem;
}

/* ✅ CSS-Variablen erleichtern Änderungen */
:root {
  --primary-text-color: firebrick;
  --accent-text-color: seashell;
  --spacing-unit: 0.5;
}

h2 {
	color: var(--primary-text-color);
	margin-top: calc(var(--spacing-unit) * 4rem);
}
.card {
  color: var(--accent-text-color);
  background-color: var(--primary-text-color);
  padding: calc(var(--spacing-unit) * 2rem);
}

CSS für die Performance

Bei umfangreichen Webprojektiven kommen schnell 50 bis 100 KB durch CSS-Dateien zusammen. Vor allem die Templates von Content Management Systemen – allen voran WordPress – geizen nicht mit Stil bis in die letzte Ecke. Aber nicht nur die Größe der CSS-Datei ist ein wichtiger Faktor, sondern auch eine performante Schreibweise.

Logo stylelint CSS-Analyse
CSSLint erstellt eine Analyse der CSS-Datei. Nicht, dass man sich nun jede Warnung gleich zu Herzen nehmen muss: Jeder nach eigenen Ansprüchen.

Inline-Stile und style-Tag vermeiden

Inline-CSS und style-Tags erschweren die Pflege und Erweiterung der Stylesheets, es sei denn, es handelt sich um wirkliche Ausnahmen.

<style>
    h1 { font-size: 1em; }
</style>

<h1 style="font-size: 1em"></h1>

Aber das wussten wir sowieso schon seit langem …

Dennoch kann ein style-Tag im Head der Seite Sinn machen: Wenn der sichtbare Anfang der HTML-Seite (Stichwort: Above the fold) mit inline-CSS versorgt wird, kann der Browser mit dem Aufbau der Seite beginnen, bevor die CSS-Datei(en) geladen sind.

Ein schneller Aufbau der Seite kommt insbesondere den Besuchern mit mobilen Geräten entgegen.

CSS-Dateien kombinieren

Früher sandte der Browser für jede externe Resource – seien es Bilder, CSS- oder Javascript-Dateien – einen HTTP-Request (Hypertext Transfer Protocol) an den Webserver. Jeder Request hatte einen kleinen Overhead und verzögerte den Aufbau der Seite.

Von daher kam der Rat, einzelne CSS-Dateien zu einer CSS-Datei zusammen zu legen. Jede einzelne CSS-Datei wurde ansonsten zu einem HTTP-Request.

Heute nutzen moderne Server und Browser das jüngere HTTP/2-Protokoll, bei dem der Server mehrere Dateien gleichzeitig in einer Verbindung versendet. Wenn die Webseiten mit dem HTTP 2-Protokoll bedient werden, bringen mehrere individuelle CSS-Dateien eine bessere Übersicht, Änderungen sind einfacher und Fehlerquellen eröffnen sich besser.

<link rel="stylesheet" href="layout.css">
<link rel="stylesheet" href="positioning.css">
<link rel="stylesheet" href="colors.css">

Damit dabei der Überblick erhalten bleibt, helfen ein paar Regeln für Organisation der CSS-Datei.

Fonts lokal speichern

Auch die Links zu Webfonts sind separate HTTP-Requests. Besser sitzen die Font-Regeln sogar im style-Tag weit oben im Head der Seite, damit der Browser sofort auf sie zugreifen kann.

Webfonts sind eine zweifache Bremse für die Anzeige der Seite und die Ladezeit. Auch wenn die Fonts von einem CDN (Content Delivery Network) geholt werden, dauert es, bis die Schrift verfügbar ist und angezeigt wird. Bis dahin zeigen die Browser den Text der Seite nicht. Davon abgesehen, können Fonts per CDN auch in Hinsicht auf den Datenschutz bedenklich sein.

Darum gehört font-display: swap in die Deklaration der Schriften.

<style>
/* greek-ext */
@font-face {
	font-family: 'Cardo';
	font-style: normal;
	font-weight: 400;
	font-display: swap;
	src: local('Cardo'), local('Cardo-Regular'), url(cardo.woff2) format('woff2');
	unicode-range: U+1F00-1FFF;
}
</style>

Dem Schutz der Privatsphäre und dem Datenschutz zuliebe werden Fonts sowieso besser vom eigenen Webspace serviert.

CSS-Kurzschriften und Gruppen verwenden

body { margin-top: 20px;
    margin-right: 10px; 
    margin-bottom: 5px; 
    margin-left: 10px; }
body { margin: 20px 10px 5px 10px }

CSS-Kurzschriften (Shorthand) vorsichtig verwenden

Unbedachte Shorthands wiederum können den Browser ins Schwitzen bringen.

.demo { background: gainsboro; }

Eigentlich soll hier doch nur die Hintergrundfarbe gesetzt werden. Was aber tatsächlich abläuft, ist Folgendes:

.demo {
   background-color: gainsboro;
   background-image: none;
   background-position-x: 0%;
   background-position-y: 0%;
   background-size: auto auto;
   background-repeat-x: repeat;
   background-repeat-y: repeat;
   background-attachment: scroll;
   background-origin: initial;
   background-clip: border-box; 
}

Kann man sich im Firefox-Inspektor unter den Regeln des Elements vor Augen führen lassen.

.demo { background-color: gainsboro } hingegen macht genau das, was es soll, und nicht mehr.

!IMPORTANT nur in höchster Not verwenden

!important hilft, wenn ein einfacher Selektor durch einen spezielleren Selektor überschrieben soll.

p, li { color: rgb(120,120,120) !IMPORTANT }
.extra { color: black }

Eigentlich sollte die Klasse .extra alles in Schwarz setzen, da sie spezifischer als p und li ist. Aber das !IMPORTANT verhindert die Anwendung von .extra. Nach einem halben Jahr haben wir das !important vergessen und suchen verzweifelt, warum die Klasse .extra keine Wirkung zeigt. !Important macht die Pflege der CSS unnötig schwer.

Überqualifizierung vermeiden

Das figure ist überflüssig, wenn das figcaption-Tag sowieso nur in figure-Elementen vorkommt:

figure figcaption a 
figcaption a

Absteigende Selektoren sind teuer.

blockquote p a span

Diese Konstruktion stammt aus der CSS-Datei des WordPress-Templates twentytwenty:

.menu-wrapper.is-toggling .menu-bottom .social-menu .menu-item {
	width: auto;
}

Da haben die Tags der Templatedateien schon so viele Klassen, und dann wird ein Element über eine lange Kette von Vorfahren angesprochen. Das kostet viel Zeit beim Aufbau der Seite. Lieber so spezifisch wie möglich werden, denn solche Regeln sind schwer lesbar und schon eine Woche später nicht mehr nachvollziehbar.

Browser lesen Stile von rechts nach links und nicht von links nach rechts (Kontext-Selektoren und Performance) wie wir – dass sollte man sich immer vor Augen halten.

Überqualifizierung kann das CSS retten

Wer die CSS-Vereinbarungen von Content Management Systemen wie WordPress oder Drupal überschreibt, kommt auf die Dauer um ein gewisses Maß an Überqualifizierung doch nicht herum.

.entry .entry-content .wp-block-group .wp-block-columns,
.entry .entry-content .wp-block-columns {
…
}

Universal Selektoren vermeiden

* { margin: 0; padding: 0; }

legt eine hohe Last auf den Browser, der jetzt erst einmal alle Elemente mit diesen Eigenschaften versorgen muss.

All you can eat macht dick und langsam

Das WordPress-Template twentytwenty »wiegt« 128KB. Die meisten der Elemente werden nie benutzt.

Aber welcher Webdesigner hat die Zeit, die ungebrauchten Elemente zu entfernen? Die Templates der Kauf-Designs sind fast immer auf CSS-Resets und CSS-Frameworks wie Bootstrap aufgebaut und wiegen noch schwerer. Bevor man sich die Finger an Verbesserungen verbrennt, ist zumindest eine Minifizierung eine Wunderdiät für auswuchernde CSS-Dateien.

CSS Architectures: Refactor Your CSS

18 Tips for Website Performance Optimization

CSS-Dateien verkleinern

Ein systematischer Aufbau der CSS-Datei hilft bei der Pflege und Erweiterung der CSS-Datei. Man könnte die WordPress-CSS-Datei style.css als Quasi-Standard für die Schreibweise bezeichen: Nahezu alle style.css-Dateien beginnen mit einem CSS-Reset, gefolgt von den Stilen für das Layout, gefolgt von den Stilen für Schriften, Listen, Bilder in der Reihenfolge, in der die Elemente auf der Seite liegen.

Die elegante Schreibweise mit neuen Zeilen für jede Eigenschaft und Einrücken am Anfang jeder Zeile bauscht die CSS-Datei auf. Während der Entwicklung und der Tests ist die luftige Schreibweise sinnvoll, aber wenn die Seite in Dienst gestellt wird, ist die Zeit reif für eine sinnvolle Kürzung und die Komprimierung.

CSS Minify

Um die CSS-Datei für kurze Ladezeiten kompakt zu halten, wird überflüssiger Weißraum gelöscht.

  • Alle Eigenschaften einer Regel in eine Zeile schreiben. Hinter dem Doppelpunkt und hinter dem Semikolon muss kein Leerzeichen stehen.
  • Das letzte Semikolon vor der schließenden geschweiften Klammer muss nicht sein und kann ebenfalls gelöscht werden.
  • Leere Zeilen und Kommentare werden gelöscht.
mark,
ins {
	background: #fff9c0;
	text-decoration: none;
}
… wird zu … 
mark,ins{background:#fff9c0;text-decoration:none}

Die Minifizierung reduziert die CSS-Datei oft um die Hälfte, ist aber auch fehlerträchtig, wenn sie manuell durchgeführt wird. Sicherer sind Tools für die Minifizierung der CSS-Datei.

Die CSS-Datei des WordPress-Themes Twentyfourteen war 87KB groß. Nach der Minifizierung hatte sie noch rund 60 KB, wurde die CSS-Datei anschließend komprimiert, nur noch 10 KB.

CSS komprimieren

Minifizieren und Komprimieren sind unterschiedliche Ansätze: Minifizieren läßt die Luft raus, Komprimieren ist das Packen der Daten (z.B. als .zip). Fürs Packen muss der Server sorgen. Mit einem Apache-Server werden nicht nur CSS-Dateien, sondern auch Javascript, SVG, XML und JSON-Dateien für den Transfer vom Server zum Browser komprimiert. Den Anstoss dazu liefert die .htaccess-Datei.

In der .htaccess-Datei von Drupal findet sich eine Vorlage.

# Rules to correctly serve gzip compressed CSS and JS files.
# Requires both mod_rewrite and mod_headers to be enabled.
<IfModule mod_headers.c>
	# Serve gzip compressed CSS files if they exist and the client accepts gzip.
	RewriteCond %{HTTP:Accept-encoding} gzip
	RewriteCond %{REQUEST_FILENAME}\.gz -s
	RewriteRule ^(.*)\.css $1\.css\.gz [QSA]

	# Serve gzip compressed JS files if they exist and the client accepts gzip.
	RewriteCond %{HTTP:Accept-encoding} gzip
	RewriteCond %{REQUEST_FILENAME}\.gz -s
	RewriteRule ^(.*)\.js $1\.js\.gz [QSA]

	# Serve correct content types, and prevent mod_deflate double gzip.
	RewriteRule \.css\.gz$ - [T=text/css,E=no-gzip:1]
	RewriteRule \.js\.gz$ - [T=text/javascript,E=no-gzip:1]

	<FilesMatch "(\.js\.gz|\.css\.gz)$">
		# Serve correct encoding type.
		Header set Content-Encoding gzip
		# Force proxies to cache gzipped & non-gzipped css/js files separately.
		Header append Vary Accept-Encoding
	</FilesMatch>
</IfModule>

Etwas übersichtlicher in der Konfiguration eines ngnix-Servers

gzip on;
gzip_proxied any;
gzip_comp_level 5;
gzip_types text/css text/javascript application/javascript application/x-javascript image/svg+xml;
gzip_vary on;