The Underscores (_s) theme with Sass and Gulp Part 5: building the menus

Untitled Document

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

On this page:

  1. Register and display menus.
  2. Apply styles to the menu.
  3. Borrow JavaScript from Twenty Seventeen to enable dropdown menus.
  4. Customize JavaScript to work with the current theme.
  5. Add a social media menu to the footer.

22. Register and display menus.

Github repo branch: part5_22

A menu like the one you see in the header requires two components. First of all it has to be registered in the back end of WordPress as a menu that can be customized in the customizer. Then, it has to be added into the themes so it can be displayed. This is done using two different functions. First, register_nav_menus which registers the menu into WordPress so WordPress knows it's available and adds it to the customizer as a location. Second, wp_nav_menu is the function that's used to display the menu on the front end. A wp_nav_menu has a ton of parameters you can use to customize the output of the menu. (See: https://developer.wordpress.org/reference/functions/wp_nav_menu/)

We're not going to use many of these here but know that they're there and know that you can customize the menu in many different ways. If you go through this list, you'll see there's a lot of functionality. Normally you don't actually need any of this because the defaults are pretty good, but there are certain circumstances where you may need to change the output of a menu. In that case, there's probably going to be some parameter you can use to get exactly the output you want. Let's take a look at how the menus are wired up in underscores. We start in functions.php, and here you'll that the menus are registered.

 // This theme uses wp_nav_menu() in one location.
  register_nav_menus( array(
  'primary' => esc_html__( 'Primary', 'pre_underscores' ),
  ) );
 
  

Down here on line 2, we have the function register_nav_menu and right now it's registering a single menu, primary, with the human readable name, 'Primary'. This can be translated using the text domain.

Registering a menu like this does not mean the menu shows up in the theme. All it means is we can now populate the menu from the customizer, and then tell the theme where to display it. If I want to add additional menu locations, I simply copy this line and give it a new name, let's say 'Secondary'. Save functions.php, back in the browser and go back to customizer.

 // This theme uses wp_nav_menu() in two locations.
		register_nav_menus( array(
			'primary' => esc_html__( 'Primary', 'pre_underscores' ),
			'secondary' => esc_html__( 'Secondary', 'pre_underscores' ),
	) );
		

Save functions.php, back in the browser and go back to customizer. Go to menus, and your locations, and here we have the secondary menu. You can also change the names of these menus to anything you want. Maybe you want primary menu to be called header menu, makes a lot more sense since it's in the header. In that case, simply go back and change the name, change it to header.

I'm also going to take out the secondary because we don't need it right now. Now when I reload the page, to menus and menu locations we have a single location that is in the header. Once a menu is registered we also need to display it somewhere within the theme, and here you can place it anywhere you want.

I've seen many use in the headers, footers, sidebars pretty much anywhere. You have to remember menus are not necessarily what you think of as menus. You can also use menus as lists because they're an easy way of managing lists and orders. In underscores, the only menu is displayed in the header. You can scroll down and find it at the bottom.

 <nav id="site-navigation" class="main-navigation">
<button class="menu-toggle" aria-controls="primary-menu" aria-expanded="false"><?php esc_html_e( 'Primary Menu', 'pre_underscores' ); ?></button>
<?php
wp_nav_menu( array(
'theme_location' => 'primary',
'menu_id'        => 'primary-menu',
) );
?>
</nav><!-- #site-navigation -->
Code 5.22.3

In code 5.22.3 we call the menu using the theme locations, so here we have wp_nav_menu. We set up an array of parameters, and the two parameters that are being used are theme_location which points at primary, that is the name of the menu right here, and then menu_id which is the ID that's appendant to the div that wraps around the entire menu element.

In the browser we, througt the inspector, we can look at the output of the menu. I'll right click on any menu item and then break out the developer tools.

<nav id="site-navigation" class="main-navigation">
<button class="menu-toggle" aria-controls="primary-menu" aria-expanded="false">Primary Menu</button>
<div id="primary-menu" class="menu"><ul aria-expanded="false" class=" nav-menu">
<li class="page_item page-item-37"><a href="//192.168.0.174:8890/about-us/">About us</a></li>
<li class="page_item page-item-703"><a href="//192.168.0.174:8890/blog/">Blog</a></li>
<li class="page_item page-item-40"><a href="//192.168.0.174:8890/contact-us/">Contact us</a></li>
<li class="page_item page-item-701 current_page_item"><a href="//192.168.0.174:8890/">Front Page</a></li>
<li class="page_item page-item-11"><a href="//192.168.0.174:8890/home/">Home</a></li>
<li class="page_item page-item-2"><a href="//192.168.0.174:8890/sample-page/">Sample Page</a></li>
<li class="page_item page-item-6 page_item_has_children"><a href="//192.168.0.174:8890/about/">About The Tests</a>
<ul class="children">
<li class="page_item page-item-1133"><a href="//192.168.0.174:8890/about/page-image-alignment/">Page Image Alignment</a></li>
<li class="page_item page-item-1134"><a href="//192.168.0.174:8890/about/page-markup-and-formatting/">Page Markup And Formatting</a></li>
<li class="page_item page-item-501"><a href="//192.168.0.174:8890/about/clearing-floats/">Clearing Floats</a></li>
<li class="page_item page-item-155"><a href="//192.168.0.174:8890/about/page-with-comments/">Page with comments</a></li>
<li class="page_item page-item-156"><a href="//192.168.0.174:8890/about/page-with-comments-disabled/">Page with comments disabled</a></li>
</ul>
</li>
<li class="page_item page-item-174 page_item_has_children"><a href="//192.168.0.174:8890/level-1/">Level 1</a>
<ul class="children">
<li class="page_item page-item-173 page_item_has_children"><a href="//192.168.0.174:8890/level-1/level-2/">Level 2</a>
<ul class="children">
<li class="page_item page-item-172"><a href="//192.168.0.174:8890/level-1/level-2/level-3/">Level 3</a></li>
<li class="page_item page-item	-746"><a href="//192.168.0.174:8890/level-1/level-2/level-3a/">Level 3a</a></li>
<li class="page_item page-item-748"><a href="//192.168.0.174:8890/level-1/level-2/level-3b/">Level 3b</a></li>
</ul>
</li>
<li class="page_item page-item-742"><a href="//192.168.0.174:8890/level-1/level-2a/">Level 2a</a></li>
<li class="page_item page-item-744"><a href="//192.168.0.174:8890/level-1/level-2b/">Level 2b</a></li>
</ul>
</li>
<li class="page_item page-item-146"><a href="//192.168.0.174:8890/lorem-ipsum/">Lorem Ipsum</a></li>
<li class="page_item page-item-733"><a href="//192.168.0.174:8890/page-a/">Page A</a></li>
<li class="page_item page-item-735"><a href="//192.168.0.174:8890/page-b/">Page B</a></li>
</ul></div>
</nav>

Here we start with a nav=id site-navigation class: main-navigation. Inside this nav we have a button, this button is what appears when we reduce the width of the screen. So if I reduce the width of the screen here to small enough you'll see the menu is replaced by this button, and that's the button you're seeing in the markup right here. Then we have the div that wraps around the menu, here you see the ID,

The menu itself is an unordered list where each of the menu items is a list item with a link inside. For drop down menus, you'll see that the list item has an appended class, page_item_has_children, and you'll see when you open that one in addition to the link that is the current menu item, you also have another unordered list. Inside it, this one has the class children and again we have list items with links inside them. WordPress does a really good job at appending class names to menu items so that it's easy to target individual situations.

As you can see if you want to style just drop down items, you simply target the children class. If you want to style items that have drop down elements, you can target page_item_has_children, or menu_item_has_children. If you navigate to an individual page, or post, or other item that is listed in the menu, that will be marked with a class name. Here you can see I navigated to about the tests, and the list item for about the tests now has an additional class, current_page_item. If you navigate to a page that is in a drop down, the parents of that drop down will get a new class, indicating that it is its ancestor.

All of this is to say, if you want to you can stall your menus down to and incredibly detailed level of specificity. However, I tend to air on the side of passion here and only apply sparse styling to the menu. That's just my personal preference. You should style the menus the way you want to, and I'm telling you there are so many classes to hook into here, that you can pretty much create a custom menu that accounts for every single scenario that you can come up with. Now that you know how menus are registered and hooked into the theme, let's look at how we can style and add functionality to the menu to make it accessible, responsive, and look the way we want to.

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

23. Apply styles to the menu.

Github repo branch: part5_23

Out of the box, the Header menu and Underscores is styled using floats. That means if I inspect one of the menu items, and go up to its list item, you'll see each of these list items are floated to the left. That's what creates this horizontal menu, and that works but it's problematic.

The good news is: we have a much better way of laying out menus now, via flexbox. So let me show you what I'm talking about. If I grab this list item and simply remove the float, and we get a vertical menu. Now, I can grab the unordered list that contains all the list items, and simply change its display from block to flex.That means I can additional features to it. For example, I can justify the content and set it to space-between. Now the browser will find all the available space around the menu and then distribute that space evenly between each of the menu items. I can also do more advanced things like change the direction of the menu.And because we are using flexbox to lay out the menu, the text direction of the page decides what direction the menu is. That means if we now swap the text direction of the page from left-to-right, to right-to-left, the order of the menu will also swap right to left. So we don't have to provide special styles for right-to-left languages. And these are just a couple of the many advantages to using flexbox to layout menus.

So in my theme, I want to use flexbox liberally in my menus.

  • First we need to get rid of the original styles for the menu, those are found under Navigation and Menus in sass/navigation/_menus.scss. Just delete the whole .main-navigation class
  • Next, we'll hook in the new menu styles. You'll find those under Site and Header. Here there's a new file, called header-menu.scss, and that header menu file is called in at the bottom of _header.scss.

Save, go back to the browser, and the menu has completely changed.

And if we open the drop-downs, which we cannot do yet because we're missing the JavaScript, you'll see that the drop-downs will have a background color as well. What you'll see in _header-menu.scss is a standard, mobile-first layout. We start by defining main navigation and provide all the styling for the mobile menu. Right now, you can't see the mobile menu because we are missing the JavaScript, but trust me when I say this is where all the styling for what the menu buttons look like, is contained. The coding is all really straightforward stuff, so I'm not going to go into it in any great detail. The only thing I'll point out is: I've added some extra classes here that you may want to experiment with, in particular these ones: current_page_item, current-menu-item, current_page_ancestor, and current-menu-ancestor. You can apply styles to these rules to highlight the current page or menu item you are on, and also highlight any parents of those items, so add in a background color or something and you'll see how that works when you navigate through your site. What matters for us, and what I want to draw your attention to, is how we use flexbox to lay out this menu. Just change the width of the browser window to mobile sizes to see the mobile menus.

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

24. Borrow JavaScript from Twenty Seventeen to enable dropdown menus

Github repo branch: part5_24

Right now, my menu looks good but it's not working properly. I have no way of getting to the dropdown elements for these two buttons that have dropdown elements. And when I reduce the width of the screen, I get a button that's supposed to open the menu, but when you click on it nothing is happening. That's because the new style sheet doesn't correspond with the JavaScript that's supposed to handle this stuff. That's the JavaSript that came with underscores. And that's fine, because I don't want to use the JavaScript from underscores. I want to use the solution that's available in 2017. It's far more refined and it's also better for both touch devices and for people who are navigating using a keyboard.

In a separate tab I've opened my site, and I'm previewing it with the twentyseventeen theme. In the customizer, you can see the customizer is hiding here on the left hand side. In twentyseventeen, when a menu item has a dropdown this chevron appears next to it; it's actually a button, and when you click on the button you reveal the dropdown. Click anywhere, and the dropdown disappears. That way, someone accessing the site who's using a touch device will be able to open the dropdown without having to do any hovers or anything else. This is what I want on my site.

So I'm going to replace a few things with what's in twentyseventeen.

  1. First I copy the content of twentyseventeen/assets/js/navigation.js and paste it into pre_underscores/js/navigation.js
  2. In my new navigation.js file I find and replace all the 'twentyseventeen' for 'pre_underscores' .
  3. And then I can save this file, go back to the browser, and here you see the menus still not working and more importantly we have a red warning up in the tool bar. This is the Debug tool we installed with the developer plugins. If you click that, and when you go to JavaScript, you'll see there's an uncaught reference error. pre_underscoresScreenReaderText is not defined. Now this is one of two errors that are being triggered right now on the page. The other one is this script here relies on jQuery, as you can see at the bottom of the navigation.js file.
  4. Right now we're indeed not queuing up jQuery in WordPress. So I'll fix that first, by going back to functions.php. Here's the call, pre_underscores-navigation, and currently there's no dependencies for it. So I'm going to place 'jquery' inside the dependency array. Change:
    wp_enqueue_script( 'pre_underscores-navigation', get_template_directory_uri() . '/js/navigation.js', array(), '20151215', true );
    
    for:
    wp_enqueue_script( 'pre_underscores-navigation', get_template_directory_uri() . '/js/navigation.js', array('jquery'), '20151215', true );
    
    This will bring in the version of jQuery that bundles inside WordPress
  5. But more importantly I need to fix the problem of this undefined reference. In functions.php I place the following code:
    wp_localize_script( 'pre_underscores-navigation', 'pre_underscoresScreenReaderText', array(
    'expand' => __( 'Expand child menu', 'pre_underscores'),
    'collapse' => __( 'Collapse child menu', 'pre_underscores'),
    ));
    
  6. So save functions.php, back in the browser. And now you see we've gotten two new buttons (check the code to see the <button> element) in our view here. Next to each of the menu items that have dropdowns, we now have these little things up in the top corners. And if you click on those things, you open the dropdown. Click on it again, close the dropdown. Furthermore, if I reduced the width of the screen now, you'll see as we get down to a screen that's small enough, I can now click on primary menu, opens the menu. Click on any of these buttons, opens the sub menus. And everything is working properly. Now that doesn't mean we're done. This looks wonky and we still don't know exactly what's going on here, but at least it's working. The next step will be described in part 25 below

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

25. Customize JavaScript to work with the current theme

Github repo branch: part5_25

First I'll change

// Add dropdown toggle that displays child menu items.
var dropdownToggle = $( '<button />', { 'class': 'dropdown-toggle', 'aria-expanded': false })
.append( pre_underscoresScreenReaderText.icon )
.append( $( '<span />', { 'class': 'screen-reader-text', text: pre_underscoresScreenReaderText.expand }) );

with:

// Add dropdown toggle that displays child menu items.
var dropdownToggle = $( '<button />', { 'class': 'dropdown-toggle', 'aria-expanded': false })
.append( $( '<span />', { 'class': 'dropdown-symbol', text: '+' }) )
.append( $( '<span />', { 'class': 'screen-reader-text', text: pre_underscoresScreenReaderText.expand }) );

save that, go back to the browser, and now you see the button contains a little plus symbol. And if we open the code in the inspector, you'll see this button has two spans, one with a class dropdown-symbol, which has the plus in it, and one with the class screen-reader-text that has Expand Child Menu. You'll see in a little bit why I wrapped the plus symbol in this span with a class on it.

But how does this button get appended only to the menu items that actually have drop-down menus? Well, that's done in this next line.

container.find( '.menu-item-has-children > a, .page_item_has_children > a' ).after( dropdownToggle );

Here we use the jQuery function container.find, to find the specific kind of container. In this case, we're looking for any container that has the class menu-item-has-children, and an anchor inside it, or any item that has the class page_item_has_children and the anchor inside that one. If we find any of those items, we append the dropdown-toggle directly after that item.

We remove the next two lines of code because we don't need it. So remove:

// Set the active submenu dropdown toggle button initial state.
container.find( '.current-menu-ancestor > button' )
.addClass( 'toggled-on' )
.attr( 'aria-expanded', 'true' )
.find( '.screen-reader-text' )
.text( pre_underscoresScreenReaderText.collapse );
// Set the active submenu initial state.
container.find( '.current-menu-ancestor > .sub-menu' ).addClass( 'toggled-on' );

Then on line 26 place the following two lines:

dropdownSymbol = _this.find( '.dropdown-symbol' );
dropdownSymbol.text( dropdownSymbol.text() === '-' ? '+' : '-');

Save this, reload the page, and see if it works. I'll click the plus button, changes to minus. Click the minus button, changes back to plus.

So this is the functionality that actually toggles the menu on and off

.

Now, let's take a look at the narrower screen. If I click on this button, the entire menu opens. Click it again, it closes. So far so good.

Of course, there is one tiny little extra thing we need to do. The buttons look pretty hideous, so before we finish, let's change these buttons very quickly. This is all CSS and the reason these buttons have these weird shadings behind them right now is because currently the buttons are styled by a file that's found inside Forms that's called buttons, and here we have a ton of different shading features. So let's go to that file and simply reset the buttons to a much cleaner look. I'll go to sass/forms/_buttons.scss. and change what you see in:

button,
  input[type="button"],
  input[type="reset"],
  input[type="submit"] {
  border: 2px solid;
  border-color: black;
  background: transparent;
  color: black;
  font-family: $font__sans;
  @include font-size(1);
  line-height: 1;
  padding: .5em 1em;
 &:hover ,
  &:active,
  &:focus {
  background: white;
  color: black;
  }
  }
  

Save buttons.scss, go back and look again, and now the styles for the new buttons combine with the styles in the menu again and we've got these nice buttons here. They look much cleaner. And when I reduce the width of the screen, you'll see the primary menu button also looks much nicer than it did before.

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

26. Add a social media menu to the footer

Github repo branch: part5_26
  1. Register a new menu.
  2. Display the menu.
  3. Style the menu

26.1 Add a social media menu to the footer

// This theme uses wp_nav_menu() in two location.
register_nav_menus( array(
'primary' => esc_html__( 'Header', 'pre_underscores' ),
'social' => esc_html__( 'Social Media Menu', 'pre_underscores' ),
) );
code 5.26.1. register a new menu in footer.php

Then I'll go the Customizer to create the new menu, so I'll click Add a Menu. Give it the name 'Social Media Menu'. Inside the menu I'll populate it with some items: some custom links. I use Twitter (www.twitter.com), Facebook www.facebook.com, Likedin (www.linkedin.com) and Instagram (www.instagram.com). So now I have four menu items, and I assign these to the Social Media Menu location. Save & Publish.

So now I have four menu items, and I want to assign these to the Social Media Menu location. Save & Publish. Now of course, I'm currently not displaying the menu anywhere, so this will do nothing to my actual site. That's the next step.

26.2 Display the social media menu to the footer

I want to add my Social Media Menu at the bottom in the footer. The footer, is contained within a file called footer.php. I want to place the footer right above the <div class="site-info"> element. And here I'm just going to cheat. So I'll go to header.php and copy out the existing code for the menu there and then make some changes to it, correponding the properties for the social menu registrtions in 26.1. Right now I don't need the menu-id so I take that out.

<nav id="site-navigation" class="main-navigation" role="navigation">
<button class="menu-toggle" aria-controls="primary-menu" aria-expanded="false">
<?php esc_html_e( 'Primary Menu', 'pre_underscores' ); ?></button>
<?php wp_nav_menu( array(
'theme_location' => 'social',
'menu_class'     => 'social-links-menu',
'depth'          => 1,
'link_before'    => '<span class="screen-reader-text">',
'link_after'     => '</span>' . pre_underscores_get_svg( array( 'icon' => 'chain' ) ),
) );
?>
</nav><!-- #site-navigation -->
code 5.26.2a. Add the social media menu to the footer

Now just for completeness, I want to wrap this menu in a <nav>, so I'll setup a nav with class="social-menu". To accomodate responsive behaviour I also wrap a <div class="site-footer__wrap"> around the content in inside the <footer> element.

<div class="site-footer__wrap">
  <?php
  // Make sure there is a social menu to display.
  if ( has_nav_menu( 'social' ) ) { ?>
  <nav class="social-menu">
  <?php
  wp_nav_menu( array(
  'theme_location' => 'social',
  'menu_class'     => 'social-links-menu',
  'depth'          => 1,
  'link_before'    => '<span class="screen-reader-text">',
  'link_after'     => '</span>' . pre_underscores_get_svg( array( 'icon' => 'chain' ) ),
  ) );
  ?>
  </nav><!-- .social-menu -->
  <?php } ?>
 <div class="site-info">
  <div><a href="<?php echo esc_url( __( 'https://wordpress.org/', 'pre_underscores' ) ); ?>"><?php printf( esc_html__( 'Proudly powered by %s', 'pre_underscores' ), 'Preludio' ); ?></a></div>
  <div><?php printf( esc_html__( 'Theme: %1$s by %2$s', 'pre_underscores' ), 'pre_underscores', '<a href="http://www.preludio.nl" rel="designer">Preludio</a>' ); ?></div>
  </div><!-- .site-info -->
  </div><!-- .site-footer__wrap -->
  
code 5.26.2b. Wrap a <nav> arount the menu with class="social-menu" and a <div class="site-footer__wrap"> around all the content inside the <footer> element

26.3 Style the menu (and the footer).

Finally I want to apply styles to the menu. Now in my sass setup, you'll remember under site, we already have header, primary, and secondary. So it would be natural to put in a new folder here, called 'footer'. Inside that folder we'll put a new sass file, called _footer.scss. Then we'll queue up that sass file inside _site.scss (see code 5.26.2c). From here on it's just a matter of applying some basic styling. While I'am at it, I'll just style the entire footer including the menu. (see code 5.26.2d).

/*--------------------------------------------------------------
## Footer
--------------------------------------------------------------*/
@import "footer/footer";
code 5.26.2c. Add the _footer.scss to the _site.scss
.site-footer {
  position: relative;
  padding: 1em;
  font-family: $font__sans;
  color: #fff;
  background-color: $color__skin;
  text-align: center;
 a {
  font-weight: 700;
  color: white;
  text-decoration: none;
 &:hover,
  &:focus {
  text-decoration: underline;
  }
  }
 @media screen and (min-width: $query__small) {
  padding: 1em 2em;
  }
}
.site-footer__wrap {
  max-width: $size__max-width;
  margin: 0 auto;
 @media screen and (min-width: $query__medium) {
  display: flex;
  justify-content: space-between;
  }
  }
.site-info {
  margin: 1em 0 1em auto;
  align-self: center;
 @media screen and (min-width: $query__medium) {
  text-align: right;
 span {
  display: block;
 }
  }
  }
.social-menu {
  margin: 1em 0;
  ul {
  display: flex;
  justify-content: center;
  flex-wrap: wrap;
  list-style-type: none;
  margin: 0;
  padding: 0;
 a {
  display: block;
  padding: .5em 1em;
  color: white;
  text-decoration: none;
  opacity: .6;
 &:hover,
  &:focus {
  text-decoration: underline;
  opacity: 1;
  }
  }
  }
  }
  
code 5.26.2d. Styling the footer, including the social media menu in sass/footer/_footer.scss

Save. Reload. Scroll to the bottom and check out the new social menu.

Leave a comment