Remove Unused CSS from your WordPress Site

Learn how to take control of your stylesheets, and reduce the amount of unused CSS you’re serving up with your page loads. This easy-to-follow tutorial is great for developers and non-developers alike.

It might not be the biggest of the warnings in your page speed Lighthouse report, but it’s an easy one to fix… and the process of fixing it will leave you with a website theme that’s easier to manage and tweak in the future.

It’s all about structuring your files nicely.

Remove unused CSS warning
Unused CSS warning

infoIf you put your custom CSS definitions into the “Additional CSS” box in the WordPress Customizer, then this tutorial isn’t for you.

To make use of this tutorial, you should be using a custom child theme so you can edit functions.php and style.css in your child theme’s folder.

How It Will Work

A classic mistake when writing software is to keep adding code to a file so it gets bigger and bigger, until it becomes impossible to find anything in your source code. So what we’re going to do here is make several smaller CSS files, instead of having a massive style.css file.

But… having lots of small files will hurt your page load speed, because the browser has to make more requests to grab all the files from the web server. The way we fix this is to use a minifier. This is a tool that takes CSS/JS files, strips out things like comments & white space, and merges them together into fewer, smushed-up files that are quicker to serve-up. We’ll use an off-the-shelf plugin to do all this for us. I like Autoptimize because it’s easy to configure, works well and it doesn’t get in the way.

For each page that’s served by WordPress, we’ll add some code to our theme’s functions.php file so we’ll only include CSS file(s) that we need for that page. This means we’re serving fewer style declarations on each page-load.

Splitting your CSS Files

An example of how you could split a child theme’s CSS into smaller files…

FileInfo
style.cssEvery page will have this, so include general colours, fonts, header and banner styles… whatever is common to every page on your site.
frontpage.cssStyles specific to your front page. Things like <h2> and <p> elements are usually quite different on a site’s front page than elsewhere on a site, so stick them in a dedicated stylesheet file.
post.cssStyles that are specific to Post single and archive pages. This will have definitions like .entry-content p and .entry-title
my-post-type.cssIf you’ve got a custom post type that needs a different layout to your post archive page (e.g. list instead of grid) then put the declarations in a separate file.
search.cssThe layout declarations for search results.
basket.cssDeclarations that only apply to the WooCommerce basket/cart page, but nowhere else on the site.
Example modular CSS files

These are only examples, but you get the idea. Site-wide Common declarations go into style.css, and everything that’s specific to a post type single/archive, search, taxonomy (or whatever) goes into separate little files.

The PHP Bit

WordPress comes with lots of useful “is_” functions that help you determine what sort of content is being served. We’re going to use some of these in a bit of PHP to dynamically add CSS links to our headers. Open your custom child theme’s functions.php file and look for a function that handles your sites wp_enqueue_scripts action. It will look something like this, depending on your parent theme.

function custom_enqueue_scripts() {
	$theme_version = wp_get_theme()->get('Version');
	$base_uri = get_stylesheet_directory_uri();

	// Enqueue our child theme's style.css file.
	wp_enqueue_style('child-style', $base_uri . '/style.css', null, $theme_version);
}
add_action('wp_enqueue_scripts', 'custom_enqueue_scripts');

All we’re going to do is put some if…then…else logic after we’ve enqueued our child theme’s style.css. This will be different for your site, but here’s an example of how it might look:

function custom_enqueue_scripts() {
	$theme_version = wp_get_theme()->get('Version');
	$base_uri = get_stylesheet_directory_uri();

	// Enqueue our child theme's style.css file.
	wp_enqueue_style('child-style', $base_uri . '/style.css', null, $theme_version);

	if (is_front_page()) {
		wp_enqueue_style('child-front-page', $base_uri . '/front-page.css', array('child-style'), $theme_version);
	} elseif (is_archive() || is_search() || is_tax()) {
		// Post archive, search and taxonomy pages look the similar on our site.
		wp_enqueue_style('child-archive', $base_uri . '/archive.css', array('child-style'), $theme_version);
	} else {
		// ...
	}

	// WooCommerce styles.
	if (!function_exists('WC')) {
		// WooCommerce not installed, so is_shop() and is_product() won't exist.
	} elseif (is_shop()) {
		wp_enqueue_style('child-product', $base_uri . '/shop-catalog.css', array('child-style'), $theme_version);
	} elseif (is_archive() && is_product()) {
		wp_enqueue_style('child-product', $base_uri . '/single-product.css', array('child-style'), $theme_version);
	} else {
		// ...
	}
}
add_action('wp_enqueue_scripts', 'custom_enqueue_scripts');

You can add as many if…then…else blocks as you like. For example…

// Custom styles for a custom post type called "my_custom_post_type".
if (is_archive() && (post_type() == 'my_custom_post_type')) {
	wp_enqueue_style('child-my_custom_post_type', $base_uri . '/my_custom_post_type.css', array('child-style'), $theme_version);
}

Deploy and Test

As long as you keep reloading your browser by bypassing the cache ([Ctrl][Shift][R] in most browsers) then you don’t really need to do anything special to deploy… apart from…

When you’ve finished tinkering with your CSS files, Autoptimze (or whatever minifier you’ve used) needs to know that something in your child theme has changed. Flushing your minifier’s cache will help, but you’ll see that we added $theme_version to our wp_enqueue_style() calls. This is so that WordPress can turn the stylesheel URLs into things like this:

.../my-child-theme/frontpage.css?ver=1.0.14

The number on the end comes from the version info line in your style.css

So all you need to do to make sure that everything gets flushed through nicely is bump the version number in your style.css file when you’ve finished. That way, all your stylesheet URLs will pick up this new version in the URL suffix, and Autoptimize will know that it needs to re-minify and re-smush everything,

A Word about Version Numbers

The standard way to record version numbers is called semantic versioning (SemVer). It works by using three numbers, separated by dots.

Major.Minor.Patch

It works like this:

  • Bump/increase the major version if you’ve fundamentally changed how your code/theme works.
  • Bump the Minor version if you’ve added a new feature, but not changed/broken anything that was already in there.
  • Bump the Patch number if you’ve just made a tweak, fix or other very minor change.

tipThe three numbers are independent – they do not work like decimal numbers, so… version 1.2.1 is completely different from 1.2.10 – this is really important, and lots of people don’t realise this.

style.css version info

That’s it, split things up, keep the files small and to-the-point, use a minifier, and see how it affects your Lighthouse scores.

Leave a Comment

Your email address will not be published. Required fields are marked *