Tailwind CSS for Email #104
thecodedrift
announced in
Thunked
Replies: 2 comments 1 reply
-
Based on the above and |
Beta Was this translation helpful? Give feedback.
0 replies
-
Thanks for the inspiration. I referenced your variable handling method and published inline-html-styles. |
Beta Was this translation helpful? Give feedback.
1 reply
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
-
I can no longer count the hours Tailwind saves me. I don't really hate the state of CSS-in-JS. I just find it gets in the way of quickly saying "make this blue, put it in a flex box oriented by row, with the items centered." It's a preference thing, and my preference is towards utility class names at this point, letting React components do any reusable composition for me. It's been great, save for one quibble; email is awful. It's not really tailwind's fault, but emails are still table-based, inline styles, and generally everything awful we definitely thought we got rid of in 2002.
What follows isn't complex enough to justify an NPM module, but it is a pattern generally usable for getting Tailwind into your HTML emails. The short version is:
tailwind.email.css
is generated using the tailwind CLI. It contains@tailwind utilities;
and any resets I'd like to do. Be sure you add your email templates to tailwind's config file, otherwise your styles won't be included.tailwind.email.css
through a conversion ofrem
topx
because it's 2022 and outlook still does not support therem
unit--tw-
variables with their resolved valuesOn Generating the HTML
For what I'm currently doing, emails are built using everyday React and a renderer that calls
ReactDOMServer.renderToStaticMarkup(element)
. The complexities oftr
andtd
nesting is easily hidden behind some common components, which I chose to nameRow
andColumn
respectively. They're nothing fancy and based on the reality we'll probably never get flexbox in HTML emails in my lifetime.Ultimately though, how you generate the HTML is up to you, as long as you have it as a string.
Tailwind Configuration
To make Tailwind email friendly, we need to disable a bunch of core plugins that don't work well across email clients. A huge shoutout goes to Can I Email. Disabling these plugins is straightforward: add them to your theme's definition, and Tailwind will make sure not to generate CSS for them. Trying to use these CSS classes will just result in useless CSS classes with no style information & can be stripped later.
Converting
rem
topx
I wasn't keen on building a completely custom
tailwind.config.js
file, and therem
unit is easy enough to convert from as long as you specify a base value in pixels. Tailwind uses a default base of16px
, so we can convert all of our units via a regular expression.Applying juice
Our stylesheet is now email friendly, and it's time for juice.
Cool. That sounds exactly what we're looking to do. We'll take our HTML plus our px-friendly CSS and ask juice to merge the two together into a final output.
That's mostly it. Juice takes care of the inline style tags for us where it can, defaulting to a
<style>
tag in the head where it can't (ie media queries), and just generally does the right thing all the time. About the only thing it can't do is fix custom CSS variables, but there's an issue for CSS variable support with a workaround. We'll use code similar to the workaround, but with a tool that's a bit faster than cheerio.Resolving CSS Variables
Resolving our CSS variables requires us to make a few assumptions about how email clients behave. First, if a client supports advanced CSS such as
@media
queries, they also support CSS variables. That means the only CSS variables we'll need to care about are those located inline and in the body. Second, we can assume that Tailwind isn't going to generate any weird syntax. CSS variables will be in the form of--declaration: value
, and usage will be in the form ofvar(--declaration)
with an optional fallback.Replacing the variables will require two passes. On the first pass, we'll capture all our CSS variables assigned to the inline style, and on the second, we'll replace our
var()
statements with the resolved values. Usingrehype
andrehype-rewrite
makes this a straightforward task.Wrapping Up
At this point, I'm pretty happy with where the code is. The generated tailwind output from the email.css is easy to understand and manage since it's just vanilla tailwind. Juice is doing what it does best, and the only manual adjustments in the code are converting the
rem
topx
and fixing of inline CSS variables. That's a really small amount of maintained code relative to an email specific library and build system in exchange for the full power of Tailwind. Emails still suck, but hopefully they'll suck a little less. With the Libraries in place, we put together the Taskless Usage in about 20 minutes.And the associated code:
Beta Was this translation helpful? Give feedback.
All reactions