Making HTML emails sane again


Styliner is a Node.js library that takes HTML documents with regular CSS stylesheets, and moves all of the CSS properties into inline styles.

This lets you write your emails using regular CSS stylesheets and selectors, and have it generate the ugly inline style="" for Gmail automatically.


First, grab Styliner from npm:

npm install styliner

Next, create a new Styliner instance. This instance stores the base directory for relative paths (used to read external stylesheets from disk or HTTP), as well as configuration settings (see below):

var styliner = new Styliner(__dirname + '/html');

The Styliner instance also caches parsed CSS files so that they can be referenced from other HTML files without re-parsing.

Finally, call the processHTML() method with the HTML source code to process. This function returns a promise of the resuling inlined source:

var originalSource = require('fs').readFileSync(__dirname + '/html/source.html', 'utf8');

        .then(function(processedSource) {
            // Do something with this string

You would typically write the processed HTML source to a file, or send it in an email or HTTP response.

Coming soon: Standalone command-line utilities and a web-based demo for testing.


The optional second parameter to the Styliner constructor is an object that can have the following options: (all options default to false)

Frequently Asked Questions

What happens to relative URLs?

Styliner will resolve relative paths to referenced stylesheets relative to the baseDirectory (passed to the Styliner constructor or as the second parameter to processHTML()). It will read stylesheets from disk or HTTP using the resulting absolute path.

Relative URLs (in HTML tags or as image URLs in stylesheets) will remain relative to the document being processed. Styliner will expand relative URLs in referenced stylesheets to include the path to the stylesheet; URLs in the HTML itself are left untouched.

To customize URLs in the final output, pass a urlPrefix of url() options (see above).

What about pseudo-classes or media queries?

Selectors that involve pseudo-classes or pseudo-elements, as well as all selectors within media queries, cannot be inlined into style="" attributes, since they don't always apply to the target element (or, for pseudo-elements, because there is no target element in source).
Therefore, Styliner will leave these rules in place, so they can work in environments that do recognize stylesheets (in particular, the iOS Mail app).

You can create responsive emails the same way you would create responsive websites, and they will work in both Gmail and more fully-featured mail clients.

What if my media query overrides a rule that was inlined?

Styliner will automatically add !important to media queries that should override rules that were inlined.

This has some limitations; see below.

What selectors are supported?

Styliner uses CSSselect to match CSS selectors; this should support all CSS2 selectors and most CSS3 selectors.

Styliner fully passes Acid1, and passes almost all of Acid2 (except for a few places where my CSS parser doesn't handle invalid syntax according to spec.
Acid3 is primarily driven by Javascript, so it isn't applicable to Styliner.

Can I use LESS?


You can easily add support for other preprocessors; see the source for Styliner-less to get started.

Can Styliner minify the generated source too?

Sure; just pass compact: true. Note that the minifier currently does little more than removing extraneous whitespace; in particular, it won't do CSS tricks like shortening colors or combining shorthand properties. Some CSS tricks are done implicitly by Styliner (eg, removing unused selectors), except in media queries.

What if I don't want to inline some rules?

Just wrap them in any kind of media query (eg, @media all). Styliner will also not inline any selectors that contain .js, under the assumption that they're meant to apply when Javascript adds a js class to the root element.

Limitations & Known issues