or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

index.md
tile.json

tessl/cargo-css-inline

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

Workspace
tessl
Visibility
Public
Created
Last updated
Describes

pkg:cargo/css-inline@0.17.x

To install, run

npx @tessl/cli install tessl/cargo-css-inline@0.17.0

index.mddocs/

CSS-Inline

CSS-Inline is a high-performance Rust library for inlining CSS into HTML 'style' attributes. It transforms HTML documents by moving CSS rules from <style> and <link> tags directly into the style attributes of matching HTML elements, making it ideal for preparing HTML emails or embedding HTML into third-party web pages.

Package Information

  • Package Name: css-inline
  • Package Type: cargo
  • Language: Rust
  • Installation: cargo add css-inline or add css-inline = "0.17" to Cargo.toml
  • Minimum Rust Version: 1.75

Core Imports

use css_inline::{inline, inline_fragment, CSSInliner, InlineOptions, Result};
use std::io::Write; // Required for *_to functions
use std::borrow::Cow; // Required for extra_css

For URL handling:

use css_inline::Url;

For error handling:

use css_inline::InlineError;

For custom resolvers:

use css_inline::{StylesheetResolver, DefaultStylesheetResolver};
use std::sync::Arc;

For caching (requires stylesheet-cache feature):

use css_inline::StylesheetCache;
use std::num::NonZeroUsize;
use std::sync::Mutex; // Required for cache configuration

Architecture

CSS-Inline follows a modular, pipeline-based architecture designed for high performance:

Core Components:

  • CSSInliner: Main entry point providing configured inlining with options
  • InlineOptions: Builder pattern configuration for customizing behavior
  • StylesheetResolver: Trait-based system for loading stylesheets from various sources
  • Document Parser: HTML5 parser that builds an internal DOM representation
  • CSS Parser: Robust CSS3 parser using Mozilla's cssparser crate
  • Selector Engine: CSS selector matching using Mozilla's selectors crate

Processing Pipeline:

  1. Parse HTML: Document parsed into internal representation with pre-allocated capacity
  2. Collect Styles: Gathers CSS from <style> tags, <link> tags, and external sources
  3. Parse CSS: CSS parsed into rules with specificity calculation
  4. Match Selectors: Rules matched to elements using selector engine with caching
  5. Resolve Conflicts: Higher specificity and !important rules take precedence
  6. Inline Styles: Matched styles merged into element style attributes
  7. Serialize Output: Final HTML serialized with inlined styles

Key Design Patterns:

  • Builder Pattern: InlineOptions for flexible configuration
  • Strategy Pattern: StylesheetResolver trait for pluggable stylesheet loading
  • Caching: Optional LRU cache for external stylesheets to avoid redundant requests
  • Error Handling: Comprehensive error types with source error chaining

Basic Usage

Simple CSS Inlining

use css_inline::inline;

fn main() -> css_inline::Result<()> {
    let html = r#"<html>
<head>
    <style>h1 { color:blue; }</style>
</head>
<body>
    <h1>Big Text</h1>
</body>
</html>"#;

    let inlined = inline(html)?;
    println!("{}", inlined);
    Ok(())
}

Fragment Inlining

use css_inline::inline_fragment;

fn main() -> css_inline::Result<()> {
    let fragment = r#"<main>
<h1>Hello</h1>
<p>Content</p>
</main>"#;

    let css = r#"
p { color: red; }
h1 { color: blue; }
"#;

    let inlined = inline_fragment(fragment, css)?;
    println!("{}", inlined);
    Ok(())
}

Capabilities

Simple Inlining Functions

Convenience functions for basic CSS inlining with default configuration.

fn inline(html: &str) -> Result<String>;
fn inline_to<W: Write>(html: &str, target: &mut W) -> Result<()>;
fn inline_fragment(html: &str, css: &str) -> Result<String>;
fn inline_fragment_to<W: Write>(html: &str, css: &str, target: &mut W) -> Result<()>;

Parameters:

  • html: &str - HTML document or fragment to process
  • css: &str - CSS to inline (for fragment functions)
  • target: &mut W - Writer to output the result to

Returns:

  • Result<String> - Inlined HTML as string
  • Result<()> - Success/failure for writer functions

Configuration Options

The InlineOptions struct provides a builder pattern for configuring CSS inlining behavior.

impl InlineOptions<'a> {
    fn inline_style_tags(self, inline_style_tags: bool) -> Self;
    fn keep_style_tags(self, keep_style_tags: bool) -> Self;
    fn keep_link_tags(self, keep_link_tags: bool) -> Self;
    fn keep_at_rules(self, keep_at_rules: bool) -> Self;
    fn base_url(self, base_url: Option<Url>) -> Self;
    fn load_remote_stylesheets(self, load_remote_stylesheets: bool) -> Self;
    fn extra_css(self, extra_css: Option<Cow<'a, str>>) -> Self;
    fn preallocate_node_capacity(self, preallocate_node_capacity: usize) -> Self;
    fn resolver(self, resolver: Arc<dyn StylesheetResolver>) -> Self;
    fn build(self) -> CSSInliner<'a>;
}

Configuration Options:

  • inline_style_tags: bool - Whether to inline CSS from "style" tags (default: true)
  • keep_style_tags: bool - Keep "style" tags after inlining (default: false)
  • keep_link_tags: bool - Keep "link" tags after inlining (default: false)
  • keep_at_rules: bool - Keep "at-rules" after inlining (default: false)
  • base_url: Option<Url> - Base URL for resolving relative URLs (default: None)
  • load_remote_stylesheets: bool - Whether remote stylesheets should be loaded (default: true)
  • extra_css: Option<Cow<'a, str>> - Additional CSS to inline (default: None)
  • preallocate_node_capacity: usize - Pre-allocate capacity for HTML nodes (default: 32)
  • resolver: Arc<dyn StylesheetResolver> - Custom stylesheet resolver (default: DefaultStylesheetResolver)

Feature-Gated Configuration (requires stylesheet-cache feature)

impl InlineOptions<'a> {
    fn cache(self, cache: impl Into<Option<StylesheetCache>>) -> Self;
}
  • cache: Option<StylesheetCache> - External stylesheet cache (default: None)

Configurable CSS Inliner

The main CSSInliner struct provides the core inlining functionality with customizable options.

impl CSSInliner<'a> {
    fn new(options: InlineOptions<'a>) -> Self;
    fn options() -> InlineOptions<'a>;
    fn inline(&self, html: &str) -> Result<String>;
    fn inline_to<W: Write>(&self, html: &str, target: &mut W) -> Result<()>;
    fn inline_fragment(&self, html: &str, css: &str) -> Result<String>;
    fn inline_fragment_to<W: Write>(&self, html: &str, css: &str, target: &mut W) -> Result<()>;
}

Usage Example:

use css_inline::{CSSInliner, Url};

let inliner = CSSInliner::options()
    .load_remote_stylesheets(false)
    .keep_style_tags(true)
    .base_url(Some(Url::parse("https://example.com")?))
    .build();

let result = inliner.inline(html)?;

Custom Stylesheet Resolvers

Implement custom logic for loading stylesheets from various sources.

trait StylesheetResolver: Send + Sync {
    fn retrieve(&self, location: &str) -> Result<String>;
    fn retrieve_from_url(&self, url: &str) -> Result<String>;
    fn retrieve_from_path(&self, path: &str) -> Result<String>;
    fn unsupported(&self, reason: &str) -> InlineError;
}

Default Implementation:

struct DefaultStylesheetResolver;

Usage Example:

use css_inline::{StylesheetResolver, Result, InlineError};
use std::sync::Arc;

#[derive(Debug, Default)]
struct CustomResolver;

impl StylesheetResolver for CustomResolver {
    fn retrieve(&self, location: &str) -> Result<String> {
        // Custom implementation
        if location.starts_with("custom://") {
            Ok("/* custom CSS */".to_string())
        } else {
            Err(self.unsupported("Only custom:// URLs supported"))
        }
    }
}

let inliner = css_inline::CSSInliner::options()
    .resolver(Arc::new(CustomResolver))
    .build();

Caching (Feature-Gated)

External stylesheet caching to avoid redundant network requests (requires stylesheet-cache feature).

type StylesheetCache<S = DefaultHasher> = LruCache<String, String, S>;

Usage Example:

use std::num::NonZeroUsize;

#[cfg(feature = "stylesheet-cache")]
let inliner = css_inline::CSSInliner::options()
    .cache(css_inline::StylesheetCache::new(
        NonZeroUsize::new(10).unwrap()
    ))
    .build();

Types

Result Type

type Result<T> = std::result::Result<T, InlineError>;

Error Types

enum InlineError {
    MissingStyleSheet { path: String },
    IO(std::io::Error),
    #[cfg(feature = "http")]
    Network { error: reqwest::Error, location: String },
    ParseError(Cow<'static, str>),
}

Error Variants:

  • MissingStyleSheet - Stylesheet file not found
  • IO - File system or I/O error
  • Network - Network-related error (requires http feature)
  • ParseError - CSS parsing or selector error

Configuration Structures

struct InlineOptions<'a> {
    pub inline_style_tags: bool,
    pub keep_style_tags: bool,
    pub keep_link_tags: bool,
    pub keep_at_rules: bool,
    pub base_url: Option<Url>,
    pub load_remote_stylesheets: bool,
    #[cfg(feature = "stylesheet-cache")]
    pub cache: Option<Mutex<StylesheetCache>>,
    pub extra_css: Option<Cow<'a, str>>,
    pub preallocate_node_capacity: usize,
    pub resolver: Arc<dyn StylesheetResolver>,
}

struct CSSInliner<'a> {
    options: InlineOptions<'a>,
}

// Both implement Default
impl Default for InlineOptions<'_>;
impl Default for CSSInliner<'_>;

Re-exported Types

// From url crate
struct Url;
enum ParseError;

HTML Attributes

CSS-Inline recognizes special HTML attributes for controlling inlining behavior:

  • data-css-inline="ignore" - Skip CSS inlining for this element or skip processing of <style>/<link> tags
  • data-css-inline="keep" - Keep <style> tags even when keep_style_tags is false

Example:

<style data-css-inline="ignore">h1 { color: blue; }</style>
<h1 data-css-inline="ignore">Not styled</h1>
<style data-css-inline="keep">@media (max-width: 600px) { h1 { font-size: 18px; } }</style>

Feature Flags

CSS-Inline provides optional feature flags:

  • http (default) - Enables remote stylesheet loading via HTTP/HTTPS
  • file (default) - Enables local file stylesheet loading
  • stylesheet-cache (default) - Enables external stylesheet caching
  • cli (default) - Enables command-line interface
  • default - Includes all above features

Usage in Cargo.toml:

[dependencies]
css-inline = { version = "0.17", default-features = false, features = ["http"] }