十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

用 React Email 写出“在邮箱里不垮”的邮件模板:Novu 场景下的完整样式指南

用 React Email 写出“在邮箱里不垮”的邮件模板:Novu 场景下的完整样式指南 用 React Email 写出“在邮箱里不垮”的邮件模板Novu 场景下的完整样式指南【免费下载链接】novuThe open-source communication infrastructure for agents and products项目地址: https://gitcode.com/GitHub_Trending/no/novu邮件是少数仍停留在二十年前渲染技术的渠道没有标准化的 CSS 支持客户端各自为政。用 React Email以 React 组件的方式编写 HTML 邮件时样式怎么写、用不用 Tailwind、为什么不能用rem这些问题的答案都直接决定模板在 Gmail、Outlook、Apple Mail 中是否正常显示。本文以 Novu 开源仓库内置的react-email技能文档 STYLING.md 为核心系统讲解 React Email 模板的样式规范、客户端限制规避技巧、品牌一致性方案并结合仓库中真实的邮件模板如 Novu onboarding 邮件、代码优先框架的 email step给出可落地的写法。先厘清上下文这份样式指南用在哪在进入细节前先交代这份文档所处的环境。Novu 仓库根目录下的.agents/skills/react-email/是一套面向“生成/维护 HTML 邮件模板”场景的完整技能skill由 SKILL.md 定义入口配套references/目录下的四份参考文档分工明确COMPONENTS.md组件总览STYLING.md样式规范本文核心PATTERNS.md常见模板范式SENDING.md投递相关。在 Novu 生态里React Email 有着真实且具体的落点Novu 的“代码优先框架Code-First Framework”允许开发者在 workflow 中用step.email()渲染邮件而邮件正文正是由 React Email 组件编译而来。仓库文档 react-email.mdx 给出了完整的接入三步走先安装react-email/components再编写模板组件并导出renderEmail()最后在 workflow 的 email step 中把渲染结果赋给body。同时novuCLI 还内置了app-react-email脚手架模板其中有一封完整的 onboarding 邮件可供对照学习。这意味着下面每一类样式规则都能在 Novu 仓库里找到真实范例。选对样式入口Tailwind组件 vs 行内样式React Email 的样式方案核心决策只有一条见 STYLING.md如果项目在使用 Tailwind CSS就用Tailwind组件包裹模板并以 className 编写样式否则退化为行内样式。推荐写法的示例代码如下import { Tailwind, pixelBasedPreset } from react-email/components; Tailwind config{{ presets: [pixelBasedPreset], theme: { extend: { colors: { brand: #007bff, }, }, }, }} {/* Email content */} /TailwindTailwind本质上会在渲染阶段把模板中的 Tailwind 工具类编译成邮件客户端可识别的内联/嵌入 CSS。为了模板之间风格一致还应在设计阶段把品牌色等 token 注入theme.extend供bg-brand、text-brand这类语义类名使用。在 Novu 仓库中app-react-email脚手架的 onboarding 邮件正是这一做法的真实范例novu-onboarding-email.tsx 中Tailwind的theme.extend.colors定义了brand: #2250f4、offwhite: #fafbfb、blurwhite: #f3f3f5三个语义色并自定义了像素级spacing随后Body classNamebg-blurwhite text-base font-sans直接消费这些 token——这就是“品牌配置集中、模板只写语义类名”的工程化形态。pixelBasedPreset为什么必须配它样式指南特别强调了一个很多 React 开发者会踩的坑邮件客户端不支持rem单位。Tailwind 默认的间距、字号体系p-4、text-base等底层换算基于rem在邮件里会失效或被错误渲染。pixelBasedPreset就是为此准备的——它是react-email/components导出的一个 Tailwind 预设preset作用是在编译阶段把基于rem的工具类换算成像素值import { pixelBasedPreset } from react-email/components; Tailwind config{{ presets: [pixelBasedPreset] }}因此规则是只要用Tailwind就应始终把pixelBasedPreset放进config.presets。可以看到 SKILL.md 中的每个示例模板都遵循了这一约定。仓库里docs/framework/content/react-email.mdx与I18N.md配套的示例例如 next-intl 本地化的邮件模板同样一律携带config{{ presets: [pixelBasedPreset] }}侧面印证了这是不可省略的标准配置。邮件客户端的 CSS 边界先知道“不能做什么”在写任何样式之前必须先背下邮件客户端的硬性限制清单STYLING.md。这些不是“建议”而是 Gmail、Outlook、Apple Mail、Yahoo Mail 渲染引擎的客观事实特性结论替代方案SVG / WEBP 图片不支持可能直接不显示只用 PNG / JPEGFlexbox / Grid 布局大部分客户端不识别Row/Column组件或原生 table媒体查询sm:md:lg:xl:多数桌面客户端忽略移动优先的堆叠式布局主题选择器dark:light:不支持需要深色版本时用固定背景色rem单位不被支持pixelBasedPreset转像素与之呼应的是react-email/components提供Row、Column、Section等组件它们在底层全部编译为table/td这正是“邮件里没有 flexbox只有表格”这一事实的体现。样式指南同步建议不要试图在邮件里用 CSS 媒体查询做响应式而是默认按移动端设计详见后文 Layout 小节。边框Border的三条铁律邮件客户端对border的简写处理尤其脆弱。如果不显式指定 border-style很多客户端根本不渲染边框。样式指南给出了正确与错误的对比STYLING.md// 正确 —— 显式声明 border style div classNameborder-solid border border-gray-300 / // 正确 —— 只画单侧边框时先清掉其余三侧 div classNameborder-none border-l border-solid border-l-gray-300 / // 错误 —— 缺少 border-style邮件客户端可能不显示 div classNameborder border-gray-300 /记住这一口诀凡是写border永远带上border-solid/border-dashed之类的 style只画单侧时先border-none再补那一侧。这也是 SKILL.md 中“Always specify border type”“单侧边框记得重置其余边框”两条行为准则的直接来源。组件结构规范Head /与 PreviewPropsHead /必须放在Tailwind /内部Tailwind 生成的样式需要被Head注入。若Head在Tailwind外层生成的style将无法正确内联。标准骨架如下STYLING.mdHtml Tailwind config{{ presets: [pixelBasedPreset] }} Head / Body.../Body /Tailwind /Html真实范例见 novu-onboarding-email.tsx组件外层是Html内部Head /紧随PreviewTailwind则包住Head /与Body /顺序与规范完全一致。PreviewProps只放组件真正用到的 props.PreviewProps是静态属性供开发预览与测试时为模板提供示例数据。规范要求只包含组件实际消费的 props避免把无用数据带进预览const Email ({ source }: { source: string }) { return ( div a href{source}Click here/a /div ); }; Email.PreviewProps { source: https://example.com, };若为 Novu 动态消息编写模板Props 来源通常是 workflow 里定义的 controls/payload schema。app-react-email脚手架中用type NovuWelcomeEmailProps ControlSchema PayloadSchema声明组件 props见 novu-onboarding-email.tsxControlSchema与PayloadSchema从相邻的 workflow 定义导入随后通过renderEmail(controls, payload)将二者合并传给模板。这相当于把 PreviewProps 的“单一数据来源”思想扩展到了生产渲染链路。默认布局结构从 Body 到 Footer为保证观感一致指南规定了一套默认布局模板STYLING.mdBody内容底色与整体排版基调例如classNamefont-sans py-10 bg-gray-100Container白色背景、水平居中、内容左对齐的卡片例如classNamemx-auto bg-white p-6 roundedFooter必须包含物理地址、退订链接、当年份例如Section classNametext-center text-gray-500 text-sm Text classNamem-0123 Main St, City, State 12345/Text Text classNamem-0copy; {new Date().getFullYear()} Company Name/Text Link href{unsubscribeUrl}Unsubscribe/Link /Section注意 Footer 中地址与版权行都加了m-0以消除列表项/段落间的默认外边距。这在项目模板里同样可见——Novu onboarding 邮件的页脚 Container 使用Container classNamemt-20Text classNametext-center text-gray-400 mb-45承载“Powered by Novu”声明novu-onboarding-email.tsx。排版用字号与间距拉开信息层级排版规范STYLING.md遵循一个朴素的视觉层级原则标题要“重”正文要“轻”标题外边距大段落外边距小。// 标题加粗、更大字号、更大外边距 Heading classNametext-2xl font-bold text-gray-900 mb-4 // 段落常规字重、较小字号、较小外边距 Text classNametext-base text-gray-700 mb-3规范进一步要求整套模板使用“尊重内容层级的一致间距”避免每个模板各自为政。在 Novu 的 onboarding 模板里可以看到统一间距 token 的做法spacing被显式覆盖为0: 0px、20: 20px、45: 45px见 novu-onboarding-email.tsxmy-20、mb-45、p-45等都落在同一套像素级间距体系内这就是“风格一致可维护”的工程化落地。图片规范只有 PNG / JPEG必须绝对 URL图片是邮件兼容问题的重灾区样式指南列出的规则STYLING.md如下只有用户明确提出才放入图片正文内容图使用响应式尺寸w-full、h-auto24–48px 的小图标允许固定尺寸绝不拉伸变形用户提供的图片绝不自行创建 SVG一律使用绝对 URL本地开发时依赖 dev server 的/static/静态目录必须带alt文本保证可访问性。Img srchttps://example.com/image.png altDescription classNamew-full h-auto /静态资源该放哪关于图片与字体的存放位置规范STYLING.md给出两条明确路径Logo / 内容图片托管在 CDN 或公网 URL本地开发时放入emails/static/目录由 dev server 提供自定义字体通过Font组件引用网络字体 URLGoogle Fonts、Adobe Fonts 或自托管字体。实践上还要区分开发/生产 URL。Novu 官方模板的做法具有参考价值onboarding 模板直接使用托管在图片服务上的绝对地址并为Img标注了width、height与altNovunovu-onboarding-email.tsx头像类图片则额外追加rounded-full圆角样式属典型的“绝对 URL 尺寸 alt”三重保险。按钮永远带上box-border按钮在多数邮件客户端中按border-box以外的盒模型计算导致 padding 溢出撑破布局。因此规范STYLING.md要求按钮类名里必须包含box-border配合block占满可用宽度与text-center no-underline去下划线让整块区域可点击Button hrefhttps://example.com classNamebg-blue-600 text-white px-5 py-3 rounded box-border block text-center no-underline Click Here /Button对应的实现事实是React Email 的Button在邮件中最终渲染为一个带display: inline-block样式包裹a的表格式按钮。仓库模板中的 CTA 同样遵循“整块点击”思路例如 onboarding 邮件内的按钮用bg-[#000000] rounded text-white ... no-underline text-center px-5 py-3novu-onboarding-email.tsx。布局移动优先 表格式多列邮件没有“响应式断点”可用因此默认就按移动端设计STYLING.md优先堆叠式布局任何屏幕宽度都成立主容器最大宽度约 600px移除列表项之间的默认间距/margin/padding。需要多列排版时用Row/Column代替 flexbox/gridRow Column classNamew-1/2Left content/Column Column classNamew-1/2Right content/Column /RowNovu onboarding 模板里的“用户邀请”区块就很有代表性先用Row align{...}建立一行再用三个Column分别承载头像、箭头、团队头像并各自通过alignright/center/left控制对齐novu-onboarding-email.tsx是“表格列布局替代 flexbox”的教科书式示范。深色模式客户端不支持选择器就手动配色由于dark:/light:主题选择器在邮件客户端里不工作当用户明确要求深色邮件时规范STYLING.md给出了两套固定的配色约定内容卡片纯黑#000页面背景深灰#151516。Body classNamebg-[#151516] Container classNamebg-black text-white本质上是把“深色模式”当成一套独立模板做静态配色而不是运行时切换主题。品牌一致性建一个集中的 Tailwind 配置动手前先收集品牌色规范建议在制作邮件前先向用户收集一套完整品牌色板STYLING.md并给出了每种颜色的默认建议值颜色角色用途建议默认值Primary按钮、链接、关键强调用户品牌主色Secondary边框、背景、次要元素用户品牌辅助色Text正文文本色#1a1a1a浅色背景Text muted说明文字、页脚#6b7280Background邮件整体背景#f4f4f5Surface容器/卡片背景#ffffff文档还附带了一段可直接发给用户的“品牌信息收集 prompt”引导其一次性提供主色 hex、可公开访问的 PNG/JPEG logo、辅助色与风格偏好避免来回确认。集中式tailwind.config.ts为了在多个模板间保持品牌一致规范要求把所有品牌 token 收敛到一个中央配置文件供全部邮件模板 importSTYLING.md。使用satisfies TailwindConfig可以让 IDE 对全部配置项提供智能提示// emails/tailwind.config.ts import { pixelBasedPreset, type TailwindConfig } from react-email/components; export default { presets: [pixelBasedPreset], theme: { extend: { colors: { brand: { primary: #007bff, secondary: #6c757d, }, }, }, }, } satisfies TailwindConfig; // 非 Tailwind 的品牌素材可选 export const brandAssets { logo: { src: https://example.com/logo.png, alt: Company Name, width: 120, }, };各模板只需引入共享配置与品牌素材import tailwindConfig, { brandAssets } from ./tailwind.config; Tailwind config{tailwindConfig} Body classNamebg-gray-100 font-sans Container classNamebg-white p-6 Img src{brandAssets.logo.src} alt{brandAssets.logo.alt} width{brandAssets.logo.width} / Button classNamebg-brand-primary text-whiteAction/Button /Container /Body /Tailwind一致性维护四条守则配套的长期维护规则STYLING.md永远使用品牌配置——任何模板不得硬编码颜色改配置、不改模板——品牌换色只更新tailwind.config.ts一处使用语义化命名——写bg-brand-primary不写bg-[#007bff]保证对比度——正文与背景的对比度至少满足 WCAG AA4.5:1。第 3 点在文案上最容易被忽略字面量色值bg-[#007bff]一旦散落多封模板改品牌色就变成全仓搜索替换而语义名把“变化点”收敛到配置文件正是上述 4 条守则共同指向的目标。收尾检查清单跨客户端与体积样式写完后需要按以下清单过一遍STYLING.md模板要独一无二——针对用户具体场景定制而非套用通用样板跨客户端测试——Gmail、Outlook、Apple Mail、Yahoo Mail 逐一验证可用 Litmus / Email on Acid 做像素级校验也可用 React Email 预览工具检查具体特性支持情况控制体积在 102KB 以内——Gmail 会对更大的邮件做截断clipping关键词策略——在正文合理布局关键词以提升互动率行内样式兜底——部分客户端会剥掉style标签必要时保留行内样式作为降级方案。在 Novu 代码优先框架中串联这套规范最后把这套样式规范放回 Novu 的真实工作流中串一遍。完整的推荐链路是编写模板按本文规范用Tailwind config{{ presets: [pixelBasedPreset] }}包裹Head /与Body /品牌色统一来自中央tailwind.config.ts导出渲染函数在模板文件底部导出renderEmail()如 novu-onboarding-email.tsx 中export function renderEmail(controls, payload) { return render(NovuWelcomeEmail ... /); }接入 workflow在 Novu 代码优先框架的 workflow 中通过step.email(send-email, ...)返回{ subject, body: renderEmail(...) }body 即最终发送的 HTML——完整示例见 react-email.mdx样板参考需要从零搭建时可参考 CLI 脚手架app-react-email模板的目录结构与写法packages/novu/src/commands/init/templates/app-react-email/其中的 onboarding 邮件几乎覆盖了本文提到的全部样式要点集中色板、像素级间距、表格式多列、绝对 URL 图片、Footer 兜底。同时建议配合阅读同技能目录下的 COMPONENTS.md组件属性与用法、PATTERNS.md密码重置、订单确认等完整范式以及 TESTS.md形成“组件选型 → 样式落地 → 范式复用 → 测试验证”的完整闭环。【免费下载链接】novuThe open-source communication infrastructure for agents and products项目地址: https://gitcode.com/GitHub_Trending/no/novu创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表