Web

CSS inliner

CSS inlined into HTML for email: style blocks and extra CSS written into each element's style attribute, with nothing ever fetched.

Version 1.0.0 · 3 functions · 1 crate

#email#css#html#templates

Functions

What use css_inline.qwer gives a program, with the types Quillo sees. To take only some, or under other names: use css_inline, css_inline_with from css_inline (Quillo 0.6).

css_inline(html: str) → stror an error

The HTML with its <style> rules inlined into style attributes (specificity and !important respected), the <style> blocks then removed. <link rel="stylesheet"> to other files are not fetched. Media queries cannot be inlined: use css_inline_with with keep_style_tags to keep them for clients that read them.

css_inline_with(html: str, extra_css: str, keep_style_tags: bool) → stror an error

As css_inline, with extra_css applied as well (your email theme, kept apart from the templates; "" for none), and the <style> blocks kept when keep_style_tags (for @media rules in clients that support them).

css_inline_fragment(html: str, css: str) → stror an error

A fragment of HTML (no <html> or <head>, as a template part or a CMS block) with css inlined into it: gives the fragment back, styled.

Get it

$ quillo install css_inline               # css_inline.qwer into this folder$ quillo install css_inline --example     # and its example, css_inline.qwe

Or with curl:

$ curl -O https://quillolang.org/plugins/files/css_inline.qwer$ curl -O https://quillolang.org/plugins/files/css_inline.qwe

Then, in your program, all of it or only what you call:

main.qwe
use css_inline.qweruse css_inline, css_inline_with from css_inline      // Quillo 0.6: only these

It uses this crate, which cargo fetches the first time the plugin is built:

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

Example

css_inline.qwe
// An order email written with a <style> block and classes, as a designer would, and inlined// before it is sent, as email clients need.////   quillo run css_inline.qwe      then open http://localhost:8080/emailuse "css_inline.qwer"let EMAIL = `<html><head><style>  body { font-family: Helvetica, Arial, sans-serif; color: #222; }  .button { background: #1a73e8; color: #fff !important; padding: 12px 20px; text-decoration: none; }  td.total { font-weight: bold; text-align: right; }  p { margin: 0 0 12px; }  @media (max-width: 600px) { .wide { width: 100% !important; } }</style></head><body><p>Thanks for your order, Ada.</p><table class="wide"><tr><td>Notebook × 2</td><td class="total">€9.98</td></tr></table><p><a class="button" href="https://shop.example/orders/1042" style="color: yellow">See your order</a></p></body></html>`route GET /email = respond(200, css_inline(EMAIL)?, { "content-type": "text/html; charset=utf-8" })route GET /themed = respond(200, css_inline_with("<html><body><h1>Hello</h1><p class=note>Small print</p></body></html>", "h1 { color: #0a7; margin: 0 } .note { font-size: 12px; color: #888 }", false)?, { "content-type": "text/plain" })route GET /fragment = { html: css_inline_fragment("<p class=lead>Big news</p><p>Details</p>", "p { margin: 0 } .lead { font-size: 20px }")? }
$ quillo run css_inline.qwe$ curl 'localhost:8080/email'$ curl 'localhost:8080/themed'$ curl 'localhost:8080/fragment'

Source

css_inline.qwer
//! CSS inlined into HTML for email: the rules of `<style>` blocks (and extra CSS you give) written into each element's `style` attribute, as Gmail and Outlook need. It never fetches anything: remote stylesheets are left out.//! version: 1.0.0//!//! [dependencies]//! css-inline = { version = "0.14", default-features = false }use std::borrow::Cow;const MAX_HTML: usize = 16 << 20;fn size(html: &str, css: &str) -> Result<(), String> {    if html.len() + css.len() > MAX_HTML {        return Err("the HTML and CSS are at most 16 MB".into());    }    Ok(())}/// The HTML with its `<style>` rules inlined into `style` attributes (specificity and `!important` respected), the `<style>` blocks then removed./// `<link rel="stylesheet">` to other files are not fetched. Media queries cannot be inlined: use `css_inline_with` with `keep_style_tags` to keep them for clients that read them.pub fn css_inline(html: &str) -> Result<String, String> {    size(html, "")?;    css_inline::CSSInliner::options().load_remote_stylesheets(false).build().inline(html).map_err(|e| e.to_string())}/// As `css_inline`, with `extra_css` applied as well (your email theme, kept apart from the templates; "" for none), and the `<style>` blocks kept when `keep_style_tags` (for `@media` rules in clients that support them).pub fn css_inline_with(html: &str, extra_css: &str, keep_style_tags: bool) -> Result<String, String> {    size(html, extra_css)?;    let extra = if extra_css.trim().is_empty() { None } else { Some(Cow::Borrowed(extra_css)) };    css_inline::CSSInliner::options()        .load_remote_stylesheets(false)        .keep_style_tags(keep_style_tags)        .extra_css(extra)        .build()        .inline(html)        .map_err(|e| e.to_string())}/// A fragment of HTML (no `<html>` or `<head>`, as a template part or a CMS block) with `css` inlined into it: gives the fragment back, styled.pub fn css_inline_fragment(html: &str, css: &str) -> Result<String, String> {    size(html, css)?;    // Inlined as a whole page around the fragment, then the body's content taken back out    // (the crate's own fragment mode kept only the first element of a fragment).    let css = css.replace("</style", "<\\/style");    let page = format!("<html><head><style>{}</style></head><body>{}</body></html>", css, html);    let out = css_inline::CSSInliner::options().load_remote_stylesheets(false).build().inline(&page).map_err(|e| e.to_string())?;    let start = out.find("<body").and_then(|i| out[i..].find('>').map(|j| i + j + 1)).ok_or("the fragment could not be read back")?;    let end = out.rfind("</body>").unwrap_or(out.len());    Ok(out[start..end.max(start)].to_string())}
Thread
76Useful? Like it, or add to its thread below.

Thread 0 posts

On CSS inliner: questions, tips, corrections, and code that uses it.

No posts yet. The first one opens the thread.