To inline Tailwind CSS for HTML email, generate the utility rules used by the rendered template, resolve them to email-friendly values, apply unconditional declarations to matching elements, and preserve responsive or dark-mode rules in a small embedded stylesheet.
Do not inline your full application CSS. It creates unnecessary markup and can push long messages toward Gmail clipping.
Source and desired output
Source:
<p class="mt-4 text-base leading-7 text-slate-600">
Your report is ready.
</p>
Output:
<p style="margin-top:16px;font-size:16px;line-height:28px;color:#475569">
Your report is ready.
</p>
The output no longer depends on the utility classes for its base appearance.
Step 1: render the complete template
Resolve Blade, JSX, Vue, or other template syntax before extracting classes. Conditional sections and component variants must be present in the rendered fixture for their utilities to be detected.
Use realistic data, including long and empty states.
Step 2: collect complete utility tokens
Tailwind scans text for complete class names. Avoid partial construction:
// Avoid
`bg-${status}-600`
// Prefer
const statuses = {
success: 'bg-emerald-600 text-white',
failure: 'bg-red-600 text-white',
};
Extract candidates from class or className attributes, or let your Tailwind integration scan the template source. Generate CSS only for those tokens.
Step 3: compile Tailwind CSS
Tailwind CSS 4 uses CSS-first configuration and may emit values optimized for modern browsers.
An email pipeline should convert or lower:
- CSS variables to concrete values;
oklch()colors to safer formats;- logical padding and margin properties to physical sides;
- calculated spacing where a concrete pixel value is safer;
- nesting into ordinary selectors; and
- unsupported declarations out of the final map.
This is where a dedicated email configuration or compiler adds value beyond the normal Tailwind build.
Step 4: separate conditional rules
Do not treat all generated rules as candidates for inlining.
Keep these embedded:
@mediabreakpoint rules;prefers-color-schemedark-mode rules;- hover and focus selectors;
- client-targeting selectors; and
- any rule whose meaning depends on context.
The class names used by those rules must remain on their elements.
Step 5: create a class-to-style map
For simple utility selectors, build a map:
mt-4 โ margin-top:16px
text-base โ font-size:16px;line-height:24px
leading-7 โ line-height:28px
text-slate-600 โ color:#475569
Apply Tailwind's generated cascade order. Class attribute order does not define precedence.
Utilities with compound selectors, child relationships, or pseudo-classes cannot be reduced to a simple unconditional map.
Step 6: merge with existing styles
Existing inline declarations may be intentional fallbacks:
<td class="bg-blue-600" style="mso-padding-alt:0">
Merge instead of replacing:
<td style="background-color:#155dfc;mso-padding-alt:0">
Handle specificity, !important, shorthand properties, and duplicate declarations deterministically. Add focused fixtures for every conflict rule.
Step 7: transform structure
Inlining alone does not make Flexbox or Grid compatible.
Before final serialization:
- convert key rows and columns to presentation tables;
- turn filled links into table-backed buttons;
- convert spacers and dividers to stable structures;
- add explicit image and table dimensions; and
- add
align,valign, orbgcolorwhere useful.
See how to convert Tailwind to email-safe HTML for the broader pipeline.
Step 8: remove unused classes
Remove a class when:
- all of its useful declarations are inline; and
- no retained conditional selector refers to it.
Keep a responsive class when a media query still targets it. Renaming escaped Tailwind selectors to email-friendlier names may be necessary, but the HTML and CSS must change together.
Step 9: wrap and sanitize
Create a complete document with language, metadata, preheader, body defaults, and a meaningful title.
Remove:
- scripts;
- forms and interactive inputs;
- unsupported embedded content;
- editor-only data attributes;
- unused CSS;
- temporary comments; and
- unresolved template tokens.
Preserve deliberate Outlook conditional comments.
Responsive example
Source:
<div class="px-5 py-8 sm:px-10 sm:py-12">...</div>
Output strategy:
- inline
px-5 py-8; - keep a compact media query for the
sm:overrides; - retain or normalize the responsive class selector; and
- ensure the base layout is complete without the query.
Never inline the sm: value unconditionally.
Measure the generated HTML
Inlining repeats styles. Track:
- total HTML bytes;
- style attribute count;
- retained CSS bytes;
- repeated declaration groups;
- unused class count; and
- Gmail clipping headroom.
Minify only after compatibility validation, and protect Outlook comments during minification.
Test the pipeline
Create fixtures for:
- colors and CSS variables;
- spacing and logical properties;
- font-size plus line-height precedence;
- arbitrary values;
- existing inline styles;
!important;- responsive variants;
- dark mode;
- filled buttons; and
- grid or flex transformations.
Then run real HTML email tests.
Common failures
A class has no inline style
It was not scanned, generated, parsed, or supported. Trace each stage instead of patching the export.
Responsive behavior disappeared
The pipeline removed the retained media query or stripped the class it targets.
Colors are invalid in clients
Modern color functions or unresolved variables reached the output. Lower them before inlining.
HTML size exploded
The build included unused utilities or repeated a large declaration group on too many elements.
Outlook still breaks
The issue is structural, not merely style placement. Add a table or component fallback.
Frequently asked questions
Can a generic CSS inliner handle Tailwind?
It can inline simple compiled selectors, but a complete Tailwind email pipeline also needs value lowering, conditional-rule preservation, layout transformation, and cleanup.
Should I inline hover styles?
No. Hover is conditional. Keep it embedded as progressive enhancement.
Can I remove every class after inlining?
Only if no retained selector uses it. Responsive and dark-mode rules often require classes.
Should the source use inline CSS instead?
You can, but utility source is easier to standardize and reuse. The build should make output placement automatic.
Make inlining deterministic
The most important property of a Tailwind email inliner is repeatability.
The same template and data should produce the same declarations, structure, and retained CSS. When the pipeline is deterministic and covered by fixtures, teams can improve source components without hand-editing generated email HTML.
Share with friends