Tailwind CSS
Maizzle uses Tailwind CSS 4, configured and optimized for email client compatibility.
You style your templates or components with Tailwind like you're used to, and the framework compiles and lowers the modern CSS syntax so that it works across all major email clients.
Usage
Use the <Tailwind> component to wrap any part of your template:
<template>
<Html>
<Head />
<Body>
<Tailwind> <Container class="bg-slate-100 p-4">
<Text class="text-lg text-slate-800">Hello!</Text>
</Container>
</Tailwind> </Body>
</Html>
</template>
A <Head> component must be present in the template when using <Tailwind>.
If you prefer a more hands-on approach, you may pull @maizzle/tailwindcss into your template yourself, either inline in a <style> tag or from an external stylesheet via a <link> tag inside your template's <Head>:
<template>
<Html>
<Head>
<style>
@import "@maizzle/tailwindcss";
</style>
</Head>
<Body class="bg-slate-100">
<Container>
<Text class="text-lg text-slate-800">Hello!</Text>
</Container>
</Body>
</Html>
</template>
With the <link> approach, point href at a CSS file that imports the preset using a path relative to the template file.
At build time the inline-link transformer replaces the <link> with a <style> tag containing the file's contents, which then goes through normal CSS processing.
Or just use our <Layout> component, it has everything set up for you:
<template>
<Layout>
<Container class="bg-slate-100 p-4">
<Text class="text-lg text-slate-800">Hello!</Text>
</Container>
</Layout>
</template>
Web vs. email
Email clients have limited and inconsistent CSS support. Maizzle uses several strategies to bridge the gap between the modern Tailwind CSS and email client rendering engines.
| Feature | Web | Maizzle |
|---|---|---|
| CSS variables | Supported | Supported (resolved at build time) |
oklch() colors | Supported | Supported (lowered to HEX) |
CSS nesting like in :hover or @media queries | Supported | Supported (flattened) |
Class names with : | Supported | Rewritten (sm:block → sm-block) |
Modern syntax
Tailwind CSS 4 uses modern CSS features like oklch() colors, CSS nesting, and custom properties. Most email clients don't support these, but Maizzle uses Lightning CSS and a few custom tools to lower this modern syntax to simpler CSS that works everywhere.
Safe class names
Some email clients (notably Gmail) strip class names that contain special characters like : or /. The Safe Selectors transformer rewrites those cool-looking Tailwind selectors like sm:text-lg to sm-text-lg so they work everywhere.
CSS inlining
Gmail Android with an IMAP email address (aka GANGA) ignores <style> tags. Other clients, like older Outlooks, only use the first class in a class="" attribute, ignoring the rest.
Maizzle inlines Tailwind CSS utilities into style attributes, so your styling stays consistent.
<template>
<Html>
<Head />
<Body>
<Tailwind>
<Text class="text-lg hover:text-blue-600">Hello</Text>
</Tailwind>
</Body>
</Html>
</template>
Custom CSS
If you really have to, you can combine Tailwind with your own custom or inline CSS:
<template>
<Html>
<Head>
<style>
@import "@maizzle/tailwindcss";
.custom-border {
@apply rounded-lg;
border: 2px solid #e2e8f0;
}
</style>
</Head>
<Body>
<Container class="custom-border p-4" style="background-color: #facade;">
...
</Container>
</Body>
</Html>
</template>
Raw styles
To prevent CSS inside a <style> tag from being compiled, use the raw attribute:
<style raw>
/* Tailwind compilation disabled here, but may still be inlined */
</style>
<style raw> doesn't prevent CSS inlining or purging, use <style embed> for that.
Configuration
@maizzle/tailwindcss is the email-friendly Tailwind CSS 4 configuration that ships with Maizzle. Besides adjusting Tailwind's defaults, it adds prose styles for HTML content, MSO utilities for Outlook on Windows, and variants for targeting specific email clients.
Prose
@maizzle/tailwindcss ships with email-safe typography styles, similar to the @tailwindcss/typography plugin but tuned for email rendering quirks (no margin collapse, table-friendly defaults, no descendant selectors that break in Outlook).
Wrap rendered HTML or Markdown content in prose to get nicely styled headings, paragraphs, lists, blockquotes, and more out of the box:
<template>
<Layout>
<Container class="prose">
<h1>Hello world</h1>
<p>Body copy with a <a href="#">link</a>.</p>
<ul>
<li>Item one</li>
<li>Item two</li>
</ul>
</Container>
</Layout>
</template>
Style individual element types via prose-* variants:
<div class="prose prose-headings:text-brand prose-a:underline">
<h2>Headings inherit brand color</h2>
<a href="#">Links get an underline.</a>
</div>
Available variants:
MSO utilities
Outlook on Windows uses Microsoft Word as its rendering engine and supports a family of mso-* CSS properties for fine-tuning spacing, fonts, and layout that other clients ignore.
@maizzle/tailwindcss exposes these as utilities:
<!-- Font tweaks for Outlook only -->
<p class="mso-ansi-font-size-16 mso-ansi-font-weight-bold">
Outlook only font styling
</p>
<!-- Control line-height in Outlook -->
<p class="leading-6 mso-line-height-rule-exactly mso-line-height-alt-8">
Outlook uses a line-height of 8px instead of 24px here.
</p>
<!-- Force-hide content in Outlook -->
<div class="mso-hide-all">Hidden in Outlook</div>
A few of the most useful ones:
| Utility | Purpose |
|---|---|
mso-line-height-alt-* | Set an alternate line-height that only Outlook reads |
mso-line-height-rule-exactly | Make Outlook honor line-height precisely |
mso-text-raise-* | Vertically nudge an element |
mso-hide-all / mso-hide-none | Hide from / show in Outlook |
mso-padding-alt-* | Padding that only Outlook reads |
mso-font-width-* | Used by <Spacer> for horizontal spacing in Outlook |
See mso.css for the full list of MSO utilities provided, or the Microsoft Office HTML and XML Reference if you really want to go down this rabbit hole.
Email client targeting
@maizzle/tailwindcss ships with variants for styling elements for specific email clients.
These are useful because email clients have various levels of CSS support and rendering quirks, so you can write client-specific fixes without affecting how it looks elsewhere.
<p class="text-gray-950 gmail:text-gray-600">
Dark text in most clients, but lighter in Gmail.
</p>
<p class="text-base ios:text-2xl">
Bigger text in iOS Mail
</p>
<!-- Combine variants -->
<p class="dark:ios:text-white">
White text in dark mode on iOS Mail
</p>
Available client variants:
For targeting Outlook on Windows, use the <Outlook> component or the mso-* utilities.
Escaped selectors
Yahoo and AOL will replace the .& with their wrapping ID name. To target it, the yahoo: variant compiles to a selector that contains the escape sequence \&:
.\& .yahoo-text-2xl { font-size: 24px !important }
That backslash is a problem for Gmail, which drops the entire <style> tag the moment its CSS parser sees a \ character. If you mix yahoo: utilities with regular Tailwind classes in the same <Tailwind> block, all of those styles end up in one <style> tag, and Gmail throws it away.
The fix is to isolate yahoo: utilities in their own <Tailwind> block so they compile into a separate <style> tag:
<template>
<Html>
<Head />
<Body>
<Tailwind>
<Text class="yahoo:text-2xl">Limited-time offer.</Text>
</Tailwind>
<Tailwind>
<Text class="text-2xl hover:text-blue-600">Limited-time offer.</Text>
</Tailwind>
</Body>
</Html>
</template>
This generates two separate <style> tags, Gmail will only discard the Yahoo-targeting one.
Stacking with dark:
The ios: variant compiles to a @supports block, and Tailwind's dark: variant compiles to @media (prefers-color-scheme: dark). Stacking them, for example dark:ios:text-white, produces nested at-rules:
@media (prefers-color-scheme: dark) {
@supports (-webkit-overflow-scrolling: touch) and (aspect-ratio: 1 / 1) {
.dark-ios-text-white { color: #fff !important }
}
}
Gmail's CSS parser does not handle nested at-rules and discards the entire <style> tag when it sees one. Use the same pattern as for Yahoo Mail: keep the stacked utilities in their own <Tailwind> block so they compile into a separate <style> tag.
<template>
<Html>
<Head />
<Body>
<Tailwind>
<Text class="dark:ios:text-white">Dark mode on iOS</Text>
</Tailwind>
<Tailwind>
<Text class="text-base">Everything else</Text>
</Tailwind>
</Body>
</Html>
</template>
This applies to any client variant that compiles to an at-rule, like ios-10: or gmail-ipad:.
Customization
Tailwind CSS 4 is configured in CSS, there's no tailwind.config.js anymore. You customize the theme directly inside the <style> tag or via the #config slot on the <Tailwind> component.
Theme tokens
Use @theme to add or override design tokens (colors, fonts, spacing, breakpoints, …):
<template>
<Layout>
<Head>
<style>
@import "@maizzle/tailwindcss";
@theme {
--color-brand: #4f46e5;
--color-brand-dark: #3730a3;
--font-display: "Inter", sans-serif;
}
</style>
</Head>
<Container class="bg-brand-dark">
<Text class="text-brand font-display">Hello!</Text>
</Container>
</Layout>
</template>
Anything declared under @theme becomes a utility (text-brand, bg-brand-dark, font-display…) and is available in your templates and components.
Override defaults
Use the same token names as Tailwind's defaults to override them. For example, redefine the default sans font or the slate palette:
@theme {
--font-sans: "Inter", "Helvetica Neue", Arial, sans-serif;
--color-slate-50: #f8fafc;
--color-slate-900: #0f172a;
}
Custom variants
Define your own variants with @custom-variant. For example, here's an any-hover: variant that only applies hover styles in clients where the user has a pointing device:
@import "@maizzle/tailwindcss";
@custom-variant any-hover {
@media (any-hover: hover) {
&:hover {
@slot;
}
}
}
Per-template config
Pass a config slot to the <Tailwind> component to scope tokens to one template:
<template>
<Tailwind>
<template #config>
@import "@maizzle/tailwindcss";
@theme {
--color-brand: #f59e0b;
}
</template>
<Body>
<Text class="text-brand">Limited-time offer.</Text>
</Body>
</Tailwind>
</template>
Intellisense
To get Tailwind CSS Intellisense working, you need an actual .css file that imports Tailwind in your project. For example, the official Maizzle starter includes a tailwind.css file:
@import "@maizzle/tailwindcss";