RunLocalTools

CSS Inliner for HTML Email — Convert Stylesheets to Inline Styles

Paste HTML and CSS and get back HTML with the CSS baked into style=”” attributes on every element — exactly what most email clients need. Hover states and media queries are preserved separately. Free, runs entirely in your browser.


What it does

Most email clients — Outlook, and plenty of webmail providers — strip out <style> blocks or ignore linked stylesheets entirely, so the only formatting that reliably survives is an inline style="" attribute on each element. This tool takes your HTML and your CSS and merges them: every element gets the computed styles that would actually apply to it, written directly onto the tag, following the real CSS cascade (specificity, source order, and !important) rather than just slapping the first matching rule on top.

How it works

The tool parses your CSS into individual rules, matches each selector against your HTML using the browser’s own selector engine, and then resolves the cascade per element — the same way a browser would, just computed once and baked in rather than applied live:

  • Static, structural selectors — class, id, attribute, :not(), :nth-child(), and similar — are matched and inlined normally.
  • Interactive pseudo-classes (:hover, :focus, :active, :visited, :link, :target) can’t mean anything in a static style="" attribute, so they’re left out of the inlining pass and moved into a fallback <style> block instead — email clients that do support <style> (a growing number of modern webmail apps) still get the hover effect; the ones that don’t simply see the inlined baseline.
  • Pseudo-elements (::before, ::after, and similar) and anything inside @media, @supports, or @keyframes are handled the same way — moved to the fallback block untouched, since none of those can be flattened into a single element’s inline style in the first place.
  • Cascade resolution respects !important — a lower-specificity rule marked !important still wins over a higher-specificity one that isn’t, exactly as it would in a real browser, and it correctly overrides a plain inline style you already had on an element (while a plain stylesheet rule never overwrites an inline style that was already there).
  • A live sandboxed preview shows the final rendered result immediately, next to a “view source” tab with the raw output HTML.

How to use it

  1. Paste your HTML into the left pane (or drop an .html file), and your CSS into the right pane (or drop a .css file) — if your HTML already has a <style> block, copy its contents into the CSS pane too, since the tool works from whatever CSS you give it rather than scanning the HTML for embedded styles.
  2. Check the summary line: how many rules were read, how many selectors got inlined, how many were kept in the fallback block, and how many matched nothing (a useful typo-catcher).
  3. Leave Strip <script> tags on unless you have a specific reason to keep them — most email clients block scripts anyway, and it’s one less risk in the sandboxed preview.
  4. Turn on Strip class/id attributes only if you want the smallest possible output — it’s automatically skipped (and you’ll see why) if any :hover-style rule still needs those classes to work in clients that support <style>.
  5. Copy the final HTML or download it as a ready-to-send .html file.

If your source CSS has duplicate or leftover rules from an old template, running it through Remove Duplicate CSS first will give you a cleaner, smaller stylesheet to inline.

FAQ

Why did some of my styles end up in a <style> block instead of inline? Only rules built on :hover/:focus/:active/:visited/:link/:target, pseudo-elements, or anything inside @media/@supports/@keyframes are held back — none of those can be represented as a static inline style, so keeping them as real CSS (rather than dropping them) is the only option that preserves the effect for clients that can render it.

A selector in my CSS matched zero elements — what happened to it? It’s dropped from the output and counted in the summary as “matched nothing.” Since it wasn’t doing anything to your HTML anyway, there’s nothing meaningful to preserve — but the count is there so you can catch a typo’d class name.

Does it handle an element that already has an inline style="" before I run it through the tool? Yes — any property you already set inline is kept exactly as-is unless a stylesheet rule for that same property is marked !important, which is the one case where a stylesheet declaration is allowed to override a plain inline value, matching real browser behavior.

Can I strip the class and id attributes to make the HTML smaller? Yes, with one safety check: if any rule still needs a class or id to match elements in the fallback <style> block (a :hover effect, for instance), stripping is automatically skipped and the tool tells you why, rather than silently breaking that effect.

Does this replace testing in an actual email client? No — different clients render inline CSS differently (particularly around things like max-width, flexbox, and background images), so this tool gets you a correctly-computed, cascade-accurate baseline, but a real send test in something like Litmus or your own inbox is still worth doing before a big campaign.

Limitations

  • Only the still, static parts of your CSS can be inlined by definition — anything relying on interaction, animation, or a media query has to stay as real CSS in a fallback block, which some older email clients (notably older Outlook desktop versions) will simply ignore.
  • The preview renders in a sandboxed iframe with scripts disabled regardless of the “strip scripts” setting, purely as a safety measure for previewing untrusted markup — the downloaded/copied HTML respects your actual toggle choice.