SMACSS-Press

Avatar of Scott Fennell
Scott Fennell on (Updated on )

The following is a guest post by Scott Fennell. Scott saw a recent post here on CSS-Tricks about class names in WordPress and had some of his own ideas about exerting control, rather than leaving things as they are. His idea is to enforce a more SMACSS style philosophy where possible.

I enjoyed a recent article in this space about Understanding WordPress CSS Classes. The author noted:

WordPress is extremely customizable and it’s possible to alter these default classes, but we aren’t going to go into that here. — @carlosinho

I have picked up some tricks on doing just that, and I’d like to share those with you.

The SMACSS Epiphany

I love the SMACSS Book. Of all the brilliant practices recommended there, my favorites are the rules for class names. At the risk of glossing over many pages of brilliance, I’m going to distill things down to one example:

<div class="pod">
  <h3 class="pod-header">heading</h3>
  <ul class="pod-items">
    <li class="pod-item"><a class="pod-item-link" href="#">Click me</a></li>
    <li class="pod-item"><a class="pod-item-link" href="#">Click me</a></li>
  </ul>
</div>

That’s not verbatim from the SMACSS book, but it represents what SMACSCS has come to mean in my workflow. Of note:

  • Every component of .pod is accessible by a css class that is prefixed with pod-.
  • Components that have a parent-to-many-children relationship take on a singular/plural naming scheme, as in pod-item and pod-items.

That’s more or less my ideal setup for targeting HTML with CSS. It’s the model I’m going to strive for in this article. Unfortunately, WordPress doesn’t ship like that.

SMACSS-ifying WordPress

Getting WordPress to obey this format takes four broad approaches:

  1. Really quick and easy filters to alter what WordPress outputs in just a few lines of code.
  2. Telling WordPress to apply certain classes to objects that we register.
  3. Developing habits in our own custom functions (either plugin stuff or functions.php) such that they can easily and manageably take on bespoke classes.
  4. Relatively large overrides of WordPress core that ultimately may not be worth the effort.

It should go without saying (but let’s say it): actually modifying WordPress core files is not an option.

Filters

The easiest way to modify many of the classes that ship with WordPress is by filtering. Let’s say we have a chunk of data. It could be the post title (a string) or an array of post classes. That data is on its way somewhere, maybe from the database to the browser, to be displayed on the front end, or maybe on its way from the post editor to the database, where it will be stored. Many times when data is moving from one place to another like that, we can alter it by using a filter. Here’s an example using the classes that are applied to the body tag:

/**
 * Extends the default WordPress body class.
 *
 * @param  array $classes An array of css classes.
 * @return array The array of css classes, filtered.
 */
function sjf_body_class( $classes ) {
	
  // If the current post has a post thumbnail, append a body class.
  if ( is_singular() && has_post_thumbnail() ) {
    $classes[] = 'body-has-post-thumbnail';
  }
	
  return $classes;
}

// Apply these classes to the body tag.
add_filter( 'body_class', 'sjf_body_class' );

Notice that we have a conditional to only do this if this is a single post with a featured image. It’s important that we return the $classes array no matter what the result of that conditional, otherwise all our other templates would get no body classes! Consider this point a “trap” icon thing in the O’Reilly animal books.

We can add any string we want to the classes array, though if we are grabbing anything that’s not hard-coded, we should clean it first, using sanitize_html_class(). For example, we might want to add some post_meta value, perhaps if this post has been art-directed into a special color scheme:

/**
 * Extends the default WordPress body class to include the accent color for this post.
 *
 * @param  array $classes An array of css classes.
 * @return array The array of css classes, plus a new class for accent-color.
 */
function sjf_body_class( $classes ) {
	
  // If the current post has an accent color, append a body class.
  if ( is_singular() ) {
    $accent_color = get_post_meta( get_the_ID(), 'sjf_accent_color', TRUE );
    $accent_color = sanitize_html_class( $accent_color );
    if( ! empty( $accent_color ) ) {
      $classes[] = "body-$accent_color";
    }
  }
	
  return $classes;
}

// Apply these classes to the body tag.
add_filter( 'body_class', 'sjf_body_class' );

We can modify the array of classes that is applied to posts in the loop in almost the exact same fashion. The only difference is that instead of adding our function to the body_class filter, we would add it to post_class:

/**
 * Extends the default WordPress post class to include the accent color for this post.
 *
 * @param  array $classes An array of css classes.
 * @return array The array of css classes, plus a new class for accent-color.
 */
function sjf_post_class( $classes ) {
	
  // If the current post has an accent color, append a post class.
  if ( is_singular() ) {
    $accent_color = get_post_meta( get_the_ID(), 'sjf_accent_color', TRUE );
    $accent_color = sanitize_html_class( $accent_color );
    if( ! empty( $accent_color ) ) {
      $classes[] = "sjf-body-$accent_color-post";
    }
  }
	
  return $classes;
}

// Apply these classes to the post tag.
add_filter( 'post_class', 'sjf_post_class' );

There are many more hooks like this. See Adam Brown’s list and search for “class” to get more ideas. Some of the best candidates for extra classes are:

DRY it out

Notice that my interpretation of SMACSS dictates that my post classes are the same as my body classes, only with the suffix -post appended to them. Thanks to the current_filter() function, we can actually use the exact same function to run our body classes and our post classes, keeping our code DRY:

function sjf_body_post_class( $classes ) {
	
  // If the current post has an accent color, add a class.
  if ( is_singular() ) {
    $accent_color = get_post_meta( get_the_ID(), 'sjf_accent_color', TRUE );
    $accent_color = sanitize_html_class( $accent_color );
    if( ! empty( $accent_color ) ) {	
      // If we are on the post_class filter, tack on a '-post'.
      $suffix = '';
      if( current_filter() == 'post_class' ) { $suffix = '-post'; }
      $classes[] = "sjf-body-$accent_color$suffix";
    }
  }
	
  return $classes;
}

// Apply these classes to the body tag.
add_filter( 'body_class', 'sjf_body_post_class' );

// Apply these classes to the post tag.
add_filter( 'post_class', 'sjf_body_post_class' );

Filters are a powerful yet simple tool for applying new classes to WordPress-generated items. But what about items that WordPress generates in response to a theme or plugin registering that item?

Registration

Consider this code, used to register a sidebar:

function sjf_widgets_init() {
  register_sidebar( array(
    'name' => __( 'Main Sidebar', 'sjf' ),
    'id' => 'sjf-main-widgets',
    'description' => __( 'Widgets in this area will be shown on all posts and pages.', 'sjf' ),
    'before_title' => '<h1>',
    'after_title' => '</h1>',
  ) );
}
add_action( 'widgets_init', 'sjf_widgets_init' );

Nice and simple. But if our goal is to apply SMACSS-style classes to as many elements as possible, what a wasted opportunity! There are lots of places to insert classes here, though unlike the filters from earlier where we were dealing with an array of classes, this situation calls for a simple string of class names. For this example, let’s say the design calls for different widget styles based on login status. Here’s what we can do:

function sjf_widgets_init() {

  $slug = 'sjf-main';

  if( is_user_logged_in() ) {
    $logged = 'logged-in';
  } else {
    $logged = 'logged-out';
  }
  
  register_sidebar( array(
    'name'          => __( 'Main Sidebar', 'theme-slug' ),
    'id'            => "$slug-widgets",
    'description'   => __( 'Widgets in this area will be shown on all posts and pages.', 'sjf' ),
    'before_widget' => "<div id='%s' class='widget $slug-widget $slug-widget-$logged_in $slug-widget-%s'>",
    'after_widget'  => '</div>',
    'before_title'  => "<h1 class='widget-title $slug-widget-title $slug-widget-title-$logged_in'>",
    'after_title'   => '</h1>',
  ) );
}
add_action( 'widgets_init', 'sjf_widgets_init' );

A few things going on there:

  • User data. With $logged we’re running our old trick from earlier, seeing if this post has been art-directed into a special color scheme.
  • sprintf(). With %s, we’re prompting WordPress to replace that little chunk with the slug name for this type of widget. Stuff like, widget_search or widget_text.
  • Permutations. In order to be SMACSS-ready, we don’t just add the $color_class class or the $id class and call it good. We account for different combinations that we’re likely to need. For example, if we need to target search forms on red-accented pages in the main sidebar, we’ll be able to do that with a single CSS class of sjf-main-widget-red-widget_search.

To be clear, one reason I’m shifting away from my post_meta example from earlier is because post data is not yet exposed when widgets get registered.

Some designs may be nuanced enough to justify even more detailed classes for widgets. There are plugins in the .org repo that offer quick per-widget classing: per-widget classing.

Having covered some ways to class WordPress-generated elements, we’ll now take a look at our own custom elements.

Custom Functions

Either in our `functions.php` or our plugin files, we’ll no doubt register many of our own functions. I’ve come up with a routine for keeping things SMACSS-ready. Consider this function, which simply returns hello world:

function sjf_hello() {
  return '<span class="sjf-hello">hello world</span>';
}

Not SMACSS-ready at all. We’ll need far more if we want to be able to target this element in a SMACSS-compliant way. For example, if this element is used inside our .pod example from the beginning of the article, we’d want it to carry a class like .pod-sjf-hello. Whatever the situation, we want to be able to conveniently inject as mnay classes as necessary. Here’s what I do:

/**
 * Get the string "hello world", wrapped and classed.
 *
 * @param  array $classes An array of CSS classes.
 * @return string The "hello world" string, wrapped and classed.
 */
function sjf_hello( $classes = array() ) {
  
  // The namespace for this element.
  $slug = 'sjf-hello';
  
  // Prepend the slug to each class name, convert to a string.
  $classes_str = sjf_prepend_slug( $classes, $slug );
  
  return "<span class='$slug $classes_str'>hello world</span>";
  
}

Notice that my function now takes an arg for $classes, which expects an array of css class names. If I’m passing a bunch of classes to it, I don’t want to have to worry about prefixing them all ahead of time, so they get prefixed inside this function using a second function, sjf_prepend_slug():

/**
 * Prepend a string to each member of an array.
 *
 * @param  array  $classes An array of CSS classes.
 * @param  string $slug    A namespace to prepend to each class.
 * @return string The $classes array, cleaned, slugged, and imploded.
 */
function sjf_prepend_slug( $classes, $slug ) {
  
  $cleaned = array();
  foreach( $classes as $class ) {
    $cleaned []= sanitize_html_class( $slug . '-' . $class );
  }
  
  // Convert the array to a string, with a space between each class name.
  $classes_str = implode( ' ', $cleaned );
  
  return $classes_str;
}

That function also does the work of cleaning all the classes. We can use it for all the template tags in our theme or plugin.

This simple routine can help us keep all of our custom functions SMACSS-ready, with minimal extra code.

Overrides

All of the techniques we’ve discussed so far have been very efficient in terms of code: payoff ratio. That is, we’re getting a lot of SMACSS goodness with relatively little code, and that was what inspired me to write this article. However, there is another avenue we might take, which is overriding core functions with our own, adding our SMACSS classes on the way.

I probably would not recommend doing this just for the same of adding SMACSS classes because it can be a lot of code and more code means more bugs. Rather, if there is some element of the default WordPress output that already needs to be revamped for our project (perhaps for design or functionality reasons), we should seize that opportunity to class it up.

searchform.php

One of the first overrides that theme developers wade into is the search form. The way to do this is add a new search form to our theme in a file with the reserved name of `searchform.php`. However, I want to be able to re-use my custom search form elsewhere and I don’t like to define functions outside of `functions.php`, so my `searchform.php` looks like this:

/**
 * sjf search form.
 *
 * @package WordPress
 * @subpackage sjf
 * @since  sjf 1.0
 */

echo sjf_get_search_form();

My sjf_get_search_form() lives in `functions.php` and looks like this:

/**
 * Returns a WordPress search form.
 *
 * Accepts arguments to inject CSS classes into the form, which this theme uses 
 * in order to comply with SMACSS.  Passing dynamic class values for each 
 * instance would not be possible with the normal use of searchform.php.
 * 
 * @param  array $classes CSS classes for the form.
 * @return string A search form.
 *
 * @since  anchorage 1.0
 */
if( ! function_exists( 'sjf_get_search_form' ) ) {
  function sjf_get_search_form( $classes = array() ) {
    
    $slug = 'sjf-searchform';
    
    // An array of CSS classes for the search form.
    $classes = sjf_prepend_slug( $classes, $slug );
    
    // Grab the search term to use as a watermark.
    $placeholder = esc_attr__( 'Search', 'sjf' );
    if( get_query_var( 's' ) ) {
      $placeholder = esc_attr( get_query_var( 's' ) );
    }

    $action = esc_url( home_url( '/' ) );
    
    $search_for      = esc_html__( 'Search for:', 'sjf' );
    $search_for_attr = esc_attr__( 'Search for:', 'sjf' );
    
    $submit = esc_html__( 'Submit', 'sjf' );

    $out ="
      <form action='$action' class='$classes $slug sjf-form' method='get' role='search'>
        <label class='$classes-label $slug-label sjf-label' for='s'><span class='screen-reader-text'>$search_for</span></label>
        <input id='s' type='search' title='$search_for_attr' name='s' value='$placeholder' class='$classes-input $slug-input sjf-input'>
        <input type='submit' value='$submit' class='$classes-submit $slug-submit sjf-submit'>
      </form>
    ";

    return $out;

  }
}

That’s one classy form! The form itself and every element within is accessible in the SMACSS-y way that I prefer. I think most developers are in the habit of replacing the default form, so it’s probably not too much trouble to hit it with some classes along the way.

The same can not necessarily be said for other components. I undertook a similar effort for the entire comments template, and emerged 500 lines later. Doable, but not necessarily worthwhile.

Nav menu walker classes: PHP classes

As I mentioned before, the classes applied to nav menu items are exposed to filtering, but if we want even finer control, perhaps to apply classes to the links themselves, the submenus, or perhaps some glyphs from our icon solution of choice, WordPress has a way for us to deep-dive. The walker class is heady stuff, probably warranting a dedicated tutorial. There are some fine examples online and just know that that’s an avenue for applying CSS classes to menus at the most fine-grained level imaginable.

Class dismissed

I’ve found that over time, the hardest part of a project to keep tidy is the stylesheet. Functions, page-templates, and sidebars may come and go, but stylesheets seem to go on forever, warts and all! I think investing in some classery up front can go a long way in keeping things maintainable down the road.