css-inline
High-performance library for inlining CSS into HTML style attributes
Repository Health
Technical Analysis
css-inline takes an HTML document and its associated CSS (from <style> tags, linked stylesheets, or passed explicitly) and rewrites it so every matched rule is applied directly as an inline style attribute on the corresponding element. This is the standard fix for HTML email, since most email clients strip or ignore <style> blocks and only honor inline styles — css-inline handles selector matching, specificity, cascade order, and CSS minification to produce email-safe HTML.
The core is written in Rust on top of html5ever and cssparser/selectors (the same parsing crates used by Servo), making it fast enough to inline thousands of emails per second, and the project ships first-class bindings for Python, Ruby, Node.js/WASM, PHP, Java, and C, plus a standalone CLI binary — so the same inlining engine can be dropped into almost any email-sending pipeline regardless of language.
What You Get
- An
Inlinerbuilder API (inline_style_tags,keep_style_tags,keep_link_tags,base_url,load_remote_stylesheets,minify_css) for configuring inlining behavior - Correct CSS cascade and specificity resolution using the same
selectors/cssparserengines that power Servo/Firefox - Optional remote stylesheet loading over HTTP via a
reqwest-backed feature flag, with a local LRU stylesheet cache - A standalone
css-inlineCLI binary for inlining files or piped HTML from the shell - Official bindings for Python, Ruby, Node.js (WASM), PHP, Java, and C, sharing the same Rust core for consistent behavior across languages
Common Use Cases
- Preparing transactional or marketing HTML emails so styles render correctly in Outlook, Gmail, and other clients that strip
<style>tags - Batch-inlining thousands of templated emails at send time without a Node.js/JS build-step dependency
- Converting a normal CSS-linked HTML template into a self-contained inlined document for embedding or archiving
- Using the CLI in a CI/build pipeline to pre-process email templates before they’re committed or deployed
- Calling the shared Rust core from Python/Ruby/PHP email-sending code paths that need the same inlining semantics as the CLI
Under The Hood
Architecture — lib.rs implements the core pipeline: parse HTML into a DOM (via html5ever), collect and chunk CSS rules (CssChunk, compute_rule_chunk_indices), match each rule’s selectors against DOM nodes using the selectors crate, then rewrite or append style attribute content per node (rewrite_style_blocks, overwrite_style_node), with resolver.rs handling remote/local stylesheet fetching and parser.rs handling CSS-specific parsing concerns. Tech Stack — Pure Rust (edition 2021, MSRV 1.85) built on cssparser, html5ever, and selectors (the Servo/Firefox CSS engine components), with optional features gating reqwest (remote stylesheets), rayon (CLI parallelism), and lru (stylesheet caching) so consumers only pay for what they use; bindings/ contains separate crates/packages per target language (Python, Ruby, Node/WASM, PHP, Java, C) wrapping the same core. Code Quality — Dedicated test suites cover inlining correctness and CSS selector edge cases (test_inlining.rs, test_selectors.rs) plus CLI behavior (test_cli.rs), with codspeed-criterion-compat benchmarks tracking performance regressions in CI — a notable rigor level for a library whose value proposition is largely speed. API Design — The builder pattern (CSSInliner::options().keep_style_tags(true).inline(&html)) keeps common cases to one line while exposing every cascade/remote-loading knob explicitly; the same semantics are mirrored across all language bindings, so switching from the Python package to the Rust crate requires no behavioral relearning.