Using Tailwind CSS
Maizzle uses the Tailwind CSS framework, so you can quickly style HTML email templates with utility classes instead of writing inline styles.
If you've never worked with CSS utility classes in HTML emails, at first you might say:
I could just write inline CSS, it's the same thing!
However, utility classes in Tailwind are much more powerful and allow you to:
- style responsive breakpoints
- style pseudos like
:hover
- do both of the above with one class
- style for dark mode, print, reduced motion and more
- stay on-brand by using a design system in your team
... all while never having to leave your HTML.
Combine that with powerful plugins like tailwindcss-email-variants
that allow you to target email clients just by using a class like gmail:hidden
and you can quickly see why utility-first CSS with Tailwind CSS is such a powerful tool for HTML emails.
For most of the time, you won't be writing CSS anymore 😎
Workflow
To use Tailwind CSS in your HTML emails, simply add the @tailwind
directives to a <style>
tag in your Layout's <head>
:
<!doctype html>
<html>
<head>
<style>
@tailwind components;
@tailwind utilities;
</style>
</head>
<body>
<yield />
</body>
</html>
Alternatively, you may store them in a CSS file:
@tailwind components;
@tailwind utilities;
img {
@apply max-w-full align-middle;
}
... and @import
that instead:
<style>
@import "css/tailwind.css";
</style>
Prefer <link>
tags? Maizzle supports that too:
<!doctype html>
<html>
<head>
<link rel="stylesheet" href="css/tailwind.css" inline>
</head>
<body>
<yield />
</body>
</html>
When using <link>
tags, you must make sure they include:
- the
rel="stylesheet"
attribute - an
inline
orexpand
attribute
Otherwise, the CSS file they reference will not be compiled.
Utility-first
Simply write your HTML markup and use Tailwind CSS utility classes to style elements.
Instead of writing something like this:
<table style="width: 100%;">
<tr>
<td style="padding: 24px 0; background-color: #e5e7eb;">
<h1 style="margin: 0; font-size: 36px; font-family: -apple-system, 'Segoe UI', sans-serif; color: #000000;">
Some title
</h1>
<p style="margin: 0; font-size: 16px; line-height: 24px; color: #374151;">
Content here...
</p>
</td>
</tr>
</table>
You can write:
<table class="w-full">
<tr>
<td class="py-6 px-0 bg-gray-200">
<h1 class="m-0 text-4xl font-sans text-black">
Some title
</h1>
<p class="m-0 text-base/6 text-gray-700">
Content here...
</p>
</td>
</tr>
</table>
Read more about the concept of utility-first CSS and familiarize yourself with the syntax in the Tailwind CSS docs. And if you're using VSCode, make sure to install the Tailwind CSS IntelliSense extension, to get autocompletion and hover tooltips for Tailwind classes.
Components
If you're repeating the same utility classes over and over again, you can extract them to a Component so you've got a single source of truth and can make changes in one place.
Custom classes
As an alternative to creating a Component, you may extract utility classes to a custom class using Tailwind's @apply
directive.
Here's a quick example:
@layer components {
.button-danger {
@apply py-3 px-6 text-white bg-red-500;
}
}
Unlike custom utility classes that you may add to tailwind.config.js
, you would add that in a CSS file that is imported in your main <style>
tag or through a <link>
tag.
And that brings us to...
CSS Files
You may organize your CSS into files if you prefer, and then @import
them in a <style>
tag or through a <link>
in your Layout's <head>
.
For example, let's import that css/components.css
file we just created:
<!doctype html>
<html>
<head>
<style>
@import "css/components.css";
@tailwind components;
@tailwind utilities;
</style>
</head>
<body>
<yield />
</body>
</html>
@import
statements need to come before any other CSS rules in the
<style>
tag, otherwise the entire CSS inside them will be discarded.
Shorthand CSS
<style>
tags. For
inline
CSS shorthand, see the
Shorthand CSS Transformer docs
.
Maizzle can automatically rewrite your padding
, margin
, and border
CSS properties in shorthand-form, when possible.
Because utility classes map one-to-one with CSS properties, this normally doesn't have any effect with Tailwind CSS. However, in the context of <style>
tags, it's useful when you extract utilities to components, with Tailwind's @apply
.
Consider this template:
<x-main>
<div class="col">test</div>
</x-main>
Let's use @apply
to compose a col
class by extracting two padding utilities:
<!doctype html>
<html>
<head>
<style>
.col {
@apply py-2 px-1;
}
@tailwind components;
@tailwind utilities;
</style>
</head>
<body>
<yield />
</body>
</html>
When running the build command, normally that would generate:
.col {
padding-top: 8px;
padding-bottom: 8px;
padding-left: 4px;
padding-right: 4px
}
However, Maizzle will merge those to shorthand-form, so we get this:
.col {
padding: 8px 4px;
}
This results in smaller HTML size, reducing the risk of Gmail clipping your email.
Using shorthand CSS for these properties is well supported in email clients and will make your HTML lighter, but the shorthand border (documented next) is particularly useful because it's the only way Outlook will render it properly.
padding
or
margin
, you need to specify property values for all four sides. For borders, keep reading.
Shorthand borders
To get shorthand-form CSS borders, you need to specify all these:
- border-width
- border-style
- border-color
With Tailwind's @apply
, that means you can do something like this:
.my-border {
@apply border border-solid border-blue-500;
}
... which will turn this:
<div class="my-border">Border example</div>
... into this:
<div style="border: 1px solid #3f83f8;">Border example</div>
Alternatively, you may use an arbitrary values:
<div class="[border:1px_solid_#3f83f8]">Border example</div>
You can even reference colors from your Tailwind config:
<div class="[border:1px_solid_theme(colors.gray.300)]">Border example</div>
Arbitrary values are actually really useful for Outlook, because something like border-b border-solid border-black
will not be shorthanded and Outlook can only apply individual borders when you write them in shorthand.
So you can do this:
<div class="[border-bottom:1px_solid_#000]">Bottom border example</div>
This might look like inline styles with extra steps, but it's still Tailwind so you can do stuff that you can't do with inline CSS, like pseudos or media queries:
<div class="hover:[border:1px_solid_#000] sm:[border:none]">
Border example
</div>
Plugins
To use a Tailwind CSS plugin, simply npm install
it and follow its instructions to add it to plugins: []
in your tailwind.config.js
:
import emailVariants from 'tailwindcss-email-variants'
/** @type {import('tailwindcss').Config} */
export default {
plugins: [
emailVariants,
],
}
Or, with a CJS config file:
/** @type {import('tailwindcss').Config} */
module.exports = {
plugins: [
require('tailwindcss-email-variants'),
],
}
See the Tailwind CSS docs for more information on plugins.
tailwindcss-email-variants
is already included in our Tailwind CSS preset, you don't need to install it separately.
Use in Template
You may use Tailwind CSS, including directives like @apply
, @layer
, and even nested syntax, right inside a Template. You simply need to use the stack/push pattern to inject a <style>
tag into the Layout being extended.
First, add a <stack name="head" />
inside your Layout's <head>
tag:
<!doctype html>
<html>
<head>
<style>
@tailwind components;
@tailwind utilities;
</style>
<stack name="head" />
</head>
<body>
<yield />
</body>
Next, push
to that stack
from a Template:
<x-main>
<push name="head">
<style>
a {
@apply text-blue-500;
}
@media screen(sm) {
table {
@apply w-full;
}
}
</style>
</push>
<!-- your email HTML... -->
</x-main>
Prevent inlining
You can prevent CSS inside a <style>
tag from being inlined:
<x-main>
<push name="head">
<style data-embed>
img {
@apply max-w-full align-middle;
}
</style>
</push>
<!-- your email HTML... -->
</x-main>
You may use any of the following attributes for this purpose:
data-embed
no-inline
embed
Although it will no longer be inlined, unused CSS will still be purged - see the css.purge Transformer docs for more information.
Email client targeting
Maizzle comes with tailwindcss-email-variants, a Tailwind CSS plugin that makes it easy to style your HTML emails for certain email clients.
It adds custom variants that you may use to style elements only for certain email clients.
Gmail
Use the gmail
variant to style elements in Gmail's webmail:
<body class="body">
<div class="gmail:hidden">...</div>
</body>
The compiled HTML will include this CSS rule:
u + .body .gmail\:hidden {
display: none;
}
body
class on your
<body>
tag.
Gmail on older versions of Android requires a different selector, so there's a separate variant provided:
<body class="body">
<div class="gmail-android:hidden">...</div>
</body>
Result:
div > u + .body .gmail-android\:hidden {
display: none;
}
Apple Mail (10+)
The apple-mail
variant will target Apple Mail 10 and up:
<div class="apple-mail:hidden">...</div>
Result:
.Singleton .apple-mail\:hidden {
display: none;
}
iOS Mail (10+)
Use the ios
variant to target iOS Mail 10 and up:
<div class="ios:hidden">...</div>
Result:
@supports (-webkit-overflow-scrolling:touch) and (color:#ffff) {
.ios\:hidden {
display: none;
}
}
iOS Mail (15)
Use the ios-15
variant if you need to target iOS Mail 15 specifically:
<div class="ios-15:hidden">...</div>
Result:
@supports (-webkit-overflow-scrolling:touch) and (aspect-ratio: 1 / 1) {
.ios-15\:hidden {
display: none;
}
}
Outlook.com dark mode
Use the ogsc
and ogsb
variants to change color
and background-color
of elements in Outlook.com dark mode.
<!-- Color -->
<div class="ogsc:text-slate-200">...</div>
<!-- Background color -->
<div class="ogsb:bg-slate-900">...</div>
Result:
[data-ogsc] .ogsc\:text-slate-200 {
color: #e2e8f0;
}
[data-ogsb] .ogsb\:bg-slate-900 {
background-color: #0f172a;
}
Open-Xchange
Use the ox
variant to target webmail clients that are powered by Open-Xchange.
Some of these email clients include Comcast, Libero, 1&1 MailXchange, Network Solutions Secure Mail, Namecheap Email Hosting, Mailbox.org, 123-reg Email, acens Correo Professional, Home.pl Cloud Email Xchange, Virgin Media Mail, and Ziggo Mail.
<div class="ox:hidden">...</div>
Result:
.ox\:hidden[class^="ox-"] {
display: none;
}
Outlook CSS
Outlook and Office 365 on Windows support proprietary mso-
CSS properties.
Maizzle includes tailwindcss-mso, allowing you to use Outlook-only utilities:
<p class="mso-hide-all">...</p>
These are utility classes that work just as you'd expect - they support arbitrary values:
<p class="mso-color-alt-[#ffcc00]">...</p>
... and, where it makes sense, negative values too:
<p class="-mso-text-raise-4">...</p>