shellscape / jsx-email

Build emails with a delightful DX
https://jsx.email
MIT License
902 stars 28 forks source link

Tailwind styles not applied on gmail #80

Closed xsyann closed 7 months ago

xsyann commented 8 months ago

Hi, thanks for your work on Tailwind, the performance has drastically improved (we went from 44s to 3-4s of rendering!)

However, if I understood correctly, twind generate the styles in a <style> tag and keep the original tailwind classnames, unlike tw-to-css which converts the styles to inline styles. The issue with that is gmail seems very sensitive with the <style> tag and removes it if it contains anything "fancy" (e.g. a selector containing !, a css variable like --tw-bg-opacity, etc).

Expected Behavior

Tailwind styles applied on gmail

Actual Behavior

All styles and tailwind classnames are dropped

Additional Information

Here is a simple template to reproduce the issue:

import { Tailwind } from '@jsx-email/tailwind';
import { Body } from '@jsx-email/body'
import { Head } from '@jsx-email/head'
import { Html } from '@jsx-email/html'
import { Text } from '@jsx-email/text'

export const TemplateName = 'jsx-email-starter';

export const Template = () => (
  <Html>
    <Head />
    <Body>
      <Tailwind config={{ preflight: false }}>
        <Text className="bg-green-500">Hello</Text>
      </Tailwind>
    </Body>
  </Html>
);

rendered to:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html lang="en" dir="ltr">
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <style twind="">
      .bg-green-500 {
        --tw-bg-opacity: 1;
        background-color: #10b981;
        background-color: rgba(16, 185, 129, var(--tw-bg-opacity));
      }
    </style>
  </head>
  <body>
    <div>
      <p
        class="bg-green-500"
        style="font-size: 14px; line-height: 24px; margin: 16px 0"
      >
        Hello
      </p>
    </div>
  </body>
</html>

If I manually edit the css, it is correctly rendered:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html lang="en" dir="ltr">
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <style twind="">
      .bg-green-500 {
        background-color: #10b981;
      }
    </style>
  </head>
  <body>
    <div>
      <p
        class="bg-green-500"
        style="font-size: 14px; line-height: 24px; margin: 16px 0"
      >
        Hello
      </p>
    </div>
  </body>
</html>

Update:

Currently, I noticed that the two main issues are css variables (e.g. var(--tw-text-opacity)) and escaped chars in css classnames (e.g. !font-base or w-[400px]).

The second one can be mitigated using twind hash feature (using isProduction on Tailwind component) but as a temporary quick workaround I chose to manually replace the variables in the generated html (using replaceAll) and hashing classes prevent to able to do so.

So my current setup is to manually replace tailwind variables in the generated html and use juice to inline the css:

const Renderer = (props: React.PropsWithChildren<TailwindProps>) => {
    const initialHtml = useData(props, () => jsxToString(<>{props.children}</>))
    const { shimmedHtml, styleTag } = renderTwind(initialHtml, props)
    const finalHtml = juice(`${shimmedHtml}${styleTag}`, {
        xmlMode: true,
    })

    return <div data-id="__jsx-email-twnd" dangerouslySetInnerHTML={{ __html: finalHtml }} />
}
shellscape commented 8 months ago

juice is interesting, I'll take a look at that as an option. out of curiosity, what does build --check return for your template? I'd like to know if it has warnings for those characters for gmail. We should probably also look at replacing css vars.

shellscape commented 8 months ago

This is likely what we'll use https://github.com/tani/rehype-inline-css since juice uses cheerio and we've moved onto rehype/unified. I'm thinking that we provide an option to inline the css, since it's a more expensive operation.

shellscape commented 7 months ago

@xsyann I think that jsx-email@latest fixes both of the concerns here. production: true compiles the multiple classes into one with compliant characters, and we're automatically replacing css variables now.

merunga commented 7 months ago

I'm using the new unified jsx-email package, but still getting the same problem

shellscape commented 7 months ago

@merunga could please you open a new issue with a reproduction for what you're seeing?

This one is about a slightly different topic

shellscape commented 7 months ago

Going to close this one for now. If anyone finds new problems, please open a new issue with a reproduction and we'll take a look quickly.