Posts tagged tamtam
Guest Post: Inline CSS Using a TextMate Command
Apr 1st
A few months back I posted a series of entries about the process of producing HTML emails. Those posts generated a number of comments and emails, including some from Christopher White who has been working with the TamTam library in TextMate to clean up his workflow. He offered me this article which I present below.
Overview
Email services like GMail and Hotmail don’t like stylesheets with the only real workaround to use inline styles. Inline styles, however, are labor and typing intensive. One way to overcome this problem is design the HTML and CSS as you would ordinarily. When you are done designing and testing, filter the HTML so the CSS styles are inlined into each matching HTML element.
This recipe shows how you can do this within the Textmate environment using a user-defined command. The command calls a Ruby library to filter the contents of the current Textmate window inlining styles you defined ‘<style>’ element into the ’style’ attribute of HTML elements.
Much work converted into little work.
Step 1: Check what version of Ruby you are running
If you are running Leopard and updated from Tiger, you need to check you are accessing the right version of Ruby.
Open a terminal window and type:
ruby -vYou should see this:
ruby 1.8.6 (2007-06-07 patchlevel 36) [universal-darwin9.0]
If not, you are running the Tiger or manually installed version of Ruby. The reason is the ‘$PATH’ contains a reference to ‘/usr/local/bin’ before ‘/usr/bin’.
If this is the case, open your ‘~/.bash_login ‘ or ‘~/.profile’ file and change the order of paths so ‘/usr/bin’ is first or remove all references to ‘/usr/local’ from ‘$PATH’.
Step 2: Install the CSS filter component
The Textmate command to inline CSS styles from the ‘<style>’ block requires the TamTam library which can be easily installed from the RubyForge repository.
From the terminal window:
gem install tamtamHere’s more about TamTam http://tamtam.rubyforge.org/
Step 3: Create the Textmate command
Next you need to create the command in Textmate. You can either create a new bundle or use an existing one. One idea is to add to the HTML bundle so it’s easily available. We’ll do that here.
Launch TextMate. Then open the Bundle Editor (Bundles > Bundle Editor > Edit Commands). Move down and select the HTML section. Now create a new command using the (+) button a the bottom of the command list. Name the command “Inline styles” and move on over to the right hand side of the command editor.
First, copy the following code to your new command replacing the default code TextMate generated for you.
#!/usr/bin/env ruby require 'rubygems' require 'tamtam' html_doc = STDIN.read html_filtered = TamTam.inline(:document => html_doc) STDOUT.write html_filtered
For future reference, note how you read from and write back to Textmate with a command. STDIN maps to the command’s Input field selection in the Bundle Editor and the same for STDOUT.
Second, we are going to set the properties of the command. Set the input to “Entire Document” and output to “Replace Document.” If you find this too draconian, then set the output to “Create New Document.”
Lastly set the scope selector to “text.html” and click on another command to save your new command. Then close the Commands Editor.
Step 4: Test your new command
So before you are done, you want to test your new command to see that it works. Start by copying the following HTML snippet into a new TextMate document.
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<title>TamTam Test</title>
<style type="text/css">
.foo { font-color: blue; }
.flank { font-size: 12pt;}
</style>
</head>
<body>
<div class="foo">Woot 1.1</div>
<div class="flank foo">Woot 1.2</div>
</body>
</html>Change the document type to HTML by typing Ctrl-Option-Shift-H key. Position the cursor in the new window and choose Bundles > HTML > Inline Styles.
Your HTML document should now look like this:
<html>
<head>
<meta content="text/html; charset=utf-8" http-equiv="Content-Type" />
<title>TamTam Test</title>
<style type="text/css">
.foo { font-color: blue; }
.flank { font-size: 12pt;}
</style>
</head>
<body>
<div class="foo" style="font-color: blue;">Woot 1.1</div>
<div class="flank foo" style="font-color: blue; font-size: 12pt;">Woot 1.2</div>
</body>
</html>That’s it. Good luck…
Exploring Ruby CSS parsers: TamTam and CSSPool
Dec 5th
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.
TamTam
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 <td id="email-header-message"> (Exception): can't convert nil into String from /Library/Ruby/Gems/gems/tamtam-0.0.2/lib/tamtam.rb:24:in `inline' </td>
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
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:
rule.selector.to_css
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.