Quick Tip: BEM Naming and WordPress Filters for Navigation

Maintaining CSS is difficult, especially in bigger projects working with a team. Naming conventions can help your CSS be more readable and easier to maintain. In this quick tip we’ll look at how to use the BEM (Block Element Modifier) naming methodology in WordPress.

Quick Tip: BEM Naming and WordPress Filters for Navigation

Content management systems like WordPress output default classes in the front-end markup. In WordPress there are lots of filters to modify these outputted classes.

If you want to dive directly into code, here is an example theme of mine; filters can be found in inc/functions-filters.php file.

What is BEM?

BEM stands for Block Element Modifier.

“BEM is a methodology that helps you to create reusable components and code sharing in front-end development.”

  • Quick Tip: BEM Naming and WordPress Filters for Navigation

    An Introduction to the BEM Methodology

     

    BEM stands for Block-Element-Modifier and is a way of organising style and markup. If you’ve ever seen a class name like “header__form—email” that’s BEM in…

I’ll use navigation markup as an example:

  • Block is a standalone entity that is meaningful on its own.
  • .menu { ... }
  • Element is part of a block and is semantically tied to its block.
  • .menu__items { ... }
  • .menu__item { ... }
  • .menu__anchor { ... }
  • Modifier changes the block or element appearance or behavior.
  • .menu--primary { ... }
  • .menu__anchor--button { ... }

Full navigation markup might look like this:

<nav class="menu menu--primary">
  <ul class="menu__items menu__items--primary">
     <li class="menu__item"><a class="menu__anchor">Link text</a></li>
     <li class="menu__item"><a class="menu__anchor">Link text</a></li>
     <li class="menu__item"><a class="menu__anchor">Link text</a></li>
     <li class="menu__item"><a class="menu__anchor menu__anchor--button">Link text</a></li>
  </ul>
</nav>

Naming conventions like BEM keep your SASS and CSS flat with low specificity (which is always nice!).

In SASS you’d style our example like this:

.menu {
  ...
  
  &__items {
     ...
  }
  
  &__item {
     ...
  }
  
  &__anchor {
     ...
  }
  
  &__anchor--button {
     ...
  }
}

The compiled CSS would look like this:

.menu {
  ...
}

menu__items {
  …
}

menu__item {
  …
}

menu__anchor {
  …
}

menu__anchor--button {
  …
}

But how might we change the navigation classes used in WordPress?

Using WordPress Filters to Change Navigation Classes

Using WordPress’ built-in function wp_nav_menu(), you can dictate classes for <nav> and <ul> elements. For example:

<nav class="menu menu--primary">

<?php
  wp_nav_menu( array(
      'theme_location' => 'primary',
      'container'          => '',
      'menu_class'      => 'menu__items menu__items--primary',
  ) );
?>

</nav>

Note the menu_class parameter for adding custom classes to the <ul> element. WordPress also has handy filters for <li> and <a> elements:

Using nav_menu_css_class Filter

In this example we reset all default classes from the menu item’s <li> element and add our own custom classes. Let’s add our example class called menu__item by inserting this filter into our functions.php:

function bemit_nav_menu_css_class( $classes, $item, $args, $depth ) {

  // Reset all default classes and start adding custom classes to array.
  $_classes = [ 'menu__item' ];

  // Return custom classes.
  return $_classes;

}

add_filter( 'nav_menu_css_class', 'bemit_nav_menu_css_class', 10, 4 );

Did you notice there are four parameters? Let’s add a menu__item--primary modifier class, where primary is the theme location. For this we can use $args parameter for adding a theme location. Now our filter looks like this:

function bemit_nav_menu_css_class( $classes, $item, $args, $depth ) {

  // Reset all default classes and start adding custom classes to array.
  $_classes = [ 'menu__item' ];

  // Get theme location, fallback for `default`.
  $theme_location = $args->theme_location ? $args->theme_location : 'default';

   // Add theme location class.
  $_classes[] = 'menu__item--' . $theme_location;

  // Return custom classes.
  return $_classes;

}

add_filter( 'nav_menu_css_class', 'bemit_nav_menu_css_class', 10, 4 );

Full code examples can be found in my example theme in inc/functions-filters.php file.

Using nav_menu_link_attributes filter

Let’s add our example class menu__anchor to each menu item’s <a> element.

function bemit_nav_menu_link_attributes( $atts, $item, $args, $depth ) {

  // Start adding custom classes.
  $atts['class'] = 'menu__anchor';

  return $atts;
}

add_filter( 'nav_menu_link_attributes', 'bemit_nav_menu_link_attributes', 10, 4 );

Adding Classes Via the Menu Builder

Sometimes we add custom classes to the menu item’s <li> element from the menu builder UI.

Quick Tip: BEM Naming and WordPress Filters for Navigation

To keep CSS flat, let’s add a menu__anchor--button class to the <a> element where the button class is present:

function bemit_nav_menu_link_attributes( $atts, $item, $args, $depth ) {

  // Start adding custom classes.
  $atts['class'] = 'menu__anchor';

  // Add `menu__anchor--button` when there is `button` class in `<li>` element.
  if ( in_array( 'button', $item->classes, true ) ) {

       $atts['class'] .= ' menu__anchor--button';

   }

return $atts;

}

add_filter( 'nav_menu_link_attributes', 'bemit_nav_menu_link_attributes', 10, 4 );

Now we can directly target one level of CSS using .menu__anchor--button{ ... }. In my demo you’ll see one menu item styled like a button, targeted without having to dive into a rabbit’s warren of specificity.

Quick Tip: BEM Naming and WordPress Filters for Navigation

Sub Pages Navigation

If you’re using wp_list_pages() for fetching child pages, there are similar filters available:

In the demo there is a bemit_sub_pages_navigation() function which lists sub pages in the sidebar. Check out the end result from the demo site.

Conclusion

So you see, you might not need a Custom Walker for navigation if you’re making smaller changes. There are plenty of filters around.

With that said, not all markup is easy to modify. For example pagination functions like the_posts_pagination() doesn’t provide good filters at the moment. But that’s a whole other tutorial topic.

I hope you find this quick tip about navigation filters useful!