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.
Es ist immer nützlich, konkrete Beispiele wie diese zu sehen. Toller Beitrag.
Toller Beitrag! Ich werde das sicher ausprobieren, denn wenn es eine Sache gibt, die ich in CSS hasse, dann ist es der Umgang mit Bildern.
Hey, Chris! Tolle Beispiele. Danke.
Zum Thema Animationsansatz. Sie können immer eine Opacity-Transition zwischen zwei Ebenen mit eingebetteten Bildern verwenden. Das ist sehr performant und erhöht, wie Sie sagten, die Größe mit Gzip nicht zu sehr.
Entschuldigung, mein Fehler. Dank Aleks. :)
Postcss ist sehr nützlich, ich benutze es immer für all meine Projekte. Schöner Artikel, danke fürs Teilen.
Toller Beitrag. Kleine Anmerkung. CSS mit 100 gleichen Inline-Bildern mag auf dem Draht ungefähr die gleiche Größe haben, aber es dehnt sich beim Client auf seine volle Größe aus und wird auch so geparst. Da der Client keine Möglichkeit hat zu wissen, dass diese Bilder meistens gleich sind, kann er das Parsen oder die Speichernutzung nicht optimieren. Möglicherweise haben Sie immer noch spürbare negative Auswirkungen.
Ich denke nicht, dass dies Ihren Ansatz ungültig macht, nur dass wir uns bewusst sein müssen, was wir tun (wie es ohnehin sein sollte).