0
# CSS-Inline
1
2
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.
3
4
## Package Information
5
6
- **Package Name**: css-inline
7
- **Package Type**: cargo
8
- **Language**: Rust
9
- **Installation**: `cargo add css-inline` or add `css-inline = "0.17"` to `Cargo.toml`
10
- **Minimum Rust Version**: 1.75
11
12
## Core Imports
13
14
```rust
15
use css_inline::{inline, inline_fragment, CSSInliner, InlineOptions, Result};
16
use std::io::Write; // Required for *_to functions
17
use std::borrow::Cow; // Required for extra_css
18
```
19
20
For URL handling:
21
```rust
22
use css_inline::Url;
23
```
24
25
For error handling:
26
```rust
27
use css_inline::InlineError;
28
```
29
30
For custom resolvers:
31
```rust
32
use css_inline::{StylesheetResolver, DefaultStylesheetResolver};
33
use std::sync::Arc;
34
```
35
36
For caching (requires `stylesheet-cache` feature):
37
```rust
38
use css_inline::StylesheetCache;
39
use std::num::NonZeroUsize;
40
use std::sync::Mutex; // Required for cache configuration
41
```
42
43
## Architecture
44
45
CSS-Inline follows a modular, pipeline-based architecture designed for high performance:
46
47
**Core Components:**
48
- **CSSInliner**: Main entry point providing configured inlining with options
49
- **InlineOptions**: Builder pattern configuration for customizing behavior
50
- **StylesheetResolver**: Trait-based system for loading stylesheets from various sources
51
- **Document Parser**: HTML5 parser that builds an internal DOM representation
52
- **CSS Parser**: Robust CSS3 parser using Mozilla's cssparser crate
53
- **Selector Engine**: CSS selector matching using Mozilla's selectors crate
54
55
**Processing Pipeline:**
56
1. **Parse HTML**: Document parsed into internal representation with pre-allocated capacity
57
2. **Collect Styles**: Gathers CSS from `<style>` tags, `<link>` tags, and external sources
58
3. **Parse CSS**: CSS parsed into rules with specificity calculation
59
4. **Match Selectors**: Rules matched to elements using selector engine with caching
60
5. **Resolve Conflicts**: Higher specificity and `!important` rules take precedence
61
6. **Inline Styles**: Matched styles merged into element `style` attributes
62
7. **Serialize Output**: Final HTML serialized with inlined styles
63
64
**Key Design Patterns:**
65
- **Builder Pattern**: `InlineOptions` for flexible configuration
66
- **Strategy Pattern**: `StylesheetResolver` trait for pluggable stylesheet loading
67
- **Caching**: Optional LRU cache for external stylesheets to avoid redundant requests
68
- **Error Handling**: Comprehensive error types with source error chaining
69
70
## Basic Usage
71
72
### Simple CSS Inlining
73
74
```rust
75
use css_inline::inline;
76
77
fn main() -> css_inline::Result<()> {
78
let html = r#"<html>
79
<head>
80
<style>h1 { color:blue; }</style>
81
</head>
82
<body>
83
<h1>Big Text</h1>
84
</body>
85
</html>"#;
86
87
let inlined = inline(html)?;
88
println!("{}", inlined);
89
Ok(())
90
}
91
```
92
93
### Fragment Inlining
94
95
```rust
96
use css_inline::inline_fragment;
97
98
fn main() -> css_inline::Result<()> {
99
let fragment = r#"<main>
100
<h1>Hello</h1>
101
<p>Content</p>
102
</main>"#;
103
104
let css = r#"
105
p { color: red; }
106
h1 { color: blue; }
107
"#;
108
109
let inlined = inline_fragment(fragment, css)?;
110
println!("{}", inlined);
111
Ok(())
112
}
113
```
114
115
## Capabilities
116
117
### Simple Inlining Functions
118
119
Convenience functions for basic CSS inlining with default configuration.
120
121
```rust { .api }
122
fn inline(html: &str) -> Result<String>;
123
fn inline_to<W: Write>(html: &str, target: &mut W) -> Result<()>;
124
fn inline_fragment(html: &str, css: &str) -> Result<String>;
125
fn inline_fragment_to<W: Write>(html: &str, css: &str, target: &mut W) -> Result<()>;
126
```
127
128
**Parameters:**
129
- `html: &str` - HTML document or fragment to process
130
- `css: &str` - CSS to inline (for fragment functions)
131
- `target: &mut W` - Writer to output the result to
132
133
**Returns:**
134
- `Result<String>` - Inlined HTML as string
135
- `Result<()>` - Success/failure for writer functions
136
137
### Configuration Options
138
139
The `InlineOptions` struct provides a builder pattern for configuring CSS inlining behavior.
140
141
```rust { .api }
142
impl InlineOptions<'a> {
143
fn inline_style_tags(self, inline_style_tags: bool) -> Self;
144
fn keep_style_tags(self, keep_style_tags: bool) -> Self;
145
fn keep_link_tags(self, keep_link_tags: bool) -> Self;
146
fn keep_at_rules(self, keep_at_rules: bool) -> Self;
147
fn base_url(self, base_url: Option<Url>) -> Self;
148
fn load_remote_stylesheets(self, load_remote_stylesheets: bool) -> Self;
149
fn extra_css(self, extra_css: Option<Cow<'a, str>>) -> Self;
150
fn preallocate_node_capacity(self, preallocate_node_capacity: usize) -> Self;
151
fn resolver(self, resolver: Arc<dyn StylesheetResolver>) -> Self;
152
fn build(self) -> CSSInliner<'a>;
153
}
154
```
155
156
**Configuration Options:**
157
- `inline_style_tags: bool` - Whether to inline CSS from "style" tags (default: `true`)
158
- `keep_style_tags: bool` - Keep "style" tags after inlining (default: `false`)
159
- `keep_link_tags: bool` - Keep "link" tags after inlining (default: `false`)
160
- `keep_at_rules: bool` - Keep "at-rules" after inlining (default: `false`)
161
- `base_url: Option<Url>` - Base URL for resolving relative URLs (default: `None`)
162
- `load_remote_stylesheets: bool` - Whether remote stylesheets should be loaded (default: `true`)
163
- `extra_css: Option<Cow<'a, str>>` - Additional CSS to inline (default: `None`)
164
- `preallocate_node_capacity: usize` - Pre-allocate capacity for HTML nodes (default: `32`)
165
- `resolver: Arc<dyn StylesheetResolver>` - Custom stylesheet resolver (default: `DefaultStylesheetResolver`)
166
167
#### Feature-Gated Configuration (requires `stylesheet-cache` feature)
168
169
```rust { .api }
170
impl InlineOptions<'a> {
171
fn cache(self, cache: impl Into<Option<StylesheetCache>>) -> Self;
172
}
173
```
174
175
- `cache: Option<StylesheetCache>` - External stylesheet cache (default: `None`)
176
177
### Configurable CSS Inliner
178
179
The main `CSSInliner` struct provides the core inlining functionality with customizable options.
180
181
```rust { .api }
182
impl CSSInliner<'a> {
183
fn new(options: InlineOptions<'a>) -> Self;
184
fn options() -> InlineOptions<'a>;
185
fn inline(&self, html: &str) -> Result<String>;
186
fn inline_to<W: Write>(&self, html: &str, target: &mut W) -> Result<()>;
187
fn inline_fragment(&self, html: &str, css: &str) -> Result<String>;
188
fn inline_fragment_to<W: Write>(&self, html: &str, css: &str, target: &mut W) -> Result<()>;
189
}
190
```
191
192
**Usage Example:**
193
```rust
194
use css_inline::{CSSInliner, Url};
195
196
let inliner = CSSInliner::options()
197
.load_remote_stylesheets(false)
198
.keep_style_tags(true)
199
.base_url(Some(Url::parse("https://example.com")?))
200
.build();
201
202
let result = inliner.inline(html)?;
203
```
204
205
### Custom Stylesheet Resolvers
206
207
Implement custom logic for loading stylesheets from various sources.
208
209
```rust { .api }
210
trait StylesheetResolver: Send + Sync {
211
fn retrieve(&self, location: &str) -> Result<String>;
212
fn retrieve_from_url(&self, url: &str) -> Result<String>;
213
fn retrieve_from_path(&self, path: &str) -> Result<String>;
214
fn unsupported(&self, reason: &str) -> InlineError;
215
}
216
```
217
218
**Default Implementation:**
219
```rust { .api }
220
struct DefaultStylesheetResolver;
221
```
222
223
**Usage Example:**
224
```rust
225
use css_inline::{StylesheetResolver, Result, InlineError};
226
use std::sync::Arc;
227
228
#[derive(Debug, Default)]
229
struct CustomResolver;
230
231
impl StylesheetResolver for CustomResolver {
232
fn retrieve(&self, location: &str) -> Result<String> {
233
// Custom implementation
234
if location.starts_with("custom://") {
235
Ok("/* custom CSS */".to_string())
236
} else {
237
Err(self.unsupported("Only custom:// URLs supported"))
238
}
239
}
240
}
241
242
let inliner = css_inline::CSSInliner::options()
243
.resolver(Arc::new(CustomResolver))
244
.build();
245
```
246
247
### Caching (Feature-Gated)
248
249
External stylesheet caching to avoid redundant network requests (requires `stylesheet-cache` feature).
250
251
```rust { .api }
252
type StylesheetCache<S = DefaultHasher> = LruCache<String, String, S>;
253
```
254
255
**Usage Example:**
256
```rust
257
use std::num::NonZeroUsize;
258
259
#[cfg(feature = "stylesheet-cache")]
260
let inliner = css_inline::CSSInliner::options()
261
.cache(css_inline::StylesheetCache::new(
262
NonZeroUsize::new(10).unwrap()
263
))
264
.build();
265
```
266
267
## Types
268
269
### Result Type
270
271
```rust { .api }
272
type Result<T> = std::result::Result<T, InlineError>;
273
```
274
275
### Error Types
276
277
```rust { .api }
278
enum InlineError {
279
MissingStyleSheet { path: String },
280
IO(std::io::Error),
281
#[cfg(feature = "http")]
282
Network { error: reqwest::Error, location: String },
283
ParseError(Cow<'static, str>),
284
}
285
```
286
287
**Error Variants:**
288
- `MissingStyleSheet` - Stylesheet file not found
289
- `IO` - File system or I/O error
290
- `Network` - Network-related error (requires `http` feature)
291
- `ParseError` - CSS parsing or selector error
292
293
### Configuration Structures
294
295
```rust { .api }
296
struct InlineOptions<'a> {
297
pub inline_style_tags: bool,
298
pub keep_style_tags: bool,
299
pub keep_link_tags: bool,
300
pub keep_at_rules: bool,
301
pub base_url: Option<Url>,
302
pub load_remote_stylesheets: bool,
303
#[cfg(feature = "stylesheet-cache")]
304
pub cache: Option<Mutex<StylesheetCache>>,
305
pub extra_css: Option<Cow<'a, str>>,
306
pub preallocate_node_capacity: usize,
307
pub resolver: Arc<dyn StylesheetResolver>,
308
}
309
310
struct CSSInliner<'a> {
311
options: InlineOptions<'a>,
312
}
313
314
// Both implement Default
315
impl Default for InlineOptions<'_>;
316
impl Default for CSSInliner<'_>;
317
```
318
319
### Re-exported Types
320
321
```rust { .api }
322
// From url crate
323
struct Url;
324
enum ParseError;
325
```
326
327
## HTML Attributes
328
329
CSS-Inline recognizes special HTML attributes for controlling inlining behavior:
330
331
- `data-css-inline="ignore"` - Skip CSS inlining for this element or skip processing of `<style>`/`<link>` tags
332
- `data-css-inline="keep"` - Keep `<style>` tags even when `keep_style_tags` is `false`
333
334
**Example:**
335
```html
336
<style data-css-inline="ignore">h1 { color: blue; }</style>
337
<h1 data-css-inline="ignore">Not styled</h1>
338
<style data-css-inline="keep">@media (max-width: 600px) { h1 { font-size: 18px; } }</style>
339
```
340
341
## Feature Flags
342
343
CSS-Inline provides optional feature flags:
344
345
- `http` (default) - Enables remote stylesheet loading via HTTP/HTTPS
346
- `file` (default) - Enables local file stylesheet loading
347
- `stylesheet-cache` (default) - Enables external stylesheet caching
348
- `cli` (default) - Enables command-line interface
349
- `default` - Includes all above features
350
351
**Usage in Cargo.toml:**
352
```toml
353
[dependencies]
354
css-inline = { version = "0.17", default-features = false, features = ["http"] }
355
```