`<Tailwind>`: Outlook.com ignores every media query after the first, because the generated `<style>` has adjacent `}}`

未关闭 适合新手
#3,889 0 条评论 0 个 reaction 已指派 0 人 在 GitHub 查看

维护者通常 1 天内回复

还没有人认领这个 Issue。

评估

难度
2/5
预计耗时
1-3 小时
新手友好度
78/100
Issue 类型
缺陷
描述清晰度
描述清楚
活跃度
活跃
技术栈
react, tailwindcss, typescript
领域
frontend

调研方向

从 packages/react-email/src/components/tailwind/tailwind.tsx 中的 generate(nonInlineStyles) 开始,阅读样式表的序列化方式;issue 还指出了 downlevel-for-email-clients.ts,其中包含现有的邮件客户端适配逻辑。检查包含多个 @media 块的 <style> 生成结果,并确认相邻的右花括号之间有空白分隔,同时保留 CSS 规则。

由索引模型根据 Issue 内容生成。

描述

Type: Bug
What versions are you using? (if relevant)

[email protected] (bundled / render), [email protected] (latest of both at time of writing)

Describe the Bug

Outlook.com deletes everything in a <style> element that comes after two adjacent closing braces, }}. This is a long-standing client bug, documented in hteumeuleu/email-bugs#92. The documented workaround is any whitespace between the braces (} }).

<Tailwind> puts every non-inlinable rule in its own @media block and writes the stylesheet minified (generate(nonInlineStyles) in tailwind.tsx). Every block ends in }} and they all share one <style>, so Outlook.com keeps the first @media block and drops the rest.

In practice only one responsive (sm:, md:, …) or dark: rule per email works in Outlook.com. Columns that sit side by side through sm: widths stay stacked at desktop width, and only the first dark-mode colour applies. I've also seen the same thing in Outlook on the web with a Microsoft 365 mailbox and in the new Outlook for Windows.

The CSS is valid, so strictly this is Outlook's bug. But <Tailwind> already adapts its output for email clients (downlevel-for-email-clients.ts), and fixing it there would cover everyone who uses responsive or dark: classes. render(..., { pretty: true }) doesn't help: the <style> contents keep their }}.

Suggested fix: when writing the non-inlinable stylesheet, put whitespace between adjacent closing braces (} }). Merging identical media queries would not be enough on its own: with two different queries (a breakpoint and dark:), the second block still comes after a }}.

What is affected (leave empty if unsure)

Tailwind Component

Link to the code that reproduces this issue

https://github.com/lpolito/react-email-outlook-double-brace

Minimal, self-contained (react-email + tailwindcss only). Core of render.tsx:

import { Body, Head, Html, render, Tailwind } from "react-email";

// Four boxes, each red inline and turned green by its own `sm:` rule.
const Email = () => (
  <Tailwind>
    <Html>
      <Head />
      <Body>
        <div className="bg-[#cf222e] sm:bg-[#1a7f37]">Box 1</div>
        <div className="bg-[#cf222e] sm:bg-[#1f883d]">Box 2</div>
        <div className="bg-[#cf222e] sm:bg-[#2da44e]">Box 3</div>
        <div className="bg-[#cf222e] sm:bg-[#116329]">Box 4</div>
      </Body>
    </Html>
  </Tailwind>
);

const html = await render(<Email />);
To Reproduce
npm install
npm run render

This writes out/raw.html (react-email's output, unchanged) and out/spaced.html (the same, with one space between each pair of adjacent closing braces inside <style>). The generated <style>:

@media (min-width: 40rem) {
  .sm_bg-_1a7f37 {
    background-color: rgb(26, 127, 55) !important;
  }
}
@media (min-width: 40rem) {
  .sm_bg-_1f883d {
    background-color: rgb(31, 136, 61) !important;
  }
}
@media (min-width: 40rem) {
  .sm_bg-_2da44e {
    background-color: rgb(45, 164, 78) !important;
  }
}
@media (min-width: 40rem) {
  .sm_bg-_116329 {
    background-color: rgb(17, 99, 41) !important;
  }
}

Open both in Outlook.com at desktop width (I used Litmus's Outlook.com client in Chrome).

out/raw.html: box 1 is green, boxes 2–4 stay red.

Outlook.com, raw output: only box 1 is green

out/spaced.html: all four are green. (A Microsoft promo pop-up covers part of the top.)

Outlook.com, spaced output: all four boxes are green

Expected Behavior

Every @media block that <Tailwind> generates survives in Outlook.com, so all four boxes are green at desktop width.

What's your node version? (if relevant)

v24.18.0

主要语言
TypeScript
星标
19.9k
派生
1.1k
平均合并
2 天 15 小时
30 天内合并 PR
61

环境准备

从这里开始

  1. 先读完整个 Issue,再读项目的贡献指南。
  2. 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
  3. Fork 仓库,在一个分支上完成修改。
  4. 提交 Pull Request,并在描述里引用这个 Issue 编号。

resend/react-email 的其他 Issue

查看 resend/react-email 的全部 Issue

相似的 Issue

更多 TypeScript Issue

把新 issue 发到你的邮箱

精选适合新手参与的 GitHub issue 摘要。