SVG mit Media Queries: responsive Grafiken mit CSS steuern

SVG mit Media Queries: responsive Grafiken mit CSS steuern

In HTML-Dokumenten kannst du Teile der Seite je nach Zustand des Viewports anzeigen, verbergen oder neu anordnen. Ist das Browserfenster zum Beispiel 480 Pixel breit, wird aus der horizontalen Navigation eine vertikale, aufklappbare Liste. Mit SVG und Media Queries geht Ähnliches.

Dieser Artikel ist ein Auszug aus dem Buch von Tiffany. CSS Master, 3. Auflage. Wirf einen Blick hinein, wenn du weitere fortgeschrittene CSS3-Funktionen wie Flexbox und Grid beherrschen willst.

CSS lässt sich nicht nur mit HTML einsetzen, sondern auch mit SVG, den Scalable Vector Graphics. SVG ist ein Auszeichnungsformat für flache, zweidimensionale Bilder. Weil es eine Auszeichnungssprache ist, hat es ein Document Object Model und lässt sich mit CSS ansprechen.

Mit CSS und SVG änderst du das Aussehen einer Grafik abhängig davon, was die Nutzer tun. Genauso lässt sich dasselbe SVG-Dokument an mehreren Stellen einsetzen und je nach Breite des Viewports können Teile davon ein- oder ausgeblendet werden.

Alle großen Browser-Engines unterstützen SVG 1.1, und das seit Jahren. Die Unterstützung der Funktionen von SVG 2 ist dagegen noch in Arbeit. Zum Zeitpunkt dieses Textes ist die Browser-Unterstützung für einige der hier behandelten Themen begrenzt, wenn du das liest, kann sich das geändert haben. Behalte das Chromium-Meta-Issue Implementierung der SVG-2-Funktionen im Auge, um den Stand in Chromium-basierten Browsern zu verfolgen. Für Firefox gibt es das Meta-Issue Unterstützung der SVG-2-Funktionen, für Safari das WebKit-Meta-Issue Implementierung von SVG 2. Issue-Tracker sind unbequem, aber derzeit der beste Weg, die SVG-2-Unterstützung zu verfolgen.

Bevor es weitergeht, klären wir aber, was SVG ist und warum du es einsetzen solltest.

Vektorgrafiken und Rastergrafiken

Die meisten Bilder, die heute im Web zum Einsatz kommen, sind Rastergrafiken, auch Bitmaps genannt. Rastergrafiken bestehen aus Pixeln auf einem festen Raster, mit einer definierten Anzahl Pixel pro Zoll. JPEG, WebP, GIF und PNG sind allesamt Rasterformate.

Rastergrafiken sind auflösungsabhängig. Ein PNG mit 144 PPI (Pixel pro Zoll) sieht auf einem Gerät mit 144 PPI Displayauflösung sehr gut aus. Auf einem Bildschirm mit höherer Auflösung (400 PPI) wirkt dasselbe Bild dagegen unscharf. Rastergrafiken haben außerdem feste Abmessungen und sehen in ihrer Originalgröße am besten aus. Ein Bild von 150 mal 150 Pixeln auf 300 mal 300 Pixel hochzuskalieren, verzerrt es.

Vektorformate arbeiten nicht mit Pixeln auf einem Raster, sondern beschreiben die Grundformen – Kreise, Rechtecke, Linien oder Pfade –, aus denen ein Bild besteht, samt ihrer Lage im Koordinatensystem des Dokuments. Vektorgrafiken sind deshalb auflösungsunabhängig und behalten ihre Qualität, unabhängig von Auflösung und Abmessungen des Bildschirms.

Die Auflösungsunabhängigkeit ist der größte Vorteil von SVG. Du skalierst Bilder ohne Qualitätsverlust nach oben und unten, und dasselbe Bild sieht auf Geräten mit hoher wie mit niedriger PPI-Zahl gut aus. Für die Farbmenge, die Fotos brauchen, ist SVG allerdings schlecht geeignet. Es passt besser zu Zeichnungen und Formen. Nimm es anstelle von PNG und GIF und als flexibleren Ersatz für Icon-Fonts.

Ein weiterer Vorteil: SVG wurde für den Einsatz mit den übrigen Websprachen entworfen. Du kannst SVG-Grafiken mit JavaScript erzeugen, ändern und manipulieren – oder sie, wie wir gleich sehen, mit CSS gestalten und animieren.

CSS mit SVG-Dokumenten verbinden

CSS mit SVG einzusetzen funktioniert weitgehend wie CSS mit HTML. Du wendest CSS über das Attribut style eines SVG-Elements an, bündelst die Regeln im Dokument im Element <style> oder verlinkst eine externe Stylesheet-Datei. Vor- und Nachteile der einzelnen Wege sind dieselben wie bei CSS mit HTML.

Das Attribut style nutzen

Hier ein einfaches SVG-Dokument, dessen Code einen schwarzen Kreis zeichnet:

svg
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200"
➥enable-background="new 0 0 200 200">
<circle cx="101.3" cy="96.8" r="79.6" />
</svg>

Das Bild unten zeigt, wie ein Browser diesen Code darstellt.

Ein Kreis in SVG

Geben wir dem Kreis über CSS und das Attribut style eine rosa Füllung:

svg
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200"
➥enable-background="new 0 0 200 200">
<circle cx="101.3" cy="96.8" r="79.6" style="fill: #f9f" />
</svg>

Das Ergebnis siehst du unten.

Mit dem Attribut style eine Füllfarbe setzen

Hier zeigt sich ein Unterschied zwischen CSS mit HTML und CSS mit SVG: die Namen der Eigenschaften. Viele CSS-Eigenschaften, die wir in HTML-Dokumenten nutzen, greifen in SVG nicht – und umgekehrt. Darauf kommen wir später im Kapitel zurück.

Das Attribut style ist natürlich nicht der beste Weg, CSS einzusetzen. Die Stile lassen sich so weder über mehrere Elemente noch über mehrere Dokumente wiederverwenden. Besser sind eingebettetes oder verlinktes CSS.

CSS in SVG-Dokumente einbetten

Statt des Attributs style können wir das Element <style> verwenden:

svg
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0
➥ 200 200" enable-background="new 0 0 200 200">
<style type="text/css">
circle {
fill: #0c0;
}
</style>
<circle cx="101.3" cy="96.8" r="79.6" />
</svg>

Eingebettetes CSS lässt sich für mehrere Elemente im selben Dokument wiederverwenden, aber nicht über mehrere Dokumente hinweg teilen. Für Logos und Icons ist das genau richtig. Baust du dagegen so etwas wie eine Bibliothek von Diagrammstilen, nimm besser eine externe CSS-Datei.

Mit einem gewöhnlichen Texteditor kannst du CSS auch in SVG-Grafiken einfügen, die mit Sketch, Inkscape oder Illustrator entstanden sind. Das Bild bleibt im Zeichenprogramm bearbeitbar, speicherst du die Datei dort aber erneut, kann die Anwendung dein CSS umschreiben oder entfernen.

Aus SVG auf eine externe CSS-Datei verlinken

Wie in HTML lassen sich die Stile über eine verlinkte CSS-Datei zwischen mehreren SVG-Dokumenten teilen. Für den Verweis auf eine externe CSS-Datei setzt du <? xml-stylesheet ?> an den Anfang deiner SVG-Datei:

svg
<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet href="https://www.gekkode.com/de/style.css" type="text/css"?>
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0
➥ 200 200" enable-background="new 0 0 200 200">
<circle cx="101.3" cy="96.8" r="79.6" />
</svg>

Das Element <link> nutzen

Alternativ funktioniert das HTML-Element <link>. Bei dieser Methode musst du das Attribut xmlns angeben, wie unten gezeigt:

svg
<link href="style.css" type="text/css" rel="stylesheet"
 xmlns="http://www.w3.org/1999/xhtml" />

Hinweis: Manche älteren Browser verlangen, dass <link> von <defs>– oder <g>-Tags umschlossen ist.

<link> ist kein SVG-Element, es gehört zu HTML und XHTML. XHTML ist eine HTML-Variante, die nach den Regeln von XML geparst wird. Nach diesen Regeln dürfen wir Elemente und ihr Verhalten aus anderen XML-Dialekten wie XHTML übernehmen. Dazu müssen wir dem Browser aber jedes Mal über xmlns mitteilen, zu welchem Namensraum das Element gehört.

Mit @import arbeiten

Eine externe Stylesheet-Datei lässt sich auch mit @import zwischen <style> und </style> einbinden:

markup
<style type="text/css">
@import("https://www.gekkode.com/style.css");
</style>

Diese Methode arbeitet ähnlich wie <link>.

SVG und das Element <img>: die Grenzen

Verweise aus einer SVG-Datei auf externe Ressourcen, CSS-Dateien eingeschlossen, funktionieren nicht, wenn die Grafik über das Element <img> eingebunden ist. Das ist eine Sicherheitsbeschränkung des <img>-Elements, die fest in den Browsern verankert ist.

Willst du verlinktes CSS mit deinen SVG-Grafiken nutzen, hast du zwei Möglichkeiten:

  • das CSS mit dem Element <style> direkt in dein SVG-Dokument schreiben,
  • ein <iframe>– oder <object>-Element verwenden (siehe Hinweis unten).

In der Regel solltest du <iframe> gegenüber <object> bevorzugen. Allerdings darf <object> Kind eines <a>-Elements sein, <iframe> nicht. Mit <iframe> oder <object> wird der Dokumentbaum des SVG außerdem für das Elterndokument erreichbar. Wir können also mit JavaScript darauf zugreifen (etwa über document.querySelector('iframe').contentDocument).

Inline-SVG und externe Assets

Bindest du SVG in HTML ein, lädt der Browser die vom SVG-Dokument referenzierten externen Assets nicht. Das CSS für unser SVG können wir aber im <head> des HTML-Dokuments verlinken:

markup
<head><link href="svg.css" type="text/css" rel="stylesheet" />
</head>

SVG-Elemente in HTML-Dokumenten gehören auch zum HTML-Dokumentbaum. Bei Inline-SVG spricht deshalb nichts dagegen, die CSS-Regeln für HTML und für SVG in derselben Stylesheet-Datei zusammenzufassen.

Unterschiede zwischen SVG und HTML

SVG und HTML sind zwar beide Auszeichnungssprachen, doch zwei wesentliche Unterschiede wirken sich darauf aus, wie sie mit CSS zusammenspielen:

  • SVG hält sich nicht an das CSS-Boxmodell.
  • SVG kennt kein Positionierungsschema.

SVG hält sich nicht an das CSS-Boxmodell

In Verbindung mit HTML folgt das CSS-Layout den Regeln des Boxmodells. SVG dagegen platziert über Koordinaten und hält sich an etwas, das man am besten als „Formmodell“ versteht.

SVG-Formen sind nicht auf rechteckige Boxen beschränkt. Deshalb greifen die meisten Eigenschaften des Boxmodells bei SVG-Elementen nicht. Du kannst zum Beispiel weder padding noch margin eines SVG-Elements ändern, und auch box-sizing, box-shadow, outline und die border-*-Eigenschaften bleiben wirkungslos. Grid-Layout, Floats und Flexbox funktionieren ebenfalls nicht.

Mit CSS setzt oder änderst du dagegen eine ganze Reihe von SVG-Eigenschaften und Attributwerten. Die vollständige Liste steht in der Spezifikation SVG 2, auch wenn die meisten Browser sie nur unvollständig umsetzen. Manche CSS-Eigenschaften wie filter lassen sich sowohl mit SVG als auch mit HTML nutzen. Einige davon kommen in diesem Kapitel bei konkreten Techniken vor.

SVG kennt kein Positionierungsschema

Wird CSS mit HTML verwendet, können die Boxen der Elemente:

  • im normalen Fluss stehen,
  • mit der Eigenschaft float aus dem normalen Fluss genommen werden,
  • mit der Eigenschaft position aus dem normalen Fluss genommen werden.

Die CSS-Spezifikation nennt das Positionierungsschemata. In SVG gibt es sie nicht: position hat auf SVG-Elemente keine Wirkung. Dasselbe gilt für Eigenschaften wie top, left und bottom, die von der Positionierung abhängen. Elemente in einem SVG-Dokument floaten lassen kannst du ebenfalls nicht.

SVG platziert Elemente stattdessen über ein Koordinatensystem. Für einen <circle> legst du zum Beispiel die Koordinaten des Mittelpunkts über cx und cy fest und die Länge des Radius über das Attribut r. Ein Polygon besteht aus einer Reihe von Punktkoordinaten und den Linien dazwischen. Anders gesagt: Du bestimmst, wo ein Element auf der SVG-Zeichenfläche gezeichnet wird, „positionieren“ im Sinne von CSS kannst du es aber nicht.

Passend dazu kennt SVG auch z-index und Stapelkontexte nicht.

Hinweis: Die Spezifikation SVG 2 definiert ein Verhalten für z-index und Stapelkontexte in SVG-Dokumenten, die meisten Browser unterstützen es aber noch nicht.

SVG-Elemente stapeln sich in ihrer Quellreihenfolge: Was später im Dokument steht, liegt oben. Willst du die Stapelreihenfolge ändern, musst du die Elemente im Quelltext verschieben oder sie per JavaScript im DOM-Baum umsortieren.

Tatsächlich gelten die meisten Eigenschaften aus CSS 2.1 nicht für SVG-Dokumente. Ausnahmen sind Animationen und Transformationen, display, overflow, visibility, filter sowie einige Schrift- und Texteigenschaften. Stattdessen brauchst du die SVG-spezifischen Style-Eigenschaften für SVG-Dokumente. Die meisten davon lassen sich auch als Attribute von SVG-Elementen schreiben.

SVG-Elemente gestalten

Hier ein einfaches Beispiel dafür, wie sich SVG-Elemente mit CSS gestalten lassen. Wir beginnen mit unserem SVG-Dokument, einer eigenständigen Datei:

svg
<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet href="https://www.gekkode.com/de/styles.css" type="text/css" ?>
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink=
➥"http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 497
➥ 184" enable-background="new 0 0 497 184" xml:space="preserve">
<polygon id="star" points="77,23.7 98.2,66.6 145.5,66.5 111.2,
➥106.9,119.3,154 77,131.8 34.7,154 42.8,106.9 8.5,67.5 55.8,
➥66.6 "/>
<circle id="circle" cx="245" cy="88.9" r="67.5"/>
</svg>

Dieses Markup erzeugt das Bild unten.

Eine einfache SVG-Grafik aus Kreis und Stern

Auch wenn die meisten CSS-Eigenschaften in SVG-Dokumenten nicht greifen, ändern wir mit CSS die Farbe eines Elements. Machen wir unseren Stern gelb:

css
#star {
fill: hsl( 44, 100%, 50% );
}

Du siehst fill häufig als Attribut an SVG-Tags, etwa <circle fill="rgb( 255, 185, 0 )" cx="3" cy="10" r="100"> – es ist aber ebenso eine Eigenschaft, die sich in CSS setzen lässt.

Mit CSS passen wir auch stroke an, also die Kontur einer SVG-Form. Diese Kontur existiert, selbst wenn kein stroke gesetzt ist. Geben wir unserem Kreis einen zehn Pixel breiten, gestrichelten dunkelblauen Rand und setzen wir seine Eigenschaft fill auf cornflowerblue:

css
circle {
fill: cornflowerblue;
stroke: darkblue;
stroke-width: 10;
stroke-dasharray: 10, 15;
stroke-linecap: round;
}

Zusammen ergibt das folgendes Bild.

Eine einfache SVG-Grafik aus Kreis und Stern

SVG-Attribute als CSS-Eigenschaften nutzen

Mit CSS setzen wir auch die Koordinaten einiger Formelemente: <rect>, <circle> und <ellipse>. Üblicherweise würden wir dafür SVG-Attribute verwenden:

svg
<svg viewBox="0 0 400 400" xmlns="http://www.w3.org/2000/svg">
<rect x="20" y="200" width="300" height="300" fill="#f60" />
</svg>

SVG 2 hat einige dieser Attribute allerdings als geometrische Eigenschaften neu definiert. Wir können ihre Werte also in CSS setzen:

css
<svg viewBox="0 0 400 400" xmlns="http://www.w3.org/2000/svg">
    <style type="text/css">
      rect {
x: 20px;
y: 50px;
width:  300px;
height: 300px;
fill: #f60;
}
    </style>
    <rect />
</svg>

Die Koordinateneigenschaften (x und y), die Mittelpunktkoordinaten (cx und cy) und die Radien (rx, ry und r) lassen sich per CSS setzen, ebenso width und height. Bei SVG-Attributen sind Einheiten optional, CSS-Werte dagegen verlangen eine Einheit. Sowohl Längen als auch Prozentwerte sind für die genannten Eigenschaften gültig, aber Längen verhalten sich in SVG-Dokumenten etwas anders. Denk daran: Das S in SVG steht für scalable. Die berechnete Größe eines SVG-Elements hängt außerdem ab von:

  • der berechneten width und height des SVG-Wurzelelements
  • dem Wert des Attributs viewBox am Wurzelelement
  • jeder Skalierungstransformation auf dem Element oder auf einem seiner Vorfahren

Anders gesagt: Die Ecken unseres <rect>-Elements liegen im SVG-Koordinatensystem bei (20, 50), (20, 320), (350, 320) und (20, 350). Die tatsächlichen Abmessungen können je nach den Faktoren oben aber größer oder kleiner ausfallen.

Nicht jedes SVG-Attribut ist über CSS erreichbar – jedenfalls nicht in allen Browsern. Chrome und Edge unterstützen zum Beispiel die CSS-Funktion path(), um die Pfaddaten der Eigenschaft d zu setzen:

css
path {
d: path("M 454.45223,559.21474 -304.96705,163.45948 417.4767,-296.33928 Z");
}

Bis heute sind das die einzigen Browser, die das tun. Die Arbeit an der Unterstützung in Firefox und WebKit hat noch nicht begonnen.

Bei den übrigen Formelementen ist die Spezifikation von SVG 2 schlicht inkonsistent. Für <line>, <polyline> und <polygon> musst du die Eigenschaften bis heute über Elementattribute setzen.

Dabei sind wir nicht auf Typ- beziehungsweise Elementselektoren festgelegt. Kleine, mittlere und große Kreise lassen sich zum Beispiel über Klassenselektoren definieren:

svg
<svg viewBox="0 0 400 400" xmlns="http://www.w3.org/2000/svg">
<style type="text/css">

.small {
cx: 20px;
cy: 20px;
r:  20px;
fill:  #0c0;
}

.medium {
cx: 80px;
cy: 80px;
r:  60px;
fill:  #fc0;
}

.large {
cx: 220px;
cy: 220px;
r:  120px;
fill: #00f;
}

</style>

<circle class="small" />
<circle class="medium" />
<circle class="large" />
</svg>

Welcher Selektor es auch ist: Wer Eigenschaften in CSS-Syntax angibt, kann sie auch leicht animieren. Wie das geht, sehen wir im nächsten Abschnitt.

SVG-CSS-Eigenschaften animieren und überblenden

Richtig interessant wird CSS mit SVG, sobald Transitions und Animationen dazukommen. Das Vorgehen entspricht dem Animieren von HTML-Elementen mit CSS, nur mit SVG-spezifischen Eigenschaften. Bauen wir mit dem folgenden SVG-Dokument einen funkelnden Stern:

svg
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" x="0px"
 y="0px" viewBox="0 0 497 184" xml:space="preserve">
<defs>
<link href="twinkle.css" type="text/css" rel="stylesheet"
 xmlns="http://www.w3.org/1999/xhtml"/>
</defs>
<polygon class="star" points="77,23.7 98.2,66.6 145.5,66.5 111.2
➥,106.9 119.3,154 77,131.8 34.7,154 42.8,106.9 8.5,67.5
➥ 55.8,66.6 "/>
<polygon class="star twinkle" points="77,23.7 98.2,66.6 145.5,
➥66.5 111.2,106.9 119.3,154 77,131.8 34.7,154 42.8,106.9
➥ 8.5,67.5 55.8,66.6 "/>
</svg>

Unser Dokument enthält zwei sternförmige Polygone, beide mit dem Klassennamen star. Für den Funkeleffekt animieren wir das erste Element. Hier ist unser CSS:

css
@keyframes twinkle {
from {
fill-opacity: .4;
}
to {
fill-opacity: 0;
transform: scale( 2 );
}
}
.star {
fill: rgb( 255,195,0 );
transform-origin: 50% 50%;
}
.twinkle {
animation: twinkle 1.5s infinite forwards ease-in;
}

Wir haben hier die SVG-spezifische Eigenschaft fill-opacity genutzt. Wie in CSS gilt: Lässt sich der Wert einer SVG-Style-Eigenschaft interpolieren, können wir ihn animieren oder überblenden. Zwei Momente der Animation siehst du im Bild unten.

Unsere pulsierende Sternanimation

Noch ein Beispiel. Diesmal bauen wir einen Zeichen-Effekt, indem wir stroke-dasharray überblenden. Hier ist unser SVG-Dokument:

svg
<svg version="1.1" xmlns="http://www.w3.org/2000/svg"
➥xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 200 200" enable-background="new 0 0 200 200">
<circle fill="transparent" stroke-width="16" cx="101.3"
 cy="96.8" r="79.6"/>
</svg>

stroke-dasharray nimmt eine kommagetrennte Liste aus Längen- oder Prozentwerten entgegen, die ein Strichmuster ergeben. Die ungeraden Werte bestimmen die Länge des Strichs, die geraden die Länge der Lücke. Ein stroke-dasharray mit dem Wert 5, 10 bedeutet also: Der Strich ist 5px lang, zwischen zwei Strichen liegen 10px. Der Wert 5, 5, 10 wechselt zwischen 5px und 10px langen Strichen mit 5px Abstand dazwischen.

Mit stroke-dasharray bauen wir einen Zeichen-Effekt: Wir starten mit der Strichlänge null und einer großen Lücke und enden mit einem langen Strich ohne Lücke. Dazwischen blenden wir über. So sieht unser CSS aus:

css
circle {
transition: stroke-dasharray 1s ease-in;
fill: transparent;
stroke-dasharray: 0, 500;
}
.animate {
stroke-dasharray: 500, 0;
}

Zu Beginn der Transition ist unsere Kontur unsichtbar, weil die Strichlänge 0 und die Lücke 500 beträgt. Sobald wir dem Kreis die Klasse animate geben, gehen wir auf eine Strichlänge von 500 und lassen die Lücke verschwinden. Der Effekt erinnert an einen Kreis, der mit dem Zirkel gezogen wird. Warum 500? Das war der kleinste Wert, mit dem dieser Effekt zustande kam.

Animierte Pfade, irgendwann

Erinnerst du dich an das Beispiel aus dem vorigen Abschnitt, in dem wir einen Pfad über CSS definiert haben? Vielleicht lassen sich Pfade eines Tages in allen Browsern per CSS animieren:

css
path {
d: path("M357.5 451L506.889 192.25H208.111L357.5 451Z");
transition: d 1s ease-in-out;
}
.straighten {
d: path("M357.5 8871L406 -10113.75H208.111L357.5 351Z");
}

Bis heute unterstützen allerdings nur Chromium-basierte Browser wie Google Chrome und Microsoft Edge das Animieren von Pfaddefinitionen auf diesem Weg. Damit es in anderen Browsern funktioniert, nimmst du eine JavaScript-Bibliothek wie GreenSock mit dem MorphSVGPlugin. Neben der Browser-Kompatibilität machen GreenSock und das MorphSVGPlugin auch das Morphing zwischen zwei Formen leicht, egal wie viele Punkte jede von ihnen hat.

SVG mit Media Queries einsetzen

In HTML-Dokumenten können wir Teile der Seite je nach Zustand des Viewports anzeigen, verbergen oder neu anordnen. Ist das Browserfenster zum Beispiel 480 Pixel breit, wird aus der horizontalen Navigation eine vertikale, aufklappbare Liste. Mit Media Queries und SVG-Dokumenten geht Ähnliches. Nehmen wir ein Logo, etwa das der fiktiven Firma Hexagon Web Design & Development, das unten abgebildet ist.

Ein sehr reales Logo für eine erfundene Firma

Ohne Media Queries würde dieses SVG-Logo sich einfach strecken oder schrumpfen, um in den Viewport oder seinen Container zu passen. Mit Media Queries geht Schlaueres.

Unterscheiden wir zwischen dem Viewport des HTML-Dokuments und dem des SVG-Dokuments. Ist das SVG inline eingebunden, sind beide identisch: Das SVG-Dokument verhält sich wie jedes andere HTML-Element. Ist das SVG-Dokument dagegen verlinkt – wie beim Element <iframe>, <object> oder <img> –, haben wir es mit dem Viewport des SVG-Dokuments zu tun.

Media Queries funktionieren in beiden Fällen, doch bei einem verlinkten SVG-Dokument ist dessen Viewport unabhängig vom HTML-Dokument. Nicht die Größe des Browserfensters bestimmt dann den SVG-Viewport, sondern die Abmessungen des <object>-, <iframe>– oder <img>-Elements. Nehmen wir das folgende (gekürzte) SVG-Dokument:

svg
<svg version="1.1" id="HexagonLogo" xmlns="http://www.w3.org/2000/
➥svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
 viewBox="0 0 555 174" xml:space="preserve">
<defs>
<style type="text/css">
/* CSS goes here */
</style>
</defs>
<g id="hex">
<polygon id="hexagonbg" points="55.2,162 10,86.5 55.2,11
➥ 145.5,11 190.7,86.5 145.5,162  "/>
<path id="letterH" fill="#FFFFFF" d="M58,35.5h33v35.2h18.
➥4V35.5 h33.2v103.4h-33.2v-38.3H91v38.3H58V35.5z M77.5,126.5V87.
➥3h45.6v39.2h4V47.9h-4v35.6H77.5V47.9h-4v78.6H77.5z"/>
</g>

<g id="word-mark">
<g id="hexagon-word">
            ...
</g>
<g id="web-design-and-dev">
            ...
</g>
</g>
</svg>

Hinweis: Das SVG-Dokument ist hier gekürzt; eine vollständige Demo dieser Technik samt komplettem Quelltext gehört zum Begleitmaterial des Buchs.

In kleineren Viewports zeigen wir nur das H im Sechseck:

css
@media (max-width: 320px) {
[id=word-mark] {
display: none;
}
}

Ist der Container unseres SVG jetzt kleiner oder gleich 20em, bleibt nur der Symbolteil unseres Logos sichtbar.

Elemente je nach Größe des SVG-Viewports ein- und ausblenden

Um diese Ansicht aus dem HTML-Dokument heraus auszulösen, setzt du die Breite des SVG-Containers:

markup
<iframe src="hexlogo.svg" style="width: 320px; border:0"></iframe>

Wie dir am Bild oben vielleicht aufgefallen ist, behält unsere SVG-Grafik ihre intrinsischen Abmessungen, obwohl ein Teil verborgen ist. Das ist leider eine Einschränkung von SVG. Dagegen hilft nur, die viewBox des SVG-Dokuments zu ändern, und zwar nur dann, wenn der Viewport eine bestimmte Größe unterschreitet. Ein ausgezeichneter Anwendungsfall für matchMedia.

Die viewBox bestimmt, wie der Name sagt, den sichtbaren Bereich eines SVG-Elements. Über sie legen wir fest, welcher Teil einer SVG-Grafik den Viewport füllt. Hier ein Beispiel, das matchMedia und eine Media Query nutzt, um das Attribut viewBox zu aktualisieren:

javascript
<script type="text/javascript">
const svg = document.querySelector( 'svg' );

/* Den ursprünglichen Wert in einer Variablen speichern */
const originalViewBox = svg.getAttribute( 'viewBox' );

/* Unsere Media Query und das Media-Query-Objekt definieren */
const mq = matchMedia( '( max-width: 320px )' );

/* Den Handler definieren */
const updateViewBox = () {
if (mq.matches) {
/* Die Maße der viewBox ändern, um das Sechseck zu zeigen */
        svg.setAttribute( 'viewBox', '0 0 200 174' );
} else {
        svg.setAttribute( 'viewBox', originalViewBox );
}
}

svg.addEventListener( 'SVGLoad', updateViewBox );

/* Wird ausgelöst, wenn sich die Media-Bedingung ändert */
mq.addEventListener( 'change', updateViewBox );
</script>

Ist der SVG-Container jetzt kleiner oder gleich 320 Pixel, lautet der Wert des Attributs viewBox "0 0 200 174". Wird er breiter als 320 Pixel, bekommt viewBox wieder seinen ursprünglichen Wert.

Das Attribut viewBox abhängig von der Viewport-Breite anpassen

Weil diese Technik entweder das Attribut onload oder das Ereignis SVGLoad nutzt, ist es eine gute Idee, CSS und JavaScript in die SVG-Datei einzubetten. Liegt das CSS extern, kann SVGLoad feuern, bevor das zugehörige CSS fertig geladen ist.

Media Queries mit background-size nutzen

SVG-Dokumente und Media Queries sind nicht auf Vordergrundbilder beschränkt. Den SVG-Viewport skalieren wir auch über die CSS-Eigenschaft background-size.

Wir beginnen mit diesem SVG-Dokument:

svg
<?xml version="1.0" encoding="utf-8"?>
<svg version="1.1" xmlns="http://www.w3.org/2000/svg"
 xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
 viewBox="-20 -20 250 250" xml:space="preserve">
<style type="text/css">
circle {
stroke: #000;
stroke-width: 30;
fill: #009688;
}
@media ( width: 100px ) {
circle {
fill: #673ab7;
}
}
@media ( width: 300px ) {
circle {
fill: #ffc107;
}
}
</style>
</defs>
<circle cx="100" cy="100" r="100" />
<circle cx="100" cy="100" r="50" />
</svg>

Der Fall ist einfach: Unsere <circle>-Elemente bekommen bei bestimmten Viewport-Breiten eine neue fill-Farbe. Standardmäßig ist die Füllung blaugrün, bei 100 Pixeln Breite wird sie violett, bei 300 Pixeln gelb.

Damit das funktioniert, müssen wir unsere SVG-Grafik als Hintergrundbild einsetzen und im Selektor background-size angeben. Hier nehmen wir das Bild als Hintergrund für <body> und für die <li>-Elemente:

css
body, li  {
background: url(circles.svg);
}
body  {
background-color: #9c27b0;
background-size: 300px auto;
}
li {
background-position: left center;
background-repeat: no-repeat;
background-size: 1em auto;
padding-left: 40px;
font-size: 24px;
margin: 1rem 0;
}

Das Ergebnis siehst du unten.

Den SVG-Viewport mit der CSS-Eigenschaft background-size steuern

Fazit

SVG mit CSS zu kombinieren, eröffnet mehr Möglichkeiten für flexible, anpassungsfähige Dokumente. Du weißt jetzt, wie du:

  • SVG-Elemente mit CSS gestaltest
  • SVG-Eigenschaften animierst
  • CSS-Media-Queries und die matchMedia-API nutzt, um Teile eines SVG-Dokuments ein- und auszublenden

CSSHTML

Damien Flandrin Webentwickler seit 2010, Gründer von Gekkode und Email Impact. Jeder Artikel wird vor der Veröffentlichung an einem echten Projekt getestet. Kontakt
Newsletter

Neue Tests, Tutorials und Projekte, per E-Mail.

Reproduzierbare Tests, versionierter Code, datierte Ergebnisse. Niemals Spam.