Die Vorteile von SVG nutzen

Skalierbare Vektor Grafiken (SVG) bevorzuge ich schon seit längerem zur Darstellung von Logos und Icons in Webseiten. Sie bringen eine ganze Reihe von Vorteilen mit, die ich nicht mehr missen möchte.

Die Hauptvorteile von SVG werden direkt in ihrem Namen ausgedrückt. Es handelt sich dabei um Vektorgrafiken die verlustlos skalieren und auf allen Ausgabegeräten gestochen scharf dargestellt werden. Da es sich bei SVG um Vektorgrafiken auf Codebasis handelt, lassen sie sich sehr gut optimieren und versionieren, können mit CSS und JavaScript angesprochen werden, verstehen Media-Queries und sind darüberhinaus noch für den barrierearmen Einsatz geeignet.

Laut caniuse.com können etwas mehr als 97% der Browser SVG darstellen. Deshalb ist in vielen Anwendungsbereichen – beispielsweise wenn sie als Icon einen Text ergänzen – kein Ersatz erforderlich. Da ich SVG auch für Logos einsetze, muss sichergestellt sein dass die Grafik immer erscheint.

Um alle Vorteile von SVG nutzen zu können, müssen diese direkt im HTML eingebunden werden. Um darüber hinaus noch den Zwischenspeicher der Browser nutzen zu können, lade ich sie als erstes Element im body und binde sie über use an den entsprechenden Stellen der Webseite ein.

Die Vorteile von SVG nutzen

Um zu demonstrieren wie SVG meiner Ansicht nach optimal genutzt werden habe ich eine kleine Liste mit Links zu meinen Social-Media-Profilen erstellt. Vielleicht ist jetzt ein guter Zeitpunkt die Demo und den Code in einem neuen Tab zu öffnen. Die Links zum jeweiligen Profil werden durch das Logo der Plattform dargestellt. Um die Serveranfragen so gering wie möglich zu halten, fasse ich alle Logos in einer Datei (Spritesheet/Sprite) zusammen.

Das SVG-Sprite sieht folgendermaßen aus:

<svg xmlns="http://www.w3.org/2000/svg" display="none" height="0" width="0">
  <symbol id="twitter" viewBox="0 0 512 512">
    <path fill="currentColor" d="m456 133c-14 7-31 11-47 13 17-10 30-27 37-46-15 10-34 16-52 20-61-62-157-7-141 75-68-3-129-35-169-85-22 37-11 86 26 109-13 0-26-4-37-9 0 39 28 72 65 80-12 3-25 4-37 2 10 33 41 57 77 57-42 30-77 38-122 34 170 111 378-32 359-208 16-11 30-25 41-42z"/>
  </symbol>
  <symbol id="instagram" viewBox="0 0 512 512">
    <path fill="currentColor" d="m256 152c-52-1-100 43-103 95a103 103 0 0 0 200 43 104 104 0 0 0 -97-138zm0 169c-40 2-74-40-65-79 7-40 54-65 91-48 36 14 52 63 31 95a66 66 0 0 1 -57 32zm131-171c1 20-27 31-41 16-14-14-3-41 17-40 13 0 24 11 24 24z"/>
    <path fill="currentColor" d="m424 89c-25-27-64-35-100-33-54 0-109-1-164 1-43 2-85 31-98 73-10 32-5 66-6 98 0 42-1 84 1 126 3 46 37 87 81 98 30 7 62 3 92 4 42 0 83 1 124-1 44-3 85-33 96-76 9-32 5-64 6-97 0-41 1-83-1-125-2-25-13-50-31-68zm-3 250c1 35-23 70-57 77-28 5-56 2-83 3-40-1-80 1-120-1-32-2-61-26-66-58-4-28-1-57-2-86 1-38-1-76 1-114 2-32 26-60 58-65 29-4 59-1 88-2 38 0 76-1 113 1 33 2 62 29 66 62 3 30 1 61 2 92z"/>
  </symbol>
  <symbol id="facebook" viewBox="0 0 512 512">
    <path fill="currentColor" d="m287 456v-182h61l9-72h-70v-45c0-21 6-35 35-35h38v-63c-7-1-29-3-55-3-54 0-91 33-91 94v52h-62v72h62v182z"/>
  </symbol>
</svg>

Das jeweilige Logo befindet sich innerhalb eines symbol welches mit der entsprechenden ID versehen ist und über die viewBox die Zeichenfläche vorgibt innerhalb der die Pfadangaben gedeutet werden. Wird dieses Sprite als erstes Element im body eingefügt, lässt sich das jeweilige Symbol mit den folgenden Zeilen einbinden:

<svg class="social-icon">
  <use xlink:href="#twitter"></use>
</svg>

Ich binde das Sprite im body ein, da alle Version des Internet Explorer und ältere WebKit-Browser extern eingebundenen SVG nicht unterstützen. Das symbol lässt sich direkt über seine ID ansprechen, und muss nicht wie in CSS durch die Position angesteuert werden.

Bevor ich das Sprite einbinde, überprüfe ich ob der Browser in der Lage ist direkt eingebundene SVG darzustellen:

var supportsSvg = function() {
    var div = document.createElement("div");
    div.innerHTML = "<svg/>";
    return (div.firstChild && div.firstChild.namespaceURI) === "http://www.w3.org/2000/svg";
};

Um das Sprite per Ajax zum body hinzuzufügen übernehme ich die Funktion von Chris Coyer:

function getSVG() {
    var ajax = new XMLHttpRequest();
    ajax.open("GET", "./assets/images/sprite.svg", true);
    ajax.responseType = "document";
    ajax.onload = function() {
        document.body.insertBefore(ajax.responseXML.documentElement, document.body.childNodes[0]);
    };
    ajax.send();
};

Kann der Browser mit direkt eingebundenen SVG umgehen, passe ich die Klasse entsprechend an und füge ich das Sprite mittels Ajax als erstes Element zum body hinzu:

if (supportsSvg()) {
    document.documentElement.className += " svg";
    getSVG();
} else {
    document.documentElement.className += " no-svg";
};

Das Sprite ist nun eingebunden, und die Symbole erscheinen an den gewünschten Stellen. Die Links zu den Profilen habe ich wie buttons gestaltet und möchte die Hintergrundfarbe und die Farbe des Icons kontrollieren. Dazu verwende ich folgende CSS-Deklarationen:

a .social-icon,
a:visited .social-icon {
  background-color: rgb(51, 51, 51);
  border-radius: 15%;
  color: rgb(255, 255, 255);
  display: block;
  height: 2.75rem;
  padding: 0.1875rem;
  width: 2.75rem;
  transition: background-color 0.3s ease-out,
    color 0.3s ease-out;
}

a:hover .social-icon,
a:focus .social-icon {
  background-color: rgb(0, 153, 0);
  color: rgb(250, 250, 250);
}

Momentan sind die Links sowohl von funktionierendem JavaScript und der SVG-Unterstützung des Browsers abhängig. Für den Fall, dass entweder das eine oder das andere nicht zur Verfügung steht benötige ich einen Ersatz. Als Ersatz biete ich ein PNG-Sprite an welches ich als Hintergrundbild lade und über die Position des Hintergrunds jeweils das entsprechende Icon anzeige. Dieses Sprite enthält dieselben Icons auf transparentem Hintergrund. Da ich dem html die entsprechenden Klassen mitgebe könnte ich den Ersatz folgendermaßen einbinden:

.no-js .social-icon,
.no-svg .social-icon {
  background-image: url("./assets/images/sprite.png");
  background-repeat: no-repeat;
}

.no-js .social--twitter .social-icon,
.no-svg .social--twitter .social-icon {
  background-position: 0 0;
}

.no-js .social--instagram .social-icon,
.no-svg .social--instagram .social-icon {
  background-position: -3.125rem 0;
}

.no-js .social--facebook .social-icon,
.no-svg .social--facebook .social-icon {
  background-position: -6.25rem 0;
}

Da ich die Klasse no-js meinem Template mitgebe, führt diese Methode dazu, dass die Browser bei jedem Aufruf auch das PNG-Sprite laden, da mein Script die Klasse no-js erst dann mit js ersetzt wenn das DOM geladen ist. Ich möchte allerdings nur das Sprite laden welches auch tatsächlich verwendet wird.

Um den doppelten Download des Sprites zu umgehen entferne ich den Selektor .no-js .social-icon aus meinem Stylesheet, und füge ihn innerhalb eines noscript dem head meines Templates hinzu.

<noscript>
    <style>
        /* TODO: keep sprite in sync with .no-svg .social-icon */
        .no-js .social-icon {
            background-image: url("./assets/images/sprite.png");
        }
    </style>
</noscript>

Wichtig ist, dass die Sprite-Referenz jederzeit synchron ist. Um dies nicht zu vergessen versehe ich die entsprechenden Stelle mit einem Kommentar. Solange sich Name und Ort des Sprites nicht ändern dürfte es an dieser Stelle jedoch zu keinem Problem kommen.

Die Vorteile von SVG lassen sich meiner Ansicht nach auf diese Weise ideal nutzen. Auch wenn die Funktionalität eines PNG-Sprites nicht exakt einem SVG-Sprite entspricht, bleibt der Wiedererkennungswert erhalten, und die notwendigen Informationen stehen allen Nutzer_inn_en der Webseite zur Verfügung.

Verwendete Ressourcen:

Alles auf einen Blick

Reagiere darauf

*