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 withpod-
. - Components that have a parent-to-many-children relationship take on a singular/plural naming scheme, as in
pod-item
andpod-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:
- Really quick and easy filters to alter what WordPress outputs in just a few lines of code.
- Telling WordPress to apply certain classes to objects that we register.
- Developing habits in our own custom functions (either plugin stuff or functions.php) such that they can easily and manageably take on bespoke classes.
- 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
orwidget_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 ofsjf-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.
Hey man, GREAT article! I was just thinking about this, with the idea of redeveloping my website from scratch. I was looking at BEM too. As I was reading this I thought that a lot of it can be achieved using page / post templates too. Based on the WP hierarchy the template names are output as classes, which I’m sure you know.
My personal opinion is that if this sort of thing is important to a developer, and you have to go to such lengths of hacking and overriding WordPress functionality to get it to do as we like, then perhaps WordPress may not be the most ideal solution.
I don’t think this sort of thing is the negative sort of hacking and overriding, it’s using WordPress’s built-in hooks that are specifically designed to be used like this.
Very nice article
I was wondering how deep editable is wp, it’s seem to be a lot to do when there’s more things to change. However, it’s never just-like-that to make your web work/look good.
Kudos for writing the article, but this just seems like a really bad idea! I’ve read carefully through the whole article and I still don’t really get what you’re doing – and THAT’s the problem.
If you’re going lone wolf on this project then fine – whatever floats your boat. But if someone else is ever going to pick up your code – then this is a total gotcha!
In my view, all code should strike a balance between performance and readability – and this seemingly serves neither.
It reminds me of a story about a British developer who refused to use the American spelling ‘color’ in his CSS, so created a script to convert it from his preferred spelling ‘colour’ at compile-time.
Why not just use ‘color’. Why not just use the WordPress classes? It smacks (SMACSS – geddit?) to me a little bit of OCD in coding (OCDing?). Ok, enough puns…
SMACSS is great.
DRY is great.
But remember, KISS!
(Not the American rock band)
Hey Steve,
Thanks for reading my article!
I agree with you the KISS is a great virtue, and I wrote this article as a way to achieve KISS, not as a way to forfeit it.
I believe that the most challenging form of complexity on web projects comes from inappropriate CSS selectors, especially after a few rounds of client revisions come through. SMACCS helps you keep things simple in your stylesheet.
If you’re saying my php functions are complex, I’d appeal and say most of them are only 5-10 lines in length. If they seem obscure, don’t worry, this article is not an introduction to php functions per se.
If you don’t get what SMACCS is after reading this article, don’t worry, this isn’t a SMACCS tutorial. There’s a great one of those already, linked in the article.
If you don’t get what WordPress classes are and how to alter them after reading this article, my apologies, I missed the target.
To your point about this being a gotcha if you work with a team, what do you mean? SMACCS is a well-known convention. I’d much rather walk into a SMACCS project than into someones pet naming scheme.
To your point about performance, what are you seeing here that’s mal-performant? A few extra classes in the markup? A little extra logic on the server? I don’t think there’s any relevant performance downfall here.
I agree that it’s possible to introduce too much complexity in pursuit of SMACCS-ifying WordPress, and I acknowledge some examples of that in the article.
Best of luck with your work, and I hope that if I am lucky enough to contribute here again, you find it more useful.
Nice code
I may be wrong, but it seems like there might be a mistake in the widgets code example:
Shouldn’t instances of
$logged_in
actually be simply$logged
to match the variable:As in this:
class='widget $slug-widget $slug-widget-$logged_in $slug-widget-%s'
Should be this:
class='widget $slug-widget $slug-widget-$logged $slug-widget-%s'
Thanks!