The Underscores (_s) theme with Sass and Gulp Part 13: advanced features

Untitled Document

==================================================================

On this page:

  1. Responsive images.
  2. Add SVG icon functionality.
  3. Add your own SVG icons in your theme.
  4. Provide fallbacks for browsers without SVG support.

==================================================================

71. Responsive images.

Github repo branch: part13_71

Responsive images markup allows us to provide a series of links to progressively larger images rather than just a link to the original image and gives the browser the necessary information to pick whatever image size is the most appropriate for the current display width and screen resolution.

The problem with image on a webpage will never be wider than the content area. When you add an image to a post or page or anything else in WordPress, that image is displayed in the browser in whatever size WordPress specifies. So, in this single post where I have this giant, full bleed image at the top, that is a max width of 2000 pixels wide, that image will be loaded up in it's entirety. So you get a 2000 pixel wide image when you load the page. And that's fine for desktop monitors, but if you load the same page on a tiny mobile screen, let's say this old one that's 320 pixels wide, you still get the same gigantic 2000 pixel wide image. And that's a wast of bandwidth and resources.

To solve this, we'll use some advanced functions to tell WordPress exactly how wide different images are in different templates and at different screen widths.

  1. pre_underscores_content_image_sizes_attr()
  2. pre_underscores_header_image_tag()
  3. pre_underscores_post_thumbnail_sizes_attr()

At first glance, these functions look pretty complex but once you know how they work and how to configure them, you'll see they're really easy to use in any theme you set up

71.1 pre_underscores_content_image_sizes_attr()

/**
  * Add custom image sizes attribute to enhance responsive image functionality
  * for content images.
  *
  * @origin Twenty Seventeen 1.0
  *
  * @param string $sizes A source size value for use in a 'sizes' attribute.
  * @param array  $size  Image size. Accepts an array of width and height
  *                      values in pixels (in that order).
  * @return string A source size value for use in a content image 'sizes' attribute.
  */
  function pre_underscores_content_image_sizes_attr( $sizes, $size ) {
  $width = $size[0];
 if ( 900 <= $width ) {
  $sizes = '(min-width: 900px) 700px, 900px';
  }
 if ( is_active_sidebar( 'sidebar-1' ) || is_active_sidebar( 'sidebar-2' ) ) {
  $sizes = '(min-width: 900px) 600px, 900px';
  }
 return $sizes;
  }
  add_filter( 'wp_calculate_image_sizes', 'pre_underscores_content_image_sizes_attr', 10, 2 );

This is the function that tells WordPress how wide the images inside the content can be. What we're doing here is defining the sizes attribute. And the sizes attribute works as follows: when the browser loads and image it says, hey here's an image, how big will that image be when it's displayed. And then we pass the sizes attribute that says, if the minimum width of the screen is wider than 900 pixels then the image will be 700 pixels wide at the widest. If the screen is narrower than that, then the image might be up to 900 pixels wide.

Now where do I get these values? Directly from the browser. So you remember that in the layout for the single post without a sidebar, we have a break point at 900 pixels. And when we break at 900 pixels, we shove the content over to the right hand side and we have this other content on the left hand side. If I open this page in the developer tools and look at it, I can see that at a certain width, the width of the content area does not change anymore. And then I'll just inspect that element and see it's 700 pixels wide.

So that means the images that are displayed in the content area, once the media query is triggered at 900 pixels, will never be wider than 700 pixels. However, if I reduce the width of the screen, so we triggered the media query at 900. Then I know that the images may be full bleed, meaning they might be up to 900 pixels wide. So therefore, we have 900 pixels unless the min width is higher than 900 pixels in which case it's 700 pixels. In addition, we can create conditional statements to test for certain situations, like a layout change.

In this case, we're testing whether or not there are sidebars, either the regular sidebar for posts or the page sidebar for pages. And, in that case, the width of the content area will be reduced a little bit more. So, over 900 pixels the width is a max of 600 pixels. Otherwise it's still 900 pixels. After defining the sizes attribute, it's just returned to WordPress using the filter, wp calculate image sizes. And that means, anytime you add an image this automatically hooks in and WordPress knows how wide that image may be at any screen size.

71.2 pre_underscores_header_image_tag( )

/**
* Filter the `sizes` value in the header image markup.
*
* @origin Twenty Seventeen 1.0
*
* @param string $html   The HTML image tag markup being filtered.
* @param object $header The custom header object returned by 'get_custom_header()'.
* @param array  $attr   Array of the attributes for the image tag.
* @return string The filtered header image HTML.
*/
function pre_underscores_header_image_tag( $html, $header, $attr ) {
if ( isset( $attr['sizes'] ) ) {
$html = str_replace( $attr['sizes'], '100vw', $html );
}
return $html;
}
add_filter( 'get_header_image_tag', 'pre_underscores_header_image_tag', 10, 3 );

The next function pre_underscores_header_image_tag() is for the header image. Now in my theme, the header image is always full bleed so the sizes attribute is simply set to 100vw. If you have a different layout and the header image is constrained, you can change the value down here.

71.3 pre_underscores_post_thumbnail_sizes_attr()

/**
  * Add custom image sizes attribute to enhance responsive image functionality
  * for post thumbnails.
  *
  * @origin Twenty Seventeen 1.0
  *
  * @param array $attr       Attributes for the image markup.
  * @param int   $attachment Image attachment ID.
  * @param array $size       Registered image size or flat array of height and width dimensions.
  * @return string A source size value for use in a post thumbnail 'sizes' attribute.
  */
  function pre_underscores_post_thumbnail_sizes_attr( $attr, $attachment, $size ) {
 if ( !is_singular() ) {
  if ( is_active_sidebar( 'sidebar-1' ) ) {
  $attr['sizes'] = '(max-width: 900px) 90vw, 800px';
  } else {
  $attr['sizes'] = '(max-width: 1000px) 90vw, 1000px';
  }
  } else {
  $attr['sizes'] = '100vw';
  }
 return $attr;
  }
  add_filter( 'wp_get_attachment_image_attributes', 'pre_underscores_post_thumbnail_sizes_attr', 10, 3 );
  

Finally, we have the function pre_underscores post_thumbnail_sizes_attribute(). Does the exact same thing, only this time for featured images. Here I know that both in single posts and single pages the feature image will be full bleed so I don't have to say anything. However, for index pages and archives there is a restriction on how wide image is.

So if we're not on a singular page, meaning we're on an archive or the index page or anywhere else and we currently have an active sidebar and the screen is narrower than 900 pixels, then the image will be about 90% of the viewport width. If we are on wider screens, so above 900 pixels, then the width of the image will always be 800 pixels. If we don't have an active sidebar, then the values change again. So under 1000 pixel wide screen the width is about 90vw again, and anything over that it will always be no wider than 1000 pixels.

Now that you know how these functions work, I urge you to add them to all your themes. Simply copy them out from here, change these sizes values to fit with your layout and everything will work perfectly. This will save both the visitor and the site owner money and time by not using unnecessarily large images on smaller or low resolution monitors and helps bring down the average page size of the web

==================================================================

72. Add SVG icon functionality.

Github repo branch: part13_72

On the topic of advanced features, let me show you something that borders on magic. There's a good chance you want to add some icons to your site, maybe social media icons on the footer, or an arrow next to an element, or a logo or something similar. Until recently, we used icon fonts for this, because it was the best-available cross-browser solution that gave us scalable, vectorized graphics that looked good across all screen resolutions. But icon fonts have serious issues, and we were always waiting around for a better option. Now that option is here in the form of SVGs, or scalable vector graphics.

The challenge with SVGs is, they're only really useful if their in-line contents of the SVG markup is in the html itself, and that makes them a bit hard to work with. If SVG is new to you, I urge you to check out my tutorials about SVG grapics before continuing so you get an in-depth explanation of exactly how they work and why they are so amazing.

Anyway, what we need is a simple method for adding icons through in-line SVGs in our theme, both in things like social media menus, and throughout the rest of the theme, and wouldn't you know it, there's a solution for this that: Samen Kayunen contributed an SVG solution to the 2017 theme that can be used in any theme, and makes the actual process of adding SVG icons as simple as just calling a function.

The core functionality of the SVG solution is in the file inc/icon-functions.php.

<?php
  /**
  * SVG icons related functions and filters
  *
  * @package WordPress
  * @subpackage Twenty_Seventeen
  * @since 1.0
  */
/**
  * Add SVG definitions to the footer.
  */
  function pre_underscores_include_svg_icons() {
  // Define SVG sprite file.
  $svg_icons = get_parent_theme_file_path( '/images/svg-icons.svg' );
 // If it exists, include it.
  if ( file_exists( $svg_icons ) ) {
  require_once( $svg_icons );
  }
  }
  add_action( 'wp_footer', 'pre_underscores_include_svg_icons', 9999 );
/**
  * Return SVG markup.
  *
  * @param array $args {
  *     Parameters needed to display an SVG.
  *
  *     @type string $icon  Required SVG icon filename.
  *     @type string $title Optional SVG title.
  *     @type string $desc  Optional SVG description.
  * }
  * @return string SVG markup.
  */
  function pre_underscores_get_svg( $args = array() ) {
  // Make sure $args are an array.
  if ( empty( $args ) ) {
  return __( 'Please define default parameters in the form of an array.', 'pre_underscores' );
  }
 // Define an icon.
  if ( false === array_key_exists( 'icon', $args ) ) {
  return __( 'Please define an SVG icon filename.', 'pre_underscores' );
  }
 // Set defaults.
  $defaults = array(
  'icon'        => '',
  'title'       => '',
  'desc'        => '',
  'fallback'    => false,
  );
 // Parse args.
  $args = wp_parse_args( $args, $defaults );
 // Set aria hidden.
  $aria_hidden = ' aria-hidden="true"';
 // Set ARIA.
  $aria_labelledby = '';
 /*
  * Twenty Seventeen doesn't use the SVG title or description attributes; non-decorative icons are described with .screen-reader-text.
  *
  * However, child themes can use the title and description to add information to non-decorative SVG icons to improve accessibility.
  *
  * Example 1 with title: <?php echo pre_underscores_get_svg( array( 'icon' => 'arrow-right', 'title' => __( 'This is the title', 'textdomain' ) ) ); ?>
  *
  * Example 2 with title and description: <?php echo pre_underscores_get_svg( array( 'icon' => 'arrow-right', 'title' => __( 'This is the title', 'textdomain' ), 'desc' => __( 'This is the description', 'textdomain' ) ) ); ?>
  *
  * See https://www.paciellogroup.com/blog/2013/12/using-aria-enhance-svg-accessibility/.
  */
  if ( $args['title'] ) {
  $aria_hidden     = '';
  $unique_id       = uniqid();
  $aria_labelledby = ' aria-labelledby="title-' . $unique_id . '"';
 if ( $args['desc'] ) {
  $aria_labelledby = ' aria-labelledby="title-' . $unique_id . ' desc-' . $unique_id . '"';
  }
  }
 // Begin SVG markup.
  $svg = '<svg class="icon icon-' . esc_attr( $args['icon'] ) . '"' . $aria_hidden . $aria_labelledby . ' role="img">';
 // Display the title.
  if ( $args['title'] ) {
  $svg .= '<title id="title-' . $unique_id . '">' . esc_html( $args['title'] ) . '</title>';
 // Display the desc only if the title is already set.
  if ( $args['desc'] ) {
  $svg .= '<desc id="desc-' . $unique_id . '">' . esc_html( $args['desc'] ) . '</desc>';
  }
  }
 /*
  * Display the icon.
  *
  * The whitespace around `<use>` is intentional - it is a work around to a keyboard navigation bug in Safari 10.
  *
  * See https://core.trac.wordpress.org/ticket/38387.
  */
  $svg .= ' <use href="#icon-' . esc_html( $args['icon'] ) . '" xlink:href="#icon-' . esc_html( $args['icon'] ) . '"></use> ';
 // Add some markup to use as a fallback for browsers that do not support SVGs.
  if ( $args['fallback'] ) {
  $svg .= '<span class="svg-fallback icon-' . esc_attr( $args['icon'] ) . '"></span>';
  }
 $svg .= '</svg>';
 return $svg;
  }
/**
  * Display SVG icons in social links menu.
  *
  * @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  $item_output The menu item output with social icon.
  */
  function pre_underscores_nav_menu_social_icons( $item_output, $item, $depth, $args ) {
  // Get supported social icons.
  $social_icons = pre_underscores_social_links_icons();
 // 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>' . pre_underscores_get_svg( array( 'icon' => esc_attr( $value ) ) ), $item_output );
  }
  }
  }
 return $item_output;
  }
  add_filter( 'walker_nav_menu_start_el', 'pre_underscores_nav_menu_social_icons', 10, 4 );

/**
  * Returns an array of supported social links (URL and icon name).
  *
  * @return array $social_links_icons
  */
  function pre_underscores_social_links_icons() {
  // Supported social links icons.
  $social_links_icons = array(
  'behance.net'     => 'behance',
  'codepen.io'      => 'codepen',
  'deviantart.com'  => 'deviantart',
  'digg.com'        => 'digg',
  'dribbble.com'    => 'dribbble',
  'dropbox.com'     => 'dropbox',
  'facebook.com'    => 'facebook',
  'flickr.com'      => 'flickr',
  'foursquare.com'  => 'foursquare',
  'plus.google.com' => 'google-plus',
  'github.com'      => 'github',
  'instagram.com'   => 'instagram',
  'linkedin.com'    => 'linkedin',
  'mailto:'         => 'envelope-o',
  'medium.com'      => 'medium',
  'pinterest.com'   => 'pinterest-p',
  'getpocket.com'   => 'get-pocket',
  'reddit.com'      => 'reddit-alien',
  'skype.com'       => 'skype',
  'skype:'          => 'skype',
  'slideshare.net'  => 'slideshare',
  'snapchat.com'    => 'snapchat-ghost',
  'soundcloud.com'  => 'soundcloud',
  'spotify.com'     => 'spotify',
  'stumbleupon.com' => 'stumbleupon',
  'tumblr.com'      => 'tumblr',
  'twitch.tv'       => 'twitch',
  'twitter.com'     => 'twitter',
  'vimeo.com'       => 'vimeo',
  'vine.co'         => 'vine',
  'vk.com'          => 'vk',
  'wordpress.org'   => 'wordpress',
  'wordpress.com'   => 'wordpress',
  'yelp.com'        => 'yelp',
  'youtube.com'     => 'youtube',
  );
 /**
  * Filter Twenty Seventeen social links icons.
  *
  * @since Twenty Seventeen 1.0
  *
  * @param array $social_links_icons
  */
  return apply_filters( 'pre_underscores_social_links_icons', $social_links_icons );
  }
  
code 13.72.1. The file inc/icon-functions.php

There on the top we find the pre_underscores_nav_menu_social_icons( $item_output, $item, $depth, $args ) function. This function first calls the file, named images/svg-icons.svg, that contains all the SVG icons and adds them in line to the bottom of the loaded document in the browser. This file contains the markup for each of the SVGs, and this markup is called in to the footer of the page. Then we include the icon-functions.php file by adding the following code in functions.php.

/**
* Implement the SVG icons option.
*/
require get_template_directory() . '/inc/icon-functions.php';

Then if we go to the page in the browser, view the page source, and scroll all the way to the bottom, you'll find the actual markup for the SVGs right here. Below we see a part of the code SVG (the code is too long to place it here).

<svg style="position: absolute; width: 0; height: 0; overflow: hidden;" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
  <defs>
	  <symbol id="icon-behance" viewBox="0 0 37 32">
	  	<path class="path1" d="M33 6.054h-9.125v2.214h9.125v-2.214zM28.5 13.661q-1.607 0-2.607 0.938t-1.107 2.545h7.286q-0.321-3.482-3.571-3.482zM28.786 24.107q1.125 0 2.179-0.571t1.357-1.554h3.946q-1.786 5.482-7.625 5.482-3.821 0-6.080-2.357t-2.259-6.196q0-3.714 2.33-6.17t6.009-2.455q2.464 0 4.295 1.214t2.732 3.196 0.902 4.429q0 0.304-0.036 0.839h-11.75q0 1.982 1.027 3.063t2.973 1.080zM4.946 23.214h5.286q3.661 0 3.661-2.982 0-3.214-3.554-3.214h-5.393v6.196zM4.946 13.625h5.018q1.393 0 2.205-0.652t0.813-2.027q0-2.571-3.393-2.571h-4.643v5.25zM0 4.536h10.607q1.554 0 2.768 0.25t2.259 0.848 1.607 1.723 0.563 2.75q0 3.232-3.071 4.696 2.036 0.571 3.071 2.054t1.036 3.643q0 1.339-0.438 2.438t-1.179 1.848-1.759 1.268-2.161 0.75-2.393 0.232h-10.911v-22.5z"></path>
	  </symbol>
	  <symbol id="icon-deviantart" viewBox="0 0 18 32">
	  	<path class="path1" d="M18.286 5.411l-5.411 10.393 0.429 0.554h4.982v7.411h-9.054l-0.786 0.536-2.536 4.875-0.536 0.536h-5.375v-5.411l5.411-10.411-0.429-0.536h-4.982v-7.411h9.054l0.786-0.536 2.536-4.875 0.536-0.536h5.375v5.411z"></path>
	  </symbol>
	  <symbol id="icon-medium" viewBox="0 0 32 32">
	  	<path class="path1" d="M10.661 7.518v20.946q0 0.446-0.223 0.759t-0.652 0.313q-0.304 0-0.589-0.143l-8.304-4.161q-0.375-0.179-0.634-0.598t-0.259-0.83v-20.357q0-0.357 0.179-0.607t0.518-0.25q0.25 0 0.786 0.268l9.125 4.571q0.054 0.054 0.054 0.089zM11.804 9.321l9.536 15.464-9.536-4.75v-10.714zM32 9.643v18.821q0 0.446-0.25 0.723t-0.679 0.277-0.839-0.232l-7.875-3.929zM31.946 7.5q0 0.054-4.58 7.491t-5.366 8.705l-6.964-11.321 5.786-9.411q0.304-0.5 0.929-0.5 0.25 0 0.464 0.107l9.661 4.821q0.071 0.036 0.071 0.107z"></path>
	  </symbol>

	...

	  <symbol id="icon-slideshare" viewBox="0 0 32 32">
	  	<path class="path1" d="M15.589 13.214q0 1.482-1.134 2.545t-2.723 1.063-2.723-1.063-1.134-2.545q0-1.5 1.134-2.554t2.723-1.054 2.723 1.054 1.134 2.554zM24.554 13.214q0 1.482-1.125 2.545t-2.732 1.063q-1.589 0-2.723-1.063t-1.134-2.545q0-1.5 1.134-2.554t2.723-1.054q1.607 0 2.732 1.054t1.125 2.554zM28.571 16.429v-11.911q0-1.554-0.571-2.205t-1.982-0.652h-19.857q-1.482 0-2.009 0.607t-0.527 2.25v12.018q0.768 0.411 1.58 0.714t1.446 0.5 1.446 0.33 1.268 0.196 1.25 0.071 1.045 0.009 1.009-0.036 0.795-0.036q1.214-0.018 1.696 0.482 0.107 0.107 0.179 0.161 0.464 0.446 1.089 0.911 0.125-1.625 2.107-1.554 0.089 0 0.652 0.027t0.768 0.036 0.813 0.018 0.946-0.018 0.973-0.080 1.089-0.152 1.107-0.241 1.196-0.348 1.205-0.482 1.286-0.616zM31.482 16.339q-2.161 2.661-6.643 4.5 1.5 5.089-0.411 8.304-1.179 2.018-3.268 2.643-1.857 0.571-3.25-0.268-1.536-0.911-1.464-2.929l-0.018-5.821v-0.018q-0.143-0.036-0.438-0.107t-0.42-0.089l-0.018 6.036q0.071 2.036-1.482 2.929-1.411 0.839-3.268 0.268-2.089-0.643-3.25-2.679-1.875-3.214-0.393-8.268-4.482-1.839-6.643-4.5-0.446-0.661-0.071-1.125t1.071 0.018q0.054 0.036 0.196 0.125t0.196 0.143v-12.393q0-1.286 0.839-2.196t2.036-0.911h22.446q1.196 0 2.036 0.911t0.839 2.196v12.393l0.375-0.268q0.696-0.482 1.071-0.018t-0.071 1.125z"></path>
	  </symbol>
	   <symbol id="icon-play" viewBox="0 0 22 28">
	  	<path d="M21.625 14.484l-20.75 11.531c-0.484 0.266-0.875 0.031-0.875-0.516v-23c0-0.547 0.391-0.781 0.875-0.516l20.75 11.531c0.484 0.266 0.484 0.703 0 0.969z"></path>
	  </symbol>
	  <symbol id="icon-pause" viewBox="0 0 24 28">
	  	<path d="M24 3v22c0 0.547-0.453 1-1 1h-8c-0.547 0-1-0.453-1-1v-22c0-0.547 0.453-1 1-1h8c0.547 0 1 0.453 1 1zM10 3v22c0 0.547-0.453 1-1 1h-8c-0.547 0-1-0.453-1-1v-22c0-0.547 0.453-1 1-1h8c0.547 0 1 0.453 1 1z"></path>
	  </symbol>
  </defs>
  </svg>
  
code 13.72.2. part of the browser output with the code of the SVG icons

Next comes the core function, pre_underscores_get_svg(). This function accepts an array of arguments where you specify what icon you want to display based on the ID of the symbol in svgicons.svg, so, for example, if I want to display the icon for slideshare, I simply call pre_underscores_get_svg, set up an array, and inside that array, call for the icon with the ID slideshare.

There are two more things we need to put in place for all this to work properly. First, some basic CSS. In a new filed called 'sass/media/_icons.scss.

/* SVG Icons base styles */
.icon {
  display: inline-block;
  fill: currentColor;
  height: 1em;
  position: relative; /* Align more nicely with capital letters */
  top: -0.0625em;
  vertical-align: middle;
  width: 1em;
  }
/*--------------------------------------------------------------
  SVG Fallbacks
  --------------------------------------------------------------*/
.svg-fallback {
  display: none;
  }
.no-svg .svg-fallback {
  display: inline-block;
  }
/* Social Menu fallbacks */
.no-svg .social-menu a {
  height: auto;
  width: auto;
  }
.no-svg .social-menu li a .screen-reader-text {
  clip: auto;
  font-size: 16px;
  font-size: 1rem;
  font-weight: 400;
  height: auto;
  position: relative !important; /* overrides previous !important styles */
  width: auto;
  }
  
code 13.72.3. sass/media/_icons.scss

I've set up some basic rules both for the icons themselves and for the fallback. So, any icon that's displayed will have the class icon, and here we're just displaying the icons at a consistent size and inheriting color and so on. Then we also provide some basic SVG fallback functionalities, so if SVGs are not displayed, we'll display something else in its place. We're not having empty containers. Speaking of fallbacks, we need a simple way of detecting whether the browser supports in-line SVGs, and we do that as follows.

  1. In header.php, at the very top, we grab the html element and give it the class no-svg, so out of the box we assume the browser does not support SVGs
    <html lang="en-US" class="no-svg">
    
  2. Then we use this class to create custom styling in sass/media/_icons.scss for whatever is displayed in place of the icons
    /*--------------------------------------------------------------
      SVG Fallbacks
      --------------------------------------------------------------*/
    .svg-fallback {
      display: none;
      }
    .no-svg .svg-fallback {
      display: inline-block;
      }
    /* Social Menu fallbacks */
    .no-svg .social-menu a {
      height: auto;
      width: auto;
      }
    .no-svg .social-menu li a .screen-reader-text {
      clip: auto;
      font-size: 16px;
      font-size: 1rem;
      font-weight: 400;
      height: auto;
      position: relative !important; /* overrides previous !important styles */
      width: auto;
      }
    
  3. In sass/media/_media.scss add the follow lines, otherwise the styles in sass/media/_icons.scss will not load.
    /*--------------------------------------------------------------
    ## Icons
    --------------------------------------------------------------*/
    @import "icons";
    
  4. Then we use Java Script, found in js/functions.js, to check if the browser supports in-line SVG. If it does, we grab the html element, find the class, no-svg, and replace it with a class svg.
    /*
    	 * Test if inline SVGs are supported.
    	 * @link https://github.com/Modernizr/Modernizr/
    	 */
    	function supportsInlineSVG() {
    		var div = document.createElement( 'div' );
    		div.innerHTML = '';
    		return 'http://www.w3.org/2000/svg' === ( 'undefined' !== typeof SVGRect && div.firstChild && div.firstChild.namespaceURI );
    	}
    	
    	if ( true === supportsInlineSVG() ) {
    		document.documentElement.className = document.documentElement.className.replace( /(\s*)no-svg(\s*)/, '$1svg$2' );
    	}
    	

    So, if the browser supports SVG then:

    <html lang="en-US" class="no-svg">
    

    will change into:

    <html lang="en-US" class="svg">
    

All-right, let's see how this actually works. We are going to do two things:

  1. In the pagination menu's on the archive pages we want an arrow at the end, pointing to the right, and on the newer button, we want an arrow in the beginning pointing to the left.
  2. In the social menu we replace the anchor texts for SVG icons

72.1 Place arrows in the pagination menu's

In my theme, at the very bottom, you'll remember we added this paging navigation. Right now you can see it says one, and then two, and four, and older, and if I click on older, and scroll to the bottom of this page, we get a newer button and an older button.

To improve this user experience, I want to add arrows to these two buttons here, the newer and olders, so on the older button, we want an arrow at the end, pointing to the right, and on the newer button, we want an arrow in the beginning pointing to the left. We can do this using the new SVG function we just added to our theme. To get the icons to appear, all we need is the function itself, pre_underscores_get_svg(), followed by an argument that calls in the icon, and then the name of the icon in images/svgicons.svg.

If you look in this file, images/svgicons.svg, you'll find, towards the bottom, we already have the icons necessary, there's a symbol with the ID 'icon-arrow-left', and another one, 'icon-arrow-right', and these are the two icons we'll use. Of course, to display them in the theme, we need to place them in the right template, and in this case, the template is index.php, right here. You'll remember from previously, we added this function, the_posts_pagination, in index.php, archive.php, and search.php, so this change needs to happen on all three locations.

So in index.php change the the_posts_pagination function into:

the_posts_pagination( array(
'prev_text' => pre_underscores_get_svg( array( 'icon' => 'arrow-left' ) ) . __( 'Newer', 'pre_underscores' ),
'next_text' => __( 'Older', 'pre_underscores' ) . pre_underscores_get_svg( array( 'icon' => 'arrow-right' ) ),
'before_page_number' => '<span class="screen-reader-text">' . __( 'Page ', 'pre_underscores' ) . '</span>',
));

In the new code I integrated pre_underscores_get_svg( array( 'icon' => 'arrow-left' ) ) before "Newer' and pre_underscores_get_svg( array( 'icon' => 'arrow-right' ) ) after 'Older'. Remember that we placed the pre_underscores_get_svg() function in icon-functions.php at the beginning of this part.

72.2 Place SVG icons in the social menu

There is a cool plug and play feature with this icon solution. If we go back to icon-functions.php and scroll a little further down, you'll discover a whole section just for a social menu.

/**
  * Display SVG icons in social links menu.
  *
  * @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  $item_output The menu item output with social icon.
  */
  function pre_underscores_nav_menu_social_icons( $item_output, $item, $depth, $args ) {
  // Get supported social icons.
  $social_icons = pre_underscores_social_links_icons();
 // 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>' . pre_underscores_get_svg( array( 'icon' => esc_attr( $value ) ) ), $item_output );
  }
  }
  }
 return $item_output;
  }
  add_filter( 'walker_nav_menu_start_el', 'pre_underscores_nav_menu_social_icons', 10, 4 );

/**
  * Returns an array of supported social links (URL and icon name).
  *
  * @return array $social_links_icons
  */
  function pre_underscores_social_links_icons() {
  // Supported social links icons.
  $social_links_icons = array(
  'behance.net'     => 'behance',
  'codepen.io'      => 'codepen',
  'deviantart.com'  => 'deviantart',
  'digg.com'        => 'digg',
  'dribbble.com'    => 'dribbble',
  'dropbox.com'     => 'dropbox',
  'facebook.com'    => 'facebook',
  'flickr.com'      => 'flickr',
  'foursquare.com'  => 'foursquare',
  'plus.google.com' => 'google-plus',
  'github.com'      => 'github',
  'instagram.com'   => 'instagram',
  'linkedin.com'    => 'linkedin',
  'mailto:'         => 'envelope-o',
  'medium.com'      => 'medium',
  'pinterest.com'   => 'pinterest-p',
  'getpocket.com'   => 'get-pocket',
  'reddit.com'      => 'reddit-alien',
  'skype.com'       => 'skype',
  'skype:'          => 'skype',
  'slideshare.net'  => 'slideshare',
  'snapchat.com'    => 'snapchat-ghost',
  'soundcloud.com'  => 'soundcloud',
  'spotify.com'     => 'spotify',
  'stumbleupon.com' => 'stumbleupon',
  'tumblr.com'      => 'tumblr',
  'twitch.tv'       => 'twitch',
  'twitter.com'     => 'twitter',
  'vimeo.com'       => 'vimeo',
  'vine.co'         => 'vine',
  'vk.com'          => 'vk',
  'wordpress.org'   => 'wordpress',
  'wordpress.com'   => 'wordpress',
  'yelp.com'        => 'yelp',
  'youtube.com'     => 'youtube',
  );
 /**
  * Filter Twenty Seventeen social links icons.
  *
  * @since Twenty Seventeen 1.0
  *
  * @param array $social_links_icons
  */
  return apply_filters( 'pre_underscores_social_links_icons', $social_links_icons );
  }
  

So what happens here is, the functionality says, "Give me any menu that is in the theme location called 'social'", which is just a menu we've created. If you find that menu, grab each of the items, look at the URL it's pointing at, and if that URL matches any of the URLs on the list in the pre_underscores_social_links_icons() function, then display the icon for that URL in place of the text. That means if you go in and manipulate your menu now to add a new menu item, and you target any of the items that are on this list, the icon for that item will automatically appear in the menu.

What's even cooler is, this menu is properly marked up with fallbacks, so later on, if SVGs are not working in the browser, you'll see the text instead. If you look at this list and see that a service is missing, all you have to do is add the service in here with the URL to the service and the name of the icon, then go into svgicons and add the icon itself. How is that done? Well, we're covering that in the next part 73.

==================================================================

73. Add your own SVG icons in your theme.

Github repo branch: part13_73

Let me show you how to use your own icons in your theme. In the pagination menu's on the archive pages, I'm currently using two arrows that came from the 'twentyseventeen' theme, but I want some different icons. I could go to Illustrator, and design my own SVG's, or use another application, or I could grab some icons from an existing icon set. Personally, I like to use IcoMoon for this because you get access to all these different icon sets.

Just pick and choose which ones you want to use, click on IcoMoon App to get started, then just go through here and pick whatever icons you need.

Just pick and choose which ones you want to use, click on IcoMoon App to get started, then just go through here and pick whatever icons you need. In my case, I picked these two arrows up here. Once you have the selection, click "Generate SVG & More", and then when you get the list of SVG's, hover your mouse over each of the items, and you'll see there's an option underneath that says, "Get Code". If you click that, you open a moto window that gives you the symbol definition, which is what you actually need. This is the full SVG defined in code. So I'll copy the symbol definition, go into svg-icons.svg that sits under the images folder, and then finally, I need to make sure that I don't have any clashes with the ID here. You can only have an ID occur once on a page, so I'll just copy that and do a "find" to see if there's more than one match. If there is more than one match I change the name of the id of my new symbol. In my cace I'll change 'arrow-right' into 'arrow-right-heavy' and 'arrow-left' into 'arrow-left-heavy' So this is the heavy right arrow. I also need a heavy left arrow. So I'll get code again, copy, and just paste it in below in the images/svg-icons.svg file. Delete the <title> element.

Now I want to use these new icons in my theme, and you saw in the previous part 72 that all we need to do is call in the icon, based on the ID for the icon. So in index.php, archive.php and search.php I'll change:

the_posts_pagination( array(
'prev_text' =>  pre_underscores_get_svg( array( 'icon' => 'arrow-left' ) ) .' &nbsp;&nbsp; ' .  __( 'Newer', 'pre_underscores' ),
'next_text' => __( 'Older', 'pre_underscores' ) . ' &nbsp;&nbsp; ' .pre_underscores_get_svg( array( 'icon' => 'arrow-right' ) ),
'before_page_number' => '<span class="screen-reader-text">' . __( 'Page ', 'pre_underscores' ) . '</span>',
));

into:

the_posts_pagination( array(
'prev_text' =>  pre_underscores_get_svg( array( 'icon' => 'arrow-left-heavy' ) ) .' &nbsp;&nbsp; ' .  __( 'Newer', 'pre_underscores' ),
'next_text' => __( 'Older', 'pre_underscores' ) . ' &nbsp;&nbsp; ' .pre_underscores_get_svg( array( 'icon' => 'arrow-right-heavy' ) ),
'before_page_number' => '<span class="screen-reader-text">' . __( 'Page ', 'pre_underscores' ) . '</span>',
));

Then I'll do the same with the other one. Next text, and here I'll replace it with arrow-long-right.

Save index.php, go back into browser, and the new icons automatically appear. This is why I say this SVGs solution is so magical. Once you have the solution built into your theme, call in any icon you want, you simply use the function, and refer to icon ID. Because the SVG is defined inside this file, svg-icons.svg, you can add as many new icons as you want, just like I did, by generating the icon either through IcoMoon, or through Illustrator, or through some other application.

Then just put the symbol code into svg-icons.svg, refer to the ID for that icon, and it will automatically appear wherever you want it.

==================================================================

74. Provide fallbacks for browsers without SVG support.

Github repo branch: part13_74

There's a good chance you're thinking, "SVG's are cool, and all, and I wish I could use them but I can't because I need to provide support for older browsers and older browsers don't understand SVG. But that's not quit true. If we step back just even two browser version, we see that basic SVG support does exists, and basic support is just what we need here. (Other SVG capabilities are not always supported, as you can see in the 'Caniuse' tables below). But, if you need basic SVG support for older browsers than that (like IE8 or lower or Android browsers lower than 4.4), then you need a fallback capability.

Browser support

SVG Basic Support

Can I Use svg? Data on support for the svg feature across the major browsers from caniuse.com.

SVG Effect for HTML

Can I Use svg? Data on support for the svg feature across the major browsers from caniuse.com.

SVG favicons

Can I Use svg? Data on support for the svg feature across the major browsers from caniuse.com.

SVG in HTML img element

Can I Use svg-img? Data on support for the svg-img feature across the major browsers from caniuse.com.

SVG in CSS backgrounds

Can I Use svg-css? Data on support for the svg-css feature across the major browsers from caniuse.com.

SVG fonts

Can I Use svg-fonts? Data on support for the svg-fonts feature across the major browsers from caniuse.com.

SVG fragment identifiers

Can I Use svg-fragment? Data on support for the svg-fragment feature across the major browsers from caniuse.com.

In fact, with this solution we have baked into our theme now we can create custom fallbacks for each individual SVG used on the site. That means, you can replace some SVG's with graphics, other SVG's with words, and so on. And create a fully customized experience even without SVG support.

Before we look at how to create these fall backs, let me remind you of one very important detail. If I inspect any other code here, and look at my HTML, you'll remember at the very top on the HTML element, we have a class="svg" because the browser supports SVG. If the browser did not support SVG, the class would be class="no-svg" on the entire HTML element and we can use that class="no-svg" to create some custom css that will kick in only when SVG's are not supported.

To understand how this works, we need to look at icon-functions.php. Tere, inside our SVG function, where the actual SVG is being generated, there's a fallback option.

// Add some markup to use as a fallback for browsers that do not support SVGs.
if ( $args['fallback'] ) {
$svg .= '<span class="svg-fallback icon-' . esc_attr( $args['icon'] ) . '"></span>';
}

we can see here it says

So, if we grab an instance of humescores get SVG, like the one we have here in index.php

the_posts_pagination( array(
'prev_text' =>  pre_underscores_get_svg( array( 'icon' => 'arrow-left-heavy' ) ) .' &nbsp;&nbsp; ' .  __( 'Newer', 'pre_underscores' ),
'next_text' => __( 'Older', 'pre_underscores' ) . ' &nbsp;&nbsp; ' .pre_underscores_get_svg( array( 'icon' => 'arrow-right-heavy' ) ),
'before_page_number' => '<span class="screen-reader-text">' . __( 'Page ', 'pre_underscores' ) . '</span>',
));

then inside the array, add a new argument called "fallback". And set that argument to "true". And I'll do that for both instances here.

Then when we go back to the browser and inspect one of these elements, let's say the older link, and look inside, you'll see here we now have the SVG itself and then we have a span with a class SVG fallback, and the class icon, arrow long right, before the span ends.

<svg class="icon icon-arrow-right-heavy" aria-hidden="true" role="img">...</svg>
<span class="svg-fallback icon-arrow-right-heavy"></span>

Now we can use some custom css to target these particular classes. And display something else when SGV is not supported. Now, what exactly that something else is, will depend on what kind of SVG you are replacing. In my case, I'm replacing arrows. So it would be natural to inject some HTML arrows in place of the SVG ones. If you ever want HTML arrows, the best place to look is the website htmlarrows.com. Here we have a ton of different arrows that you can call in in HTML. And I want to use these two, left arrow, and right arrow. So I've just copied them out, and added them into icons.scss



.no-svg .svg-fallback.icon-arrow-left-heavy:before {
  content: "←";
  }
.no-svg .svg-fallback.icon-arrow-right-heavy:before {
  content: "→";
  }


  

Save icons.scss, go back in the browser reload, and if I inspect the 'Older' element again, we can still see the SVG arraw, but if I go into the inspector to the top, find the <HTML> element and change the class to <class="no-svg">, and hit enter, the html arrows appear.

fallback arrows

Now of course, this doesn't turn off the SVG, because, working in the inspector, the rest of the page didn't change, but you can see what happens when we put the .no-svg class at work.

Leave a comment