SVG Social Menu in WordPress

Justin Tadlock have invented social nav menu system. Pretty much everybody is using it on public themes because users can create social links using WordPress menu system. And social links can be drag ‘n dropped in any order you want. It is great! Here is a screenshot how it might look like.

SVG Social menu and the Customizer

But lately (several months) I’ve been thinking can we use SVG icons in Social Menu instead of icon fonts. Leland Fiegel picked up my question in Twitter and rolled his own idea. It’s not bad solution but what if we could do this without Javascript.

I’ll try to explain how and why I’m using social menu with SVG icons. Some WordPress theme works as an demo example and it can be found in Github also.

Why SVG instead of icon fonts

There is nothing wrong using icon fonts. Just make sure you are using icon fonts in a accessible way. But there are several reasons why SVG images are a little better than icon fonts. CSS Tricks article Inline SVG vs Font Icons explains the differences really well. Comparison included these bullet points:

  • SVG icons are vectors not text which can lead blurry looking font icons.
  • CSS control is even better because you can control different parts of multi-part icon. Or even animate them.
  • Positioning can sometimes be really annoying when using icon fonts (line height, pseudo after or before rules). That’s not going to be problem when using SVG images.
  • Font loading can have issues.
  • @font-face is not supported in every browser. Well, not all old browser support SVG either.
  • SVG images seems more semantic way of adding icons than icon font.
  • In both methods accessibility can be a problem, just remember to take care of it.
  • SVG sprite system is fast and icons are easy to create in Icomoon App.
  • Icon fonts have deeper browser support you can roll fallback for SVG if needed.

There are also other articles about SVG vs icon fonts:

I like the summarize on Github article:

By switching from icon fonts, we can serve our icons more easily, more quickly, and more accessibly. And they look better. Enjoy.

Where to get SVG icons

I have only used service called Icomoon App. It has it’s own icons but you can also import any icons you want in there. I usually import icons from Genericons or Fontawesome. Great thing about Icomoon App is that you can pick the icons you want, generate SVG code one be one, or download example .zip file which includes SVG sprite file. It’s called symbol-defs.svg. At the moment I manually clean up the file a little bit by removing all titles and fills.

You can also use Gulp tools to generate SVG sprite for you. wd_s starter theme has a perfect example.

In custom projects I also import all icons from designer and let Icomoon App generate the SVG code for me.

Using SVG sprites

There are two main ways to use SVG sprite system.

  1. Inline SVG sprite.
  2. External SVG sprite.

I both ways we can use helpful function for generating SVG markup. By the way read everything what Sara Soueidan writes about SVG images.

1. Inline SVG sprite

The code below demonstrates how to use inline SVG sprite:

<body>
<?php
// 1. Include SVG images from assets/images folder right after <body> element.
$svg_icons = get_template_directory() . '/assets/images/svg-icons.svg';
require_once( $svg_icons );


// 2. Include for example Twitter icon from SVG sprite. This can be anywhere in your markup.
?>
<svg class="icon icon-twitter">
  <use xlink:href="#twitter-icon"></use>
<svg>
	
<?php
// 3. Or even better use function _s_get_svg() found in wd_s to generetate the markup.
// @link: https://github.com/WebDevStudios/wd_s/blob/master/inc/template-tags.php#L124

echo _s_get_svg( array( 'icon' => 'twitter' ) );
  1. Include the SVG sprite right after body element.
  2. Use SVG markup anywhere you want.
  3. Or even better use _s_get_svg() function to output markup for you.

The problem with inline SVG can be caching. If you have lot’s of icons and they are included in every page after body element, page might render a little bit slower.

If you have only couple of icons in your SVG sprite, I’d still use inline sprite method. Caching can be solved using external  SVG sprite.

2. External SVG sprite

Using external SVG image is pretty much the same but you don’t need to include SVG icons after body element. And in <use>-element you reference the external SVG file.

<body>

// 2. Include for example Twitter icon from SVG sprite. This can be anywhere in your markup.
?>
<svg class="icon icon-twitter">
  <use xlink:href="path/to/icons.svg#twitter-icon"></use>
<svg>
	
<?php
// 3. Or even better use function some_get_svg() found in "Some" to generetate the markup.
// @link: https://github.com/samikeijonen/some/blob/master/inc/template-tags.php#L41

echo some_get_svg( array( 'icon' => 'twitter' ) );

There is one big problem in this method also. IE browsers (Edge does) doesn’t support external SVG in <use>. Luckily there is polyfill called svgxuse and it’s licensed by MIT.

I my tests even Firefox didn’t show external SVG icons without the svgxuse polyfill. Go figure.

SVG social menu

We are finally ready to talk about how to use SVG icon in social menu. Let’s start with with social menu markup. It’s the same as in Justin’s tutorial but we’re using rating-full icon (star) as a fallback icon. See the link_after argument.

<nav class="menu-social social-navigation menu" role="navigation">
	<?php wp_nav_menu(
		array(
			'theme_location'  => 'social',
			'container_class' => 'social-menu-wrapper',
			'menu_id'         => 'menu-social-items',
			'menu_class'      => 'menu-social-items',
			'depth'           => 1,
			'link_before'     => '<span class="screen-reader-text">',
			'link_after'      => '</span>' . some_get_svg( array( 'icon' => 'rating-full' ) ),
			'fallback_cb'     => '',
		)
	); ?>
</nav><!-- .menu-social -->

And finally the most important part. How are we going to replace fallback icon to social icon we support? The magical filter is walker_nav_menu_start_el.

/**
 * Display SVG icons in social navigation.
 *
 * @since 1.0.0
 *
 * @param string  $item_output The menu item output.
 * @param WP_Post $item        Menu item object.
 * @param int     $depth       Depth of the menu.
 * @param array   $args        wp_nav_menu() arguments.
 * @return string Menu item with possible description.
 */
function some_nav_social_icons( $item_output, $item, $depth, $args ) {
	
	// Supported social icons.
	$social_icons = apply_filters( 'some_nav_social_icons', array(
		'codepen.io'      => 'codepen',
		'digg.com'        => 'digg',
		'dribbble.com'    => 'dribbble',
		'dropbox.com'     => 'dropbox',
		'facebook.com'    => 'facebook',
		'flickr.com'      => 'flickr',
		'foursquare.com'  => 'foursquare',
		'plus.google.com' => 'googleplus',
		'github.com'      => 'github',
		'instagram.com'   => 'instagram',
		'linkedin.com'    => 'linkedin-alt',
		'mailto:'         => 'mail',
		'pinterest.com'   => 'pinterest-alt',
		'getpocket.com'   => 'pocket',
		'polldaddy.com'   => 'polldaddy',
		'reddit.com'      => 'reddit',
		'skype.com'       => 'skype',
		'skype:'          => 'skype',
		'soundcloud.com'  => 'cloud',
		'spotify.com'     => 'spotify',
		'stumbleupon.com' => 'stumbleupon',
		'tumblr.com'      => 'tumblr',
		'twitch.tv'       => 'twitch',
		'twitter.com'     => 'twitter',
		'vimeo.com'       => 'vimeo',
		'wordpress.org'   => 'wordpress',
		'wordpress.com'   => 'wordpress',
		'youtube.com'     => 'youtube',
	) );
	
	// Change SVG icon inside social links menu if there is supported URL.
	if ( 'social' == $args->theme_location ) {
		foreach ( $social_icons as $attr => $value ) {
			if ( false !== strpos( $item_output, $attr ) ) {
				$item_output = str_replace( $args->link_after, '</span>' . some_get_svg( array( 'icon' => esc_attr( $value ) ) ), $item_output );
			}
		}
	}
	return $item_output;
}
add_filter( 'walker_nav_menu_start_el', 'some_nav_social_icons', 10, 4 );
  • Inside the filter we first set an array of supported social links.
  • Then we use strpos function for checking the supported link.
  • And finally str_replace function for replacing the correct social icon.

Styling SVG icon

Edit: Styling and accessibility chapter have been added after the this post was published.

End result of SVG social menu

I use these base styles for all icons which are recommended by Icomoon:

.icon {
	display: inline-block;
	fill: currentColor;
	height: 1em;
	width: 1em;
	vertical-align: middle;
	position: relative; /* Align more nicely with capital letters */
	top: -0.0625em;
}

After that it’s totally up to you and CSS how you want style individual icons. For example changing the size and color in social menu looks like this:

.menu-social .icon {
	color: #fafafa; /* You can use fill: rule also. */
	width: 2em;
	height: 2em;
}

Accessibility and SVG images

In my some_get_svg() function I use aria-hidden="true" by default in my SVG markup because in most cases SVG icon in just decorative image. For example generated markup inside social menu look like this:

<a href="https://twitter.com/samikeijonen/">
<span class="screen-reader-text">Follow me on twitter</span>
<svg class="icon icon-twitter" aria-hidden="true">
   <use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#icon-twitter"></use>
</svg>
</a>

In 404 page I have experiment where I try to set SVG icon arguments in away that also screen readers could access the icon info.

Some theme as an example

Uh, that’s it. As mentioned before Some theme works as an example. Play around with the code in Github and let me know if there is better way to do this.