In response to yesterday’s post about inlining CSS for HTML emails, I got a couple of comments suggesting alternatives to my CSS parser class. Not wanting to have to maintain code unless I have to, I decided to give them both a try and see how they worked out.
First up is TamTam, suggested by batnight. I’d actually spotted TamTam and link blogged it a few weeks ago, which shows how transient attention can be. TamTam is a complete solution for inlining CSS, so I should be able to replace all my code with:
require 'rubygems' require 'hpricot' require 'tamtam' inlined = TamTam.inline( :css => File.read('/path/to/my.css'), :body => File.read('/path/to/my.html') ) puts inlined
That looks ideal, but unfortunately as soon as I tried passing my code into it an error emerged.
/Library/Ruby/Gems/gems/tamtam-0.0.2/lib/tamtam.rb:77:in `apply_to': Trouble on style td#email-header-message on element
(Exception): can't convert nil into String from /Library/Ruby/Gems/gems/tamtam-0.0.2/lib/tamtam.rb:24:in `inline'
Digging into the library it seems that its parsing fails if there’s a CSS rule that doesn’t match any elements in the document. That’s not a problem if your CSS file is targetted to a specific block of HTML, but if the idea is to build a set of rules that can be applied across a selection of page/emails that may be a problem.
CSSPool, suggested by Dan Kubb, is a more generic CSS parser which is designed to work with hpricot to map between CSS and HTML. Initially I just used something very much like the examples they offer to get a sense of the format of the objects they give access to:
require 'rubygems' require 'hpricot' require 'csspool' sac = CSS::SAC::Parser.new css_doc = sac.parse(File.read('/path/to/my.css')) html_doc = Hpricot.parse(File.read('/path/to/my.css')) css_doc.find_all_rules_matching(html_doc).each do |rule| puts rule end
Unfortunately this too failed on me with:
NoMethodError: undefined method `accept' for nil:NilClass
From a quick look at the source code it seemed that this error was occurring when a CSS selector didn’t match the supplied document, just as in TamTam. It turned out to be pretty easy to patch, though, and I’ve submitted a report in their tracker.
That done I was able to iterate over it and access the selectors easily enough, but what I’ve not yet been able to find is a way to get the parser to give me the actual CSS declarations appropriately formatted for including in the HTML. When I have a rule I can get the selector with:
But to find out what styles the selector applies isn’t so straightforward. If anyone has an easy way to do that, I’d love to hear about it in the comments!
Update (later that day): Version 0.2.3 of CSSPool is now out and includes my fix.