• Stars
    star
    125
  • Rank 285,074 (Top 6 %)
  • Language
    HTML
  • Created almost 12 years ago
  • Updated over 3 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Turns CSS stylesheets into inline style="" attributes for HTML emails

Styliner

Styliner is a Node.js library that reads CSS rules from external stylesheets and converts them to inline style="" attributes in an HTML document.

Styliner is intended for use with HTML emails. With it, you can write regular CSS or LESS (with the Styliner-less package) stylesheets, then merge them into your HTML and create emails that work with Gmail (which drops all <style> tags). Unfortunately, though, you'll still need to use <table>s to get complex layout.

You can also use advanced features ("dynamic rules") like :hover selectors or media queries, and Styliner will leave them in a <style> tag. This way, you can build interactive emails that will light up when viewed in an email client that supports <style> tags, while still maintaining Gmail support.

In effect, you get graceful degradation for your email designs.

Usage

Styliner uses the Q promise library.

var styliner = new Styliner(baseDir, { options });
styliner.processHTML(htmlSource, directory)
    .then(function(source) { ... });
//Pass filePath to processHTML
styliner.parseFile(filePath)
      .then(function(source) { ... });

The baseDir parameter specifies the base directory for relative paths. When processing an HTML source file, you can optionally specify a directory for that source, and any relative paths within the file will be treated as relative to that directory (instead of relative to the Styliner instance's baseDir).
The processHTML method returns a Q promise of the inlined HTML source.

You can pass an options hash as the second parameter to the Styliner constructor with the following options: (all options default to false):

  • compact: true
    • True to minify all output. This option removes all extraneous whitespace from the generated HTML (including any remaining inline stylesheets)
  • noCSS: true
    • True to not emit <style> tags for rules that cannot be inlined. This option will completely drop any dynamic CSS rules. (such as media queries, pseudo-elements, or @font-faces)
  • keepRules: true
    • True to keep all rules in <style> tags instead of inlining static rules into elements. This results in smaller files, but will not work with Gmail.
  • fixYahooMQ: true
    • True to add an attribute/ID selector to all rules in media queries to fix a bug in Yahoo Mail. Yahoo Mail drops all media queries, converting their contents to regular rules that will always be applied. This option adds a workaround for that.
  • keepInvalid: true
    • Don't skip properties that parserlib reports as invalid. (all invalid properties are always logged as winston errors)
    • Pass this option if you're using features that parser doesn't recognize, or if you come across a bug in parserlib
    • This option breaks Acid2, which tests that valid properties from earlier rules replace invalid properties from later rules. (see also the first known issue)
  • urlPrefix: "dir/"
    • The path containing referenced URLs. All non-absolute URLs in <a> tags, <img> tags, and stylesheets will have this path prepended. For greater flexibility, pass a url() function instead.
  • url: function(path, type)
    • A function called to resolve URLs. All non-absolute URLs in HTML or CSS will be replaced by the return value of this function. The function is passed the relative path to the file and the source of the URL ("img" or "a" or other HTML tags; URLs from CSS pass "img"). It can return a promise or a string

Known Issues

  • Browser property fallbacks don't cascade
    • If you specify background: red; in one rule, and background: linear-gradient(...) in a more specific rule, Styliner will replace the property from the first rule with the more specific one. This means that browsers that don't support linear-gradient() won't see any background at all.
    • Instead, put both properties in the same rule, and Styliner will know to keep both of them. To make this easier, you can use a LESS mixin
  • Except for margin and padding, shorthand inlined properties that are overridden by non-inlined non-shorthand counterparts will not be overridden correctly.
    • To fix this, add splitter methods in Preprocessor.js to split other shorthand properties.
  • If one element receives a property from a soft-dynamic (pseudo-class) rule and a static rule, and a different element receives a property from an earlier static rule that overrides that soft-dynamic rule, the soft-dynamic rule will incorrectly override the static rule on the second element.
    • This happens because I need to make the dynamic rule !important in order to override the inlined static rule on the first element.
    • I could do another pass to find the and !important-ize the inline property in the second element, but that would leave an unfixable problem if there is another dynamic rule that should override that property on the second element.
  • Similarly, if one element receives a property from a soft-dynamic (pseudo-class) rule and a static rule, and a different element receives a property from an later static rule that overrides that soft-dynamic rule, but is in turn overridden by a second soft-dynamic rule, the second soft-dynamic rule will be incorrectly overridden.
    • This happens because the inlined property from its static rule is made !important to override the already-!important-ized less-specific soft-dynamic rule. It is then impossible to make the more-specific soft-dynamic rule override the inlined property.
    • This is impossible to fix. This issue could be flipped (to match the previous issue) by only setting important = true after the first pass.
  • These issues could be made fixable by adding additional levels of importance to the CSS spec (!important1, !important2, etc), and changing Styliner to keep running additional passes and making overridden rules more and more important until it stabilizes.

Acid Test Issues

Acid3 doesn't work because most of its rules need to be applied dynamically (for elements created in Javascript). I can fix this by adding .js to those rules.

Acid2 has the following issues:

  • background: red pink (which is an invalid value) incorrectly overrides the valid background: yellow for .parser, resulting in a white background.

  • This happens because parserlib does not validate the background property at all. (this would be a complex validation rule to add)

  • width: 200 (which is an invalid value) incorrectly overrides the valid width: 2em for .parser, resulting in the element stretching to fit the browser.

  • This is fixed by parserlib#48, which has not been merged.

  • * html .parser incorrectly matches .parser, resulting in a gray background.

  • This is caused by CSSselect#8 and cheerio#162.

License

MIT

More Repositories

1

Silon

Logic Gates and Adders in pure CSS
HTML
327
star
2

VSEmbed

Embed the Visual Studio editor & theme architecture in your own programs
C#
110
star
3

Ref12

Sends F12 in Visual Studio to the new .Net Reference Source Browser
C#
73
star
4

Minimatch

A C# glob matcher, ported from Javascript
C#
54
star
5

Rebracer

Saves editor formatting settings as part of each solution.
C#
44
star
6

ExcelExporter

Simple, easy-to-use .Net Excel exporting
C#
43
star
7

Qx

A set of LINQ-like extensions to Q for working with arrays of promises.
JavaScript
31
star
8

Root-VSIX

Installs VSIX packages to custom RootSuffixes for Visual Studio
C#
21
star
9

csrf-crypto

Connect middleware for session-less CSRF protection using cryptography
JavaScript
13
star
10

CSS-Gallery

An interactive, data-bound photo gallery in pure CSS
CSS
12
star
11

ConfOxide

Fast, DRY, strongly-typed configuration system for C# projects
C#
11
star
12

DroidMaster

A one-stop solution for bulk Android automation & scripting
C#
6
star
13

jqPresentation

PowerPoint-style slideshows in HTML and jQuery
JavaScript
6
star
14

jsDelegate

C#-style multicast delegates in Javascript
JavaScript
3
star
15

Managed.AndroidDebugBridge

Fork of https://madb.codeplex.com
C#
3
star
16

NetPresenter

Presents slideshows or other content, synchronized across multiple monitors or computers.
C#
3
star
17

Styliner-less

Adds LESS support to Styliner
JavaScript
3
star
18

vs-nodesense

Abandoned Node.js IntelliSense for Visual Studio 2012+
JavaScript
3
star
19

PageOptions

Stores Knockout viewmodels in cookies or URL, with history support
JavaScript
2
star
20

jsObject

Powerful Javascript OOP
JavaScript
2
star
21

OOMath

Building math from scratch in C#
C#
2
star
22

Progression

Allows business logic to report progress without coupling to the UI
C#
2
star
23

logup-emitter

A lightweight log emitter used by library packages to emit log entries to an optional logup-hub in the host application.
JavaScript
2
star
24

configdir-loader

Loads environment-specific configuration files into a config object
JavaScript
1
star
25

ScriptLESS-UI

A complex web app UI without any Javascript
1
star
26

LibraryBrowser

C#
1
star
27

shutdown-sequence

Invokes a series of asynchronous shutdown handlers for composable applications.
JavaScript
1
star
28

Glisten

Displays lists from Trello or Google Calendar in poster-screen format
JavaScript
1
star
29

net.slaks.parallelProcessor

Interview question
Java
1
star
30

SLaks.net

Homepage for http://slaks.net
1
star
31

Mvc.ClientRoutes

ASP.Net Routing Framework for Javascript
1
star
32

Ternion

Triangle matching game
JavaScript
1
star
33

logup-hub

A central hub that receives log messages from all logup-emitters in an application.
JavaScript
1
star