SVG für responsive Webseiten

SVG responsive

SVG-Grafiken sind zwar responsive und passen sich geschmeidig an den verfügbaren Platz an, was ihnen aber fehlt, sind die Eigenschaften des HTML-picture-Elements: Mehr Elemente für große Viewports, weniger Elemente und ein anderes Seitenverhältnis für kleine Viewports.

Da Inline-SVG und auch externe SVG-Dateien auf eigenes CSS und auf Media Queries reagieren, lassen sich die Elemente in einem SVG mit CSS anpassen. Nur das Ändern des Seitenverhältnis bleibt am besten Javascript vorbehalten.

SVG Inline responsive

So gut sich SVG auch an den verfügbaren Platz anpasst, macht es trotzdem nicht immer Sinn, die Grafik in einen kleinen Raum zu quetschen. Ein HTML-picture-Element kann zwar Varianten eines Bildes setzen, aber das Handling von mehreren Varianten einer Grafik ist auch nicht der goldene Weg. SVG-Sprites sind nicht gerade intuitiv, insbesondere, wenn SVG Sprites responsiv adaptiert werden sollen.

Mehr oder weniger Elemente einer SVG-Grafik je nach Größe des Viewports ist einfach, wenn das SVG inline gesetzt wird und das Seitenverhältnis der Grafik gleich bleibt. Eine Zeile CSS reicht, um den Text auf kleinen Monitoren auszublenden.

CSS der HTML-Datei
@media (max-width: 599px) {
   .logo { width: 200px}
   .text-on { display: none}
}

@media (min-width: 600px) {
   .logo { width: 300px}
}

SVG als background-image

Hier wird's einen Hauch trickreich. Jetzt braucht die SVG-Datei selber ein paar Zeilen CSS – kein Problem, denn das style-Element wirkt auch bei SVG.

SVG-Datei
<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%" viewBox="0 0 150 150">
<style>
@media (width: 200px) {
  .textinside {display: none;}
  .colorcircle { fill: cornsilk}
}

@media (width: 300px) {
  .colorcircle { fill: cornflowerblue}
}
</style>
… 

<circle class="colorcircle" cx="75" cy="75" r="75"/>

<text class="textinside" font-size="21px">
   <textPath xlink:href="#textpath">
      <tspan>Javascript if then else</tspan>
   </textPath>
</text>
</svg>

Die Media Query in der SVG-Datei fragt nicht nach der verfügbaren Bildschirmbreite, sondern nach dem verfügbaren Platz im Einsatzgebiet, also im HTML. Darum lautet die Abfrage nicht @media (min-width: 200px), sondern nur @media (width: 200px).

Dazu kommt jetzt das CSS in der HTML-Datei.

CSS der HTML-Datei
<style>
.logobackground { 
   background-image: url(two-snakes-circle.svg); 
   margin: 1em auto;
}
	
@media (max-width: 599px) {
   .logobackground { 
      background-size: 200px;
      width: 200px; 
      height: 200px; 
	}
}

@media (min-width: 600px) {
   .logobackground { 
      background-size: 300px;
      width: 300px; 
      height: 300px; 
   }
}
</style>

CSS background-size ist der Eckpfeiler für die Anzeige der SVG-Grafik als Hintergrundbild.

SVG als img-Tag

In einem img-Tag funktioniert die Adaption der SVG-Elemente eigentlich genauso wie bei einem Hintergrundbild: In die SVG-Datei kommt wieder das style-Tag mit den Media Queries wie beim SVG als Hintergrundbild.

Das CSS in der HTML-Datei gibt mit width wieder den Platz vor, background-size ist hier nicht nötig.

CSS im HTML
<style>
@media (max-width: 599px) {
   .logoinside {
      width: 200px; 
      height: 200px;
   }
}

@media (min-width: 600px) {
   .logoinside {
      width: 300px; 
      height: 300px;
   }
}
</style>
…
<div class="logoinside">
   <img src="two-snakes-circle.svg">
</div>

SVG Seitenverhältnis mit Javascript ändern

Bis hier hin hatte CSS alles im Griff. Wenn das Bild bei einem kleinen Viewport ein anderes Seitenverhältnis haben soll als bei einem großen Viewport, hat SVG keine einfache Lösung in Petto: Das Seitenverhältnis wird durch das viewBox-Attribute bestimmt, das nicht per CSS geändert werden kann.

Zwar wirkt auch hier wieder ein einfaches text { display: none } im CSS der HTML-Datei, aber die CSS-Grafik würde sich weiter über ihre gesamte Breite erstrecken.

Die einfachste Lösung ist tatsächlich ein kleines Javascript, das mittels matchMedia die Größe des Viewports erkundet und die viewBox on the Fly ändert.

<script>
const long = document.querySelector ('.longinline svg');
const mql = window.matchMedia("(min-width:600px)");

// Beim Laden Bildausschnitt initialisieren
clipImage(mql);

// Spezieller Event Listener für MediaQueryList
mql.addListener(clipImage);

function clipImage(mql) {
   if (mql.matches) {
   	// Wenn das Browserfenster mind. 600px breit ist
      long.setAttribute('viewBox','0 0 500 150');
   } else {
   	// Wenn das Browserfenster max. 599px breit ist
      long.setAttribute('viewBox','0 0 150 150');
   }
}
</script>

Javascript matchMedia – Media Queries mit Javascript

Das Script kommt am besten ans Ende der HTML-Datei – vor dem schließenden body-Tag. Das erspart die komplizierte Abfrage, ob das SVG-Element bereits im Browser geladen ist. Das gilt für Javascript in einer externen Datei und auch, wenn das Javascript gleich in die HTML-Datei geschrieben wird.

Seitenverhältnis ändern: SVG in einem iframe laden

Soll die SVG-Grafik als Hintergrundbild oder Grafik geladen werden, kann Javascript nicht eingesetzt werden, um das Seitenverhältnis zu ändern. Externe SVG-Dateien können aus Sicherheitsgründen kein Javascript ausführen oder selber externe Dateien laden.

Wenn es sich nur um eine kleine SVG-Datei handelt, ist Inline-SVG mit Javascript der einfachste Weg, das Seitenverhältnis zu ändern. Soll das SVG aber unter keinen Umständen in der HTML-Datei geladen werden, bleibt noch der Umweg über ein HTML iframe.

In der iframe-Datei sitzt das SVG inline, gefolgt von dem kleinen Javascript zur Anpassung des viewBox-Attributs.

CSS der HTML-Datei
iframe {
   overflow: hidden;   /** Keine Laufleisten **/
   border: none;       /** Kein Rand         **/
}
@media (max-width: 599px) {
   iframe { width: 200px; height: 200px}
}
@media (min-width: 600px) {
   iframe { width: 600px; height: 182px}
}
iframe mit SVG und Javascript
<style>
   svg { width: 100%; height: auto; }
</style>

<body>
<svg width="100%" height="100%" viewBox="0 0 500 150">
…
</svg>

<script>
const long = document.querySelector ('svg');
const mql = window.matchMedia("(min-width:600px)");

clipImage(mql);

mql.addListener(clipImage);

function clipImage(mql) {
   if (mql.matches) {
      long.setAttribute('viewBox','0 0 500 150');
   } else {
      long.setAttribute('viewBox','0 0 150 150');
   }
}
</script>

</body>
SVG RESPONSIVE Kopfüber