Painless Widget Armor

» 09 Nov 2010

Developing attractive widgets for embedding on random pages can be an exercise in frustration. For NabeWise, we’ve been through many iterations of our widgets for purely technical reasons with almost no change in styling (though we have some new designs in the pipeline that will significantly improve look/feel). Our first iteration was a simple iframe embed. After a frantic call from our SEO guy , we realized that we probably wanted to get some Google Juice out of these things, so we finally dove into the hell that is CSS armoring. The current version that we’re offering is based on http://www.gabrielweinberg.com/blog/2009/06/a-harsh-css-environment-for-testing-widgets.html. This armor is very thorough, but its a pain to actually do work with, causing the simple header and footer that wrap around the content iframe to take almost as much time to style as the actual content of the widget.

We’re in the process of drastically changing how we do widgets, shifting from the iframe technique to fully JavaScript templated widgets through Mustache and a new (private) API. This of course means dealing with more armor (a fate we tried and failed to cheat through less thorough CSS reseting). When it finally became clear that we were going to have to use real armor (1am, last night), there was much gnashing of teeth. Luckily, the process this time around was painless and finished in time to catch the tail end of a rerun of Millionaire Matchmaker (1:45 am, Bravo).

1

The key this time was using CleanSlateCSS . Hey, if it’s good enough for the bbc, it’s good enough for me. The two changes necessary for this to work were adding the class “cleanslate” to our widget container and then changing all of our CSS rules to !important. We already had all of our CSS written, and I had no intention of adding !important to each declaration manually (and then remembering to always do so in the future), so I whipped up a quick hack to do it for me based on CssParser . Just call CssImportantizer.process(css_string) and it’s done for you.

require 'css_parser'

  class CssImportantizer
    class << self
      include CssParser
  
      def process(string)
        s = string.dup
        parser = Parser.new
        parser.add_block!(s)
  
        parser.each_rule_set do |rule_set|
          rule_set.each_declaration do |prop, val, important|
            rule_set.add_declaration!(prop, val + " !important")
          end
        end
        parser.to_s
      end
    end
  end

Because of the way CssParser handles existing !important values (setting them as a separate flag on the parsed data), just reseting the declaration with string concatenation of " !important" works.

The one major caveat here, is that the resulting string will not be compressed, so you’re going to want to pass the result through the YUI CSS Compressor or similar before using it.

In any case, this worked like a charm and I think I had to change exactly one other rule in our CSS to make it perfect.

1 Photo from marfis75