---
title: 'Use Deadweight in Your Integration Suite to Automatically Find Unused CSS
  Rules

  '
teaser: How to find unused CSS rules in your Ruby test suite.
tags: web,testing,css
author: Jason Morrison
published_on: 2011-01-04
---

We're refactoring the <abbr title="Cascading Style Sheets">CSS</abbr> in Hoptoad
-- the app is a few years old, and has been through several rounds of design
improvement.  We're looking to combine duplicated rules, make selectors more
intention-revealing, and want to reorganize the stylesheets to reflect our
current best practices.  We're also switching to Sass, which we've found to be a
tremendous help.  Together, these improvements should cut developer and designer
time during maintenance and future design improvements.

Before diving in, we wanted to know what <abbr title="Cascading Style
Sheets">CSS</abbr> is used or unused.

[Deadweight](https://github.com/aanand/deadweight) is an excellent tool for
identifying unused <abbr title="Cascading Style Sheets">CSS</abbr> selectors.
It's flexible, too: you can identify paths in your application for deadweight to
scan, or use it as an HTTP proxy and click around in your site, or just pass it
static <abbr title="HyperText Markup Language">HTML</abbr> files to analyze.
There are a few caveats (for example, selectors inserted by Javascript aren’t
tracked) but it’s generally a solid approach for finding unused <abbr
title="Cascading Style Sheets">CSS</abbr> rules.

I didn't want to spend a lot of time clicking through the site, and wanted to
eliminate the possibility that I would forget a page or two to include.  I
decided to run the Hoptoad integration tests (Cucumber features, in this case)
and capture the response bodies.

## Rack middleware to the rescue

I wrote the following piece of Rack middleware:

```ruby
class ResponseLoggerMiddleware
  RESPONSE_LOG_DIR = Rails.root.join('log', 'responses')
  Dir.mkdir(RESPONSE_LOG_DIR) unless Dir.exist?(RESPONSE_LOG_DIR)

  def initialize(app)
    @app = app
  end

  def call(env)
    response = @app.call(env)
    log(response)
    response
  end

  def log(rack_response)
    code, headers, response = rack_response

    if response.respond_to?(:body)
      html = response.body
      filename = "response_#{Time.now.to_f}.html"
      File.open(RESPONSE_LOG_DIR.join(filename), 'wb') do |file|
        file.puts(html)
      end
    end
  end
end
```

and included it:

```ruby
# For Rails 2, include in config/environment.rb
# For Rails 3, include in config/application.rb
# Why Rack::Lock? http://guides.rubyonrails.org/rails_on_rack.html#internal-middleware-stack
config.middleware.insert_after 'Rack::Lock', 'ResponseLoggerMiddleware'
```

After the tests ran, I processed the output with deadweight:

```sh
gem install deadweight
cat public/stylesheets/*.css | deadweight log/responses/*
```

Ta-da!  If you're curious what Deadweight output looks like, here's what I got:
[https://gist.github.com/cf34709887bf63b1ec15](https://gist.github.com/cf34709887bf63b1ec15)

## Further reading

* [Ryan Bates' Railscast on
  Deadweight](http://railscasts.com/episodes/180-finding-unused-css) will get
  you familiar with the tool.
* The [Helium tool](https://github.com/geuis/helium-css) lets you drop a piece
  of JavaScript on your site and paste in a list of <abbr title="Uniform
  Resource Locator">URL</abbr>s, and will compute unused <abbr title="Cascading
  Style Sheets">CSS</abbr> selectors.
* SitePoint's [Dust-Me Selectors](http://www.sitepoint.com/dustmeselectors/) is
  a Firefox plugin that identify unused <abbr title="Cascading Style
  Sheets">CSS</abbr> selectors.  It can spider through your site with a sitemap,
  too.

## Extra credit

You could bundle this all up into a rake task, run it on your CI server, and
fail the build whenever there are new unused <abbr title="Cascading Style
Sheets">CSS</abbr> rules.
