Icon-System mit SVG Sprites

Avatar of Chris Coyier
Chris Coyier am

DigitalOcean bietet Cloud-Produkte für jede Phase Ihrer Reise. Starten Sie mit 200 $ kostenlosem Guthaben!

Ich bin ein großer Verfechter von Icon-Schriftarten. Viele Seiten brauchen wirklich ein System für Icons, und Icon-Schriftarten bieten ein verdammt gutes System. Ich glaube jedoch, dass die Verwendung von Inline-SVG und dem <use>-Element zum Verweisen auf ein Icon ein überlegenes System ist, vorausgesetzt, Sie sind mit IE 9+ vertraut.

Zuerst behandeln wir, wie es funktioniert.

Eine gute Möglichkeit, Ihre Icons zu handhaben, ist ein Ordner voller .svg-Dateien.

Das ist eine der coolen Sachen an der Arbeit mit SVG – sie sind die Quelldateien.

Sie können gefärbt, ungefärbt, mehrere Formen, Größen, was auch immer sein.

Sie können Illustrator (oder was auch immer) sie speichern lassen, wie sie sind, mit all dem unnötigen Ballast, der mitgeliefert wird

<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.0.4, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
	 width="100px" height="100px" viewBox="0 0 100 100" enable-background="new 0 0 100 100" xml:space="preserve">
<g>
	<path d="M50.049,0.3c14.18,0.332,25.969,5.307,35.366,14.923S99.675,36.9,100,51.409c-0.195,11.445-3.415,21.494-9.658,30.146 - yadda yadda yadda"/>
</g>
</svg>

Kombinieren Sie die .svg-Dateien

Sie können das manuell tun, wenn Sie möchten. Ich habe es getan. Sie müssen die finale Datei nicht einmal ansehen. Nennen Sie sie einfach svg-defs.svg oder so etwas.

Es sollte einfach ein <svg>-Tag mit einem <defs>-Tag sein (was nur bedeutet, dass Sie Dinge definieren, die später verwendet werden sollen), und dann eine Reihe von <g> (Gruppen)-Tags. Jeder <g>-Tag hat eine eindeutige ID und umschließt alle Pfade und was auch immer für jedes Icon.

<svg>
  <defs>

    <g id="shape-icon-1">
      <!-- all the paths and shapes and whatnot for this icon -->
    <g>

    <g id="shape-icon-2">
      <!-- all the paths and shapes and whatnot for this icon -->
    <g>

    <!-- etc -->

  </defs>
</svg>
<symbol> ist anscheinend eine bessere Wahl als <g>. Lesen Sie alles darüber!

Auch das können Sie von Hand machen, aber das ist natürlich etwas mühsam. Fabrice Weinberg hat ein Grunt-Plugin namens grunt-svgstore erstellt, das dies automatisiert.

Wenn Sie noch nie Grunt verwendet haben, können Sie das tun. Hier ist ein Screencast, der Ihnen den Einstieg erleichtert.

Sie können es installieren mit

npm install grunt-svgstore --save-dev

Stellen Sie sicher, dass die Aufgabe verfügbar ist mit

grunt.loadNpmTasks('grunt-svgstore');

Und dann in der Konfiguration

svgstore: {
  options: {
    prefix : 'shape-', // This will prefix each <g> ID
  },
  default : {
      files: {
        'dest/svg-defs.svg': ['svgs/*.svg'],
      }
    }
  }
},

In der Ausgabedatei, svg-defs.svg, wird jedes Icon (welche Pfade und Dinge auch immer aus der Quell-.svg-Datei) in einem <g>-Tag mit einer eindeutigen, vorangestellten ID und dem Dateinamen (ohne .svg) verpackt. So etwa:

<g id="shape-codepen">

Injizieren Sie dieses SVG am Anfang des Dokuments

Fügen Sie es buchstäblich ein, so:

<!DOCTYPE html>
<html lang="en">

<head>
  ...
</head>

<body>
  <?php include_once("processed/svg-defs.svg"); ?>

Oder wie auch immer Sie das tun wollen.

Es muss leider ganz oben stehen, da es einen Chrome-Bug gibt, bei dem dies nicht funktioniert, wenn es später definiert wird. Obwohl... es gibt noch mehr zu dieser Geschichte, denn während ich diese Worte schreibe, hat das Theme, das diese Seite verwendet, die Icons am Ende des Dokuments definiert und es funktioniert. Ughkgh verwirrend.

Verwenden Sie die Icons überall

Jetzt können Sie sie überall verwenden! Zum Beispiel:

<svg viewBox="0 0 100 100" class="icon shape-codepen">
  <use xlink:href="#shape-codepen"></use>
</svg>
Beachten Sie, dass grunt-svgstore jetzt <symbol> verwendet, sodass Sie nicht einmal den viewBox verwenden müssen!

Stellen Sie sicher, dass Sie diese Klassennamen auf dem SVG verwenden, um es zu dimensionieren.

/* Do whatever makes sense here.
   Just know that the svg will be an 
   enormous 100% wide if you don't 
   reign in the width. */
.icon {
  display: inline-block;
  width: 25px;
  height: 25px;
}

Juhu: Sie können sie (und ihre Teile) mit CSS gestalten

Einer der Gründe, warum wir Icon-Schriftarten liebten, ist die Möglichkeit, sie mit CSS zu gestalten. Diese Technik übertrifft das noch, denn wir tun alles, was wir dort konnten, und mehr, weil

  1. Wir können alle einzelnen Teile gestalten
  2. SVG hat noch mehr Dinge, die Sie steuern können, wie spezielle Filter und Striche

Das SVG ist (irgendwie) im DOM, also auch JavaScript. Hier sind einige Styling-Möglichkeiten und eine Demo, wie das alles funktioniert

Siehe den Pen EBHlD von Chris Coyier (@chriscoyier) auf CodePen.

Eine andere Möglichkeit: IcoMoon

IcoMoon, bekannt für die Erstellung von Icon-Schriftarten, leistet auch hervorragende Arbeit bei der Erstellung von SVG-Sprites. Nachdem Sie alle gewünschten Fonts ausgewählt haben, klicken Sie auf die SVG-Schaltfläche unten und Sie erhalten die Ausgabe, einschließlich einer Demo-Seite mit der Inline-SVG-Methode.

Browser-Unterstützung

Auf der Seite der Browserunterstützung sind die Gefahrenzonen IE 8 und darunter, Safari 5 und darunter, iOS 4.3 und darunter sowie Android 2.3 und darunter. Aber wenn Ihre Richtlinie lautet "die letzten beiden Hauptversionen" – dann haben Sie fast 100 % Unterstützung.

Denken Sie daran, dass Icons nur eine unterstützende Rolle spielen können, immer begleitet von einem Wort. Wenn das der Fall ist, ist die Unterstützung nicht so wichtig. Wenn es sich um eigenständige Icons handelt und deren Nichtanzeige die Website unbenutzbar machen würde, ist das eine große Sache.

Ich würde wahrscheinlich zu Icon-Schriftarten greifen, da die Unterstützung dort viel tiefer ist. Stellen Sie nur sicher, dass Sie es richtig machen.

Das wird noch viel besser werden

Idealerweise könnten wir das so machen

<svg viewBox="0 0 100 100" class="icon shape-codepen">
  <use xlink:href="/images/svg-defs.svg#shape-codepen"></use>
</svg>

Das funktioniert in einigen Browsern, was bedeutet, dass Sie den Include am Anfang des Dokuments überspringen könnten. Dies bedeutet eine zusätzliche HTTP-Anfrage, aber das bedeutet, dass Sie Caching effizienter nutzen können (nicht das Dokument-Caching aufblähen). In Tests hat Jonathan Neal entdeckt, dass Sie das xmlns-Attribut für das <svg> benötigen, damit es funktioniert

<svg xmlns="http://www.w3.org/2000/svg">

Aber selbst dann gibt es keine Unterstützung in irgendeinem IE. Es sei denn, Sie möchten das gesamte <svg><use> durch ein <object> ersetzen, was funktioniert. Jonathan Neal hat dies wieder herausgefunden

/MSIE|Trident/.test(navigator.userAgent) && document.addEventListener('DOMContentLoaded', function () {
  [].forEach.call(document.querySelectorAll('svg'), function (svg) {
    var use = svg.querySelector('use'); 

    if (use) {
      var object = document.createElement('object');
      object.data = use.getAttribute('xlink:href');
      object.className = svg.getAttribute('class');
      svg.parentNode.replaceChild(object, svg);
    }
  });
});

Seine Demo enthält jetzt auch eine Methode, die einen Ajax-Aufruf für den Inhalt macht und diesen injiziert, was die Füllungen in IE 9 ermöglicht. Nicht so effizient, aber eher wie ein Polyfill.

Ich stelle mir vor, dass <svg><use>, das direkt auf die .svg verweist, eines Tages der Weg sein wird. Oder vielleicht sogar <img>, das mit URL-Fragmentbezeichnern für SVG funktioniert.


Browser behandeln <use> wie den Shadow DOM

Derzeit können wir beispielsweise einen einzelnen <path> mit CSS ansprechen, wie

.targetting-a-path {
  fill: red;
}

Aber das beeinflusst alle Instanzen dieses Pfades. Sie würden denken, Sie könnten tun

svg.shape-version-2 .targetting-a-path {
  fill: red;
}

Aber das funktioniert nicht. Es überschreitet diese Shadow DOM-Grenze. Idealerweise würden Sie den "Hut"-Selektor verwenden, um diese zu durchbrechen

svg.shape-version-2 ^ .targetting-a-path {
  fill: red;
}

Aber das wird noch nicht unterstützt und es ist nicht ganz klar, ob das genau so funktioniert oder nicht.

"Gegenüber" Icon-Schriftarten

Vektor-basiert: Unentschieden

Mit CSS gestalten: leichter Vorteil für SVG-Sprites (Zielen auf Teile, SVG-spezifische Stile wie Striche)

Seltsame Fehler: SVG scheint einfach zu funktionieren (wenn unterstützt). Icon-Schriftarten scheinen auf seltsame Weise zu versagen. Zum Beispiel ordnen Sie die Zeichen normalen Buchstaben zu, dann schlägt das Laden der Schriftart fehl und Sie erhalten zufällige Zeichen. Oder Sie ordnen sie dem "Private Use Area" zu und einige Browser entscheiden sich, sie in wirklich seltsame Zeichen wie Rosen umzuordnen, aber es ist schwer zu reproduzieren. Oder Sie möchten die @font-face-Dateien auf einem CDN hosten, aber das ist Cross-Origin und Firefox hasst das, also benötigen Sie Ihren Server, um die richtigen Cross-Origin-Header zu liefern, aber Ihre Nginx-Einrichtung erfasst das nicht richtig, SEUFZ. SVG gewinnt diesen Punkt.

Semantik: Keine große Sache, aber ich denke, ein <svg> ist für ein Bild sinnvoller als ein <span>.

Barrierefreiheit: Vielleicht kann mir jemand sagen? Können/sollten wir dem <svg> ein Titel-Attribut oder etwas geben? Oder ein <text>-Element darin, das wir visuell verstecken? Update: Das <title>-Element könnte das tun. Oder vielleicht das <desc>-Element, wie in dieser SVG-Zugänglichkeits-Spezifikation verwendet.

Benutzerfreundlichkeit: Werkzeuge wie Fontello und IcoMoon sind für einen Icon-Schriftarten-Workflow ziemlich gut, aber die Ordner voller SVGs, die Grunt für Sie zusammenquetscht, sind meiner Meinung nach noch einfacher.


Ian Feather hat einen Artikel darüber gepostet, warum sie sich auch von Icon-Schriftarten abgewandt haben, und ich stimme jedem einzelnen Punkt zu.