High-performance library for inlining CSS into HTML 'style' attributes
pkg:cargo/css-inline@0.17.x
npx @tessl/cli install tessl/cargo-css-inline@0.17.0CSS-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.
cargo add css-inline or add css-inline = "0.17" to Cargo.tomluse css_inline::{inline, inline_fragment, CSSInliner, InlineOptions, Result};
use std::io::Write; // Required for *_to functions
use std::borrow::Cow; // Required for extra_cssFor 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 configurationCSS-Inline follows a modular, pipeline-based architecture designed for high performance:
Core Components:
Processing Pipeline:
<style> tags, <link> tags, and external sources!important rules take precedencestyle attributesKey Design Patterns:
InlineOptions for flexible configurationStylesheetResolver trait for pluggable stylesheet loadinguse 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(())
}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(())
}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 processcss: &str - CSS to inline (for fragment functions)target: &mut W - Writer to output the result toReturns:
Result<String> - Inlined HTML as stringResult<()> - Success/failure for writer functionsThe 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)stylesheet-cache feature)impl InlineOptions<'a> {
fn cache(self, cache: impl Into<Option<StylesheetCache>>) -> Self;
}cache: Option<StylesheetCache> - External stylesheet cache (default: None)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)?;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();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();type Result<T> = std::result::Result<T, InlineError>;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 foundIO - File system or I/O errorNetwork - Network-related error (requires http feature)ParseError - CSS parsing or selector errorstruct 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<'_>;// From url crate
struct Url;
enum ParseError;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> tagsdata-css-inline="keep" - Keep <style> tags even when keep_style_tags is falseExample:
<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>CSS-Inline provides optional feature flags:
http (default) - Enables remote stylesheet loading via HTTP/HTTPSfile (default) - Enables local file stylesheet loadingstylesheet-cache (default) - Enables external stylesheet cachingcli (default) - Enables command-line interfacedefault - Includes all above featuresUsage in Cargo.toml:
[dependencies]
css-inline = { version = "0.17", default-features = false, features = ["http"] }