Arbeiten mit Bildern in Stylesheets mit PostCSS

Avatar of Aleks Hudochenkov
Aleks Hudochenkov am

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

Der folgende Beitrag ist ein Gastbeitrag von Aleks Hudochenkov. Aleks leistet hier hervorragende Arbeit, indem er zeigt, wofür PostCSS gut geeignet ist und welche Rolle es im Frontend-Stack spielt. Und zwar: kleine, nützliche Aufgaben innerhalb von CSS zu erledigen. Sie werden eine Vielzahl von PostCSS-Plugins in Aktion sehen, die alle mit der Arbeit mit Bildern zu tun haben. Am Ende, da wette ich, werden Sie sich vorstellen können, wie PostCSS auch in anderen Nischen bei der Arbeit mit CSS nützlich sein kann.

Wir alle arbeiten mit Bildern in unserem CSS. Routinekram. Wir erkennen es vielleicht nicht einmal, aber es steckt viel manuelle Arbeit dahinter, die viel einfacher gemacht werden könnte. Ich werde Ihnen eine Vielzahl von PostCSS-Plugins zeigen, die speziell dafür entwickelt wurden, die Arbeit mit Bildern in CSS zu erleichtern.

Jedes in diesem Artikel beschriebene Plugin funktioniert mit jeder Syntax, die PostCSS parsen kann – CSS, SCSS, Less und durch PostCSS-Plugins erstellte Syntaxen. Ich werde nicht beschreiben, wie man PostCSS selbst verwendet, da es bereits einen exzellenten Artikel von Drew Minns gibt.

Beginnen wir mit Plugins, die die meisten Anwendungsfälle bei der Arbeit mit Bildern in CSS abdecken.

Bild-Helfer

Das postcss-assets Plugin ist ein fast unverzichtbares Plugin für die Arbeit mit Bildern. Es hat viele Funktionalitäten.

Bilder einbetten

Manchmal ist es nützlich, Bilder in unseren Stylesheets in Data-URLs umzuwandeln. Eine HTTP-Anfrage weniger!

/* input.css */
div {
  background: inline("images/logos/postcss.png");
}

/* output.css */
div {
  background: url("data:image/png;base64,iVBORw0KGgoAAAA...ggg==");
}

Abmessungen berechnen

Manchmal müssen Sie Elemente oder den Hintergrund selbst basierend auf den Abmessungen des verwendeten Bildes dimensionieren. Dieses Plugin kann diese Messungen vornehmen und sie nach Bedarf ausgeben.

/* input.css */
body {
  width: width("images/foobar.png");
  height: height("images/foobar.png");
  background-size: size("images/foobar.png");
}

/* output.css */
body {
  width: 320px;
  height: 240px;
  background-size: 320px 240px;
}

Wenn wir mit hochauflösenden Bildern arbeiten, können wir die Ausgabe korrigieren, indem wir den zweiten Parameter hinzufügen

/* input.css */
body {
  width: width("images/foobar.png", 2);
  height: height("images/foobar.png", 2);
  background-size: size("images/foobar.png", 2);
}

/* output.css */
body {
  width: 160px;
  height: 120px;
  background-size: 160px 120px;
}

URL-Auflösung

Das Plugin kann bei Dateipfaden helfen. Wir müssen nicht den vollständigen Pfad zum Bild kennen. Nur der Dateiname reicht aus.

Zum Beispiel haben wir diese Ordnerstruktur

images/
 logos/
  postcss.png
input.css

Wir konfigurieren die Optionen für das Plugin wie folgt. Die ** bedeuten, dass in allen Ordnern und Dateien rekursiv gesucht wird.

postcss([
  require('postcss-assets')({
   loadPaths: ['**']
  })
])
/* input.css */
div {
  background: resolve("postcss.png");
  background: resolve("logos/postcss.png");
}

/* output.css */
div {
  background: url("/images/logos/postcss.png");
  background: url("/images/logos/postcss.png");
}

Cachebusting

Dieses Plugin kann Bilder cachebustieren.

postcss([
  require('postcss-assets')({
    cachebuster: true
  })
])
/* input.css */
div {
  background: resolve("images/logos/postcss.png");
}

/* output.css */
div {
  background: url("images/logos/postcss.png?153bd5d59c8");
}

SVGs einbetten und modifizieren

Fast jede Grafik, mit der ich in letzter Zeit zu tun habe, ist SVG. Es ist ein großartiges Format, das Displays mit jeder Pixeldichte unterstützt. Noch besser ist, dass die Syntax dafür Text ist, was bedeutet, dass wir sie ohne schwere Werkzeuge wie Grafikbearbeitungsprogramme bearbeiten können.

Es gibt ein Plugin zum Einbetten von SVGs: postcss-inline-svg. Sie fragen sich vielleicht, warum wir es brauchen, wenn postcss-assets, das wir bereits behandelt haben, das auch kann. Der Grund ist, dass postcss-inline-svg über eine Killer-Funktion verfügt: es kann SVGs modifizieren.

Nehmen wir an, wir haben ein Stern-Symbol, das wir an zehn verschiedenen Stellen mit unterschiedlichen Farben auf unserer Website verwenden. Es gibt viele Möglichkeiten, dies zu tun. Wir könnten ein Inline-SVG-System mit <symbol></symbol> und <use> und allem Drum und Dran verwenden. Oder wir können die background-Eigenschaft in CSS verwenden!

Es gibt zwei Möglichkeiten, ein Bild in CSS zu verwenden. 1) ein Wert url(/path/to/image.jpg) mit einem Dateipfad oder 2) ein Wert url(data:...) mit einer Data-URL. Letzteres ist manchmal als "Inlining" von Bildern bekannt, was einen der Hauptvorteile von Bild-Sprites erzielt: die Kombination von HTTP-Anfragen. Mit postcss-inline-svg können wir das tun (und unsere CSS-Datei zu unserem Sprite machen) und trotzdem die Farben unabhängig voneinander anpassen.

/* input.css */
.star--red {
  background-image: svg-load("img/star.svg", fill=#f00);
}
.star--green {
  background-image: svg-load("img/star.svg", fill=#0f0, stroke=#abc);
}

/* output.css */
.star--red {
  background: url("data:image/svg+xml;charset=utf-8,%3Csvg fill='%23f00'%3E...%3C/svg%3E");
}
.star--green {
  background: url("data:image/svg+xml;charset=utf-8,%3Csvg fill='%230f0' stroke='%23abc'%3E...%3C/svg%3E");
}

Wird die ausgegebene CSS-Datei riesig sein, fragen Sie sich? Ja und nein. Die Ausgabe-CSS wird größer sein wegen Code-Duplizierung, aber wegen Gzip spielt das keine Rolle! Zur Veranschaulichung habe ich einen Test gemacht. Ich habe eine CSS-Datei mit 100 verschiedenen Selektoren erstellt und in jeder Regel ein Inline-Icon mit einer zufälligen Farbe für fill hinzugefügt. So:

.wibcsidpuaeqgbxvcjqq { 
  background: svg-load("images/star.svg", fill: #8c0); 
}

Dann habe ich eine Kopie dieser Datei erstellt und all diese Inline-Hintergründe entfernt. Hier sind die Dateigrößen-Ergebnisse:

Originalgröße Gzip-komprimiert
Mit 100 Bildern 48500 Bytes 2560 Bytes
Mit 1 Bild 3158 Bytes 1817 Bytes

Unterschied: 2560 – 1817 = 743 Bytes

Kein großer Unterschied!

Der einzige Nachteil dieses Ansatzes: Es gibt keine Möglichkeit, Bildänderungen zu animieren. Wenn sich beispielsweise die Farbe beim Überfahren mit der Maus ändern soll, aber mit einer Transition, gibt es keine bequeme Möglichkeit, dies zu tun, da transition nicht auf background-image angewendet wird.

Diese Plugins ergänzen sich

Reales Beispiel: Wir brauchen einen Button, der ein Icon ist. Das Bild im Button benötigt eine bestimmte Bildgröße und soll beim Überfahren mit der Maus die Farbe ändern. Es gibt nur eine einzige Quell-SVG-Datei.

Markup

<button type="button" class="delete">Delete</button>

Ohne jegliche Hilfe könnten wir so etwas tun:

.delete {
  box-sizing: content-box;
  padding: 15px;

  /* Values based on this particular image */
  width: 26px;
  height: 32px;

  border: 1px solid #ef5350;
  border-radius: 3px;
  background: #fff url("images/trash.svg") 50% 50% no-repeat;
  text-indent: -9999px;
}
.delete:hover {
  border-color: #c62828;

  /* Manually duplicate file and change things */
  background-image: url("images/trash-hover.svg");
}

Wenn wir die Dinge mit postcss-assets automatisieren, könnten wir das tun:

postcss([
  require('postcss-inline-svg')(),
  require('postcss-assets')()
]);
.delete {
  box-sizing: content-box;
  padding: 15px;
  width: width("images/trash.svg");
  height: height("images/trash.svg");
  border: 1px solid #ef5350;
  border-radius: 3px;
  background: #fff svg-load("images/trash.svg", fill=#ef5350) 50% 50% no-repeat;
  text-indent: -9999px;
}
.delete:hover {
  border-color: #c62828;
  background-image: svg-load("images/trash.svg", fill=#c62828);
}

Ausgabe

.delete {
  box-sizing: content-box;
  padding: 15px;
  width: 26px;
  height: 32px;
  border: 1px solid #ef5350;
  border-radius: 3px;
  background: #fff url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='26' height='32' viewBox='12 8 26 32' fill='%23ef5350'%3E%3Cpath d='M20 18h2v16h-2z'/%3E%3Cpath d='M24 18h2v16h-2z'/%3E%3Cpath d='M28 18h2v16h-2z'/%3E%3Cpath d='M12 12h26v2H12z'/%3E%3Cpath d='M30 12h-2v-1c0-.6-.4-1-1-1h-4c-.6 0-1 .4-1 1v1h-2v-1c0-1.7 1.3-3 3-3h4c1.7 0 3 1.3 3 3v1z'/%3E%3Cpath d='M31 40H19c-1.6 0-3-1.3-3.2-2.9l-1.8-24 2-.2 1.8 24c0 .6.6 1.1 1.2 1.1h12c.6 0 1.1-.5 1.2-1.1l1.8-24 2 .2-1.8 24C34 38.7 32.6 40 31 40z'/%3E%3C/svg%3E") 50% 50% no-repeat;
  text-indent: -9999px;
}
.delete:hover {
  border-color: #c62828;
  background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='26' height='32' viewBox='12 8 26 32' fill='%23c62828'%3E%3Cpath d='M20 18h2v16h-2z'/%3E%3Cpath d='M24 18h2v16h-2z'/%3E%3Cpath d='M28 18h2v16h-2z'/%3E%3Cpath d='M12 12h26v2H12z'/%3E%3Cpath d='M30 12h-2v-1c0-.6-.4-1-1-1h-4c-.6 0-1 .4-1 1v1h-2v-1c0-1.7 1.3-3 3-3h4c1.7 0 3 1.3 3 3v1z'/%3E%3Cpath d='M31 40H19c-1.6 0-3-1.3-3.2-2.9l-1.8-24 2-.2 1.8 24c0 .6.6 1.1 1.2 1.1h12c.6 0 1.1-.5 1.2-1.1l1.8-24 2 .2-1.8 24C34 38.7 32.6 40 31 40z'/%3E%3C/svg%3E");
}

Wenn sich das Bild ändert, müssen Sie nichts tun! postcss-assets aktualisiert die Größen. Müssen Sie eine Farbe ändern? Sie sind direkt hier in CSS. Sie können sogar Variablen verwenden, wenn Sie ein anderes Plugin oder einen Preprocessor verwenden, der diese anbietet.

Beispiel für die Ausgabe

Siehe den Pen NNvGJP von Aleks Hudochenkov (@hudochenkov) auf CodePen.

Sprites

Es gibt immer noch einige Gründe, warum Sie die Art von Bild-Sprite verwenden möchten, bei der alle Bilder zu einem größeren Bild zusammengefügt werden. Zum einen ist bekannt, dass Mobiltelefone Inline-Bilder etwas langsamer dekodieren als normale Bilder.

Es gibt viele Tools zur Erstellung von Bild-Sprites. Zum Beispiel: grunt-spritesmith. Diese sind leistungsfähig, aber nicht besonders einfach einzurichten oder bequem. Bei grunt-spritesmith müssen Sie zum Beispiel verstehen, wie seine Template-Engine funktioniert.

Das postcss-sprites Plugin (basierend auf spritesmith) ist viel bequemer. So funktioniert es:

/* input.css */
.comment {
  background-image: url("images/sprite/ico-comment.png");
}
.bubble {
  background-image: url("images/sprite/ico-bubble.png");
}

/* output.css */
.comment {
  background-image: url("images/sprite.png");
  background-position: 0 0;
}
.bubble {
  background-image: url("images/sprite.png");
  background-position: 0 -50px;
}

Es findet jedes Bild in CSS (Filterung ist möglich), erstellt einen Sprite und gibt die korrekte background-position aus, um dorthin zu gelangen.

Sprites für hochauflösende Bildschirme behandeln

Trotz der Unterstützung von Retina-Bildern durch postcss-sprites liefert es Ihnen keinen vollständig produktionsreifen Code. Zum Beispiel fügt es keine Media Queries hinzu, um hochauflösende Bildschirme anzusprechen und diese Bilder tatsächlich zu verwenden. Dieses Problem kann mit einem weiteren PostCSS-Plugin gelöst werden. Das ist die Schönheit des PostCSS-Ökosystems – es gibt viele Plugins, die nur eine Aufgabe erfüllen, und Sie können sie kombinieren, um komplexere Probleme zu lösen.

Es gibt ein postcss-at2x Plugin, das Media Queries hinzufügt, die hochauflösende Bildschirme ansprechen. Kombinieren wir diese Plugins, um einen Sprite für normale und hochauflösende Bildschirme zu generieren.

postcss([
  require('postcss-at2x')(),
  require('postcss-sprites').default({
    retina: true
  })
]);
/* input.css */
.circle {
  background-image: url("images/circle.png") at-2x;
}
.square {
  background-image: url("images/square.png") at-2x;
}

/* output.css */
.circle {
  background-image: url("sprite.png");
  background-position: 0px 0px;
}
.square {
  background-image: url("sprite.png");
  background-position: -25px 0px;
}
@media (min--moz-device-pixel-ratio: 1.5), (-o-min-device-pixel-ratio: 3/2), (-webkit-min-device-pixel-ratio: 1.5), (min-device-pixel-ratio: 1.5), (min-resolution: 144dpi), (min-resolution: 1.5dppx) {
  .circle {
    background-image: url("[email protected]");
    background-position: 0px 0px;
    background-size: 50px 25px;
  }
}
@media (min--moz-device-pixel-ratio: 1.5), (-o-min-device-pixel-ratio: 3/2), (-webkit-min-device-pixel-ratio: 1.5), (min-device-pixel-ratio: 1.5), (min-resolution: 144dpi), (min-resolution: 1.5dppx) {
  .square {
    background-image: url("[email protected]");
    background-position: -25px 0px;
    background-size: 50px 25px;
  }
}

Perfekt.

Bilder "on the fly" erstellen

Manchmal brauchen wir wirklich einfache Bilder (wie geometrische Formen), aber wir öffnen trotzdem einen Grafikeditor, erstellen das Bild, exportieren es, legen es an die richtige Stelle, optimieren es und verwenden es in CSS. Was wäre, wenn wir einfache Bilder direkt in CSS erstellen könnten? Ich wette, Sie können es erraten: das geht!

postcss-write-svg ermöglicht es Ihnen, einfache SVG-Bilder direkt in CSS zu erstellen. Beschreiben Sie einfach SVG-Elemente und sie werden als background-image eingebettet.

/* input.css */
@svg square {
  @rect {
    fill: var(--color, black);
    width: 100%;
    height: 100%;
  }
  @polygon {
    fill: green;
    points: 50,100 0,0 0,100;
  }
}

#example {
  background: white svg(square param(--color #00b1ff));
}

/* output.css */
#example {
  background: white url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg'%3E%3Crect fill='%2300b1ff' width='100%25' height='100%25'/%3E%3Cpolygon fill='green' points='50%2C100 0%2C0 0%2C100'/%3E%3C/svg%3E");
}

Es gibt andere Plugins, um Kreise und Dreiecke nur mit CSS-Eigenschaften zu erstellen. Dreiecke. Sie können Dreiecke selbst in CSS erstellen, aber es ist nicht gerade einfach und wird schwieriger, wenn Sie verschiedene Arten von Dreiecken erstellen möchten. postcss-triangle ermöglicht es Ihnen, leicht gleichschenklige, rechtwinklige gleichschenklige und gleichseitige Dreiecke zu erstellen.

/* input.css */
.isosceles-triangle {
  triangle: pointing-right;
  width: 150px;
  height: 115px;
  background-color: red;
}
.right-isosceles-triangle {
  triangle: right-iso pointing-down;
  width: 250px;
  background-color: red;
}
.equilateral-triangle {
  triangle: equilateral pointing-up;
  height: 100px;
  background-color: red;
}

/* output.css */
.isosceles-triangle {
  width: 0;
  height: 0;
  border-style: solid;
  border-color: transparent;
  border-width: 57.5px 0 57.5px 150px;
  border-left-color: red;
}
.right-isosceles-triangle {
  width: 0;
  height: 0;
  border-style: solid;
  border-color: transparent;
  border-width: 125px 125px 0;
  border-top-color: red;
}
.equilateral-triangle {
  width: 0;
  height: 0;
  border-style: solid;
  border-color: transparent;
  border-width: 0 57.73503px 100px;
  border-bottom-color: red;
}

Kreise sind etwas einfacher, aber postcss-circle kann Ihnen ein paar Codezeilen sparen und für mehr Lesbarkeit sorgen.

/* input.css */
.circle {
  circle: 100px red;
}

/* output.css */
.circle {
  border-radius: 50%;
  width: 100px;
  height: 100px;
  background-color: red;
}

Cachebusting

Angenommen, Sie müssen ein in einem Stylesheet verlinktes Bild aktualisieren. Wir könnten auf ein Problem stoßen, wenn wir weit in der Zukunft liegende Ablauf-Header verwenden und der Browser des Benutzers dieses Bild im Cache hält. Die Lösung besteht darin, den Browser des Benutzers zu zwingen, die neue Version herunterzuladen (cachebust). Dafür gibt es zwei Möglichkeiten: Ändern Sie einen Dateinamen oder ändern Sie die URL. Das Ändern des Dateinamens ist eine große Aufgabe, aber das Ändern der URL ist dank URL-Parametern einfach.

So funktioniert das Ändern von URLs mit postcss-urlrev

/* input.css */
.foo {
  background: url("images/test.png") 0 0 no-repeat;
}

/* output.css */
.foo {
  background: url("images/test.png?v=e19ac7dee6") 0 0 no-repeat;
}

Diese Aufgabe kann auch mit postcss-cachebuster und postcss-assets erledigt werden.

Utilities

PostCSS-Plugins können bei der Optimierung von Stylesheets helfen. Zum Beispiel kann postcss-svgo eingebettete SVGs mit SVGO, dem besten SVG-Optimierungstool, optimieren.

Wenn Sie weiterhin Browser unterstützen müssen, die SVG nicht unterstützen, Sie aber SVG verwenden möchten, kann postcss-svg-fallback Ihnen helfen. Dieses Plugin generiert PNG-Fallback-Versionen für SVG in Ihrem CSS (sowohl eingebettet als auch über url() verlinkt) und fügt zusätzliche Regeln in CSS für alte Browser hinzu.

Das Einbetten von Bildern kann die Ausgabe-CSS aufblähen, aber es gibt eine Lösung: postcss-data-packer kann eingebettete Data-URLs in eine separate Datei extrahieren. Jetzt können Sie diese Datei asynchron laden, um die Seitenladezeit zu verkürzen.

Fazit

Vor PostCSS haben wir viel mühsame manuelle Arbeit geleistet: Dinge kopiert und eingefügt, Dinge dupliziert oder manuelle Berechnungen durchgeführt. Jetzt können wir einige PostCSS-Plugins verwenden, damit unsere Computer die Dinge für uns erledigen. Das beschleunigt unsere Arbeit und macht uns zu glücklicheren Menschen.