Customising the WordPress body Tag

A recent post on Smashing Magazine (look for the section titled “Add Body Classes Based on Special Conditions”) got me thinking about how I use the body tag in WordPress.

When marking up a design I will often add the “page name” to the body as a class or id. I mainly use this to style current page links in a navigation menu. For example in BreadApp I do the following to highlight the current section:

1
2
3
4
5
6
7
/* Nav hover styles */
header nav .primary li a:hover,
#latest header nav .primary li a.latest,
#design header nav .primary li a.design,
#development header nav .primary li a.development,
#mobile header nav .primary li a.mobile,
#tags header nav .primary li a.tags { background: #E9E7DC url(/img/light_background_tile.jpg); color: #545351; text-shadow: none; }

It’s a common technique and a useful aid to targeting certain elements based on the current page. Although WordPress does automatically add a number of classes to the body tag it does require you to have specific knowledge of things like the template name and page id.

Ugly Generic Output

This approach doesn’t scale that well and means you often have to revisit your carefully crafted CSS file to replace nice and relevant selectors (e.g. .about) with far more generic ugly ones (e.g. .page-78).

For example here’s the classes added to the body tag on my about page when using the body_class() function:

1
page page-id-2 page-template page-template-default logged-in admin-bar

Like most people I use “nice permalinks” in WordPress. This gives each post or page a nice “page name”. In the case of my about page it is simply “about”. Wouldn’t it be nice if we could add this “page name” to our body class instead of “page-78″?

It’s Actually Quite Easy

Following on from the example in the Smashing Magazine I created my own function in functions.php that appends the post or page “name” to the list of classes automatically added. Here it is:

1
2
3
4
5
6
add_filter('body_class', 'add_slug_to_body_class');
function add_slug_to_body_class($classes) {
	global $post;
    	$classes[] = $post->post_name;
	return $classes;
}

Here’s a quick explanation of how this works:

  1. We use the add_filter function to “hook” into the native WordPress body_class function. This means that every time it is called our function add_slug_to_body_class will get called as well.
  2. The main function add_slug_to_body_class uses the $post data ($post contains all the information relating to the current page or post) to append the post_name (or slug as it is often refered to) to the classes it has already compiled. This is then returned to the original function which will output the HTML.

Going back to my previous example of my about page, which has a page name of “about”, the body class will now look as follows:

1
page page-id-2 page-template page-template-default logged-in admin-bar about

This means no more changing of our carefully crafted CSS file. Nice and elegant!

This approach has helped me with the transition from my flat HTML designs to WordPress themes, I hope you find it useful too. If you have any thoughts or comments do let me know.

11 Comments

I’ve been doing the same thing in my themes, but in a more hacky way in header.php, I had no idea there was a function!

*Goes and adds this straight into default theme*

Cheers Keir!

It was news to me too! Hope it comes in handy.

There’s also a hook: ‘admin_body_class’ if you need to perform a similar trick on admin pages.

Thanks Dan, I can see that coming in handy too. There’s so many hooks I have yet to discover!

I’ve used this hook quite a bit in the past for various purposes – definitely better than creating separate header files and the like.

On a similar note, I’m working on a WordPress Multisite build at the moment where I have a base theme with individual blogs inheriting the base theme and then adding to it with a child theme. I used the same function to add a class which determined which blog I was currently on:


function additional_body_classes($classes) {

global $blog_id;
$this_blog_details = get_blog_details( array( 'blog_id' => $blog_id ) );
$this_blog_path = str_replace('/','',$this_blog_details->path);

if(!empty($this_blog_path)){
$this_blog_class = 'style-' . $this_blog_path;
} else {
$this_blog_class = 'style-root';
}

$classes[] = $this_blog_class;
return $classes;
}

Pretty handy way of sharing CSS and overriding it in the blog specific CSS file as applicable.

I’ve been using the ‘Ambrosite Body Class Enhanced’ plug-in from the off-set to achieve something similar.

I’ve had it from my very first build so I’ll admit that my view is a little blurred on what it does and what WordPress is already handling! From what I’m reading, it seems that it achieves something similar.

Like your’s, it adds the post/page slug in the body class…eg. pagename-how-to
But it also adds category information the single posts…eg. category-news

But, there is one inherent problem with relying on names instead of ids…meddlers like to change names / URLs.

The client – or their SEO consultant(s) – may wish to rename stuff; whether that’s categories or post/page names or URLs. If they don’t consult you first, the site may fall over if you rely on name-based body classes.

For the sake of some slightly nicer CSS, it might not be worth the headache from the fallout of a meddler.

Genius idea! That’s definitely going to come in handy in the future. Thanks for sharing.

It’s funny, I didn’t even think there would be a plugin for it! I like the idea of it adding in things such as the category, good idea.

Great point about the fact that these things can be changed, it’s certainly an issue and you are spot on when you say it would break things completely.

I have only used it on personal projects and a couple of sites where the theme won’t be touched even though it’s for a client. Really good point though and well worth considering.

This is a great technique Keir. A couple of points:

Firstly, you should be sanitising the values so that they are guaranteed suitable for use as an attribute value. Pass them through sanitize_html_class() e.g.

1
$classes[] = sanitize_html_class( $post->post_name );

Secondly. your code doesn’t check that there is a single post or page, it should, otherwise there will likely be a warning produced, and a little unnecessary error handling by PHP, when, for instance, on an archive page.

Thirdly, standard WordPress already adds classes like category-slugs, tag-slugs, etc to the body class as mentioned by one of your respondents . See http://codex.wordpress.org/Function_Reference/body_class for reference.

In the past I have also added parent page classes when displaying a child page.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
add_filter('body_class', 'add_slug_to_body_class');
function add_slug_to_body_class( $classes ) {
	global $post;
	if ( is_page() ) {
		if ( $post->post_parent ) {
			_get_post_ancestors( $post );
			foreach ( $post->ancestors as $ancestor) {
				$p = get_post($ancestor);
				$classes[] = sanitize_html_class( $p->post_name );
			}
		}
	}
	if ( is_singular() ) {
		$classes[] = sanitize_html_class( $post->post_name );
	}
	return $classes;
}

Note, this could be quite heavy processing in a deep hierarchy.

Mike

Hey Mike,

Thanks for stopping by :) This is really really helpful feedback. I wasn’t actually aware of the function sanitize_html_class, looks useful.

In terms of checking for safe characters I thought I was OK as the post_name is used in the URL and therefore can’t contain unsafe characters (or so I thought). I’ll certainly be adding in the post and page check too, that hadn’t even occurred to me. I tend to run with a not too verbose warning setting as some plugins have a tendency to be quite loud!

I did actually check the output of the the body_class function and there’s a lot of useful stuff in there. The main reason I wanted to add in the post_name was due to highlighting navigational elements and sadly category and tag classes didn’t help me there. I’ll often base my CSS structure around sections such as about or archive so it makes sense for me to hang the CSS of the body tag in my mockups, this just helped me safe some time porting over to the theme.

Thanks also for the heads up on the ancestral function, I can see that coming in useful sometime.

Hi there thanks for sharing this nice info,finally got an explanation bout how to make use of body class,tha