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. Structural tags (<html>, <head>, <body>) are stripped from the output; only their contents are preserved. Use inline if you need to keep the full document 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.2.tar.gz (73.1 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.2-pp311-pypy311_pp73-manylinux_2_24_x86_64.whl (2.0 MB view details)

Uploaded PyPymanylinux: glibc 2.24+ x86-64

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

Uploaded PyPymanylinux: glibc 2.24+ ARM64

css_inline-0.20.2-pp311-pypy311_pp73-macosx_10_12_x86_64.whl (1.9 MB view details)

Uploaded PyPymacOS 10.12+ x86-64

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

Uploaded CPython 3.9+Windows x86-64

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

Uploaded CPython 3.9+Windows x86

css_inline-0.20.2-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.2-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.2-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.2-cp39-abi3-manylinux_2_24_armv7l.whl (1.8 MB view details)

Uploaded CPython 3.9+manylinux: glibc 2.24+ ARMv7l

css_inline-0.20.2-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.2-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (2.0 MB view details)

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

css_inline-0.20.2-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.2-cp39-abi3-macosx_10_12_x86_64.whl (1.9 MB view details)

Uploaded CPython 3.9+macOS 10.12+ x86-64

css_inline-0.20.2-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.2.tar.gz.

File metadata

  • Download URL: css_inline-0.20.2.tar.gz
  • Upload date:
  • Size: 73.1 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.2.tar.gz
Algorithm Hash digest
SHA256 f495c24b96386dcc8fd6621507deeb81c5bc33ad4da26ec4fe625bda644eb937
MD5 9751168ca8fef688590d61aed743d02d
BLAKE2b-256 cd5983ae1248fa993be44420ff52118f12f4149e52e0c5bc1250e1754f885128

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for css_inline-0.20.2-pp311-pypy311_pp73-manylinux_2_24_x86_64.whl
Algorithm Hash digest
SHA256 e4d54030dbd4b3a8c6270f21a046dbbc322d096e6269a0377fbf6ed56834d1c2
MD5 bb32c8f9bd59e9661584856e2269f77c
BLAKE2b-256 c7f75d3813c972891db9455d20d4d6b5f56c80ce4e937f49a3d38a23ed868580

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for css_inline-0.20.2-pp311-pypy311_pp73-manylinux_2_24_aarch64.whl
Algorithm Hash digest
SHA256 701fa64d3a8d9ee55d6256504566f65a6cca0a79818cedfedd7771bbd8762d78
MD5 4565ac30ddc7e1878dcbfd4fbee51d39
BLAKE2b-256 bffb04c493ae019ae6daa855668feb50f35ca75f9d2834a5de198da416e14b49

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for css_inline-0.20.2-pp311-pypy311_pp73-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 9293120ef81c85aefbcb0f3b3afca9d659c899a9d3d1f799aa0703b733b3cde7
MD5 913dfd023eec7405dd225dad3469ba7e
BLAKE2b-256 56a234504aed3d02e871cef54951a654d1f6169e1e2a2257124588694e643c6d

See more details on using hashes here.

File details

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

File metadata

  • Download URL: css_inline-0.20.2-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.2-cp39-abi3-win_amd64.whl
Algorithm Hash digest
SHA256 8fc01121103925581b18b64f99e8ef42b4d77e646aff9fb51dcede2ac7a704c1
MD5 c808bf4041d0ac559b821708784f8374
BLAKE2b-256 95676b8a8e2f6805928b9762342fb1fb4252b0960e7b739227811d7e45b044f0

See more details on using hashes here.

File details

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

File metadata

  • Download URL: css_inline-0.20.2-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.2-cp39-abi3-win32.whl
Algorithm Hash digest
SHA256 f2f36a346f7b166246645a895a4c45dac43f108baaf8be7f6249663a8d34a0c2
MD5 26788ebfeadd80d59c70f016acc169e0
BLAKE2b-256 bf78fb81302b3ca1810cba8ed721e964649b50d0f0b88a08b94324af9ae9c5f0

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for css_inline-0.20.2-cp39-abi3-musllinux_1_2_x86_64.whl
Algorithm Hash digest
SHA256 fef2ca4692d1494153022694e938720f58b12d5105423534a1b72b5d61d8c9e2
MD5 58b6b4e86454732f4b60375c9c01a416
BLAKE2b-256 0c5118651de4f5e1b28dd138d3460ad73109c4d785ee32e7d4a39f4291d08663

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for css_inline-0.20.2-cp39-abi3-musllinux_1_2_armv7l.whl
Algorithm Hash digest
SHA256 0b8a73611b6ccfaa5b17fa3d3a3634bedd729942b6a5cd3c0bd3868886ca3730
MD5 6a8658e55a4c02220afc33afd2d95474
BLAKE2b-256 762cbb2a239f6be9cb413e59ae524710a31e4c074890dac645a3a6e786830434

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for css_inline-0.20.2-cp39-abi3-musllinux_1_2_aarch64.whl
Algorithm Hash digest
SHA256 60f42c3740fd194dd1bfafab97ab8a61db0db9ed51841c4c7667084773a9f915
MD5 85520086ff4d46172fde2dd7c33bede7
BLAKE2b-256 4062834c5ee3b8b10a5909bdddedff3731dbd70559bce24e8ac1fd9d629cf223

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for css_inline-0.20.2-cp39-abi3-manylinux_2_24_armv7l.whl
Algorithm Hash digest
SHA256 c1400881ac4755fb2c2e3c8fbec5a62a8a26c9fb174767ff63faf7eebc7486c5
MD5 56d1da8e769499e73ba1896eb17458a6
BLAKE2b-256 d5019b87a09c619c1414a8c28aab1961ae612a39da6716f639887e60d760420e

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for css_inline-0.20.2-cp39-abi3-manylinux_2_24_aarch64.whl
Algorithm Hash digest
SHA256 7782c6b5d7d8d5560cdd41a2793da7d4be7440a9754222b930dac0f5fbeda094
MD5 f43815a21abe5ca84f4b0b92261b10f7
BLAKE2b-256 b9edabed2d142f5e7c962949f7d895d3678360a386981bab78080742aa1c4465

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for css_inline-0.20.2-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 38515194a5d378506d2d00a0a99d492722ee1582925fd3e693cc4cc336b63424
MD5 0d8212a8b4fd743b9ee684bc247e7405
BLAKE2b-256 5b1fbc77087533096b76b53bc4fff49046848396b3248252630d3104fbd2c97d

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for css_inline-0.20.2-cp39-abi3-manylinux_2_12_i686.manylinux2010_i686.whl
Algorithm Hash digest
SHA256 c45e416bd80db486d9c88df6e51ea3fc25ec3e22758b6f8aeba9ce21c508843a
MD5 c84b277ef9c6ccf6be2db93a8e3bdd87
BLAKE2b-256 0eab69746c0245be305ed285b0f31579fb43a914a191fb380d1149293c283931

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for css_inline-0.20.2-cp39-abi3-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 9b31375376f076c3ea26b24262767608ecab07b40a72764161b7f902310d5a9d
MD5 3798fc8b8cb5b33e26cc3930be10e7c0
BLAKE2b-256 5e59ad8ea0ab32121d72ff35e502921d755a188aac3f2bd957c0f7b13021da6b

See more details on using hashes here.

File details

Details for the file css_inline-0.20.2-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.2-cp39-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl
Algorithm Hash digest
SHA256 7b876c111138e7c226ef3e0aa2691f34f5feda467cc8b07ad1168e786b607dfc
MD5 e00e97305a68426a1a9aa88a84530d33
BLAKE2b-256 1876315480e34160c31cd831eebcdcae69e14e2d7d75586599f9085e6d483f02

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