Tikku.com CSS Inline Transformer

Overview

Developers looking to send content-rich HTML emails should know that numerous email clients do not support the transfer of HTML emails with CSS style blocks. Instead, developers are required to utilize inline style definitions and provide the complete markup in order to guarantee the widest coverage.

There are a variety of utilities available online that take CSS style definitions, along with HTML markup, and return usable HTML with the styles applied inline.

These services require sending your data over HTTP to a third party service, which you may not want to do. The CSS Inliner utility that I have created is entirely client side; there’s no need to worry about sending your content to anyone.

In the implementation I present, developers can continue to maintain their styles and markup separately (CSS and HTML files), and then use this utility to produce a single HTML output.

How to use the tool

Copy and paste your HTML snippet that you want to apply the inline styles to, then hit Build HTML.

(!) You must put the stylesheet in ONE style tag, and define type='text/css'.
(!) You must include the <html>, <head> and <body> tags

Provide HTML with a <style> tag containing your CSS

 
1
<html>
2
<head>
3
<meta charset="UTF-8">
4
<title>Example title</title>
5
<style type="text/css">
6
body { margin:0px;padding:0px; }
7
header { color:#999; font-weight:bold; }
8
header h1,
9
header h2 { text-align:center; }
10
.test-class { border:1px solid blue; color:blue; }
11
.test-class h1 { font-size: 25px; }
12
.test-class p { padding: 10px; *padding: 3px; }
13
.test-class .link { letter-spacing: 1px; color: #abc; }
14
footer .special { color:green; }
15
</style>
16
</head>
17
<body>
18
<div class="test-class">
19
<header>
20
<h1>CSS Inliner Demo Header</h1>
21
<h2>All in JavaScript!</h2>
22
</header>
23
<section>
24
<p>
25
This is the HTML that will be converted. It is worth noting, that any elements with
26
href and src attributes that have HTML entities will be decoded. At this time this only
27
applies to the '&amp' entity and will be replaced with &. This means you should be
28
mindful of any SRC or HREF attributes with &amp; already baked in; You will have to
29
manually replace them for now. Also, as can be seen with the inlinfication of this paragraph,
30
all HTML entities will be encoded.
31
</p>
32
<p>
33
An example of this can be seen with the following link:
34
<a href="http://site.com?k1=v1&k2=v2&k3=v3&k4=v4&k5=v4_part1&v4_part2" class="link">
35
Test Link
36
</a>
37
</p>
38
<p>
39
Another example with an image and its src attribute:
40
<img src="http://chart.com?w=400&h=300&title=An Example Title" class="link"/>
41
</p>
42
</section>
43
<footer>
44
Hope you find the <span class="special">CSS Inliner</span> useful!
45
</footer>
46
</div>
47
</body>
48
</html>



Copy your email ready HTML




What is the CSS Inline Transformer?

Most email clients strip out style tags (styles) in HTML emails making it difficult to implement quality designs in emails. In order to realize these designs, inline styles are an acceptable workaround and helps guarantee that emails will be rendered more successfully.

Any email template designer that has to maintain code for an email will build a CSS stylesheet. Rather than writing style attributes in markup, sticking with the power of cascading rules means sticking with a standard development flow -- with defining CSS and HTML separately.

The CSS Inline Transformer will transform CSS styles + HTML markup into a single HTML payload with all styles applied inline. While there are services out there that offer similar functionality, this utility is unique because it's completely done client side - meaning that your email data isn't transferred to a server or anywhere else.

If you're implementing beautifully designed emails, it is important to incorporate the inline transformer into your workflow. One process might look like:

  • Email content and messaging created (and reasonably modifiable) by marketing team
  • Deliver messaging + design to email template designer
  • Designer implements html markup around content and messaging
  • Construct CSS stylesheet corresponding to newly added html markup
  • Have the CSS styles apply inline to the email to be sent using CSS Inline Transformer
  • Send with email template



What does it do?

Conceptually, it's pretty straight forward -- it is possible to use jQuery and the rendering capabilities of the browser, and then extract the associated HTML with their computed styles defined inline.

First, a new document 'styleSheet' object must be created. Using native Javascript, you can construct the new stylesheet using: document.createElement('style'). Isolate any collisions with the existing browser styles by creating a new window with the HTML, and starting with a clean stylesheet applying the CSS.

After integrating custom styles into the stylesheet object, loop through all the cssRules and get the 'selectorText' field as part of the native cssRule object. This selector text can be used with jQuery (thanks Sizzle), as selector expressions. Passing this selector text into the jQuery object, will return an array of the various different DOM elements to have the styles applied to.

The array of elements that the CSS rule applies to, can be chained with the jQuery.css method to effectively apply those styles, which will result in our HTML with inline styles. So basically with the list of nodes, using jQuery(items).css({prop: value, _prop: _value}).

Once the HTML has inline style definitions the process is complete. As part of this utility, the new window will close, and the original window will be returned to, and the HTML will be displayed for you to take. Once polishing up, the HTML markup will no longer contain the <style> definition, so it can be used immediately for email.



Code

Here’s the JavaScript that powers the utility.

/**
* CSS Inline Transform v0.1
* http://tikku.com/css-inline-transformer-simplified
*
* Copyright 2010-2012, Nirvana Tikku
* Dual licensed under the MIT or GPL Version 2 licenses.
* https://github.com/jquery/jquery/blob/master/MIT-LICENSE.txt
*
* This tool leverages the jQuery library.
*
* Compatibility only tested with FireFox 3.5+, Chrome 5+
*
* @author Nirvana Tikku
* @dependent jQuery 1.4
* @date Wed Mar 31 14:58:04 2010 -0500
* @updated Sat Mar 10 16:21:20 2012 -0500
*
*/
(function(){
//
// private methods
//
/**
* @param stylesArray - the array of string
* "{name}:{value};" pairs that are to be broken down
*
*/
function createCSSRuleObject (stylesArray) {
var cssObj = {};
for(_s in stylesArray){
var S = stylesArray[_s].split(":");
if(S[0].trim()==""||S[1].trim()=="")continue;
cssObj[S[0].trim()] = S[1].trim();
}
return cssObj;
}
/**
* @param $out - the tmp html content
*
*/
function interpretAppendedStylesheet ($out) {
var stylesheet = $out[0].styleSheets[0]; // first stylesheet
for(r in stylesheet.cssRules){
try{
var rule = stylesheet.cssRules[r];
if(!isNaN(rule))break; // make sure the rule exists
var $destObj = $out.find(rule.selectorText);
var obj = rule.cssText.replace(rule.selectorText, '');
obj = obj.replace('{','').replace('}',''); // clean up the { and }'s
var styles = obj.split(";"); // separate each
$destObj.css(createCSSRuleObject(styles)); // do the inline styling
} catch (e) { }
}
};
function isPatternRelevant (newHTML, pattern, relevantPatterns) {
if( newHTML.indexOf(pattern) > -1 )
relevantPatterns.push(new RegExp(pattern,"i"));
};
/**
* The main method - inlinify
* this utilizes two text areas and a div for final output -
* (1) css input textarea for the css to apply
* (2) html content for the css to apply TO
*/
function inlinify (input) {
var tmpWindow = window.open("", "tmpHtml", "width=0,height=0");
window.blur(); // re focus on main window
var tmpDoc = tmpWindow.document; // create a window that we can use
var $tmpDoc = jQuery(tmpDoc); // jquerify the temp window
tmpDoc.write(input); // write the HTML out to a new window doc
interpretAppendedStylesheet($tmpDoc); // apply styles to the document just created
$tmpDoc.find("style").remove(); // sanitize all style tags present prior to the transformation
var newHTML = $tmpDoc.find("html").html();
tmpWindow.close();
var relevantPatterns = [];
isPatternRelevant(newHTML, "href=\"", relevantPatterns);
isPatternRelevant(newHTML, "src=\"", relevantPatterns);
return sanitize( newHTML, relevantPatterns );
};
function sanitize(html, patterns){
var ret = html;
for(var i=0; i<patterns.length; i++){
ret = san(ret, patterns[i])
}
return ret;
};
/**
* This method will take HTML and a PATTERN and essentially
* sanitize the following chars within the HTML with that
* pattern through a filter:
* Currently this only applies to &amp;' -> &
*/
function san(html, pattern){
var ret = "";
var remainingString;
var hrefIndex;
for(var i=0; i<html.length; i++){
remainingString = html.substring(i);
hrefIndex = remainingString.search(pattern);
if( hrefIndex === 0 ){
// actually sanitize the pattern, i.e. href="[sanitize-candidate]"
// must be encapsulated within quotes, "
(function(){
// get the start of what we will sanitize
var startIndex = remainingString.indexOf("\"");
// and the end
var endIndex = remainingString.indexOf("\"",startIndex+1);
// get the data to sanitize
var newHREF = html.substring(i+startIndex+1, i+endIndex+1);
// here we actually perform the replacement
newHREF = newHREF.replace(/&amp;/g, '&');
// add the pattern + the new data + a closing quote
var regExpStartLen = "/".length;
var regExpFlagsLen = "/i".length;
ret += String(pattern).substring( regExpStartLen, String(pattern).length - regExpFlagsLen)
+ newHREF;
i += endIndex;
})();
continue;
} else {
// if we have another href, copy everything until that href index
if( hrefIndex > 0 ) {
ret += html.substring(i, hrefIndex);
i = hrefIndex-1;
} else {
// otherwise just add the remaining chars and stop trying to sanitize
ret += html.substring(i);
break;
}
}
}
return ret;
};
//
// public methods
//
doInline = function(input) {
return inlinify(input);
}
})();