DiggingIntoWordPress

by Chris Coyier & Jeff Starr

Custom CSS Per Post

Posted by on

I’ve long been a fan of “art directing” posts. That is, to apply unique CSS styling to an individual page of content when the situation calls for it. In the past, I’ve used the Art Direction plugin and I even created a screencast on using it.

As it turns out, there is a major problem with the art direction plugin. Using it with any caching plugin will result in a crazy epic meltdown of your site. Without too much gory detail, in trying to cache my blog CSS-Tricks, I tried all the major caching plugins (DB Cache, WP Super Cache, W3 Total Cache) and ultimately it would trash my WordPress database and serve up white pages. Very not good. The happy ending is that Frederick Townes from W3 Edge and creator of the W3 Total Cache plugin helped me out by patching the art direction plugin and getting CSS-Tricks cached with W3 Total Cache. I would love to release the updated code, but it’s not my code to release. We managed to get in touch with the original author, who said he planed to eventually update it but didn’t sound too particularly interested in the patch.

SO, with that extensive backstory, what is a poor fellow to do if they want to apply custom CSS to pages TODAY? Couple ideas, read on.

style-XXXX.css

One of my “WordPress Wishes” was that you could drop an appropriately named CSS file into a theme and it would recognize it and apply itself to the proper post. For example, /art-direction/style-XXXX.css, where XXXX is the ID of the post.

A reader named Hassan commented with a cool idea for the functions.php file:

function artStyle() {
    global $post;
    if (is_single()) {
        $currentID = $post->ID;
        $serverfilepath = TEMPLATEPATH.'/art-direction/style-'.$currentID.'.css';
        $publicfilepath = get_bloginfo('template_url');
        $publicfilepath .= '/art-direction/style-'.$currentID.'.css';
        if (file_exists($serverfilepath)) {
            echo "<link rel='stylesheet' type='text/css' href='$publicfilepath' media='screen' />"."\n";
        }
    }
}
add_action('wp_head', 'artStyle');

I tested it out and it works great. To use it, simply create a new folder called “art-direction” in your theme. Then to style any particular Post or Page, just drop a file in that folder named style-XXXX.css where XXXX is the ID of the Post or Page. When that Post or Page loads, WordPress will look for a file of that name. If it exists, it will load in in the head section.

Custom Panel

Reader Kerrick Long commented another cool solution. Similar to the Art Direction plugin, this adds an input area below the main content writing area. For the functions.php file:

//Custom CSS Widget
add_action('admin_menu', 'custom_css_hooks');
add_action('save_post', 'save_custom_css');
add_action('wp_head','insert_custom_css');
function custom_css_hooks() {
	add_meta_box('custom_css', 'Custom CSS', 'custom_css_input', 'post', 'normal', 'high');
	add_meta_box('custom_css', 'Custom CSS', 'custom_css_input', 'page', 'normal', 'high');
}
function custom_css_input() {
	global $post;
	echo '<input type="hidden" name="custom_css_noncename" id="custom_css_noncename" value="'.wp_create_nonce('custom-css').'" />';
	echo '<textarea name="custom_css" id="custom_css" rows="5" cols="30" style="width:100%;">'.get_post_meta($post->ID,'_custom_css',true).'</textarea>';
}
function save_custom_css($post_id) {
	if (!wp_verify_nonce($_POST['custom_css_noncename'], 'custom-css')) return $post_id;
	if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) return $post_id;
	$custom_css = $_POST['custom_css'];
	update_post_meta($post_id, '_custom_css', $custom_css);
}
function insert_custom_css() {
	if (is_page() || is_single()) {
		if (have_posts()) : while (have_posts()) : the_post();
			echo '<style type="text/css">'.get_post_meta(get_the_ID(), '_custom_css', true).'</style>';
		endwhile; endif;
		rewind_posts();
	}
}

Custom CSS Write Panel

As written, it’s more limiting than the Art Direction plugin as it is for CSS only rather than “anything” (e.g. JavaScript), although that would be a fairly trivial adjustment to what gets echoed out. The Art Direction plugin also allowed the CSS to be applied in other places the post might be output, for example, archives pages, whereas this does not. But to be honest, I never used that anyway.

And so…

Two pretty sweet and totally function solutions by Digging Into WordPress readers. Awesome. Huge thanks to Hassan and Kerrick! I would also love to see the Art Direction plugin updated as well, if nothing else, because I’m sure people download that plugin every single day from the plugin repository and have caching going on their blogs and end up in the same sore spot I was.

30 Responses

  1. Php is not my thing yet and I am an novice to wordpress but I am reading DIWP the book. The code in your solution is minimal but can you do the same thing by creating a custom post page that uses a specific header which calls the stylesheet?

    • You could, but that’s an awful lot more work than either of those two solutions. The more templates you maintain the harder a redesign is, especially ones with their own head sections.

  2. This is awesome, thank you.

    I installed Art Direction plugin on my site and used it for a post about a month ago…my site crashed and burned every time I opened it in IE 6 & 7. I had recently added a jQuery nav bar & a few other plugins to my site so it was hard to tie it back to this particular plugin.

    Any chance you can simply release the chunk of code that you fixed? I’m not sure what the license is or how much code you modified, but maybe that’s an option.

    Again, huge thanks for these alternate options though!

  3. Interesting solutions, both of them and they probably can be used in conjunction. Sometimes you just need smaller classes added, sometimes you need longer styles, where a .css file would be cleaner in the end.

  4. I personally hate file management, which is why I suggested the Custom CSS box on the page – but either choice is awesome, based on your personal preferences.

    Thanks for writing this article, and including my suggestion – it made my day!

  5. Thanks Chris for these. I currently have a custom key in my head that I can use to call a .css I manually upload to the server in the theme’s folder.

    The larger question here is how to design an overall theme that can accomodate some posts being art directed. It comes down to making your theme modular in some respects, so that you can swap out a standard post for a blank slate in which to create something unique.

    I use an if/then statement to house the default layout, and then if it is in a category of ‘unique’ I have a very simple I simple have the_content and thats it.

    • I like the blank slate idea. I think a good way to do that would be to create a CSS file called like blank-slate.css, and <link> to it first, then write your custom CSS.

      On my personal blog, I have a few CSS files, for example white.css, where I can use like that. A group of styles to make one sweeping change to use as a a staring point.

  6. What’s the difference between the Art Direction plugin and creating Custom Fields to input css into individual posts?

    What I’ve done for my portfolio website is run the below code on the header.php file and create a custom field relevant to the Custom Field name. It’s quite simple and doesn’t need a plugin to do that.

    <?php if (is_single()) {
    $css = get_post_meta($post->ID, 'css', true);
    if (!empty($css)) { ?>
    <style type="text/css">
    <?php echo $css; ?>
    </style>
    <?php }
    } ?>

    • The second code snippet for Custom CSS does, in fact, use custom fields. Because the custom field’s name begins with and underscore, it’s hidden from the custom fields dropdown – which means we need to create an input on the post/page edit screen for it.

      In my opinion, it’s easier to find the box labeled “Custom CSS” and type in it, than it is to type “custom_css” into a custom field key, and then fill out its value.

      • I’m in agreement with it being easier to find the labeled “Custom CSS” field and simply enter the relevant value. But having avoided naming a Custom Field that starts with a ( _ ) underscore has kept my Custom Fields on the drop down menu. Which is not laborish to say after you’ve set up the backend work.

        But that is something I’m looking forward to simplify on my current portfolio theme.

    • Yep I suppose that would work. I was kind of thinking that in order to use get_post_meta you need to be inside the loop and running a loop in your header.php file is kinda weird. But if it’s a single page I guess you don’t really need to run a loop.

      This would also (like the solutions above) be limited to the individual post or page instead of also having the option for the CSS to be available anywhere the post appears (e.g. archives)

      • Well for my Archive’s page I run two loops which at first when trial and testing thought that couldn’t be done, but viola it works!

        For the header (copied from the actual header.php to the archive.php file which doesn’t call the header.php include file) I run this loop code for the archives individual post background image:

        <?php if (have_posts()) : ?>
        <style type="text/css">
        <?php while (have_posts()) : the_post(); ?>

        <?php $cssWorks = get_post_meta($post->ID, 'cssWorks', false); ?>
        <?php foreach($cssWorks as $cssWorks) {
        echo $cssWorks;
        } ?>

        <?php endwhile; ?>
        </style>
        <?php endif; ?>

        Then below on the archive.php is the posts loop which excludes all other Custom Fields that I’ve created and only output the Custom Field not mentioned:

        <?php while (have_posts()) : the_post(); ?>
        <li <?php post_class() ?>>
        <h2 id="post-<?php the_ID(); ?>"><a href="<?php the_permalink() ?>" rel="bookmark" title="<?php the_title_attribute(); ?>"><?php the_title(); ?></a></h2>
        <?php global $more; $more = 0; ?>
        <?php if ( $keys = get_post_custom_keys() ) {
        echo '';
        foreach ( (array) $keys as $key ) {
        $keyt = trim($key);
        if ( '_' == $keyt{0} || 'post-img' == $keyt || 'materials' == $keyt || 'extras-img' == $keyt || 'css' == $keyt || 'cssWorks' == $keyt || 'cssHome' == $keyt )
        continue;
        $values = array_map('trim', get_post_custom_values($key));
        $value = implode($values,', ');
        echo apply_filters('the_meta_key', "
        <p>$value</p>
        \n", $key, $value);
        }
        echo '';
        } ?>
        <?php endwhile; ?>
        <?php endif; ?>

  7. Thanks, Chris. Add me to the list of folks that would love to see the Art Direction plugin updated. I’ve always wanted to use it, but was too afraid of a DB meltdown that I’m unable as of yet to fix.

    Props to Hassan as well, for a clean fix.

  8. Isn’t this plugin suitable for this task?
    Custom CSS and JS

    Also found this one: Easy JS/CSS and there’s probably others in WP Repository.

    The way I do it now is putting these posts in a separate (hidden) category and call this in either wp_head or wp_footer with

    if (in_category('hidden-cat-name')) {
           // include whatever
    }

  9. PS Since the Art Direction plugin is GPL anyway, why don’t your or Frederick just fork it as ‘Art Direction – Cache Enhanced’ and be done with it ;) You’re helping countless others who have no idea this solution exists otherwise.

  10. Is there a way to extend Hassan’s version up to categories and possibly tags?

  11. I haven’t heard anything from you Chris and I appreciated Fredrick patching the plugin, however it was found that his patch also caused a few headaches for other plugins.

    I’ll be updating the plugin this week. Thanks for the heads up. Next time you need any help, just send me a note yourself and I’ll try to more swiftly patch it up.

    • Hey Noël, that’s great news!

      I was in a strange middle-man position here which is why you didn’t hear from my directly. “Hey, can you patch a plugin with code that isn’t mine that you already know about…”

      But anyway yeah, if you have ideas worked up to patch the plugin in an even better way, that’s great I’ll surely be using it.

  12. Really useful as usual Chris! I love this kind of solutions, and I believe that the less plugins the better. Personally, I have gotten away with just using body_class(); and then using CSS hierarchy. Also, David Walsh posted a great “extension” of this idea in one of his latest posts.

    • Yeah man I’ve long been a fan of “IDing the body”. But where do you put the custom CSS then? You could put it in the main stylesheet but if you do a lot of post customization, that’s going to bloat it pretty quickly. The trick here is inserting custom CSS only for particular pages (keeping it out of the main stylesheet).

      • You are right, in the cases where you do do a lot of customization it would make more sense. I also thought the idea of looking for stylesheets based on IDs was really clever and I was thinking that would sky rocket the number of HTTP requests and then I thought: hmm, wait a minute, this is only for a particular post!

  13. Great Job guys .. an awesome way of creating more style ..

  14. I was wondering if this could be used to make a custom homepage for a wordpress site. I am designing a site where a client wants a wordpress but wants his homepage to look like a normal site not a blog.

  15. I was wondering if anyone out there had seen this plugin:

    Per-Post CSS

    It actually works really well.. Unfortunately since no work has been done on it for a few years it causes some small w3c errors, but I am sure something someone can easily fix.

    http://trevorcreech.com/geekery/wordpress/per-post-css/

    If there is anyone out there that can take this code and make it work without the errors in w3c that would be awesome. Well at least for me.
    I posted here because it seems like a decent group of people wanting to solve a common condition. I just hope I did not read it wrong.

    Kind regards,
    Saxamo

  16. Sparky Teaching March 5, 2010

    Thanks, as always, Chris… I’ve got the latest update of Art Direction on my site and, for some reason, I’m getting those blank screens… I’ll try your solutions. Thanks for putting the time in.

  17. Aaron Weyenberg March 7, 2010

    Nice solutions, Chris. I’m trying out the first one myself.

    I was scratching my head for a while, though, wondering why it wasn’t working until I realized there’s a small bug in the code for functions.php (single quote missing):

    type=text/css'

  18. I have an update to the function if you are using a child theme.
    The important parts being how you get the child theme directory absolute server path and url.

    <?php
    function artStyle() {
       global $post;
       $currentID = $post->ID;
       $serverfilepath = get_stylesheet_directory().'/art-direction/style-'.$currentID.'.css';
       $publicfilepath = get_bloginfo('stylesheet_directory');
       $publicfilepath .= '/art-direction/style-'.$currentID.'.css';
       if (file_exists($serverfilepath)) {
       echo "<link rel='stylesheet' type='text/css' href='$publicfilepath' media='screen' />"."\n";
       }
    }
    add_action('wp_head', 'artStyle');
    ?></pre>

Comments are closed. Contact us with any critical information. Thank you!

Code is poetry