MCPcopy Index your code
hub / github.com/Stranger6667/css-inline

github.com/Stranger6667/css-inline @c-v0.21.0

Chat with this repo
repository ↗ · DeepWiki ↗ · release c-v0.21.0 ↗ · + Follow
822 symbols 2,140 edges 70 files 90 documented · 11%
What it actually does AI analysis from the code graph — generated when you open this
loading…
README

css-inline

build status crates.io docs.rs 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 crate 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>
  • Extremely fast with a minimal memory footprint
  • 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
  • Bindings for Python, Ruby, JavaScript, Java, C, PHP, and a WebAssembly module to run in browsers.
  • Elixir bindings maintained by Knock
  • Command Line Interface

Playground

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

Installation

To include it in your project, add the following line to the dependencies section in your project's Cargo.toml file:

[dependencies]
css-inline = "0.20"

The Minimum Supported Rust Version is 1.85.

Usage

const HTML: &str = r#"<html>
<head>
    <style>h1 { color:blue; }</style>
</head>
<body>
    <h1>Big Text</h1>
</body>
</html>"#;

fn main() -> css_inline::Result<()> {
    let inlined = css_inline::inline(HTML)?;
    // Do something with inlined HTML, e.g. send an email
    Ok(())
}

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. Structural tags (<html>, <head>, <body>) are stripped from the output; only their contents are preserved. Use inline if you need to keep the full document structure:

const FRAGMENT: &str = r#"<main>
<h1>Hello</h1>
<section>


who am i


</section>
</main>"#;

const CSS: &str = r#"
p {
    color: red;
}

h1 {
    color: blue;
}
"#;

fn main() -> css_inline::Result<()> {
    let inlined = css_inline::inline_fragment(FRAGMENT, CSS)?;
    Ok(())
}

Configuration

css-inline can be configured by using CSSInliner::options() that implements the Builder pattern:

const HTML: &str = "...";

fn main() -> css_inline::Result<()> {
    let inliner = css_inline::CSSInliner::options()
        .load_remote_stylesheets(false)
        .build();
    let inlined = inliner.inline(HTML)?;
    // Do something with inlined HTML, e.g. send an email
    Ok(())
}
  • inline_style_tags. Specifies whether to inline CSS from "style" tags. Default: true
  • keep_style_tags. Specifies whether to keep "style" tags after inlining. Default: false
  • keep_link_tags. Specifies whether to keep "link" tags after inlining. Default: false
  • keep_at_rules. Specifies whether to keep "at-rules" (starting with @) after inlining. Default: false
  • minify_css. Specifies whether to remove trailing semicolons and spaces between properties and values. Default: false
  • base_url. The base URL used to resolve relative URLs. If you'd like to load stylesheets from your filesystem, use the file:// scheme. Default: None
  • load_remote_stylesheets. Specifies whether remote stylesheets should be loaded. Default: true
  • cache. Specifies cache for external stylesheets. Default: None
  • extra_css. Extra CSS to be inlined. Default: None
  • preallocate_node_capacity. 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
  • remove_inlined_selectors. Specifies whether to remove selectors that were successfully inlined from <style> blocks. Default: false
  • apply_width_attributes. Specifies whether to add width HTML attributes from CSS width properties on supported elements (table, td, th, img). Default: false
  • apply_height_attributes. Specifies whether to add height HTML attributes from CSS height properties on supported elements (table, td, th, img). Default: false

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>

  <h1 data-css-inline="ignore">Big Text</h1>
</body>

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

<head>

  <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. Such tags will be kept in the resulting HTML even if the keep_style_tags option is set to false.

<head>

  <style data-css-inline="keep">h1 { color:blue; }</style>
</head>
<body>
  <h1>Big Text</h1>
</body>

Another possibility is to set keep_at_rules option to true. At-rules cannot be inlined into HTML therefore they get removed by default. This is useful if you want to keep at-rules, e.g. @media queries for responsive emails in separate style tags but inline any styles which can be inlined. Such tags will be kept in the resulting HTML even if the keep_style_tags option is explicitly set to false.

<head>

  <style>h1 { color: blue; } @media (max-width: 600px) { h1 { font-size: 18px; } }</style>
</head>
<body>
  <h1>Big Text</h1>
</body>

If you set the the minify_css option to true, the inlined styles will be minified by removing trailing semicolons and spaces between properties and values.

<head>

  <style>h1 { color: blue; font-weight: bold; }</style>
</head>
<body>
  <h1>Big Text</h1>
</body>

If you'd like to load stylesheets from your filesystem, use the file:// scheme:

const HTML: &str = "...";

fn main() -> css_inline::Result<()> {
    let base_url = css_inline::Url::parse("file://styles/email/").expect("Invalid URL");
    let inliner = css_inline::CSSInliner::options()
        .base_url(Some(base_url))
        .build();
    let inlined = inliner.inline(HTML);
    // Do something with inlined HTML, e.g. send an email
    Ok(())
}

For resolving remote stylesheets it is possible to implement a custom resolver:

#[derive(Debug, Default)]
pub struct CustomStylesheetResolver;

impl css_inline::StylesheetResolver for CustomStylesheetResolver {
    fn retrieve(&self, location: &str) -> css_inline::Result<String> {
        Err(self.unsupported("External stylesheets are not supported"))
    }
}

fn main() -> css_inline::Result<()> {
    let inliner = css_inline::CSSInliner::options()
        .resolver(std::sync::Arc::new(CustomStylesheetResolver))
        .build();
    Ok(())
}

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

use std::num::NonZeroUsize;

#[cfg(feature = "stylesheet-cache")]
fn main() -> css_inline::Result<()> {
    let inliner = css_inline::CSSInliner::options()
        .cache(
            // This is an LRU cache
            css_inline::StylesheetCache::new(
                NonZeroUsize::new(5).expect("Invalid cache size")
            )
        )
        .build();
    Ok(())
}

// This block is here for testing purposes
#[cfg(not(feature = "stylesheet-cache"))]
fn main() -> css_inline::Result<()> {
    Ok(())
}

Caching is disabled by default.

Performance

css-inline typically inlines HTML emails within hundreds of microseconds, though results may vary with input complexity.

Benchmarks for css-inline==0.20.0:

  • Basic: 4.09 µs, 230 bytes
  • Realistic-1: 78.94 µs, 8.58 KB
  • Realistic-2: 48.56 µs, 4.3 KB
  • GitHub page: 16.78 ms, 1.81 MB

These benchmarks, conducted using rustc 1.91 on Ryzen 9 9950X, can be found in css-inline/benches/inliner.rs.

Command Line Interface

Installation

Install with cargo:

cargo install css-inline

Usage

The following command inlines CSS in multiple documents in parallel. The resulting files will be saved as inlined.email1.html and inlined.email2.html:

css-inline email1.html email2.html

For full details of the options available, you can use the --help flag:

css-inline --help

Further reading

If you're interested in learning how this library was created and how it works internally, check out these articles:

Support

If you have any questions or discussions related to this library, please join our gitter!

License

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

Extension points exported contracts — how you extend this code

StylesheetResolver (Interface)
Blocking way of resolving stylesheets from various sources. [4 implementers]
css-inline/src/resolver.rs
JNIExt (Interface)
(no doc) [1 implementers]
bindings/java/src/main/rust/lib.rs
Options (Interface)
(no doc)
bindings/javascript/js-binding.d.ts
StylesheetCache (Interface)
(no doc)
bindings/javascript/js-binding.d.ts
Options (Interface)
(no doc)
bindings/javascript/index.d.ts
StylesheetCache (Interface)
(no doc)
bindings/javascript/index.d.ts
InlineOptions (Interface)
(no doc)
bindings/javascript/wasm/index.d.ts

Core symbols most depended-on inside this repo

push
called by 97
css-inline/src/lib.rs
inline
called by 74
css-inline/src/lib.rs
build
called by 64
css-inline/src/lib.rs
as_bytes
called by 33
css-inline/src/html/selectors/local_name.rs
iter
called by 31
css-inline/src/html/selectors/mod.rs
get
called by 30
css-inline/src/html/node.rs
contains
called by 27
css-inline/src/html/attributes.rs
remove_inlined_selectors
called by 25
css-inline/src/lib.rs

Shape

Function 429
Method 304
Class 70
Enum 12
Interface 7

Languages

Rust73%
TypeScript10%
Java6%
PHP6%
Python3%
C1%

Modules by API surface

css-inline/tests/test_inlining.rs131 symbols
css-inline/src/lib.rs50 symbols
css-inline/tests/test_selectors.rs42 symbols
css-inline/src/html/document.rs42 symbols
css-inline/src/html/serializer.rs37 symbols
css-inline/src/html/element.rs34 symbols
css-inline/tests/test_cli.rs28 symbols
bindings/php/tests/CssInlineTest.php28 symbols
css-inline/src/html/parser.rs27 symbols
bindings/javascript/wasm/index.min.js24 symbols
bindings/javascript/wasm/index.js24 symbols
bindings/ruby/ext/css_inline/src/lib.rs22 symbols

For agents

$ claude mcp add css-inline \
  -- python -m otcore.mcp_server <graph>

⬇ download graph artifact