180: Finding Unused CSS
Any medium or large-sized website probably has a large amount of CSS spread across one or more stylesheets and the chances are that there are selectors in those stylesheets that are no longer used on any of the pages in that site. These selectors should be removed but they rarely are as there’s always a small risk that they’re still used somewhere on the site. Fortunately a couple of useful tools exist to search through a site and find any unused CSS and we’ll take a look at them in this episode.
The first solution we’ll look at is a Firefox plugin called Dust-Me Selectors. Once it’s installed we can visit a web page, click the pink brush in the status bar at the bottom and Dust-Me Selectors will scan through the page’s stylesheets and pop up a report that shows the unused selectors on that page.
Of course some CSS selectors might be marked as unused as the elements that match them are on other pages. To help with this Dust-Me selectors has a Spider Log tab which can take the URL of a sitemap file. Dust Me Selectors will then report on the selectors that weren’t found on any of the pages in the sitemap which will give us a more accurate report about the CSS selectors that aren’t used in our site.
Dust-Me Selectors is a good solution for looking for unused CSS selectors across a website but what if we’re looking for a solution that integrates more tightly with a Ruby on Rails application and which we can have more control over scripting? This is where a gem called Deadweight comes in. It does exactly what we want, searching through a Rails application’s CSS for unused selectors and using Ruby code to specify the pages and stylesheets to parse.
To install the gem we first need to ensure that we have Github in our sources list. If not we can add it with
gem sources -a http://gems.github.com
Once it’s added we can install the gem.
sudo gem install aanand-deadweight
Creating a rake Task
The usual way to run Deadweight is as a rake task, so we’ll create a
deadweight.rake file in our application’s
/lib/tasks directory to do this.
At the top of the file we’ll need to require the Deadweight gem. We have to be careful when requiring gems in rake tasks. If we don’t have the gem installed, say on our production server, then every rake task will break. To protect against this we’ll wrap the require statement in a
begin require 'deadweight' rescue LoadError end
We can now write the rest of the task.
desc "run Deadweight (requires script/server)" task :deadweight do dw = Deadweight.new dw.stylesheets = ['/stylesheets/application.css'] dw.pages = ['/'] puts dw.run end
We start by adding a description of the task, then create the task itself. This creates a new instance of Deadweight then specifies the full path to the stylesheets and pages that should be parsed. We finish by outputting the results of the Deadweight run.
Note from the description that we need to have start
script/server to run our rake task. With the server running we can try out our task.
$ rake deadweight (in /Users/eifion/rails/asciicasts) /stylesheets/application.css / html body h1 ... found 31 unused selectors out of 60 total h2 a code pre form.searchForm input[type=text] form.searchForm input[type=submit] ...
The (truncated) output above shows a list of the selectors that are matched on a page followed by a list of those for which no match was found. There are 31 unused selectors in the list above, but we’re only checking for matching selectors on the home page so we should add more of the pages from our application to the array of pages that Deadwood searches. One way to work out which pages we should be searching is to use
rake routes to see a list of the routes in our application.
To get a more accurate view of the unused selectors in our stylesheets we’ll add a few more pages from our site to the array of pages. It’s not a complete list, but it’s enough to cover most of the site.
dw.pages = ['/', '/episodes/all', '/about', '/episodes/150-rails-metal', '/episodes/155-beginning-with-cucumber', '/search/index']
If we run
rake deadweight again it will run through the pages we’ve supplied and we’ll see a shorter list of unmatched selectors. (Again this list has been cut down here.)
$ rake deadweight (in /Users/eifion/rails/asciicasts) /stylesheets/application.css / html body h1 ... /episodes/all dl#episodeArchive dt dl#episodeArchive dd /about /episodes/150-rails-metal h2 a code pre ... /search/index found 10 unused selectors out of 60 total form.searchForm input[type=text] form.searchForm input[type=submit] code.terminal ...
If there are selectors that we know are used in a site, but which only show under certain circumstances, such as selectors used to display error messages or flash notifications we can use
ignore_selectors to exclude them from the results.
dw.ignore_selectors = /flash_notice|flash_error|errorExplanation|fieldWithErrors/
When we run
rake deadweight now it will return an even smaller list of unmatched selectors. We now have few enough to be able to manually work through them and remove the ones we know aren’t used. For the ones we’re not sure about we could use TextMate’s Find In Project option (or whatever similar feature your favourite text editor provides). to search through the site to see if there are any matches.
That’s all for this episode. We’ve covered most of the basic features of Deadweight, but there are some advanced options that are worth looking at too. These are detailed on Deadweight’s GitHub page.