A WordPress Class Template Tag to Rule Them All

Last week, on Twitter…

Yes, I know, that’s the beginning of a terrible blog post. But really, a tweet by Jonathan Christopher piqued my curiosity last week:

SMACSS? I hadn’t heard of it and figured it was probably some Mac-only app, but looked it up anyway. Lo and behold, it was actually interesting. I haven’t really changed the fundamentals of the way I write CSS for a couple of years, don’t use a pre-made grid, and have yet to adopt a pre-processor.

In any case, it got me to thinking and while playing around with some of the concepts, I realized how often I have to override or duplicate some CSS to create different layouts for the various content templates in WordPress and how many rules actually need to be tweaked to make seemingly minor changes.

During the course of all this, I thought it would be nice to be able to modify the classes assigned to standard elements in the header, footer, and sidebar templates without having to create new templates or template parts, since the markup doesn’t really need to change (I’m veering away from SMACSS a bit here, but that’s what got us to this point and it’s worth checking out).

WordPress has a couple of template tags for filtering the classes that are added to the body element and posts. While something similar could certainly be implemented for each element I wanted to filter, creating multiple template tags and filters to do something as simple as change a class posed way too high a burden.

What I really wanted was a simple way to easily declare classes, and maybe even remove them, on the important layout elements in the various template parts not otherwise accessible from the current template I was working on.

Maybe a generic template tag for adding classes to any important element that might change between templates would work?

A Contrived Example

Taking the basic structure of the default Twenty Twelve theme and modifying it a bit for demonstration purposes, here’s how we might mark up various elements using the new template tag:

<body <?php blazersix_class( 'body' ); ?>>
<div id="page" <?php blazersix_class( 'page', array( 'hfeed', 'site' ) ); ?>>
<header id="masthead" <?php blazersix_class( 'header', 'site-header' ); ?>></header>
<div id="main" <?php blazersix_class( 'main', 'wrapper' ); ?>>
<div id="primary" <?php blazersix_class( 'primary', array( 'three-fourths' ) ); ?>>
<article <?php blazersix_class( 'post', array( 'default-post-class' ) ); ?>></article>
</div>
<div id="secondary" <?php blazersix_class( 'secondary', array( 'widget-area', 'one-fourth' ) ); ?>></div>
</div>
<footer <?php blazersix_class( 'footer' ); ?>></footer>
</div>
</body>
  1. The first parameter is an id so that we know which element is being referenced in our filter.
  2. The second parameter is optional and is a list of default classes as an array or space-separated string. If it’s not declared, the id is used as a default class.
  3. The third parameter not shown here takes additional arguments to change the behavior (echo, post ID for reference).

You might also be wondering about the body_class() and post_class() tags that ship with WordPress; the blazersix_class() template tag takes those into account and simply adds the defaults to the list of classes generated by WordPress.

Modifying the Defaults

Now let’s say I have a “Super” page template where I wanted to modify some of the classes in the template parts, but otherwise didn’t need to change their markup structure. I can now use a single filter to modify any class added using blazersix_class() by placing it above the call to get_header() in the Super page template (or add it in an appropriate action in functions.php if you’d like).

<?php
/**
* Template Name: Super
*/
add_filter( 'blazersix_class', 'blazersix_super_page_template_classes', 10, 2 );
function blazersix_super_page_template_classes( $classes, $id ) {
$classes = array(
'body' => 'my-super-page-template-class',
'page' => 'clearfix',
'main' => 'new-main-class --wrapper',
'primary' => 'two-thirds',
'secondary' => 'one-third',
'footer' => 'wide-footer'
);
return $classes;
}
get_header();
?>

All the modifications here are arbitrary, they’re just to demonstrate what’s possible. Notice how the “wrapper” class was removed from the “main” element? Pretty cool!

The resulting markup would look like this:

<body class="{{wordpress classes}} my-super-page-template-class">
<div id="page" class="hfeed site clearfix">
<header id="masthead" class="header site-header"></header>
<div id="main" class="new-main-class">
<div id="primary" class="one-third">
<article class="{{wordpress-classes}} default-post-class"></article>
</div>
<div id="secondary" class="widget-area one-third"></div>
</div>
<footer class="footer wide-footer"></footer>
</div>
</body>

Here’s the template tag to drop in functions.php that does it all.

Feel free to modify it to suit your needs and let me know if you find it useful or have any feedback about better ways to do something like this.

<?php
/**
* WordPress template tag to allow for CSS classes to be easily filtered across templates.
*
* @author Brady Vercher (twitter.com/bradyvercher)
* @link http://www.blazersix.com/blog/wordpress-class-template-tag/
*
* @param string $id Element identifier.
* @param array|string $classes Optional. List of default classes as an array or space-separated string.
* @param array|string $args Optional. Override defaults.
* @return null|array Null when displaying, otherwise an array of classes.
*/
function blazersix_class( $id, $classes = array(), $args = array() ) {
$id = sanitize_key( $id );
$args = wp_parse_args( (array) $args, array(
'echo' => true,
'post_id' => null
) );
if ( ! empty( $classes ) && ! is_array( $classes ) ) {
// Split a string.
$classes = preg_split( '#\s+#', $classes );
} elseif ( empty( $classes ) ) {
// If the function call didn't pass any classes, use the id as a default class.
// Otherwise, the calling function can pass the id as a class along with any others.
$classes = array( $id );
}
// Add support for the body element.
if ( 'body' == $id ) {
$classes = array_merge( get_body_class(), $classes );
}
// Add support for post classes.
if ( 'post' == $id ) {
$classes = array_merge( get_post_class( '', $args['post_id'] ), $classes );
}
// A page template should set modifier classes all at once in the form of an array.
$class_mods = apply_filters( 'blazersix_class', array(), $id, $args );
if ( ! empty( $class_mods ) && isset( $class_mods[ $id ] ) ) {
$mods = $class_mods[ $id ];
// Split a string.
if ( ! is_array( $mods ) ) {
$mods = preg_split( '#\s+#', $mods );
}
foreach( $mods as $key => $mod ) {
// If the class starts with a double minus, remove it from both arrays.
if ( 0 === strpos( $mod, '--' ) ) {
$unset_class = substr( $mod, 2 );
unset( $mods[ $key ] );
unset( $classes[ array_search( $unset_class, $classes ) ] );
}
}
$classes = array_merge( $classes, $mods );
}
// Last chance to modify.
$classes = apply_filters( 'blazersix_classes', $classes, $id, $args );
$classes = apply_filters( 'blazersix_classes-' . $id, $classes, $args );
if ( $args['echo'] ) {
echo 'class="' . join( ' ', array_map( 'sanitize_html_class', $classes ) ) . '"';
} else {
return esc_attr( $classes );
}
}
view raw html-class.php hosted with ❤ by GitHub