Skip to main content

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.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.

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.21.1.tar.gz (73.0 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.21.1-pp311-pypy311_pp73-manylinux_2_24_x86_64.whl (1.9 MB view details)

Uploaded PyPymanylinux: glibc 2.24+ x86-64

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

Uploaded PyPymanylinux: glibc 2.24+ ARM64

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

Uploaded PyPymacOS 10.12+ x86-64

css_inline-0.21.1-cp310-abi3-win_amd64.whl (1.9 MB view details)

Uploaded CPython 3.10+Windows x86-64

css_inline-0.21.1-cp310-abi3-win32.whl (1.6 MB view details)

Uploaded CPython 3.10+Windows x86

css_inline-0.21.1-cp310-abi3-pyemscripten_2025_0_wasm32.whl (527.7 kB view details)

Uploaded CPython 3.10+PyEmscripten 2025.0 wasm32

css_inline-0.21.1-cp310-abi3-musllinux_1_2_x86_64.whl (2.2 MB view details)

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

css_inline-0.21.1-cp310-abi3-musllinux_1_2_armv7l.whl (2.0 MB view details)

Uploaded CPython 3.10+musllinux: musl 1.2+ ARMv7l

css_inline-0.21.1-cp310-abi3-musllinux_1_2_aarch64.whl (2.1 MB view details)

Uploaded CPython 3.10+musllinux: musl 1.2+ ARM64

css_inline-0.21.1-cp310-abi3-manylinux_2_24_armv7l.whl (1.8 MB view details)

Uploaded CPython 3.10+manylinux: glibc 2.24+ ARMv7l

css_inline-0.21.1-cp310-abi3-manylinux_2_24_aarch64.whl (1.9 MB view details)

Uploaded CPython 3.10+manylinux: glibc 2.24+ ARM64

css_inline-0.21.1-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (1.9 MB view details)

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

css_inline-0.21.1-cp310-abi3-manylinux_2_12_i686.manylinux2010_i686.whl (1.9 MB view details)

Uploaded CPython 3.10+manylinux: glibc 2.12+ i686

css_inline-0.21.1-cp310-abi3-macosx_10_12_x86_64.whl (1.8 MB view details)

Uploaded CPython 3.10+macOS 10.12+ x86-64

css_inline-0.21.1-cp310-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl (3.6 MB view details)

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

File details

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

File metadata

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

File hashes

Hashes for css_inline-0.21.1.tar.gz
Algorithm Hash digest
SHA256 66114bb51b8e54e6e47bc196d5169d85f31c8e94f2c3dc0fb82ae2e7242c3e82
MD5 7888d3538fffce1294207796455b55d7
BLAKE2b-256 0044d9b79d83f360cb4272f162868f2ddcd30c1cd11e3e110d1904e6e9e41018

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for css_inline-0.21.1-pp311-pypy311_pp73-manylinux_2_24_x86_64.whl
Algorithm Hash digest
SHA256 55c66b400ad8e410d727c3fd6dded05ae7c5b8e4872ef57698596eea48f1f04e
MD5 41441ccd7289d24d58f993367306b17e
BLAKE2b-256 28f00e00d0267eccec2d823da9af91495c9939faacfe6c28185f7c645da7d7fd

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for css_inline-0.21.1-pp311-pypy311_pp73-manylinux_2_24_aarch64.whl
Algorithm Hash digest
SHA256 aca998df1973cdda337ad14ee724b74779a99a25b6d0e83da7240d42b66ff5e4
MD5 1dce8e68d2fb9e670380ec6192622334
BLAKE2b-256 0c2d1b49d82b8248dfa38d95a13320987125a6bdbb1e76d2b51d88f424e54fff

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for css_inline-0.21.1-pp311-pypy311_pp73-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 dcda41cdc90d2221506bfa8f8f8a51df62b6f17b2c4d98032e70dbf5285a3b75
MD5 63af3fde624ef82943a21c8e8442b65f
BLAKE2b-256 e2a671c976add3e838dd827b81aa89e5777898dd8b3a3f7d818dd4e8affaa5b7

See more details on using hashes here.

File details

Details for the file css_inline-0.21.1-cp310-abi3-win_amd64.whl.

File metadata

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

File hashes

Hashes for css_inline-0.21.1-cp310-abi3-win_amd64.whl
Algorithm Hash digest
SHA256 b64ecde080961f019f345edd0b9b7e8da200374464f7ac310459ee6df148fbee
MD5 8bef7907650e03b5da56a9d50100994a
BLAKE2b-256 9db917c334c4cf1b402967db12860d49992b2bf2160f2b1df307b51ca2a7e772

See more details on using hashes here.

File details

Details for the file css_inline-0.21.1-cp310-abi3-win32.whl.

File metadata

  • Download URL: css_inline-0.21.1-cp310-abi3-win32.whl
  • Upload date:
  • Size: 1.6 MB
  • Tags: CPython 3.10+, Windows x86
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.10.12

File hashes

Hashes for css_inline-0.21.1-cp310-abi3-win32.whl
Algorithm Hash digest
SHA256 3fad6a74a6c292c75df26355139121a27b8d4913c2e4f8dbdc79f98fd9cb4bf3
MD5 1d4ed08cd3bd382c66f26b1a6b9b708f
BLAKE2b-256 374a10e4501cf03a49bf2cd76776e18635697e82659afaf8149529c81bf5684c

See more details on using hashes here.

File details

Details for the file css_inline-0.21.1-cp310-abi3-pyemscripten_2025_0_wasm32.whl.

File metadata

File hashes

Hashes for css_inline-0.21.1-cp310-abi3-pyemscripten_2025_0_wasm32.whl
Algorithm Hash digest
SHA256 228586da7651de7e461d5acfd87e1f75e07c0a6724f91a2c6d47d2c966e3652e
MD5 0aa5f9d75aa93c172c4480c12998d434
BLAKE2b-256 d2f3d58099cb5b780d8afa83428ffe47b98dc923e4b9664f7d310660b264b4bf

See more details on using hashes here.

File details

Details for the file css_inline-0.21.1-cp310-abi3-musllinux_1_2_x86_64.whl.

File metadata

File hashes

Hashes for css_inline-0.21.1-cp310-abi3-musllinux_1_2_x86_64.whl
Algorithm Hash digest
SHA256 f8ed5cd4c441420933897aa35d674f0d42711f734bcd5b15f481cdcda122d42f
MD5 3b5962f35e963e546a02d557e026a360
BLAKE2b-256 573ab65994d33ecfead7eb354a055acb21e0ed80f44bf7f06e0533b0c24b7571

See more details on using hashes here.

File details

Details for the file css_inline-0.21.1-cp310-abi3-musllinux_1_2_armv7l.whl.

File metadata

File hashes

Hashes for css_inline-0.21.1-cp310-abi3-musllinux_1_2_armv7l.whl
Algorithm Hash digest
SHA256 2e2171c69eeee5e3703060aef77c83ca1215c59ca022cc7fbf03ae7f8dc366d2
MD5 2a3876507317b41495ab9e9d65a58ab0
BLAKE2b-256 37d64dc71bb72d01928caeed76cfd07fe606f5165b62a371621cf882202d1973

See more details on using hashes here.

File details

Details for the file css_inline-0.21.1-cp310-abi3-musllinux_1_2_aarch64.whl.

File metadata

File hashes

Hashes for css_inline-0.21.1-cp310-abi3-musllinux_1_2_aarch64.whl
Algorithm Hash digest
SHA256 20e228771975d4d9bfcbd2726722e8b1b9f622fe3fa35cb48e9c2ca6033782ff
MD5 a17c9b77173d5e024ef6c7eb52a66dac
BLAKE2b-256 bc70780bc0a03f5d1803dc190b3a6f11793ee7d3255bad57994991be37ce1738

See more details on using hashes here.

File details

Details for the file css_inline-0.21.1-cp310-abi3-manylinux_2_24_armv7l.whl.

File metadata

File hashes

Hashes for css_inline-0.21.1-cp310-abi3-manylinux_2_24_armv7l.whl
Algorithm Hash digest
SHA256 b7c67133a8333f532098770c387d202350c9e6ca9383ee6ff22985e31c6d3b4d
MD5 183bf2ed56e26a860f04d2dced955eb9
BLAKE2b-256 def4069305fed16f751c5c16c51f73cd7372e1005d0cf96fde441af47ee63a83

See more details on using hashes here.

File details

Details for the file css_inline-0.21.1-cp310-abi3-manylinux_2_24_aarch64.whl.

File metadata

File hashes

Hashes for css_inline-0.21.1-cp310-abi3-manylinux_2_24_aarch64.whl
Algorithm Hash digest
SHA256 166433261046fd57b18a8ec209a546d3bbd3ce731db96d5ce4e2aef2034edb93
MD5 d4720d694c8c157cc12a6fee17c6061a
BLAKE2b-256 d0ce34dd83b4ff6be7490958c45b0413cc15c3e1986ed5282ceae92f54a32a47

See more details on using hashes here.

File details

Details for the file css_inline-0.21.1-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for css_inline-0.21.1-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 7671120a3771756e38a7bdb690f348e1b066a05f8dbd3ee27917f213b4afa689
MD5 9af1190828d29682639cd750a609f755
BLAKE2b-256 934533bef4a54686609b677a615a97c598a90e6ea1681e0d9a59d3b0b84b9fe6

See more details on using hashes here.

File details

Details for the file css_inline-0.21.1-cp310-abi3-manylinux_2_12_i686.manylinux2010_i686.whl.

File metadata

File hashes

Hashes for css_inline-0.21.1-cp310-abi3-manylinux_2_12_i686.manylinux2010_i686.whl
Algorithm Hash digest
SHA256 cb4e04a68f1f68c87a5f82fb343f42541343f82ed6c3a023d98dc1b6dab6e3e4
MD5 4732496395a591b24833d2dafb0be39f
BLAKE2b-256 046950b28337de6ded1d6d08004ee1f0219439b1061ba9082e445342dba3d202

See more details on using hashes here.

File details

Details for the file css_inline-0.21.1-cp310-abi3-macosx_10_12_x86_64.whl.

File metadata

File hashes

Hashes for css_inline-0.21.1-cp310-abi3-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 d936fa058d31884630a0d44d910bf4c828315f0b63a87b1a4e91c232429c2697
MD5 5feb6c492281e8b4f2f4605b0b578f91
BLAKE2b-256 3d69ea235c91b410423638c2fe86f820d7c3c0e505173460fb02886a8976e6d4

See more details on using hashes here.

File details

Details for the file css_inline-0.21.1-cp310-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl.

File metadata

File hashes

Hashes for css_inline-0.21.1-cp310-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl
Algorithm Hash digest
SHA256 8ef4ad71e0bd1452029421694e0b779dca48b666f5885bde424109acf267a047
MD5 24de6bf3302b7d83e629b1de4cb8b0d9
BLAKE2b-256 dacdf8d7eef12f977a6b027730f0320393cfdfc00f18253aea51e48f0e645746

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 Sentry Error logging StatusPage Status page