Skip to main content

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

Project description

css_inline

build status pypi versions license codecov.io gitter

css_inline is a high-performance library for inlining CSS into HTML 'style' attributes.

This library is designed for scenarios such as preparing HTML emails or embedding HTML into third-party web pages.

For instance, the library transforms HTML like this:

<html>
  <head>
    <style>h1 { color:blue; }</style>
  </head>
  <body>
    <h1>Big Text</h1>
  </body>
</html>

into:

<html>
  <head></head>
  <body>
    <h1 style="color:blue;">Big Text</h1>
  </body>
</html>
  • Uses reliable components from Mozilla's Servo project
  • 10-500x faster than alternatives
  • Inlines CSS from style and link tags
  • Removes style and link tags
  • Resolves external stylesheets (including local files)
  • Optionally caches external stylesheets
  • Can process multiple documents in parallel
  • Works on Linux, Windows, macOS and in the browser via PyOdide
  • Supports HTML5 & CSS3
  • Tested on CPython 3.9, 3.10, 3.11, 3.12, 3.13, 3.14 and PyPy 3.11.

Playground

If you'd like to try css-inline, you can check the WebAssembly-powered playground to see the results instantly.

Installation

Install with pip:

pip install css_inline

Pre-compiled wheels are available for most popular platforms. If not available for your platform, a Rust compiler will be needed to build this package from source. Rust version 1.65 or higher is required.

Usage

import css_inline

HTML = """<html>
<head>
    <style>h1 { color:blue; }</style>
</head>
<body>
    <h1>Big Text</h1>
</body>
</html>"""

inlined = css_inline.inline(HTML)
# HTML becomes this:
#
# <html>
# <head>
#    <style>h1 { color:blue; }</style>
# </head>
# <body>
#     <h1 style="color:blue;">Big Text</h1>
# </body>
# </html>

Note that css-inline automatically adds missing html and body tags, so the output is a valid HTML document.

Alternatively, you can inline CSS into an HTML fragment, preserving the original structure:

FRAGMENT = """<main>
<h1>Hello</h1>
<section>
<p>who am i</p>
</section>
</main>"""

CSS = """
p {
    color: red;
}

h1 {
    color: blue;
}
"""

inlined = css_inline.inline_fragment(FRAGMENT, CSS)
# HTML becomes this:
# <main>
# <h1 style="color: blue;">Hello</h1>
# <section>
# <p style="color: red;">who am i</p>
# </section>
# </main>

When there is a need to inline multiple HTML documents simultaneously, css_inline offers inline_many and inline_many_fragments functions. This feature allows for concurrent processing of several inputs, significantly improving performance when dealing with a large number of documents.

import css_inline

css_inline.inline_many(["<...>", "<...>"])

Under the hood, inline_many, spawns threads at the Rust layer to handle the parallel processing of inputs. This results in faster execution times compared to employing parallel processing techniques at the Python level.

Note: To fully benefit from inline_many, you should run your application on a multicore machine.

Configuration

For configuration options use the CSSInliner class:

import css_inline

inliner = css_inline.CSSInliner(keep_style_tags=True)
inliner.inline("...")
  • inline_style_tags. Specifies whether to inline CSS from "style" tags. Default: True
  • keep_style_tags. Specifies whether to keep "style" tags after inlining. Default: False
  • keep_link_tags. Specifies whether to keep "link" tags after inlining. Default: False
  • keep_at_rules. Specifies whether to keep "at-rules" (starting with @) after inlining. Default: False
  • minify_css. Specifies whether to remove trailing semicolons and spaces between properties and values. Default: False
  • base_url. The base URL used to resolve relative URLs. If you'd like to load stylesheets from your filesystem, use the file:// scheme. Default: None
  • load_remote_stylesheets. Specifies whether remote stylesheets should be loaded. Default: True
  • cache. Specifies caching options for external stylesheets (for example, StylesheetCache(size=5)). Default: None
  • extra_css. Extra CSS to be inlined. Default: None
  • preallocate_node_capacity. Advanced. Preallocates capacity for HTML nodes during parsing. This can improve performance when you have an estimate of the number of nodes in your HTML document. Default: 32
  • remove_inlined_selectors. Specifies whether to remove selectors that were successfully inlined from <style> blocks. Default: False
  • apply_width_attributes. Specifies whether to add width HTML attributes from CSS width properties on supported elements (table, td, th, img). Default: False
  • apply_height_attributes. Specifies whether to add height HTML attributes from CSS height properties on supported elements (table, td, th, img). Default: False

You can also skip CSS inlining for an HTML tag by adding the data-css-inline="ignore" attribute to it:

<head>
  <style>h1 { color:blue; }</style>
</head>
<body>
  <!-- The tag below won't receive additional styles -->
  <h1 data-css-inline="ignore">Big Text</h1>
</body>

The data-css-inline="ignore" attribute also allows you to skip link and style tags:

<head>
  <!-- Styles below are ignored -->
  <style data-css-inline="ignore">h1 { color:blue; }</style>
</head>
<body>
  <h1>Big Text</h1>
</body>

Alternatively, you may keep style from being removed by using the data-css-inline="keep" attribute. This is useful if you want to keep @media queries for responsive emails in separate style tags. Such tags will be kept in the resulting HTML even if the keep_style_tags option is set to false.

<head>
  <!-- Styles below are not removed -->
  <style data-css-inline="keep">h1 { color:blue; }</style>
</head>
<body>
  <h1>Big Text</h1>
</body>

Another possibility is to set keep_at_rules option to true. At-rules cannot be inlined into HTML therefore they get removed by default. This is useful if you want to keep at-rules, e.g. @media queries for responsive emails in separate style tags but inline any styles which can be inlined. Such tags will be kept in the resulting HTML even if the keep_style_tags option is explicitly set to false.

<head>
  <!-- With keep_at_rules=true "color:blue" will get inlined into <h1> but @media will be kept in <style> -->
  <style>h1 { color: blue; } @media (max-width: 600px) { h1 { font-size: 18px; } }</style>
</head>
<body>
  <h1>Big Text</h1>
</body>

If you set the the minify_css option to true, the inlined styles will be minified by removing trailing semicolons and spaces between properties and values.

<head>
  <!-- With minify_css=True, the <h1> will have `style="color:blue;font-weight:bold"` -->
  <style>h1 { color: blue; font-weight: bold; }</style>
</head>
<body>
  <h1>Big Text</h1>
</body>

If you'd like to load stylesheets from your filesystem, use the file:// scheme:

import css_inline

# styles/email is relative to the current directory
inliner = css_inline.CSSInliner(base_url="file://styles/email/")
inliner.inline("...")

You can also cache external stylesheets to avoid excessive network requests:

import css_inline

inliner = css_inline.CSSInliner(
    cache=css_inline.StylesheetCache(size=5)
)
inliner.inline("...")

Caching is disabled by default.

XHTML compatibility

If you'd like to work around some XHTML compatibility issues like closing empty tags (<hr> vs. <hr/>), you can use the following snippet that involves lxml:

import css_inline
from lxml import html, etree

document = "..."  # Your HTML document
inlined = css_inline.inline(document)
tree = html.fromstring(inlined)
inlined = etree.tostring(tree).decode(encoding="utf-8")

Performance

css-inline is powered by efficient tooling from Mozilla's Servo project and significantly outperforms other Python alternatives in terms of speed. Most of the time it achieves over a 10x speed advantage compared to the next fastest alternative.

Here is the performance comparison:

Size css_inline 0.19.0 premailer 3.10.0 toronado 0.1.0 inlinestyler 0.2.5 pynliner 0.8.0
Basic 230 B 4.27 µs 85.05 µs (19.93x) 495.30 µs (116.05x) 1.02 ms (238.87x) 867.79 µs (203.32x)
Realistic-1 8.58 KB 80.59 µs 1.03 ms (12.76x) 11.55 ms (143.29x) 26.37 ms (327.21x) 11.71 ms (145.36x)
Realistic-2 4.3 KB 46.88 µs 1.44 ms (30.73x) ERROR 17.71 ms (377.77x) ERROR
GitHub page 1.81 MB 17.57 ms 10.78 s (613.48x) ERROR ERROR ERROR

The "Basic" case was obtained by benchmarking the example from the Usage section. Note that the toronado, inlinestyler, and pynliner libraries encountered errors when used to inline CSS in the last scenarios.

The benchmarking code is available in the benches/bench.py file. The benchmarks were conducted using the stable rustc 1.91, Python 3.14.2 on Ryzen 9 9950X.

Comparison with other libraries

Besides performance, css-inline differs from other Python libraries for CSS inlining.

  • Generally supports more CSS features than other libraries (for example, toronado and pynliner do not support pseudo-elements);
  • It has fewer configuration options and is not as flexible as premailer;
  • Works on fewer platforms than LXML-based libraries (premailer, inlinestyler, toronado, and optionally pynliner);
  • Does not have debug logs yet;
  • Supports only HTML 5.

Further reading

If you want to know how this library was created & how it works internally, you could take a look at these articles:

License

This project is licensed under the terms of the MIT license.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

css_inline-0.20.0.tar.gz (72.2 kB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

css_inline-0.20.0-pp311-pypy311_pp73-manylinux_2_24_x86_64.whl (1.9 MB view details)

Uploaded PyPymanylinux: glibc 2.24+ x86-64

css_inline-0.20.0-pp311-pypy311_pp73-manylinux_2_24_aarch64.whl (1.9 MB view details)

Uploaded PyPymanylinux: glibc 2.24+ ARM64

css_inline-0.20.0-pp311-pypy311_pp73-macosx_10_12_x86_64.whl (1.8 MB view details)

Uploaded PyPymacOS 10.12+ x86-64

css_inline-0.20.0-cp39-abi3-win_amd64.whl (1.9 MB view details)

Uploaded CPython 3.9+Windows x86-64

css_inline-0.20.0-cp39-abi3-win32.whl (1.6 MB view details)

Uploaded CPython 3.9+Windows x86

css_inline-0.20.0-cp39-abi3-musllinux_1_2_x86_64.whl (2.2 MB view details)

Uploaded CPython 3.9+musllinux: musl 1.2+ x86-64

css_inline-0.20.0-cp39-abi3-musllinux_1_2_armv7l.whl (2.0 MB view details)

Uploaded CPython 3.9+musllinux: musl 1.2+ ARMv7l

css_inline-0.20.0-cp39-abi3-musllinux_1_2_aarch64.whl (2.1 MB view details)

Uploaded CPython 3.9+musllinux: musl 1.2+ ARM64

css_inline-0.20.0-cp39-abi3-manylinux_2_24_armv7l.whl (1.7 MB view details)

Uploaded CPython 3.9+manylinux: glibc 2.24+ ARMv7l

css_inline-0.20.0-cp39-abi3-manylinux_2_24_aarch64.whl (1.9 MB view details)

Uploaded CPython 3.9+manylinux: glibc 2.24+ ARM64

css_inline-0.20.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (1.9 MB view details)

Uploaded CPython 3.9+manylinux: glibc 2.17+ x86-64

css_inline-0.20.0-cp39-abi3-manylinux_2_12_i686.manylinux2010_i686.whl (1.9 MB view details)

Uploaded CPython 3.9+manylinux: glibc 2.12+ i686

css_inline-0.20.0-cp39-abi3-macosx_10_12_x86_64.whl (1.8 MB view details)

Uploaded CPython 3.9+macOS 10.12+ x86-64

css_inline-0.20.0-cp39-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl (3.6 MB view details)

Uploaded CPython 3.9+macOS 10.12+ universal2 (ARM64, x86-64)macOS 10.12+ x86-64macOS 11.0+ ARM64

File details

Details for the file css_inline-0.20.0.tar.gz.

File metadata

  • Download URL: css_inline-0.20.0.tar.gz
  • Upload date:
  • Size: 72.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.10.12

File hashes

Hashes for css_inline-0.20.0.tar.gz
Algorithm Hash digest
SHA256 9ae6e9bbbe0b355025332cddd10e06c8aaf63882387b77ffb88555f00f57e69e
MD5 3ef4c4fe3e93b786d60660ea9d4aa454
BLAKE2b-256 e6383f4a093c6d4cd07a231855eb3a42366803acb4bb5469c0ca747dbc54b38f

See more details on using hashes here.

File details

Details for the file css_inline-0.20.0-pp311-pypy311_pp73-manylinux_2_24_x86_64.whl.

File metadata

File hashes

Hashes for css_inline-0.20.0-pp311-pypy311_pp73-manylinux_2_24_x86_64.whl
Algorithm Hash digest
SHA256 10703e65e68e1ad1c42870bf3add67b4fe4566186ebf8e93daebfba4072e5303
MD5 d9670f4f8a2b671309abd39fa343e25e
BLAKE2b-256 1dd33e050fd993158b074fa4b8a6dfcd7976a143bdde7ec365feb4ddba52bfd2

See more details on using hashes here.

File details

Details for the file css_inline-0.20.0-pp311-pypy311_pp73-manylinux_2_24_aarch64.whl.

File metadata

File hashes

Hashes for css_inline-0.20.0-pp311-pypy311_pp73-manylinux_2_24_aarch64.whl
Algorithm Hash digest
SHA256 b00afdb5c2e89b27892ad9ea27583b4cffc17f8219ae9a7164e865098e1c93ab
MD5 bd38c812322191f4639f48ec381cd110
BLAKE2b-256 216de52ab87f0b71bcc655c80e028a7f969a2df820814a6aaa732c0060ce8800

See more details on using hashes here.

File details

Details for the file css_inline-0.20.0-pp311-pypy311_pp73-macosx_10_12_x86_64.whl.

File metadata

File hashes

Hashes for css_inline-0.20.0-pp311-pypy311_pp73-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 3d8512abd9f3de1c55cc07e0218bd29e6bf30ceb42f164b274f53eff1985cb29
MD5 f133c591c5ea14710beca09297003d29
BLAKE2b-256 913a55997b8c001080913376c93b1249dec6c5165765dcf46673b3254163187b

See more details on using hashes here.

File details

Details for the file css_inline-0.20.0-cp39-abi3-win_amd64.whl.

File metadata

  • Download URL: css_inline-0.20.0-cp39-abi3-win_amd64.whl
  • Upload date:
  • Size: 1.9 MB
  • Tags: CPython 3.9+, Windows x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.10.12

File hashes

Hashes for css_inline-0.20.0-cp39-abi3-win_amd64.whl
Algorithm Hash digest
SHA256 a1f7bacfb8ad70582b1e92a9102c33badee3e955e7a164716bec393644c29acd
MD5 39efbc25cba5063ccf66fa118c615720
BLAKE2b-256 fd3dfe0475eb823de10e82834bb6e8b3109d12b7055564befda45fd78cf03581

See more details on using hashes here.

File details

Details for the file css_inline-0.20.0-cp39-abi3-win32.whl.

File metadata

  • Download URL: css_inline-0.20.0-cp39-abi3-win32.whl
  • Upload date:
  • Size: 1.6 MB
  • Tags: CPython 3.9+, Windows x86
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.10.12

File hashes

Hashes for css_inline-0.20.0-cp39-abi3-win32.whl
Algorithm Hash digest
SHA256 714bfc10af6f7e4e81955bd1947296329b94aef3d64fcb93bd8c46d82a664468
MD5 05c164786bdf912bf7a441bcd2e6d51c
BLAKE2b-256 fbf295e57a8cc0ae4772a921e0ff1f5c588c6d1a421a32271a86c22d16f9c94a

See more details on using hashes here.

File details

Details for the file css_inline-0.20.0-cp39-abi3-musllinux_1_2_x86_64.whl.

File metadata

File hashes

Hashes for css_inline-0.20.0-cp39-abi3-musllinux_1_2_x86_64.whl
Algorithm Hash digest
SHA256 a06da4a139bbe868a83df6b05659d26b2fec8e3b4f9ce3e95c78dfcbf5ae3af6
MD5 f97bac73bf7a927e641999ae37903936
BLAKE2b-256 d1bf71b3db9af6cf5b08f222442e6358088bef2a1ebc063d069731207e4cd0f8

See more details on using hashes here.

File details

Details for the file css_inline-0.20.0-cp39-abi3-musllinux_1_2_armv7l.whl.

File metadata

File hashes

Hashes for css_inline-0.20.0-cp39-abi3-musllinux_1_2_armv7l.whl
Algorithm Hash digest
SHA256 dfbb243bdde36ae5b0d0816f7af6cce0eadd2b8e0975f3b6981843bcdb70d299
MD5 46a32ebdd63ac029fdd4123378e6d761
BLAKE2b-256 e7637c3b321eefb832460e964f1485b09bacf46d0834d855d26373755b457267

See more details on using hashes here.

File details

Details for the file css_inline-0.20.0-cp39-abi3-musllinux_1_2_aarch64.whl.

File metadata

File hashes

Hashes for css_inline-0.20.0-cp39-abi3-musllinux_1_2_aarch64.whl
Algorithm Hash digest
SHA256 6153ce79d3a20c5d0b99e469137072fab78d2046dcc23c0bd7148acbc08630f2
MD5 a2473d475da17f3818b6e44c3d17855f
BLAKE2b-256 70b274bdb0fb3414b079d5f4ae310c76c8967ff85d470a1bb7a0d90ddebd3fd9

See more details on using hashes here.

File details

Details for the file css_inline-0.20.0-cp39-abi3-manylinux_2_24_armv7l.whl.

File metadata

File hashes

Hashes for css_inline-0.20.0-cp39-abi3-manylinux_2_24_armv7l.whl
Algorithm Hash digest
SHA256 7eb7a7202ce09a14bafbb9713e10710d944ca4f1b61d40681563036d27f211e0
MD5 cd9a9f9a620276ee3384578f228c0012
BLAKE2b-256 ea3c11e053a3ad7267b8e82c85494e9bf06ad6f7e96dc04aa754ff17f12a8f8f

See more details on using hashes here.

File details

Details for the file css_inline-0.20.0-cp39-abi3-manylinux_2_24_aarch64.whl.

File metadata

File hashes

Hashes for css_inline-0.20.0-cp39-abi3-manylinux_2_24_aarch64.whl
Algorithm Hash digest
SHA256 c2b320cd22c4c53ffc8f255dc9374fcc7135f54d6b66348f4b321386bb031999
MD5 d1d6aad4ce50e80ed741a6f3d29a495b
BLAKE2b-256 1a26af4707420936be17ccffaf19083337b12576d4ee94d8b101672fa18a3242

See more details on using hashes here.

File details

Details for the file css_inline-0.20.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for css_inline-0.20.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 85f374306c002d73e7615b3e926d488ed9955f966bb61efa66ac8091e89f2e49
MD5 16cc7072c13dcaf57b4cc5e6bc4ec6d8
BLAKE2b-256 9d4e820e7db2b2e70b6a3b583e3d3411441d56719e092d62eca49d5d015ee7af

See more details on using hashes here.

File details

Details for the file css_inline-0.20.0-cp39-abi3-manylinux_2_12_i686.manylinux2010_i686.whl.

File metadata

File hashes

Hashes for css_inline-0.20.0-cp39-abi3-manylinux_2_12_i686.manylinux2010_i686.whl
Algorithm Hash digest
SHA256 729d2fe6c6df7ba8f9d92fd20cbb359f6ba3a08edfc6253bd63bf33c9ea085bd
MD5 d2bef07bd7dbcabb9bed032f2aa87cd7
BLAKE2b-256 3377d08129f399623a3adcfeeec830ed7a5e74ddd44b844e18bbd9554542754b

See more details on using hashes here.

File details

Details for the file css_inline-0.20.0-cp39-abi3-macosx_10_12_x86_64.whl.

File metadata

File hashes

Hashes for css_inline-0.20.0-cp39-abi3-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 75068ad2d6cf35ca1d1eaa4a2949a4bc319af27370a5ab5f88a6ba7014225863
MD5 886beb22c74cadb0ccfc9d1f8cb83447
BLAKE2b-256 50fd5f1dc7439d2e7388ace154f3f17912397a060bb9cb653d24465a95b108e1

See more details on using hashes here.

File details

Details for the file css_inline-0.20.0-cp39-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl.

File metadata

File hashes

Hashes for css_inline-0.20.0-cp39-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl
Algorithm Hash digest
SHA256 443ff39befe2b0b09057cf4435e3f1b571d4ca24f0177a864c5f3632465a15db
MD5 10fd71811379b4bc10ad3411eb8c1cca
BLAKE2b-256 04cc8af945430accb200043b84234228a1dc9686d798cf82a2d2c219f75bb1bc

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page