5 Gotchas, die Sie beim Einsatz von Inline-SVG in der Produktion erleben werden

Avatar of Rob Levin
Rob Levin am

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

Der folgende Text ist ein Gastbeitrag von Rob Levin. Rob ist Senior UI/UX Developer bei Mavenlink und Co-Autor der Unicorn UI CSS Button Library. Ihr Release 2.0 verwendet ein SVG-Icon-System, und hier teilt er einige Probleme, auf die er unterwegs gestoßen ist, und wie Sie diese erkennen und beheben können. Außerdem stellt Rob ein vollständiges System zur Verfügung, das Sie nutzen können, einschließlich eines funktionierenden Build-Prozesses und einer Demo.

Sie haben gelesen, wie Inline-SVGs besser sind als Schrift-Icons, und sind bereit, den Sprung zu wagen. Sie rufen ein Meeting mit Ihrem Team ein, um den Umstieg auf Inline-SVG-Icons zu besprechen. Ihr Chef ist skeptisch. Er sieht Ihnen in die Augen und sagt: „Können Sie garantieren, dass uns das nicht auf die Füße fällt?“ Sie zögern ein wenig, sammeln aber irgendwie den Mut, um zu bestätigen: „Ja, das ist definitiv der Weg, den wir einschlagen müssen!“

Das war ich vor ein paar Monaten, und hier sind einige „Gotchas“, auf die ich gestoßen bin, mit entsprechenden Workarounds. Ich werde zuerst die Workarounds einzeln untersuchen und dann am Ende ein funktionierendes Beispiel geben.

Bitte beachten Sie, dass dies kein überzeugender Artikel darüber ist, warum Sie Inline-SVG verwenden sollten. Dafür sollten Sie diesen beliebten CSS-Tricks-Artikel lesen, der die Vorteile von Inline-SVG gegenüber Schrift-Icons aufzeigt.

Gotcha Eins: Das Ziel verfehlt

Um Caching mit einer externen SVG-Datei zu erreichen (Sie wollen wirklich nicht ~1,5 KB * 50 Icons auf Ihre Seite werfen, ohne dass sie cachebar sind, oder?!), müssen Sie die svg4everybody-Bibliothek auf Ihrer Seite einbinden. Im Wesentlichen wird dieser Shim UA-Sniffing verwenden, um zu erkennen, ob Sie eine „problematische Version“ von IE oder Android verwenden, die externe Definitionsdateien nicht richtig cacht, und wenn ja, werden alle svg use-Elemente entfernt und durch eingebettete Elemente ersetzt, die die entsprechenden SVG-Definitionsdaten enthalten, die per AJAX abgerufen werden. Am Ende des Tages interessiert uns nur die Tatsache, dass unser ursprüngliches SVG, das vielleicht so aussah

<svg viewBox="0 0 16 16">
  <use xlink:href="/path/to/svgdef.svg#your-icon" … ></use>
</svg>

Ersetzt wird durch ein eingebettetes Element, das etwa so aussieht

<svg viewBox="0 0 16 16">
  <path> ... </path>
</svg>

CSS: Das Ziel treffen

Abhängig vom Quell-SVG kann die Hierarchie wie folgt aussehen: svg path (wie oben), oder es kann svg g sein, oder möglicherweise eine Kombination aus gruppierten und Pfad-Nachkommen – aber denken Sie daran, dass Sie Ihr CSS benötigen, um die „pollyfilled“ Fälle anzusprechen – das bedeutet, Ihre CSS-Regeln sollten absolut niemals direkt das Element svg > use ansprechen… es wird in IE komplett entfernt!

JavaScript: Das Ziel treffen

Die gleiche Idee gilt für alle JavaScript-Manipulationen am SVG-Klon selbst. Zum Beispiel möchten wir möglicherweise Icons austauschen beim Überfahren mit der Maus, und eine Technik, die wir wählen könnten, ist die Änderung des Attributs xlink:href mit JavaScript, wenn ein solches Ereignis ausgelöst wird. Da wir in diesem Artikel eine externe Datei mit dem oben beschriebenen Shim verwenden, können wir diese Technik nicht zuverlässig nutzen (wiederum wird das use-Element in IE ersetzt). Meine Empfehlung ist, einfach per CSS-Klassen ausblenden/anzeigen (Technik #1 im Artikel Swapping Out SVG Icons) und sicherstellen, dass Sie den SVG-Klon selbst ansprechen.

Wenn wir die Inline-SVG-Definition direkt am Anfang unserer Seite einfügen (z. B. direkt nach dem öffnenden <body>-Tag), haben wir dieses Problem nicht und die Technik des Manipulierens des xlink:href-Attributs ist in Ordnung.

Selektor-Beispiele

Um die obigen Punkte kristallklar zu machen, hier ist ein CSS-Selektor, der in Browsern funktionieren würde, die externe SVG-Definitionen vollständig unterstützen, aber fehlschlägt, wenn svg4everybody IE pollyfills

.my-svg use {
  fill: red;
}

Es stellt sich heraus, dass es keinen wirklichen Bedarf oder Vorteil gibt, use anzusprechen, also ändern Sie es einfach wie folgt, um für alle Fälle zu funktionieren

.my-svg {
  fill: red;
}

Während wir über Selektoren sprechen, sollten wir diese Gelegenheit nutzen, um darauf hinzuweisen, dass Sie nicht mit etwas wie dem Folgenden in die ursprüngliche SVG-Definition „hineingreifen“ können

svg.parent path.child { /* won't work! */ }

Das Gleiche würde für den Versuch gelten, etwas im Def selbst über die geklonte Instanz zu stylen, sei es eine Form, ein Pfad, eine Gruppe usw. Es mag offensichtlich sein, aber dies ist hier nur ein Problem, weil wir die use xlink:href-Strategie verwenden.

Gotcha Zwei: Zusammenarbeit mit einem Designer

Wenn Ihre Icons im Allgemeinen nur eine Farbe verwenden, ist das Anwenden von CSS-Styling auf eine geklonte Instanz „in einem Rutsch“ trivial mit: fill: <Ihre-Farbe>. Für solche Fälle muss der Designer des Projekts darauf achten, die Vektorgrafiken zu erstellen, indem er entweder nur schwarze Füllungen mit transparenten Strichen oder nur Pfaddaten (transparente Füllungen und Striche) verwendet. Sie können immer noch Striche über CSS anwenden, wenn Sie dies benötigen.

Um zu verstehen, warum dies so ist, müssen wir zuerst verstehen, wie unsere Vektoranwendung SVG exportiert.

Ich verwende Adobe Illustrator, aber wenn Sie ein anderes Vektorprogramm wie Inkscape oder Sketch usw. verwenden, sollten Sie die Dokumentation dieser Software konsultieren, um zu sehen, ob ihr Verhalten mit dem Folgenden übereinstimmt. Im schlimmsten Fall können Sie die Dateien auf verschiedene Weise exportieren, wie ich im Folgenden beschreibe, und testen, wie das von Ihrer Anwendung generierte SVG aussieht.

Illustrator SVG Export Verhalten

Zum Zeitpunkt der Erstellung exportiert die neueste Version von Illustrator CC SVG wie folgt

  • Wenn Sie keinen Füll- oder Strich definieren oder nur eine Füllung definieren, diese Füllung aber *schwarz* ist (vollständig schwarz, wie #000), enthalten die exportierten SVG-Pfade und Formen keine Füll- oder Strichattribute
<path ... positional information ... >
<rect ... positional information ... >
  • Wenn Sie eine nicht-schwarze Füllung definieren oder einen Strich (beliebiger Farbe, einschließlich *schwarz*) definieren, enthalten die entsprechenden Pfade und Formen des exportierten SVG Strich- und/oder Füllattribute wie
<path stroke="#000000" ... >
<rect fill="fabdad" ... >

Diese Präsentationsattribute sind immer durch CSS überschreibbar, wenn Sie einen Stil direkt auf das SVG-Symbol, den Pfad, die Form usw. anwenden. Die Einschränkungen beim Anwenden von CSS auf eine *geklonte Instanz* (nicht direkt) werden als nächstes beschrieben.

Schwarze Füllung Exportieren / Strich Transparent

Wenn Ihr Quell-SVG mit ausschließlich schwarzen Füllungen (ohne Striche) oder nur mit Pfaden exportiert wurde, haben Sie viel Flexibilität, da Sie einen Strich oder eine Füllung über CSS anwenden können.

In diesem ersten Beispiel (beachten Sie, dass ich SCSS-Syntax verwende) wenden wir eine CSS-Füllung und einen Strich auf die geklonte Instanz an

.filled-instance {
  stroke: #cc8ac1;
  stroke-width: 5px;
  fill: lighten(#cc8ac1, 20%);
}

Sie können auch einen umrandeten Effekt erzielen, indem Sie einfach die Füllung deaktivieren und einen Strich über CSS anwenden

.filled-instance-off {
  stroke: #d08aaf;
  stroke-width: 5px;
  fill: transparent;
}

Der Grund, warum dies so wunderbar funktioniert, ist, dass unsere SVG-Definition keine definierten Füll- oder Strichattribute hat und daher unser CSS angewendet wird und alles in Ordnung ist.

Strich Exportieren / Füllung Transparent

Die schlechte Nachricht ist, dass Sie dem Strich auf der geklonten Instanz keinen Stil zuweisen können (denken Sie daran, unsere geklonte Instanz verweist in diesem Beispiel wiederum auf eine SVG-Definition, die wir mit „nur Strich“ erstellt haben). Wir können auch keine Füllung auf unsere geklonte Instanz anwenden, da unsere SVG-Formen jetzt fill="none" haben und dies Vorrang hat)

.stroked-instance {
  stroke: green; /* nothing happens */
  fill: red; /* nothing happens */
}

Es ist ähnlich wie bei HTML/CSS, wie

<div id="very-strong">
  <span>still green.</span>
</div>
#very-strong { color: red; }
span { color: green; } /* I'm the actual element, so I win. */

Einige Workarounds

Sie sollten diese Situation wahrscheinlich ganz vermeiden, indem Sie schwarze Füllungen mit transparenten Strichen oder nur Pfade exportieren, aber wenn Sie aus irgendeinem Grund immer noch möchten, dass Ihre Stile wirksam werden, müssen Sie stattdessen das SVG-Symbol *direkt* mit etwas wie folgendem stylen

symbol#completed-copy-stroked [stroke] {
  stroke: #dd6435;
  stroke-width: 5px;
}

Beachten Sie, dass der Teil symbol des obigen Selektors unnötig ist, aber hier zur Klarheit verwendet wird, welches Element wir ansprechen

Auch dies ist bei der Verwendung eines Inline-SVG mit geklonten Instanzen nicht wirklich ideal, da wir, wo immer möglich, Stile auf unsere geklonten Instanzen anwenden möchten.

Eine weitere Technik, die Sie immer verwenden können, ist, einfach Klassen innerhalb des Quell-SVG hinzuzufügen und diese direkt über CSS anzusprechen. Diese Stile sind *global*, was ein Problem sein kann oder auch nicht – das müssen Sie für Ihren Fall entscheiden. Diese Technik funktioniert unabhängig davon, wie Sie das SVG exportiert haben. Da Sie eine CSS-Klasse von Hand hinzufügen, steht sie Ihnen zur Verfügung (wenn auch direkt – wir sprechen das Unterelement der SVG-Definition direkt an und stylen nicht über eine geklonte Instanz)

.ibestrokin {
  stroke: magenta;
  stroke-width: 5px;
}
.istrokeittotheeast {
  stroke: green;
  stroke-width: 7px;
}

Vielleicht zeige ich mein Alter, aber ich muss unweigerlich an Clarence Carter denken, wenn wir über „Stroking“ sprechen.

Meiner Meinung nach ist es nicht ideal, nur das SVG direkt stylen zu können (und nicht die geklonte Instanz). Daher empfehle ich, Striche in Fällen, in denen Sie Inline-SVG und geklonte Instanzen verwenden, komplett zu vermeiden. Wenn Sie bereits Kunstwerke mit definierten Strichen haben, können Sie überlegen, diese vor dem Export über etwas wie *Kontur umwandeln* in Pfade umzuwandeln.

Sehen Sie den Pen Inline SVG Fill and Stroke von Rob Levin (@roblevin) auf CodePen.

Nachbearbeitung

Eine weitere Überlegung ist, wenn Sie eine Nachbearbeitungsbibliothek wie grunt-svgstore verwenden, um Füllungen und Striche zu bereinigen. In diesem Fall hat das generierte SVG möglicherweise überhaupt keine expliziten Füll- oder Strichattribute, und nur Pfadinformationen bleiben in der resultierenden Definitionsdatei erhalten. In diesem Fall möchten Sie auf jeden Fall alle Striche in Pfade umwandeln oder riskieren, die entsprechenden sichtbaren Linien ganz zu verlieren. Alternativ sollten Sie nicht verlangen, dass Striche vom Nachbearbeiter entfernt werden (aber einige der zuvor diskutierten Probleme in Kauf nehmen). Kurz gesagt, wenn Ihre Kunstwerke wirklich einen Strich haben müssen, müssen Sie das SVG direkt ansprechen.

Schlussfolgerungen

Die Quintessenz ist also, dass Sie eine Entscheidung mit dem Designer treffen müssen, ob Sie alle Füllungen und Striche vollständig über CSS steuern möchten – in diesem Fall sollten Sie nur Pfade mit transparenten Füllungen und Strichen verwenden (oder schwarze Füllungen, wenn Sie diese zur Referenz behalten möchten) – oder ob es mehr Sinn macht, mit sinnvollen Standardwerten zu beginnen und diese dann bei Bedarf über CSS zu überschreiben. Wenn Sie Inline-SVG verwenden und bevorzugen, auf geklonte Instanzen zu stylen (nicht direkt auf das SVG), empfehle ich, einfach Pfade zu verwenden.

Gotcha Drei: Farbunterschiede erzielen

Einer der angeblichen Vorteile der Verwendung von SVG im Allgemeinen ist die flexible Stilkontrolle, die wir erhalten, da wir CSS auf einen SVG- *Pfad*, eine Form usw. anwenden können. Die Verwendung des use xlink:href-Mechanismus führt jedoch zu einem nicht aufgedeckten geklonten DOM-Baum, auf den unsere fill- oder stroke-Stile *global* auf das referenzierte SVG angewendet werden. Die Konsequenz daraus ist, dass alle geklonten Instanzen die gleiche Füllfarbe teilen.

Glücklicherweise gibt es einen Trick, mit dem wir zumindest eine eindeutige Farbe pro Instanz erhalten können. Wenn wir in die SVG-Definition selbst gehen, können wir fill=“currentColor” auf eine Form oder einen Pfad unserer Wahl anwenden. Was bewirkt das? Nun, der seit langem unterstützte CSS-Wert currentColor gibt an, dass die Farbe vererbt wird. Das bedeutet, dass wir eine Schriftfarbe weiter oben im Baum definieren können (z. B. auf den geklonten Instanzen selbst), und die Füllung für diesen Pfad oder diese Form erbt die Farbe. Ich bin mir nicht sicher, wer sich das zuerst ausgedacht hat, aber ich gebe Credit, wo ich es zuerst gesehen habe: Jenna Smiths Tweet.

Implementierung

Wir beginnen mit unserer Basisfüllung, die etwa so aussehen könnte

.icon-primary {
  fill: #ccc;
  color: #3bafda;
}

Diese Klassen würden auf die nicht aufgedeckte svg-Klon-Instanz angewendet werden

<svg class="icon-primary"> ...

Hier beginnt die Magie – unsere fill definiert die allgemeine Füllfarbe des Icons (in diesem Fall #cc), aber jetzt definiert unsere Schrift-color die geerbte Akzentfarbe, die wir wie oben beschrieben definiert haben, z. B.:

<path fill="currentColor" ... />
Beispiel für die Verwendung von currentColor, um eine Akzentfarbe auf einem SVG-Pfad zu erzielen

Wenn Sie grunt-svgstore in Ihrem Build-Prozess verwenden (das Beispiel am Ende dieses Artikels tut dies), werden Sie es wahrscheinlich so konfigurieren, dass unerwünschter Ballast über die Eigenschaft cleanup entfernt wird, und diese Bibliothek behält Füllattribute mit dem Wert currentColor bei… Sie müssen sich also keine Sorgen machen, das benutzerdefinierte Attribut zu überschreiben, das oben definiert wurde.

Ich habe einen Sass-Mixin (bewusst abwärtskompatibel bis 3.2) dafür erstellt

@mixin svgColors($fill: false, $color: false, $patchCurrentColorForIE: false) {
  @if $fill {
    fill: $fill;
  }
  @if $color {
    color: $color;
  }
}

Und ich rufe ihn mit etwas wie folgendem auf

.icon-primary {
  @include svgColors($neutralColor, $primaryColor, true);
}

Wenn Sie sich fragen, was es mit dem Parameter $patchCurrentColorForIE auf sich hat, habe ich ihn für die Icons dort, die keine mehreren Farben benötigen und daher den Shim nicht anwenden müssen.

Andere Techniken für Farbunterschiede

Zusätzlich zur Verwendung der oben genannten currentColor-Technik können Sie auch eine preserve---Attributfunktion verwenden, die jetzt in grunt-svgstore verfügbar ist. Sie funktioniert so, dass, wenn Sie preserve-- als Präfix für ein beliebiges gültiges Attribut im Quell-SVG verwenden, dieses Attribut im resultierenden SVG erhalten bleibt (wobei das preserve---Präfix entfernt wird). Zum Beispiel würde preserve--stroke nur stroke in der Ausgabe-SVG-Definition ergeben.

Eine weitere Technik, die Sie in Betracht ziehen können, wenn Sie sich fragen, wie Sie Farbunterschiede erzielen können – in diesem Fall für ein background-image – ist, die Vorgehensweise zu wählen, ein data-uri eines SVG zu verwenden, aber zuerst eine Suche und Ersetzung des fill-Werts durchzuführen, wie hier beschrieben. Dieser Ansatz ist jedoch etwas außerhalb des Rahmens und vom Thema dieses Artikels abweichend, da er einen nicht cachebaren data-uri verwendet und somit unser Hauptziel, eine cachebare externe SVG-Definitionsdatei zu verwenden, konterkariert.

Gotcha Vier: jQuery wirft Fehler

Wenn Sie jQuery auf Ihrer Seite eingebunden haben, führt ein direkter Klick auf ein gerendertes svg use-Element wahrscheinlich dazu, dass jQuery einen Fehler auslöst, der in ihrem Bug-Tracker dokumentiert ist. Es ist tatsächlich etwas knifflig, diesen Fehler zu reproduzieren, da Sie wahrscheinlich ein umschließendes Element haben werden, das als Anker oder Schaltfläche dient – und dieses Element hat die größere Trefferfläche –, aber er tritt erneut auf, wenn Sie direkt auf das Icon selbst klicken. Hier ist der bevorzugte Workaround

svg { pointer-events: none; }

Da pointer-events *vererbt* werden, bewirkt dies, dass auch alle „Unterelemente“ des SVG nicht auf pointer-events reagieren. Sobald Sie dies eingerichtet haben, sollten Sie darauf achten, dass die gesamte Ereignisbehandlung (wie z. B. ein JavaScript click-Handler) von einem Elternelement und nicht vom nicht aufgedeckten SVG-Klon selbst übernommen wird – eine Schaltfläche oder ein Anker sind offensichtliche Beispiele für umgebende Elemente, die in diesem Fall die Ereignisbehandlung übernehmen müssten.

Wenn Sie Maus- oder Pointer-Ereignisse für Animationen oder Ähnliches auf dem SVG selbst behandeln möchten, sollten Sie wahrscheinlich erwägen, für dieses spezielle SVG einfach ein img oder einen CSS-background-Tag zu verwenden; dies macht das Problem irrelevant.

Gotcha Fünf: GitHub Diffs

Ein Anliegen, das wir hatten, war, dass die SVG-Diffs für einen potenziellen Code-Reviewer ziemlich archaisch sind. Chris Coyier wies mich darauf hin, dass GitHub kürzlich eine tolle SVG-Anzeigefunktion eingeführt hat, die es ermöglicht, eine Ansicht des *Blobs* umzuschalten. Sehr praktisch.

GitHub unterstützt SVG-Diffs!

Darüber hinaus ist eine teamweite Richtlinie, solche SVG-Arbeiten in separaten Commits zu halten (damit sie keine bedeutsameren Codeänderungen *trüben*), wahrscheinlich die pragmatischste Wahl hier.

Ein funktionierendes Beispiel

Ich habe ein „Spielzeugbeispiel“ eingerichtet, das hoffentlich hilfreich ist. Es implementiert Inline-SVG mit einem Grunt-Workflow und setzt auf eine cachebare externe SVG-Definitionsdatei. Beachten Sie, dass dieses Beispiel IE9 und höher erfordert.

Wenn Sie noch nicht bereit sind, dies einzurichten, hier ist eine Demoseite dessen, was wir bereitstellen werden.

Wenn Sie IE8 und darunter unterstützen müssen, können Sie auf ein PNG-Bild mit demselben Namen wie Ihr SVG (aber mit der .png-Erweiterung) zurückgreifen. Die Einrichtung wird in den Anweisungen für die svg4everybody-Bibliothek beschrieben, die wir bereits verwenden, also fangen Sie dort an.

Um das Beispiel auszuführen, benötigen Sie Folgendes installiert

Führen Sie die folgenden Befehle in Ihrem Terminal aus, um das Beispiel abzurufen und lokal bereitzustellen

Je nach Systemkonfiguration müssen Sie möglicherweise sudo vor den beiden folgenden npm install-Befehlen verwenden

# Install the Buttons example and set up npm dependencies
git clone -b svg-inline-experiments --single-branch https://github.com/unicorn-ui/Buttons.git Buttons && cd Buttons && npm install

# Install the SVG specific dependencies and run example
pushd svg-builder && npm install && grunt && popd && grunt dev

Mit diesen Befehlen:

  • Klonen Sie das Repository und greifen Sie nur auf den relevanten svg-inline-experiments-Zweig zu
  • Installieren Sie die Node-Abhängigkeiten von Buttons
  • Installieren Sie die Node-Abhängigkeiten des SVG-Builders
  • Führen Sie unseren Grunt-Entwicklungsworkflow aus, der die Inline-SVG-Definitionen des Beispiels erstellt und auch die Beispielseite erstellt

Abhängig von Ihrer Konfiguration sollte der letzte Schritt das Öffnen der folgenden Seite in Ihrem Standardbrowser Ihres Systems zur Folge haben (andernfalls besuchen Sie https://:8000 manuell)

Vorschau der SVG-Icons – beachten Sie, dass wir zwei Instanzen haben, die auf dieselbe SVG-Definition verweisen, aber mit unterschiedlichen Akzentfarben

Wenn Sie diese Einrichtung umkehren möchten, um Ihre eigene Projekteinrichtung zu informieren, sind die Dateien, auf die Sie sich beziehen sollten:

  • styleguide/includes/svg.html zeigt die Markup für die Erstellung von SVG-Instanzen
  • styleguide/scss/module/_svgs.scss zeigt die CSS-Stile, die auf unsere SVG-Icons angewendet werden
  • svg-builder/Gruntfile.js eine funktionierende Grunt-Konfiguration, die speziell für Inline-SVG angepasst ist
  • styleguide/pages/index.html Das einzig wirklich Bemerkenswerte in dieser Datei ist, dass wir svg4everybody.min.js einbinden

Schlussfolgerungen

Nachdem die oben genannten Herausforderungen erfolgreich bewältigt wurden, behaupte ich, dass Teams, die moderne Webanwendungen entwickeln, Inline-SVG definitiv in Betracht ziehen sollten. Unicorn-UI bereitet die Implementierung eines „interaktiven Spielplatzes“ für unsere kürzlich veröffentlichten Buttons 2.0 Beta vor, und aus offensichtlichen Gründen werden wir Inline-SVG für dieses Projekt definitiv nutzen… vielleicht sollten Sie es auch für Ihr nächstes Projekt in Betracht ziehen.