Ein Social-Icons-Menü mit SVGs in WordPress umsetzen

Für mein neuestes WordPress-Theme habe ich mich damit auseinandergesetzt, wie ein normales WordPress-Menü, das mit Social-Media-Links gefüllt ist, im Frontend mit SVGs statt Icons eines Icon Font angezeigt werden kann. Wie meine Lösung dazu aussieht, zeige ich euch hier.

Update vom 14. Februar 2016: Eigentlich nur spaßeshalber habe ich neulich das Menü mal mit einem Screen-Reader getestet und dabei festgestellt, dass die Standards noch nicht so richtig gut unterstützt werden. In diesem Update ist also einiges von dem ursprünglichen Beitrag überarbeitet worden, um das zu korrigieren.

Wieso SVG, wenn Icon Font per CSS doch so einfach ist?

Icon Fonts haben einige Nachteile im Vergleich zu SVGs – nicht nur den, dass sie manchmal nicht korrekt angezeigt werden sondern nur ein Rechteck mit einem Fragezeichen zu sehen ist. Eine SVG ist beispielsweise semantisch und barrierefreier als Icon Fonts. Im Smashing Book 5 hat Sara Soueidan ein Kapitel über SVGs geschrieben, in dem folgender Absatz vorkommt:

„SVGs are semantic. An SVG icon is a graphic — an image — so what better way to mark up an image than to use a (scalable vector) graphic tag? An SVG element (<svg>) represents an icon simply and semantically, while icon fonts usually require non-semantic markup like pseudo-elements and empty s to be displayed. For people concerned about semantics and accessibility this introduces a serious issue: these elements don’t accommodate screen readers well, not to mention that the markup generally just doesn’t make much sense — if any — for an image.“

Aus diesem Grund wollte ich keinen Icon Font einsetzen, sondern SVGs. Das Problem: Die lassen sich nicht einfach per CSS einbinden.

Die Lösung für das SVG-Menü: Eine angepasste Walker_Nav_Menu-Klasse

Die Social-Media-Symbole sind wie folgt als SVGs in einer SVG-Datei gespeichert:

<svg version="1.1" xmlns="http://www.w3.org/2000/svg" style="display: none;">
    <symbol id="icon-twitter" viewBox="0 0 16 16">
        <path d="M15,3.627c-0.515,0.229-1.068,0.383-1.649,0.452c0.592-0.355,1.048-0.918,1.262-1.589
	c-0.554,0.329-1.169,0.568-1.822,0.697c-0.524-0.558-1.271-0.907-2.098-0.907c-1.586,0-2.872,1.286-2.872,2.872
	c0,0.225,0.026,0.444,0.075,0.654c-2.387-0.12-4.504-1.263-5.921-3.001C1.728,3.229,1.586,3.724,1.586,4.25
	c0,0.996,0.506,1.875,1.277,2.39C2.393,6.625,1.95,6.496,1.562,6.281c0,0.013,0,0.024,0,0.037c0,1.392,0.989,2.552,2.304,2.816
	C3.625,9.199,3.372,9.234,3.11,9.234c-0.186,0-0.365-0.02-0.541-0.051c0.366,1.142,1.427,1.971,2.683,1.993
	c-0.982,0.771-2.222,1.23-3.566,1.23c-0.232,0-0.461-0.014-0.686-0.041c1.271,0.816,2.78,1.291,4.402,1.291
	c5.284,0,8.174-4.377,8.174-8.172c0-0.124-0.004-0.248-0.01-0.371C14.127,4.708,14.615,4.203,15,3.627z"/>
    </symbol>

    <symbol id="icon-feed" viewBox="0 0 16 16">
        <path d="M2,6v2c3.309,0,6,2.691,6,6h2C10,9.582,6.418,6,2,6z M2,2v2c5.514,0,10,4.486,10,10h2
		C14,7.373,8.627,2,2,2z M3.5,11C2.671,11,2,11.672,2,12.5S2.671,14,3.5,14S5,13.328,5,12.5S4.329,11,3.5,11z"/>
    </symbol>
</svg>

Theoretisch gibt es mit dem title-Element innerhalb von svg die Möglichkeit, einen Titel zum Beispiel für Screen-Reader einzubauen. Das funktioniert aber mit vielen Kombinationen noch nicht. Um einen Titel für Screen-Reader et cetera angeben zu können, setzen wir deshalb auf ein span-Element, das via CSS versteckt wird. Das gewünschte SVG-Symbol wird mit Hilfe des use-Elements eingebunden.

<svg class="icon-twitter">
   <use xlink:href="/pfad-zum-theme/svg/social-media-icons.svg#icon-twitter"/>
</svg>
<span class="screen-reader-text">Twitter</span>

Der Hash-Wert hinter dem Pfad zur SVG-Datei entspricht der ID des symbol-Elements. Dieser Code-Schnipsel muss mit dem richtigen Hash-Wert für jeden Menüpunkt des Menüs ausgegeben werden, inklusive des span-Elements mit dem Titel. Um die Ausgabe der Menü-Funktion anzupassen, kann bei dem wp_nav_menu()-Aufruf eine Walker-Klasse angegeben werden, die sich dann um die Ausgabe kümmert:

if ( has_nav_menu( 'social' ) ) {
   wp_nav_menu(
      array(
         'theme_location' => 'social',
         'menu_class'     => 'social-menu',
         'container'      => '',
         'walker'         => new Svg_Social_Menu_Walker(),
         'depth'          => 1
      )
   );
}

Um nicht wieder bei Null anfangen zu müssen, kann eine Kindklasse zu Walker_Nav_Menu erstellt werden, die normalerweise die Ausgabe von Menüs steuert. In dieser Klasse muss lediglich die Methode start_el() überschrieben werden, die sich um die Ausgabe eines Menüelements kümmert. Dabei können auch in dieser Mehtode große Teile übernommen werden, weshalb hier nicht der komplette Code dargestellt ist, sondern nur die veränderten Teile mit einigen übernommenen zur Referenz, wo wir uns in der Klasse befinden.

class Svg_Social_Menu_Walker extends Walker_Nav_Menu {
   // […]
   public function start_el( &$output, $item, $depth = 0,
      $args = array(), $id = 0 ) {
      // […]
      $title = apply_filters( 'nav_menu_item_title', $title, $item, $args, $depth );

      $social_media_channels = array(
         'plus.google.com' => array(
            'id'                 => 'icon-google-plus',
            'screen-reader-text' => __( 'Google Plus', 'hannover' )
         ),
         'wordpress.org'   => array(
            'id'                 => 'icon-wordpress',
            'screen-reader-text' => __( 'WordPress.org', 'hannover' )
         ),
         'wordpress.com'   => array(
            'id'                 => 'icon-wordpress',
            'screen-reader-text' => __( 'WordPress.com', 'hannover' )
         ),
         'facebook.com'    => array(
            'id'                 => 'icon-facebook',
            'screen-reader-text' => __( 'Facebook', 'hannover' )
         ),
         'twitter.com'     => array(
            'id'                 => 'icon-twitter',
            'screen-reader-text' => __( 'Twitter', 'hannover' )
         ),
         'dribbble.com'    => array(
            'id'                 => 'icon-dribbble',
            'screen-reader-text' => __( 'Dribbble', 'hannover' )
         ),
         'pinterest.com'   => array(
            'id'                 => 'icon-pinterest',
            'screen-reader-text' => __( 'Pinterest', 'hannover' )
         ),
         'github.com'      => array(
            'id'                 => 'icon-github',
            'screen-reader-text' => __( 'GitHub', 'hannover' )
         ),
         'tumblr.com'      => array(
            'id'                 => 'icon-tumblr',
            'screen-reader-text' => __( 'Tumblr', 'hannover' )
         ),
         'youtube.com'     => array(
            'id'                 => 'icon-youtube',
            'screen-reader-text' => __( 'YouTube', 'hannover' )
         ),
         'flickr.com'      => array(
            'id'                 => 'icon-flickr',
            'screen-reader-text' => __( 'Flickr', 'hannover' )
         ),
         'vimeo.com'       => array(
            'id'                 => 'icon-vimeo',
            'screen-reader-text' => __( 'Vimeo', 'hannover' )
         ),
         'instagram.com'   => array(
            'id'                 => 'icon-instagram',
            'screen-reader-text' => __( 'Instagram', 'hannover' )
         ),
         'linkedin.com'    => array(
            'id'                 => 'icon-linkedin',
            'screen-reader-text' => __( 'LinkedIn', 'hannover' )
         ),
         'xing.de'         => array(
            'id'                 => 'icon-xing',
            'screen-reader-text' => __( 'Xing', 'hannover' )
         ),
         'xing.com'        => array(
            'id'                 => 'icon-xing',
            'screen-reader-text' => __( 'Xing', 'hannover' )
         ),
         '/feed'           => array(
            'id'                 => 'icon-feed',
            'screen-reader-text' => __( 'Feed', 'hannover' )
         ),
      );

Selbst geschrieben ist in diesem Code-Block der Teil ab $social_media_channels. Hier müssen die URLs zu den verschiedenen sozialen Netzwerken beziehungsweise anderen Kanälen mit den IDs, die in der SVG-Datei für die verschiedenen symbol-Elemente verwendet wurden, sowie dem Screen-Reader-Text in Verbindung gebracht werden. Wir erstellen also ein Array mit einer URL als Schlüssel und einem weiteren Array mit ID und Screen-Reader-Text als Wert.

$svg_id = "";

foreach ( $social_media_channels as $key => $value ) {
   $pattern = "|$key|";
   preg_match( $pattern, $atts['href'], $matches );
   if ( ! empty( $matches[0] ) ) {
      $match                  = $matches[0];
      $svg_id                 = $social_media_channels[ $match ]['id'];
      $svg_screen_reader_text = $social_media_channels[ $match ]['screen-reader-text'];
      break;
   }
}

Im nächsten Schritt speichern wir einen leeren String in der Variablen $svg_id und durchlaufen anschließend das Array mit einer foreach-Schleife, in der wir mit $key auf den aktuellen Array-Schlüssel und mit $value auf den zugehörigen Wert, also das Array mit ID und Screen-Reader-Text, zugreifen können – in $atts['href'] ist die URL des aktuellen Menüelements gespeichert. Diesen Wert müssen wir auf Vorkommen des Array-Keys durchsuchen, weshalb wir als Pattern |$key| speichern.

Anschließend übergeben wir das Pattern, die URL des Menüelements und eine Variable, in der der Treffer gespeichert wird, an preg_match(). Wenn $matches[0] nicht leer ist, wurde eine Übereinstimmung aus dem aktuellen Array-Schlüssel in der URL des Menüelements gefunden – der Wert für diese Array-Position ist dann $key, also die URL des Netzwerks. Ist das gegeben, wird der Wert in $match gespeichert und danach über $social_media_channels[ $match ]['id']; auf den Wert zu der URL zugegriffen, der der SVG-ID entspricht, und über $social_media_channels[ $match ]['screen-reader-text']; auf die Beschriftung. Danach wird die Schleife abgebrochen.

if ( $svg_id != "" ) {
   $icon_url    = plugins_url( "svg/social-media-icons.svg#$svg_id", __DIR__ );
   $item_output = $args->before;
   $item_output .= '<a' . $attributes . '>';
   $item_output .= '<svg class="' . $svg_id . '"><use xlink:href="' . $icon_url . '"></use></svg><span class="screen-reader-text">' . $svg_screen_reader_text . '</span>';
   $item_output .= '</a>';
   $item_output .= $args->after;
} else {
   $item_output = $args->before;
   $item_output .= '<a' . $attributes . '>';
   $item_output .= $args->link_before . $title . $args->link_after;
   $item_output .= '</a>';
   $item_output .= $args->after;
}

Abschließend muss noch das Markup zusammengesetzt werden. Wenn $svg_id nicht leer ist, kann eine SVG angezeigt werden. In der Variable $icon_url wird in dem Fall die URL zu der SVG-Datei gespeichert – hinter # wird mit $svg_id die ID des SVG-Symbols eingesetzt. Die folgenden zwei Zeilen sind einfach aus der Oberklasse übernommen, interessant wird es in der Zeile mit <svg. Hier wird als Klasse für das svg-Element die ID eingefügt, damit dem stylen mit CSS nichts im Weg steht. Für das href-Attribut des use-Elements wird die URL eingefügt und danach alle Tags wieder geschlossen. Anschließend wird ein span mit der Klasse screen-reader-text eingefügt, in dem der Titel ausgegeben wird. Der Code aus dem else-Zweig entspricht der Standard-Ausgabe.

Plugin

Für den leichteren Einsatz habe ich ein kleines Plugin geschrieben, das die Funktion in Form eines Widgets einfügt. Um es zu nutzen, muss nach der Aktivierung des Plugins folgendes gemacht werden:

  1. Ein Menü mit Links zu sozialen Netzwerken erstellen
  2. Als Menü-Position „SVG Social Menu“ auswählen
  3. Das Widget „SVG Social Menu“ in einen der Widget-Bereiche ziehen

Das könnte auch interessant sein

8 Kommentare zu »Ein Social-Icons-Menü mit SVGs in WordPress umsetzen«

    1. Florian

      Gab einen blöden Fehler, sodass die Standard-Styles gar nicht erst geladen wurden (habe vergessen, den Filter nach dem Test wieder rauszunehmen …). Ist mit der neuen Version gefixt!

  1. HP

    Das Einbinden des Plugins mit anschließendem Aufbau eine Menüs hat gut funktioniert - auch mit WordPress 4.7 (mit PHP 7). Nun habe ich auch (m)einen XING-Button drin.

    Danke dafür!

  2. Christopher Kurth

    In einem aktuellen Projekt benötige genau ein solches Widget, zum Glück habe ich mich an dein Plugin erinnert.

    Hab aber ein Problem damit, ich bekomme den Filter für custom CSS einfach nicht zum laufen. Das einzige Ergebnis was ich bekomme sobald ich den Filter in die functions.php einfüge ist, dass gar keine CSS Eigenschaften mehr übergeben werden.

    Kannst du mir vielleicht ein Beispiel zeigen bei dem das funktioniert?

    Dank dir schonmal 😉

    1. Florian

      Hi!

      Oh, du hast recht, da ist ein Fehler in der Doku – fehlt ein return (korrigiere ich gleich) 🙂

      So sollte es funktionieren:

      function slug_edit_svg_social_menu_styles( $styles ) {
          $styles = '<style>HierDasCSS</style>';
      
          return $styles;
      }
      
      add_filter( 'svg_social_menu_inline_style', 'slug_edit_svg_social_menu_styles' );

      Der Filter überschreibt dann halt komplett das Standard-CSS. Wenn du nur etwas hinzufügen möchtest, müsstest du ein weiteres style-Element aufmachen:

      function slug_edit_svg_social_menu_styles( $styles ) {
          $styles .= '<style>HierDasZusätzlicheCSS</style>';
      
          return $styles;
      }
      
      add_filter( 'svg_social_menu_inline_style', 'slug_edit_svg_social_menu_styles' );

      Hoffe, das hilft 🙂

      Edit: Kleiner Tippfehler im Zweiten Code-Block – da war ein Leerzeichen an einer falschen Stelle.

Schreib einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.