Important: This documentation covers Yarn 1 (Classic).
For Yarn 2+ docs and migration guide, see yarnpkg.com.

Package detail

@css-inline/css-inline

Stranger6667696.9kMIT0.14.3TypeScript support: included

High-performance library for inlining CSS into HTML 'style' attributes

css, html, email, stylesheet, inlining

readme

css-inline

build status npm codecov.io gitter

css-inline is a high-performance library for inlining CSS into HTML 'style' attributes.

This library is designed for scenarios such as preparing HTML emails or embedding HTML into third-party web pages.

For instance, the library transforms HTML like this:

<html>
  <head>
    <style>h1 { color:blue; }</style>
  </head>
  <body>
    <h1>Big Text</h1>
  </body>
</html>

into:

<html>
  <head></head>
  <body>
    <h1 style="color:blue;">Big Text</h1>
  </body>
</html>
  • Uses reliable components from Mozilla's Servo project
  • Inlines CSS from style and link tags
  • Removes style and link tags
  • Resolves external stylesheets (including local files)
  • Optionally caches external stylesheets
  • Works on Linux, Windows, and macOS
  • Supports HTML5 & CSS3
  • Tested on Node.js 18 & 20.

Playground

If you'd like to try css-inline, you can check the WebAssembly-powered playground to see the results instantly.

Installation

Node.js

Install with npm:

npm i @css-inline/css-inline

Usage

import { inline } from "@css-inline/css-inline";

var inlined = inline(
  `
  <html>
    <head>
      <style>h1 { color:red }</style>
    </head>
    <body>
      <h1>Test</h1>
    </body>
  </html>
  `,
);
// Do something with the inlined HTML, e.g. send an email

Note that css-inline automatically adds missing html and body tags, so the output is a valid HTML document.

Alternatively, you can inline CSS into an HTML fragment, preserving the original structure:

import { inlineFragment } from "@css-inline/css-inline";

var inlined = inlineFragment(
  `
  <main>
    <h1>Hello</h1>
    <section>
      <p>who am i</p>
    </section>
  </main>
  `,
  `
  p {
      color: red;
  }

  h1 {
      color: blue;
  }
  `
);
// HTML becomes this:
// <main>
// <h1 style="color: blue;">Hello</h1>
// <section>
// <p style="color: red;">who am i</p>
// </section>
// </main>

Configuration

  • inlineStyleTags. Specifies whether to inline CSS from "style" tags. Default: true
  • keepStyleTags. Specifies whether to keep "style" tags after inlining. Default: false
  • keepLinkTags. Specifies whether to keep "link" tags after inlining. Default: false
  • baseUrl. The base URL used to resolve relative URLs. If you'd like to load stylesheets from your filesystem, use the file:// scheme. Default: null
  • loadRemoteStylesheets. Specifies whether remote stylesheets should be loaded. Default: true
  • cache. Specifies caching options for external stylesheets (for example, {size: 5}). Default: null
  • extraCss. Extra CSS to be inlined. Default: null
  • preallocateNodeCapacity. Advanced. Preallocates capacity for HTML nodes during parsing. This can improve performance when you have an estimate of the number of nodes in your HTML document. Default: 32

You can also skip CSS inlining for an HTML tag by adding the data-css-inline="ignore" attribute to it:

<head>
    <style>h1 { color:blue; }</style>
</head>
<body>
    <!-- The tag below won't receive additional styles -->
    <h1 data-css-inline="ignore">Big Text</h1>
</body>
</html>

The data-css-inline="ignore" attribute also allows you to skip link and style tags:

<head>
  <!-- Styles below are ignored -->
  <style data-css-inline="ignore">h1 { color:blue; }</style>
</head>
<body>
  <h1>Big Text</h1>
</body>

Alternatively, you may keep style from being removed by using the data-css-inline="keep" attribute. This is useful if you want to keep @media queries for responsive emails in separate style tags:

<head>
  <!-- Styles below are not removed -->
  <style data-css-inline="keep">h1 { color:blue; }</style>
</head>
<body>
  <h1>Big Text</h1>
</body>

Such tags will be kept in the resulting HTML even if the keep_style_tags option is set to false.

You can also cache external stylesheets to avoid excessive network requests:

import { inline } from "@css-inline/css-inline";

var inlined = inline(
  `
  <html>
    <head>
      <link href="http://127.0.0.1:1234/external.css" rel="stylesheet">
      <style>h1 { color:red }</style>
    </head>
    <body>
      <h1>Test</h1>
    </body>
  </html>
  `,
  { cache: { size: 5 } },
);

Caching is disabled by default.

WebAssembly

css-inline also ships a WebAssembly module built with wasm-bindgen to run in browsers.

<script src="https://unpkg.com/@css-inline/css-inline-wasm"></script>
<script>
    // Initialize the WASM module first
    cssInline.initWasm(fetch('https://unpkg.com/@css-inline/css-inline-wasm/index_bg.wasm'));

    const inlinedHtml = cssInline.inline(`<html>
  <head>
    <style>h1 { color:blue; }</style>
  </head>
  <body>
    <h1>Big Text</h1>
  </body>
</html>`);

    document.getElementById('output').src = inlinedHtml
</script>

NOTE: WASM module currently lacks support for fetching stylesheets from network or filesystem and caching.

Performance

css-inline is powered by efficient tooling from Mozilla's Servo project and significantly outperforms other JavaScript alternatives in terms of speed. Most of the time it achieves over a 3x speed advantage compared to the next fastest alternative.

Here is the performance comparison:

| | Size | css-inline 0.14.0 | css-inline-wasm 0.13.0 | juice 10.0.0 | inline-css 4.0.2 | |-------------|---------|---------------------|--------------------------|-----------------------|----------------------| | Basic | 230 B | 16.82 µs | 20.92 µs (1.24x) | 57.00 µs (3.38x) | 68.43 µs (4.06x) | | Realistic-1 | 8.58 KB | 351.74 µs | 452.28 µs (1.28x) | 859.10 µs (2.44x) | 1.04 ms (2.98x) | | Realistic-2 | 4.3 KB | 203.25 µs | 269.54 µs (1.32x) | 1.02 ms (5.04x) | 861.32 µs (4.23x) |

The "Basic" case was obtained from benchmarking the example from the Usage section.

The benchmarking code is available in the benches/bench.ts file. The benchmarks were conducted using the stable rustc 1.77.1 on Node.js v21.1.0.

License

This project is licensed under the terms of the MIT license.

changelog

Changelog

Unreleased

0.14.3 - 2024-11-14

Fixed

  • Prioritize !important rules when computing element styles. #398

0.14.2 - 2024-11-11

Changed

  • Bump MSRV to 1.70.

Fixed

  • Replace double quotes when merging styles. #392

0.14.1 - 2024-04-27

Fixed

  • Precedence of element styles over other styles. #364

0.14.0 - 2024-04-01

Added

  • External stylesheet caching. #314
  • Inlining to HTML fragments. #335

Changed

  • Update html5ever to 0.27.
  • Update rayon to 1.10.

0.13.0 - 2024-01-19

Added

  • A way to customize resolving remote stylesheets.
  • Support for the data-css-inline="keep" attribute to enforce keeping the style tag.

Changed

  • Replace attohttpc with reqwest to simplify implementing non-blocking stylesheet resolving in the future release.

Fixed

  • Lookups for previous / next siblings, affecting selectors like nth-child. #324

Performance

  • Avoid using binary search on attributes.

0.12.0 - 2023-12-28

Changed

  • Display stylesheet location in network-related errors.
  • Implement std::error::Error::source for InlineError.

Performance

  • Optimize serialization of attributes and text nodes.

0.11.2 - 2023-12-09

Performance

  • Avoid iterating over non-Element nodes.
  • Reuse caches for nth index selectors.

0.11.1 - 2023-12-04

Changed

  • Update indexmap to 2.1.
  • Update cssparser to 0.31.2.
  • Update selectors to 0.25.
  • Bump MSRV to 1.65.

Fixed

  • Replace double quotes in all property values.

Performance

  • Avoid allocation when replacing double quotes in property values.

0.11.0 - 2023-09-26

Added

  • The inline_style_tags option to control whether inlining from "style" tags should be performed. #253

Performance

  • Reuse existing attributes when creating an element during parsing.

Changed

  • Bump MSRV to 1.63.

0.10.5 - 2023-08-30

Performance

  • Pre-allocate space during serialization.
  • Optimized class attribute handling: up to 25% faster for extensive class-dependent selectors.
  • Fast-path class check for shorter class attribute values.
  • Use a Bloom filter to detect if an element has no given class.
  • Avoid allocating a vector during selectors compilation.
  • Use FxHasher in more cases.

Changed

  • Drop usage of memchr.
  • Bump MSRV to 1.62.1.

0.10.4 - 2023-08-12

Fixed

  • Applying new styles only to the first matching tag during styles merging. #224

Performance

  • Fix under-allocating storage for intermediate CSS styles.
  • Perform CSS inlining as late as possible to avoid intermediate allocations. #220

0.10.3 - 2023-07-01

Performance

  • Optimized HTML serialization for a performance boost of up to 25%.

0.10.2 - 2023-06-25

Changed

  • Standardized the formatting of CSS declarations: now consistently using : separator between properties and values.

Performance

  • Various performance improvements.

0.10.1 - 2023-06-18

Performance

  • Use a simpler way for HTML tree traversal.
  • Avoid hashing in some cases.

0.10.0 - 2023-06-16

Added

  • keep_link_tags configuration option.

Changed

  • Replace remove_style_tags with keep_style_tags.

Fixed

  • SECURITY: Passing unescaped strings in attribute values introduced in #184. Previously, escaped values became unescaped on the serialization step.

Removed

  • The inline_style_tags configuration option.

0.9.0 - 2023-06-10

Fixed

  • Serialize all HTML attributes without escaping. #184

Internal

  • Replaced the kuchiki crate with our custom-built HTML tree representation. #176

Performance

  • 30-50% average performance improvement due switch to a custom-built HTML tree representation and serializer.

0.8.5 - 2022-11-10

Added

  • --output-filename-prefix CLI option to control the output files prefix.
  • Support for the file:// URI scheme in base_url. #171

Changed

  • Return 1 exit code if any of the input files were not processed successfully via CLI.

0.8.4 - 2022-11-02

Added

  • data-css-inline="ignore" attribute to ignore CSS inlining. #10

0.8.3 - 2022-10-20

Fixed

  • Ignoring selectors' specificity when applying declarations from different qualified rules. #148

0.8.2 - 2022-07-21

Added

  • New http & file features which give a way to disable resolving external stylesheets and reduce the compiled artifacts size.

Fixed

  • !important rules not overriding inlined styles. #152

0.8.1 - 2022-04-01

Fixed

  • Not respecting specificity in case of inlining overlapping rules like padding and padding-top. #142

Performance

  • Pre-allocate more memory for output HTML to avoid resizing.

0.8.0 - 2022-01-09

Added

  • Separate InlineError::MissingStyleSheet error variant to improve debugging experience. #124

0.7.6 - 2022-01-08

Fixed

  • Invalid handling of double-quoted property values like in font-family: "Open Sans". #129

Performance

  • Use std::fs::read_to_string in CLI to avoid over/under allocating of the input buffer.

0.7.5 - 2021-07-24

Fixed

  • Panic on invalid URLs for remote stylesheets.

0.7.4 - 2021-07-06

Changed

  • Update rayon to 1.5.

Performance

  • Optimize loading of external files.

0.7.3 - 2021-06-24

Performance

  • Avoid allocations in error formatting.

0.7.2 - 2021-06-22

Fixed

  • Incorrect override of exiting style attribute values. #113

Performance

  • Use specialized to_string implementation on &&str.
  • Use ahash.

0.7.1 - 2021-06-10

Fixed

  • Ignored style tags when the document contains multiple of them and the remove_style_tags: true config option is used. #110

0.7.0 - 2021-06-09

Fixed

  • Ignored selectors specificity. #108

0.6.1 - 2020-12-07

Fixed

  • Compatibility with the new cssparser crate version.

Performance

  • Avoid string allocations during converting ParseError to InlineError.

0.6.0 - 2020-11-02

Changed

  • Links to remote stylesheets are deduplicated now.

Fixed

  • Wrong inlined file prefixes handling in CLI. #89

Performance

  • Use Formatter.write_str instead of write! macro in the Display trait implementation for InlineError. #85
  • Use Cow for error messages. #87

0.5.0 - 2020-08-07

Added

  • CSSInliner::options() that implements the Builder pattern. #71

Changed

  • Restrict visibility of items in parser.rs

Performance

  • Avoid string allocation in get_full_url

0.4.0 - 2020-07-13

Added

  • Option to disable processing of "style" tags. #45
  • Option to inline additional CSS. #45

Changed

  • Switch from openssl to rustls in attohttpc dependency. #49

Performance

  • Use codegen-units=1 and lto=fat.
  • Reduce the number of allocations in CLI.
  • Avoid CLI output formatting when it is not needed.

0.3.3 - 2020-07-07

Performance

  • Pre-allocate the output vector.
  • Minor improvement for creating new files via CLI.
  • Reduce the average number of allocations during styles merge by a factor of 5.5x.

0.3.2 - 2020-06-27

Changed

  • Remove debug symbols from the release build

Performance

  • Reduce the number of String allocations.
  • Avoid BTreeMap::insert when style attribute already exists

0.3.1 - 2020-06-25

Changed

  • Fix links in docs

0.3.0 - 2020-06-25

Added

  • Command Line Interface. #33

0.2.0 - 2020-06-25

Added

  • CSSInliner and customization options. #9
  • Option to remove "style" tags (remove_style_tags). Disabled by default. #11
  • CSSInliner::compact() constructor for producing smaller HTML output.
  • CSSInliner.inline_to that writes the output to a generic writer. #24
  • Implement Error for InlineError.
  • Loading external stylesheets. #8
  • Option to control whether remote stylesheets should be loaded (load_remote_stylesheets). Enabled by default.

Changed

  • Improved error messages. #27
  • Skip selectors that can't be parsed.

Fixed

  • Ignore @media queries since they can not be inlined. #7
  • Panic in cases when styles are applied to the currently processed "link" tags.

Performance

  • Improve performance for merging new styles in existing "style" attributes.

0.1.0 - 2020-06-22

  • Initial public release