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.
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.