SVG Sprites mit symbol oder use

SVG Sprites ähneln den Image Sprites mit CSS. Statt eine SVG-Datei für jedes Symbol zu laden, liegen alle Symbole in einer SVG-Datei. In der einfachsten Variante holt SVG symbol ein einzelnes Icon oder die Icons werden über ein hash – das #-Symbol am Ende des Dateinamens – angesprochen.

SVG Icons und Sprites
23-02-02 SITEMAP CSS HTML JS Basis JS Web Tutorial SVG

Von Icon-Fonts zu SVG Icons

Noch immer setzen viele Webseiten Icon-Fonts anstelle von SVG-Symbolen ein – ein kompliziertes Verfahren, bei dem Symbole, Icons oder kleine Grafiken in Zeichen eines Fonts umgewandelt werden.

Icon-Fonts sind zwar beliebig skalierbar, aber jedes Icon kann nur eine Farbe annehmen. Dazu kommt der Overhead, eine Grafik in ein Zeichen eines Fonts umzuwandeln, um es dann wieder als Grafik anzuzeigen. Da SVG Klartext ist, können wir schnell Änderungen an den Grafiken vornehmen, was bei Icon-Fonts nicht möglich ist.

Die Icons in diesem Beispiel werden aus einer SVG-Datei geladen, in der jedes Icon in einem symbol-Element sitzt. Im HTML können sie individuell geladen werden:

<svg viewBox="0 0 184 184">
	<use href="svg/haushalt-icons.svg#eisschrank" />
</svg>

Diese Variante wird von allen modernen Browsern unterstützt (wie immer: nicht IE11 und älter). Schade natürlich, dass diese Variante nicht mit einem HTML img-Element funktioniert, für das loading lazy eingesetzt werden könnte.

In diesem Sinne ist auch WordPress auf SVG-Icons umgestiegen und setzt SVG anstelle von Icon-Fonts ein, während viele WordPress-Themes noch auf FontAwesome setzten.

SVG-Icons in der HTML-Datei

Wenn nur eine begrenzte Zahl von Icons in einzelne HTML-Seiten gesetzt werden, lohnt es sich u.U., die Symbole nicht erst in eine SVG-Datei auszulagern, sondern inline ans Ende der HTML-Datei zu setzen. So muss nicht ein weiterer HTTP-Request für das Laden der SVG-Datei spendiert werden (siehe auch HTTP-Request)

…
<svg style="display:none">
<symbol id="fish" viewBox="0 0 140 140">
   <title>Fisch</title>
   <desc>Frische Fischgerichte</desc>
   <circle cx="70" cy="70" r="40" fill="seashell"/>
   <path fill-opacity=".6" fill="#c0c0c0" d="m64 46s-.7 2.7-2.6 5.3c-1.9 2.5-4 3.4-4 3.4s11.53 0 21.23 2.2c9.7 2 17.5 10.12 17.5 10.12s-.8-1.2-1.8-3.02c-1-1.8.8-4.4.8-4.4s-2.7.5-5.8-.2c-3-.6-3.9-5.2-3.9-5.2s-2.8.5-6 .5-3.6-4.5-3.6-4.5-4.5 1.2-7.7-.2c-3.13-1.4-4.03-4-4.03-4zm-2.5 10.7c-24.1 0-39.4 22.62-39.4 22.62l9 3.1h-6.8s8.2 16.7 27.9 20c.7 1.4 1.8 3.5 3.4 4.8 2.6 2 7.6 3.7 7.6 3.7s-3.3-2.9-3.7-5c-.2-.9-.3-2-.3-2.9 1.6 0 3.3-.1 5.1-.3 28.33-2.7 32.43-17.7 41.13-17.7 8.8 0 21 10.6 21 10.6l-11-16.9 11-14.66c-1.6 1.66-8.1 8.76-19.1 8.76-12.5 0-21.9-15.76-45.98-15.76zm5.93 5c2.1.8 3.6 3.1 3.6 5.22 0 1.9-1.7 3.7-3.9 4.5 1.7-1.1 2.5-2.7 2.5-4.5 0-2.02-.6-4.12-2.2-5.22zm-18.03 1.3c5.2 2.62 7.6 10.12 7.6 16.82s-3 12.4-8.1 15.1c4-3.6 5.9-9 5.9-15.1 0-6.2-1.4-13.3-5.4-16.82zm-7.4 3.82c1.9 0 3.5 1.6 3.5 3.5s-1.6 3.5-3.5 3.5c-2 0-3.6-1.6-3.6-3.5s1.6-3.5 3.6-3.5zm34.03 1.2c2 .8 3.7 3.1 3.7 5.2 0 1.9-1.8 3.7-4 4.5 1.6-1.1 2.5-2.7 2.5-4.5 0-2-.5-4.1-2.2-5.2zm7.2 5.9c2.3.8 3.8 3.1 3.8 5.2 0 1.9-1.8 3.7-4 4.5 1.7-1.1 2.5-2.7 2.5-4.5 0-2-.5-4.1-2.3-5.2zm-16 1.1c2.1.8 3.6 3.1 3.6 5.2 0 1.9-1.7 3.7-3.9 4.5 1.7-1.1 2.5-2.7 2.5-4.5 0-2-.6-4.1-2.2-5.2zm7.9 6.2c2.1.8 3.7 3.1 3.7 5.2 0 1.9-1.8 3.7-3.9 4.5 1.6-1.1 2.4-2.7 2.4-4.5 0-2-.6-4.1-2.2-5.2zm-7.9 6.8c2.2.8 3.8 3.1 3.7 5.2.1 1.9-1.7 3.7-3.9 4.5 1.7-1.1 2.5-2.7 2.5-4.5 0-2-.6-4.1-2.3-5.2z"/>
</symbol>
<symbol id="ice" viewBox="0 0 140 140">
   <title>Dessert</title>
   <circle cx="70" cy="70" r="40" fill="seashell"/>
   <path fill-opacity=".6" fill="#c0c0c0" d="m15.5 39h-1.6c6.9 6.64 6.6 11.74 10.1 19.64 2.9 6.4 5.8 11.6 10.5 15.1h-12.2c-1.5 0-2.9 1.3-2.9 2.9v.1c0 1.6 1.4 2.9 2.9 2.9h94.5c1.6 0 3-1.3 3-2.9v-.1c0-1.5-1.4-2.9-3-2.9h-26.4c.1-.7.2-1.4.2-2.1 0-8.9-7.4-16.1-16.5-16.1s-16.5 7.2-16.5 16.1c-1.6.7-3.3 1.3-5 1.5-4 .4-7.5-.9-10.5-2.8-5.4-3.3-9.1-8.6-11.5-14.2-1.6-3.8-3.8-7.3-6-10.8 1 1.1 2 2.3 2.9 3.5 2.8 3.2 4.6 7.1 6.8 10.7 2.1 3.7 5.2 6.8 8.9 9.1 3 1.8 6.8 2.2 10.2 2 1.1-.1 2.3 0 3.3-.4 2.2-3 .9-7.3-.4-10.1-.5-1.1-1-2.4-1.8-3.4-1.4.7-2.9 1.4-4.3 1.9 1-2.2 3.1-4.5 1.7-5.7-1.8-1.7-3.3-3.6-5.4-5-.9 1.5-1.7 3.1-2.7 4.4-.1-2.1-.1-4.3-.3-6.5-2.6-1.6-6.1-3.34-9.5-4.34-.6.2-.2 4.04-.5 5.74-.9-2.2-1.7-4.44-2.5-6.54-3.5-.80-11.1-1.7-15.5-1.7zm5.9 41.14c0 8.2 8.7 15.4 21.9 19.6h52.5c13.2-4.2 21.9-11.4 22-19.6h-96.4z" />
</symbol>
</svg>
</body>

Bei der Lösung mittels SVG-symbol-Element kann jedes Icon ein title- und desc-Tag haben.

Elemente in einem symbol-Element werden nicht gerendert, aber die SVG-Grafik nimmt trotzdem ihre volle Größe ein. Damit kein leerer Platz am Ende der Seite erscheint – display:none im öffnenden SVG-Tag.

Kein viewBox-Attribut im öffnenden SVG-Tag, sondern jedes symbol-Tag kann (anders als g-Tags für Gruppen) ein eigenes viewBox-Attribut enthalten.

<svg class="orange"><use xlink:href="#orange" /></svg>
<svg class="citro"><use xlink:href="#citro" /></svg>
<svg class="cherry"><use xlink:href="#cherry" /></svg>

Das Schöne an dieser Lösung: Die Icons müssen nicht mehr allesamt mit demselben Seitenverhältnis in die Seite eingesetzt werden, das Markup zum Einsetzen der Icons ist minimal und einfach.

Die SVG-Icons müssen dank eigenem viewBox-Attribut im symbol-Tag nicht übereinander liegen. Die Icon-Datei kann bequem mit einem grafischen SVG-Editor wie Inkscape oder Illustrator erstellt erstellt werden.

SVG Sprites in object- oder iframe-Elementen

Wenn die Icons auf vielen Seiten verwendet werden, ist u.U. die ausgelagerte SVG-Datei im Vorteil – sie liegt beim nächsten Besuch schon im Cache. Genauso wie bei einem Icon-Font liegen alle Symbole oder Grafiken in einer einzigen SVG-Datei, allerdings liegen die einzelnen Grafiken oder Symbole übereinander gestapelt in der SVG-Grafik und sind nicht neben- oder untereinander aufgereiht.

Jedes Symbol liegt in einer SVG-Gruppe <g> mit einer ID. display:none verhindert das Rendern der Icons. Die Alternative ist <symbol> anstelle von <g>.

.sprite:target { display: inline } überschreibt das display:none für ein einzelnes Symbol, das über ein # am Ende des Dateinamens im object-Tag identifiziert wird.

<svg xmlns='http://www.w3.org/2000/svg' width='100%' height='100%' viewBox='0 0 140 140'>
<style>
   .sprite { display:none}
   .sprite:target { display: inline; }
</style>
   <g id='fish' class="sprite">
      <circle r='40' cy='70' cx='70' />
      <path d='m 64,46 c 0,0 -0.7,2.7 -2.6,5.3 -1.9,2.5 -4,3.4 -4,3.4 0,0 11.53,0 21.23,2.2 9.7,2 17.5,10.12 17.5,10.12 0,0 -0.8,-1.2 -1.8,-3.02 -1,-1.8 0.8,-4.4 0.8,-4.4 0,0 -2.7,0.5 -5.8,-0.2 -3,-0.6 -3.9,-5.2 -3.9,-5.2 0,0 -2.8,0.5 -6,0.5 -3.2,0 -3.6,-4.5 -3.6,-4.5 0,0 -4.5,1.2 -7.7,-0.2 -3.13,-1.4 -4.03,-4 -4.03,-4 z m -2.5,10.7 c -24.1,0 -39.4,22.62 -39.4,22.62 l 9,3.1 -6.8,0 c 0,0 8.2,16.7 27.9,20 0,0 0,0 0,0 0.7,1.4 1.8,3.5 3.4,4.8 2.6,2 7.6,3.7 7.6,3.7 0,0 -3.3,-2.9 -3.7,-5 -0.2,-0.9 -0.3,-2 -0.3,-2.9 1.6,0 3.3,-0.1 5.1,-0.3 28.33,-2.7 32.43,-17.7 41.130002,-17.7 8.8,0 21,10.6 21,10.6 l -11,-16.9 11,-14.66 c -1.6,1.66 -8.1,8.76 -19.1,8.76 -12.500002,0 -21.900002,-15.76 -45.980002,-15.76 z m 5.93,5 c 2.1,0.8 3.6,3.1 3.6,5.22 0,1.9 -1.7,3.7 -3.9,4.5 1.7,-1.1 2.5,-2.7 2.5,-4.5 0,-2.02 -0.6,-4.12 -2.2,-5.22 z m -18.03,1.3 c 5.2,2.62 7.6,10.12 7.6,16.82 0,6.7 -3,12.4 -8.1,15.1 4,-3.6 5.9,-9 5.9,-15.1 0,-6.2 -1.4,-13.3 -5.4,-16.82 z m -7.4,3.82 c 1.9,0 3.5,1.6 3.5,3.5 0,1.9 -1.6,3.5 -3.5,3.5 -2,0 -3.6,-1.6 -3.6,-3.5 0,-1.9 1.6,-3.5 3.6,-3.5 z m 34.03,1.2 c 2,0.8 3.7,3.1 3.7,5.2 0,1.9 -1.8,3.7 -4,4.5 1.6,-1.1 2.5,-2.7 2.5,-4.5 0,-2 -0.5,-4.1 -2.2,-5.2 z m 7.2,5.9 c 2.3,0.8 3.8,3.1 3.8,5.2 0,1.9 -1.8,3.7 -4,4.5 1.7,-1.1 2.5,-2.7 2.5,-4.5 0,-2 -0.5,-4.1 -2.3,-5.2 z m -16,1.1 c 2.1,0.8 3.6,3.1 3.6,5.2 0,1.9 -1.7,3.7 -3.9,4.5 1.7,-1.1 2.5,-2.7 2.5,-4.5 0,-2 -0.6,-4.1 -2.2,-5.2 z m 7.9,6.2 c 2.1,0.8 3.7,3.1 3.7,5.2 0,1.9 -1.8,3.7 -3.9,4.5 1.6,-1.1 2.4,-2.7 2.4,-4.5 0,-2 -0.6,-4.1 -2.2,-5.2 z m -7.9,6.8 c 2.2,0.8 3.8,3.1 3.7,5.2 0.1,1.9 -1.7,3.7 -3.9,4.5 1.7,-1.1 2.5,-2.7 2.5,-4.5 0,-2 -0.6,-4.1 -2.3,-5.2 z' />
   </g>
   <g id='ice' class="sprite" transform="translate(-140)">
      <circle cx='210' cy='70' r='40' />
      <path d='m 160,39 c -0.6,0 -1.2,0 -1.6,0 6.9,6.64 6.6,11.74 10.1,19.64 2.9,6.4 5.8,11.6 10.5,15.1 l -12.2,0 c -1.5,0 -2.9,1.3 -2.9,2.9 l 0,0.1 c 0,1.6 1.4,2.9 2.9,2.9 l 94.5,0 c 1.6,0 3,-1.3 3,-2.9 l 0,-0.1 c 0,-1.5 -1.4,-2.9 -3,-2.9 l -26.4,0 c 0.1,-0.7 0.2,-1.4 0.2,-2.1 0,-8.9 -7.4,-16.1 -16.5,-16.1 -9.1,0 -16.5,7.2 -16.5,16.1 -1.6,0.7 -3.3,1.3 -5,1.5 -4,0.4 -7.5,-0.9 -10.5,-2.8 -5.4,-3.3 -9.1,-8.6 -11.5,-14.2 -1.6,-3.8 -3.8,-7.3 -6,-10.8 1,1.1 2,2.3 2.9,3.5 2.8,3.2 4.6,7.1 6.8,10.7 2.1,3.7 5.2,6.8 8.9,9.1 3,1.8 6.8,2.2 10.2,2 1.1,-0.1 2.3,0 3.3,-0.4 2.2,-3 0.9,-7.3 -0.4,-10.1 -0.5,-1.1 -1,-2.4 -1.8,-3.4 -1.4,0.7 -2.9,1.4 -4.3,1.9 1,-2.2 3.1,-4.5 1.7,-5.7 -1.8,-1.7 -3.3,-3.6 -5.4,-5 -0.9,1.5 -1.7,3.1 -2.7,4.4 -0.1,-2.1 -0.1,-4.3 -0.3,-6.5 -2.6,-1.6 -6.1,-3.34 -9.5,-4.34 -0.6,0.2 -0.2,4.04 -0.5,5.74 -0.9,-2.2 -1.7,-4.44 -2.5,-6.54 -3.5,-0.8 -11.1,-1.7 -15.5,-1.7 z m 5.9,41.14 c 0,8.2 8.7,15.4 21.9,19.6 l 52.5,0 c 13.2,-4.2 21.9,-11.4 22,-19.6 l -96.4,0 z' />
   </g>
</svg>

Im Dateinamen des object-Tags die Id des Icons mit dem # als Target setzen:

<object type="image/svg+xml" 
        width="100" height="100" 
        data="sprites.svg#fish"></object>
<object type="image/svg+xml" 
        width="100" height="100" 
        data="sprites.svg#shrimp"></object>
<object type="image/svg+xml" 
        width="100" height="100" 
        data="sprites.svg#coc"></object>

Geht doch: SVG mit ::before / ::after einsetzen

Der Einsatz der SVG-Icons aus einer Datei (die gecacht werden kann) in der CSS-Datei äquivalent zu Icon-Fonts funktioniert auch, wenn IE11 keine Rolle mehr spielt:

  • Waschmaschine
  • Fernseher
  • Eisschrank
<ul class="spritelist">
	<li class="waschmaschine">Waschmaschine</li>
	<li class="fernseher">Fernseher</li>
	<li class="eisschrank">Eisschrank</li>
</ul>

Das CSS ist nicht gerade intuitiv (wie auch bei den klassischen Icon-Fonts).

ul.spritelist {
	list-style:none;
}		
.spritelist li::before {
	content: " ";
	display:inline-block; 
	height:50px; 
	width:50px;
	background-size: cover;
	margin-right: 0.5em;
}
.waschmaschine::before {
	background-image: url("svg/haushalt-icons.svg#waschmaschine");
}
.fernseher::before {
	background-image: url("svg/haushalt-icons.svg#fernseher");
}
.eisschrank::before {
	background-image: url("svg/haushalt-icons.svg#eisschrank");
}
<?xml version="1.0" encoding="UTF-8"?>
<svg width="184" height="184" viewBox="0 0 184 184" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg">
<style>
	.sprite { display:none}
	.sprite:target { display: inline; }
</style>
<g class="sprite" id="waschmaschine">
	…
</g>
<g class="sprite" id="fernseher">
	…
</g>
</svg>
Frische Orangen Orangen-Icon mit blauen Blättern Saure Zitrone Zitronen-Icon mit blauen Blättern Rote Kirschen Kirschen-Icon mit blauen Blättern