For my latest WordPress theme, I modified the output of a standard WordPress menu so that at the frontend URLs to social media channels are displayed as SVG icons. In this post, I show you my solution.
Update from February 14, 2016: I recently tested the menu with a screen reader and noticed, that the SVG accessibility features are not very well supported. Because of that, I updated the post’s content.
Why SVG and not the easy to use icon fonts?
Icon fonts have some disadvantages in comparison to SVGs — not only, that they are sometimes rendered incorrectly. SVGs are more semantic and accessible than icon fonts. Sara Soueidan wrote a chapter about SVGs in the Smashing Book 5, this is one paragraph from it:
»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.«
For this reason, I did not want to use an icon font, but SVGs.
The solution for the SVG menu: a custom Walker_Nav_Menu
class
The SVG file with the social media icons looks like that:
<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>
Code language: HTML, XML (xml)
Theoretically, there is the possibility to insert a title
element inside the svg
— for example, to provide a title for screen reader users. That does not work in many combinations of screen readers and browsers. To provide an accessible title for the icons, we use a span
element which is hidden via CSS. The desired SVG icon is shown with the help of the use
element.
<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>
Code language: HTML, XML (xml)
The hash value at the end of the file path matches the ID of the icon’s symbol
element. So we need this code with the right hash value inside each menu item. To modify the output of wp_nav_menu()
, we can specify a Walker class.
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
)
);
}
Code language: PHP (php)
We will create a child class of Walker_Nav_Menu
because we only need to modify its start_el()
method. This method controls the output of menu items. We can copy large parts of this method, and the following code shows only the modified parts with copied code lines for reference.
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' )
),
);
Code language: PHP (php)
Own code begins at $social_media_channels
. Here we have to connect the URLs of the social networks with the IDs from the SVG file and the screen reader titles. We create an array of channel URL keys with value arrays, which include the respective ID and screen reader text for this channel.
$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;
}
}
Code language: PHP (php)
In the next step, we save an empty string in a variable $svg_id
and loop through the channel array. Inside the foreach
loop, we can access the current array key with $key
and the respective value (the array with ID and screen reader text) with $value
. $atts['href']
holds the URL of the current menu item. We need to compare this value with the array key, so we save the pattern |$key|
.
After that, we use preg_match()
and pass the pattern, the menu item’s URL and a variable for matches as parameters. If $matches[0]
is not empty, the current array key matches a part of the menu item’s URL. The value of this array position is $key
, so the URL of the social media channel. If there is a match, we save this URL from $matches[0]
in $match
and fetch the ID from $social_media_channels[ $match ]['id'];
and the screen reader text from $social_media_channels[ $match ]['screen-reader-text'];
. After that, we break the loop.
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;
}
Code language: PHP (php)
Finally, we have to build the markup. If $svg_id
is not empty, we can display an SVG. In this case, we store the URL from the SVG file in $icon_url
and append the ID after a #
. The following two lines are copied from the parent class. In the next line, we create an svg
element with the ID as a class, so it can be styled. As the href
attribute of the use
element, we pass the URL and close the opened tags. After that, we wrap a span
with the class screen-reader-text
around the title. The else
code is the default behavior.
Plugin
I created a small plugin that implements the feature as a widget. To use it, do the following after activation the plugin:
- Create a menu with social media links
- Choose »SVG Social Menu« as menu position
- Drag the widget into a widget area