I’ve had an on/off relationship with WordPress over the years but recently it’s proved to be the right tool for a couple of projects. This has led me to revisit one of my main gripes with WP – the state of the theme markup, particularly the header and footer of an average WordPress site.
A quick browse at the source of most WordPress sites reveals quite a mess. For a start there are endless link and meta tags in the header. At a glance you can see the WordPress version number and usually clues to which plugins are in use.
If you’re into your RSS and pings and trackbacks then maybe this is all great and this article is probably not for you. Personally though, blogs are not my thing. When I need WordPress I need it as a CMS for small business websites, and I want clean markup.
Also, from a security perspective version numbers and plugin names are potential attack vectors. If vulnerabilities are discovered in a certain version then the site is very exposed.
A lot of the issues here span security and markup. I’ll focus just on markup for now and discuss security in a later post.
I’m sure you’re familiar with creating valid, semantic markup for the inner portions of your templates so I won’t get into that.
Outside of that, it’s a simple enough task to take an axe to a theme and take out all the WP functions that fill up the page with cruft, but that’s not a good solution. I want to give WP control over tags that are injected into the header and footer, I just want to calm it down a bit and take back control.
Preparing a DRY solution
What we’re about to do:
- Will need to be initiated from your theme functions.php file
- Will need to be applied to all your templates, probably with slight variations
- Should not be applied to the Control Panel
I’m also going to set a rule you may or may not agree with: No static assets get loaded from the Theme folder. At all. End of story. I’m making a top-level folder called assets and it all goes in there.
I’ll now go over the individual steps we’re going to take in detail. The following assumes using the Twenty Ten theme as a starting point.
Removing surplus LINK tags
The first thing I do is remove any LINK tags in header.php.
There are many remaining in the output though and they can be traced back to wp-includes/default-filters.php. There are a LOT of them. What you do and don’t want will depend on your site, but anything attached with add_action() can be removed with a matching call to remove_filter().
For example, to get rid of the WordPress version included in your head, we run the following:
Note: It’s sometimes necessary to add a third parameter that matches the priority of the original addition. Theme Wrangler takes care of that annoyance for you.
Controlling script loading
It’s unlikely (for me anyway) that the version of JQuery coming with WordPress is the version the site needs. Similarly, I’ve absolutely no use for the l10n JS file that WP3 includes. These can be removed as follows:
wp_deregister_script( 'jquery' ); wp_deregister_script( 'l10n' );
You can then re-instate JQuery with a similar command
wp_enqueue_script() provides more toys than just that though, the command I actually run is:
wp_enqueue_script( 'jquery', 'http://ajax.googleapis.com/ajax/libs/jquery/1.6/jquery.min.js', array(), 1.6, true);
You can see there are five parameters here:
- The label or ‘handle’ for the script
- The full path to the script
- An array of handles of other scripts that this one depends on. I don’t use this feature but I need the later parameters so I have to pass something
- The version number of the script
- Whether or not to load in the footer of the site instead. Yes please!
You can add as many scripts as you like this way provided the handles are unique. It’s very useful for selectively loading scripts that are only required by a particular template.
Controlling CSS loading
wp_enqueue_style( 'default', '/assets/css/default.css, array(), '2011-06-15', 'all');
In this case parameters are as follows:
- Label or handle
- Full path
- Dependencies (none)
- Version (date used for example)
- Stylesheet Media
As per scripts, you can add more of these to certain templates as needed.
A note on Plugins
The techniques described above will allow plugins to continue to insert their CSS and JS into your theme whilst keeping your markup as clean as possible.
However, I do suggest you give some serious thought to not allowing plugins to do this. As pointed out in the earlier example, default paths for plugin CSS and JS provide a potential attack vector.
If you have the means and knowledge, I strongly suggest moving and renaming the static assets of your plugins to your assets folder. In many cases it could be as simple as de-registering and then re-registering the assets used by the plugin.
Pulling it all together
The Theme Wrangler library provides a simple way to quickly apply all of the above techniques. All you have to do is include the file in your functions.php and define your site defaults. A single line of code is then added to each of your templates and everything is taken care of.
See the example below or read the full documentation.
A working example
It probably won’t surprise you to find out the page you’re reading is an example of these techniques in action. View the source and check it out.
Here is a simplified example of the theme source code:
<?php require_once(TEMPLATEPATH.'/themewrangler.class.php'); $settings = array( 'available_scripts' => array( 'jquery' => array('http://ajax.googleapis.com/ajax/libs/jquery/1.6/jquery.min.js','1.6'), 'global' => array('/assets/js/global.js',1), 'cycle' => array('/assets/js/jquery.cycle.lite.js','1.3'), 'fancybox' => array('/assets/fancybox/jquery.fancybox-1.3.1.pack.js','1.3.1'), ), 'default_scripts' => array('jquery'), 'available_stylesheets' => array( 'dmlogic' => array('/assets/css/dmlogic.css',1), 'addons' => array('/assets/css/addons.css',2), 'documentation' => array('/assets/css/documentation.css',1), 'fancybox' => array('/assets/fancybox/jquery.fancybox-1.3.1.css',1), 'blog' => array('/assets/css/blog.css',1), ), 'default_stylesheets' => array('dmlogic','lato'), 'remove_from_head' => array( 'rsd_link', 'wlwmanifest_link', 'wp_generator', 'rel_canonical', 'index_rel_link', 'parent_post_rel_link', 'start_post_rel_link', 'adjacent_posts_rel_link', 'adjacent_posts_rel_link_wp_head', 'wp_shortlink_wp_head', 'wp_shortlink_wp_header', 'feed_links', 'feed_links_extra', ), 'deregister_scripts' => array('jquery','l10n') ); Themewrangler::set_defaults( $settings );
<?php Themewrangler::setup_page(); get_header(); if ( have_posts() ) while ( have_posts() ) : the_post(); ?>
<?php endwhile; ?> <?php get_footer(); ?>
<?php the_title(); ?><?php the_content(); ?>
Nice and simple huh?
If you like the direction this is heading, there’s plenty more you can do. Common functions such as wp_nav_menu() and get_the_category_list() come with their own glut of divs and class names that really aren’t needed.
When you stop to consider what is actually produced, in most cases these functions can be replaced with a much simpler function that performs a basic database query, runs a loop and generates some code.
Theme Wrangler can easily be extended to house functionality such as this.