CSSInliner
A simple and modern CSS inliner. Optionally supports CSS preprocessors
(e.g. Less) and templating languages (e.g. Handlebars).
Usage
const inliner = new CSSInliner({
directory: 'stylesheets'
});
inliner.inlineCSSAsync(html)
.then(function(result) {
console.log(result.inlined);
});
Configuring
First, create a new CSSInliner
object. The inliner object serves as a cache
for processed stylesheets, inlined documents, and other assets. You don't have
to keep it around, but it is useful to speed up processing.
You configure the inliner with the following options:
directory
- The base directory from which local stylesheets are loaded. Only
files in this directory or its sub-directories are accessible.
plugins
- Array of PostCSS plugins to use while processing
stylesheets.
precompile
- A function that precompiles a stylesheet into CSS. See the
preprocessors section.
template
- A function that can extract tags from a templating language. See
the templates section.
loadAsync
- A function that reads a stylesheet reference (path or URL) into
a Buffer or string. Recommended only when directory
and precompile
are not
enough.
How It Works
The inliner extracts any style
elements appearing in the document, with all
their rules. It also extracts any external stylesheets that use relative URLs.
All stylesheets are processed using PostCSS and any plugins you opt to use, and
cached in memory.
The style
elements and link
elements to known stylesheet are then removed
from the document. Their rules will either be inlined or added back into the
document later.
External stylesheets that reference absolute URLs (anything with a hostname,
such as //example.com/
) are retained in the document. These are expected to
resolve when the document is rendered in the browser or email client.
Styles are processed in document order, that is, the order in which the style
or link
elements appear in the document, such that the early rules take
precedence (for the same specificity).
Rules that can be applied to the style
attribute of an element (inlined) are
applied to any matching element found in the document, if there is one. These
are never added back into a style
element in the document.
Rules that cannot be inlined include pseudo selectors such as :hover
and
::after
, as well as all media queries such as @media screen
. These can only
be applied to a live document when rendered by a browser or email client.
Thus they are added back to the document inside a style
element.
If a rule has multiple selectors, it may be inlined using one selector, and
included in the document with another selector.
Working with preprocessors (Less, Sass, Stylus, etc)
If you're working with a language that compiles to CSS, you need to use the
precompile
option.
A precompile function for Less is included by default, and you can use it like
this:
const precompile = CSSInliner.less;
const inliner = new CSSInliner({ precompile });
(Less is an optional dependency, so you need to add it in your package.json
if
you want to use it.)
The precompile
option takes a function that will be called with two arguments:
the pathname, and the stylesheet. You can use the pathname to determine the
file type based on its extension (e.g. does it end with .less
?)
The function should return the compiled CSS in the form of a string or a Buffer,
or a promise that resolves to a string or Buffer.
Working with templates (Handlebars, etc)
Inlining requires parsing the document as HTML, and when there are non-HTML
tags in the document, they are often parsed incorrectly. Many templating
languages use non-HTML tags.
Use the template
option with an appropriate template parser. For example,
when working with Handlebars templates:
const template = CSSInliner.handlebars;
const inliner = new CSSInliner({ template });
(Handlebars is an optional dependency, so you need to add it in your
package.json
if you want to use it.)
The template handler is a function that will be called with the source template,
and must return an array of all template tags found there.
These tags are then replaced with HTML-compatible markers before parsing and
inlining, and are restored before resolving to the final document.
Note: you shouldn't use the templating language to define a class inside a
class
attribute. CSSInliner doesn't run the template - it only inlines styles
as if the template tags weren't there. If you need to define an element's class
based on a condition:
<div class="{{ active_or_inactive }}"></div>
{{#if active}}
<div class="active"></div>
{{else}}
<div class="inactive"></div>
{{/if}}
Watching for warnings
The inliner may report warnings while processing CSS or HTML documents by
emitting a warning
event.
You can use an event handler to catch and log the warning. You can also halt
processing by throwing an error from your event handler. If there are no event
handlers, warnings will be logged to stderr.
const inliner = new CSSInliner(options);
inliner.on('warning', function(warning) {
console.log('So this happened:', warning);
});
References
CSS 3: Calculating a selector's specificity
PostCSS API
PostCSS Selector Parser API