<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Mailpeek</title>
    <description>The latest articles on DEV Community by Mailpeek (@mailpeek).</description>
    <link>https://dev.to/mailpeek</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F184374%2Ff7fa8d72-5dd9-4142-a478-f66a355040bf.png</url>
      <title>DEV Community: Mailpeek</title>
      <link>https://dev.to/mailpeek</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mailpeek"/>
    <language>en</language>
    <item>
      <title>Dark Mode is still breaking your HTML emails. Here is the logic to fix it.</title>
      <dc:creator>Mailpeek</dc:creator>
      <pubDate>Thu, 05 Mar 2026 12:37:45 +0000</pubDate>
      <link>https://dev.to/mailpeek/dark-mode-is-still-breaking-your-html-emails-here-is-the-logic-to-fix-it-g5c</link>
      <guid>https://dev.to/mailpeek/dark-mode-is-still-breaking-your-html-emails-here-is-the-logic-to-fix-it-g5c</guid>
      <description>&lt;p&gt;Email clients like Gmail and Outlook don't just "invert" colors; they apply unpredictable, proprietary rendering logic that can ruin your brand's UI. &lt;/p&gt;

&lt;p&gt;I'm currently building Mailpeek, a Vue-based suite to solve this. While building the preview engine, I had to deep-dive into how these clients actually handle color inversion. Here is the logic I used to build a reliable dark mode emulator and how you can apply it to your own emails.&lt;/p&gt;

&lt;p&gt;Building for email doesn't have to feel like coding for a browser from 2004. We have the tools in the Vue ecosystem to make this better.&lt;/p&gt;

&lt;h1&gt;
  
  
  Dark Mode in Email: Why Every Client Does It Differently
&lt;/h1&gt;

&lt;p&gt;You test your email in Gmail. It looks great. You toggle dark mode and check again. Still looks great. You relax.&lt;/p&gt;

&lt;p&gt;Then a user on Gmail iOS screenshots your email and your white logo has vanished into a black background, your carefully chosen brand colours have been replaced with something you have never seen before, and your call-to-action button now blends into the surrounding content.&lt;/p&gt;

&lt;p&gt;This is dark mode in email. There is no spec. There is no standard. Every client does whatever it wants.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Three Dark Mode Strategies
&lt;/h2&gt;

&lt;p&gt;Email clients fall into three broad categories when it comes to dark mode. Understanding which strategy each client uses is the key to not being surprised by what your users see.&lt;/p&gt;

&lt;h3&gt;
  
  
  Strategy 1: No transformation
&lt;/h3&gt;

&lt;p&gt;The client darkens its own UI (the toolbar, sidebar, message list) but leaves the email body completely untouched. Your email renders exactly as you wrote it - light background, dark text, everything intact.&lt;/p&gt;

&lt;p&gt;This sounds ideal, but it creates a jarring experience. Your bright white email sits inside a dark UI, like a flashlight in a dark room. Users notice, and some will blame your email for "not supporting dark mode" even though the client chose not to transform it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Clients that use this strategy:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Gmail web (desktop browser)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Strategy 2: Full colour inversion
&lt;/h3&gt;

&lt;p&gt;The client applies a blanket colour inversion across the entire email body. Light backgrounds become dark. Dark text becomes light. Images get inverted and then re-inverted so photographs do not look like negatives.&lt;/p&gt;

&lt;p&gt;This is the most aggressive approach. It transforms everything, which means it also breaks things. A dark-on-dark section in your original email becomes light-on-light after inversion. Transparent PNGs with dark content disappear. Brand colours shift to their complementary opposites.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Clients that use this strategy:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Gmail iOS app&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Gmail Android app (varies by device)&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Strategy 3: Partial inversion
&lt;/h3&gt;

&lt;p&gt;The client selectively replaces specific colours rather than inverting everything. It targets background colours and text colours individually, attempting to create a dark version that preserves the visual hierarchy without flipping the entire palette.&lt;/p&gt;

&lt;p&gt;This sounds smarter than full inversion, and sometimes it is. But "selective" means the client is making judgment calls about which colours to replace and what to replace them with. Those judgments do not always match your design intent. A brand green might become a shade of teal. A light grey background might stay light grey while the text on top of it turns white.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Clients that use this strategy:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Outlook desktop (Word engine)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Outlook.com / New Outlook&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Apple Mail (macOS and iOS)&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What Each Client Actually Does
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Gmail Web Desktop: Nothing
&lt;/h3&gt;

&lt;p&gt;Gmail web in dark mode does not touch your email content. The Gmail UI around it goes dark, but the email iframe renders exactly as written. If your email has a white background, it stays white.&lt;/p&gt;

&lt;p&gt;This is the easiest client to support in dark mode because there is nothing to support. But it is also why testing only in Gmail web gives you false confidence. You think dark mode is fine when you have not actually tested dark mode at all.&lt;/p&gt;

&lt;h3&gt;
  
  
  Gmail iOS: Full Inversion
&lt;/h3&gt;

&lt;p&gt;Gmail on iOS applies a CSS filter that inverts the entire email. The algorithm roughly works like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Invert all colours (white becomes black, black becomes white, everything in between flips)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Reverse inversion on certain elements so photographs look normal&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Re-invert background-image elements for the same reason&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The result is that your text and background colours are all inverted, but your images survive. Mostly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What breaks:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Transparent PNGs with dark content.&lt;/strong&gt; If your logo is a dark graphic on a transparent background, the inversion turns it light. But because the image is re-inverted to protect photographs, it flips back to dark. Now you have a dark logo on a dark background. It disappears.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="c"&gt;/* Your original */&lt;/span&gt;
&lt;span class="nt"&gt;background-color&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;#ffffff&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;  &lt;span class="c"&gt;/* white */&lt;/span&gt;
&lt;span class="nt"&gt;color&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="err"&gt;#1&lt;/span&gt;&lt;span class="nt"&gt;a1a1a&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;            &lt;span class="c"&gt;/* near-black */&lt;/span&gt;

&lt;span class="c"&gt;/* After Gmail iOS inversion */&lt;/span&gt;
&lt;span class="nt"&gt;background-color&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="err"&gt;#000000&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;  &lt;span class="c"&gt;/* black */&lt;/span&gt;
&lt;span class="nt"&gt;color&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;#e5e5e5&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;            &lt;span class="c"&gt;/* near-white */&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Dark-on-dark sections.&lt;/strong&gt; If you have a section with a dark background and light text (like a footer or a hero banner), inversion makes it a light background with dark text. That is fine on its own. But if the section next to it was originally light background with dark text (now inverted to dark background with light text), you end up with alternating sections that look inverted from your original design intent.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Brand colours.&lt;/strong&gt; Every colour gets its complementary opposite. Your brand blue #2563eb becomes #da9c14 (a muddy gold). There is no way to override this from within the email.&lt;/p&gt;

&lt;h3&gt;
  
  
  Outlook Desktop (Word Engine): Selective Replacement
&lt;/h3&gt;

&lt;p&gt;Outlook's Word rendering engine takes a more surgical approach. Instead of inverting everything, it examines individual elements and replaces colours it deems "too light for dark mode."&lt;/p&gt;

&lt;p&gt;The rules are not fully documented, but through testing the behaviour is roughly:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Background colours close to white&lt;/strong&gt; get replaced with a dark grey (#1e1e1e or similar)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Text colours close to black&lt;/strong&gt; get replaced with a light grey or white&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Colours far from black or white&lt;/strong&gt; (your brand colours, accent colours) are left alone or shifted slightly&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Images are not inverted&lt;/strong&gt; - they render as-is&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This means Outlook dark mode mostly works, but with unpredictable edge cases:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="c"&gt;/* Your original */&lt;/span&gt;
&lt;span class="nt"&gt;background-color&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;#f5f5f5&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;  &lt;span class="c"&gt;/* light grey */&lt;/span&gt;
&lt;span class="nt"&gt;color&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="err"&gt;#333333&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;             &lt;span class="c"&gt;/* dark grey */&lt;/span&gt;

&lt;span class="c"&gt;/* Outlook dark mode might render as */&lt;/span&gt;
&lt;span class="nt"&gt;background-color&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="err"&gt;#2&lt;/span&gt;&lt;span class="nt"&gt;d2d2d&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;  &lt;span class="c"&gt;/* dark grey (replaced) */&lt;/span&gt;
&lt;span class="nt"&gt;color&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;#d4d4d4&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;             &lt;span class="c"&gt;/* light grey (replaced) */&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What breaks:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mid-range colours.&lt;/strong&gt; Outlook has to decide where "light" ends and "coloured" begins. A light blue background (#dbeafe) might get replaced with dark grey, or it might be left as-is. The threshold is not consistent across Outlook versions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Explicit colour pairings.&lt;/strong&gt; If you set background-color: #1a1a1a and color: #ffffff to create your own dark section, Outlook might not realise you already handled dark mode for that element. It could replace your white text with light grey and your dark background with slightly different dark grey, producing a low-contrast result that is technically readable but visually flat.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Borders and dividers.&lt;/strong&gt; A border: 1px solid #e5e7eb (light grey border) might disappear against Outlook's dark background, or it might be replaced with something heavier than you intended.&lt;/p&gt;

&lt;h3&gt;
  
  
  Outlook.com / New Outlook: Lighter Touch
&lt;/h3&gt;

&lt;p&gt;The web and new desktop versions of Outlook use a browser rendering engine (not Word), so they have better CSS support overall. Their dark mode transformation is similar to Outlook desktop's selective approach but less aggressive.&lt;/p&gt;

&lt;p&gt;Colours are remapped rather than replaced, producing results closer to a proper dark theme than a colour swap. But the same fundamental problem exists: the client is guessing what your dark mode should look like, and guesses are sometimes wrong.&lt;/p&gt;

&lt;h3&gt;
  
  
  Apple Mail: Color Scheme Aware
&lt;/h3&gt;

&lt;p&gt;Apple Mail on macOS and iOS is the most standards-friendly. It respects the color-scheme meta tag and the prefers-color-scheme media query, meaning you can actually provide your own dark mode styles that Apple Mail will use.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"color-scheme"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"light dark"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"supported-color-schemes"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"light dark"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="k"&gt;@media&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prefers-color-scheme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;dark&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nc"&gt;.email-body&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#1a1a1a&lt;/span&gt; &lt;span class="cp"&gt;!important&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#e5e5e5&lt;/span&gt; &lt;span class="cp"&gt;!important&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nc"&gt;.brand-header&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#0f172a&lt;/span&gt; &lt;span class="cp"&gt;!important&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you provide these styles, Apple Mail uses them. If you do not, it falls back to its own partial inversion - similar to Outlook's approach but generally more predictable.&lt;/p&gt;

&lt;p&gt;The catch: &lt;a class="mentioned-user" href="https://dev.to/media"&gt;@media&lt;/a&gt; (prefers-color-scheme) is stripped by Gmail, so these styles only work in Apple Mail and a few other clients that support them. You cannot write a single set of dark mode styles that works everywhere.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Comparison Table
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Behaviour&lt;/th&gt;
&lt;th&gt;Gmail Web&lt;/th&gt;
&lt;th&gt;Gmail iOS&lt;/th&gt;
&lt;th&gt;Outlook Desktop&lt;/th&gt;
&lt;th&gt;Outlook.com&lt;/th&gt;
&lt;th&gt;Apple Mail&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Strategy&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;Full inversion&lt;/td&gt;
&lt;td&gt;Partial inversion&lt;/td&gt;
&lt;td&gt;Partial inversion&lt;/td&gt;
&lt;td&gt;Partial inversion&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Transforms backgrounds&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes (inverted)&lt;/td&gt;
&lt;td&gt;Yes (replaced)&lt;/td&gt;
&lt;td&gt;Yes (remapped)&lt;/td&gt;
&lt;td&gt;Yes (replaced or CSS)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Transforms text colour&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes (inverted)&lt;/td&gt;
&lt;td&gt;Yes (replaced)&lt;/td&gt;
&lt;td&gt;Yes (remapped)&lt;/td&gt;
&lt;td&gt;Yes (replaced or CSS)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Transforms images&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Inverts then re-inverts&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Respects &lt;code&gt;prefers-color-scheme&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Partial&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Respects &lt;code&gt;color-scheme&lt;/code&gt; meta&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Partial&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Predictability&lt;/td&gt;
&lt;td&gt;High (nothing happens)&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;td&gt;High (if you provide styles)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Practical Advice
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Do not rely on a single dark mode test
&lt;/h3&gt;

&lt;p&gt;If you check dark mode in Gmail web and everything looks fine, you have tested nothing. Gmail web does not transform emails. You need to check at least Gmail iOS (full inversion) and Outlook (partial inversion) to cover the two transformation strategies that actually change your email.&lt;/p&gt;

&lt;h3&gt;
  
  
  Use solid backgrounds on images
&lt;/h3&gt;

&lt;p&gt;Transparent PNGs are the most common dark mode casualty. If your logo or icon uses transparency, it will likely disappear or look wrong after inversion. Use a solid background colour on the image itself, or provide a version with a built-in background.&lt;/p&gt;

&lt;h3&gt;
  
  
  Avoid relying on pure white and pure black
&lt;/h3&gt;

&lt;p&gt;Elements with #ffffff backgrounds and #000000 text are the first targets for colour replacement. Using slightly off-white (#fafafa) and slightly off-black (#1a1a1a) does not help - clients are smart enough to catch near-white and near-black values. But being aware that these specific colours will be transformed helps you anticipate the result.&lt;/p&gt;

&lt;h3&gt;
  
  
  Test your dark-on-dark sections
&lt;/h3&gt;

&lt;p&gt;If your email has sections with intentionally dark backgrounds (hero banners, footers, CTAs), these are the most likely to break. After inversion, they become light sections. After partial replacement, they become slightly different dark sections with potentially lower contrast. Check these areas specifically.&lt;/p&gt;

&lt;h3&gt;
  
  
  Keep critical information in text, not in styled containers
&lt;/h3&gt;

&lt;p&gt;If a piece of information is only readable because of its background colour or container styling, dark mode can make it unreadable. The text content of your email should make sense even if every background colour changes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Preview Before You Send
&lt;/h2&gt;

&lt;p&gt;I built &lt;a href="https://www.npmjs.com/package/@mailpeek/preview" rel="noopener noreferrer"&gt;@mailpeek/preview&lt;/a&gt; specifically because dark mode testing was the most painful part of email development. Sending test emails to multiple devices and toggling dark mode on each one is time-consuming and error-prone.&lt;/p&gt;

&lt;p&gt;The preview component lets you toggle dark mode on and off for each client and see the transformation in real time:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight vue"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt; &lt;span class="na"&gt;setup&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"ts"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;EmailPreview&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@mailpeek/preview&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@mailpeek/preview/style.css&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="c"&gt;&amp;lt;!-- Gmail dark mode: no transformation (chrome darkens, email stays the same) --&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;EmailPreview&lt;/span&gt; &lt;span class="na"&gt;:html=&lt;/span&gt;&lt;span class="s"&gt;"emailHtml"&lt;/span&gt; &lt;span class="na"&gt;client=&lt;/span&gt;&lt;span class="s"&gt;"gmail"&lt;/span&gt; &lt;span class="na"&gt;dark-mode&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;

  &lt;span class="c"&gt;&amp;lt;!-- Outlook dark mode: partial colour inversion --&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;EmailPreview&lt;/span&gt; &lt;span class="na"&gt;:html=&lt;/span&gt;&lt;span class="s"&gt;"emailHtml"&lt;/span&gt; &lt;span class="na"&gt;client=&lt;/span&gt;&lt;span class="s"&gt;"outlook"&lt;/span&gt; &lt;span class="na"&gt;dark-mode&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The dark mode approximation covers Gmail's no-op strategy, full colour inversion for iOS clients, and partial inversion for Outlook. It is not pixel-perfect - actual rendering varies between OS versions, app versions, and device settings. But it catches the big problems (disappearing logos, invisible text, broken contrast) before you send.&lt;/p&gt;

&lt;p&gt;Both &lt;a class="mentioned-user" href="https://dev.to/mailpeek"&gt;@mailpeek&lt;/a&gt;/preview and &lt;a class="mentioned-user" href="https://dev.to/mailpeek"&gt;@mailpeek&lt;/a&gt;/components are free, open source, and MIT licensed. Vue 3 only, zero runtime dependencies, full TypeScript support.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;npm&lt;/strong&gt;: &lt;a href="https://www.npmjs.com/package/@mailpeek/preview" rel="noopener noreferrer"&gt;@mailpeek/preview&lt;/a&gt; and &lt;a href="https://www.npmjs.com/package/@mailpeek/components" rel="noopener noreferrer"&gt;@mailpeek/components&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Docs&lt;/strong&gt;: &lt;a href="https://mailpeek.dev/" rel="noopener noreferrer"&gt;mailpeek.dev&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;GitHub&lt;/strong&gt;: &lt;a href="https://github.com/mailpeek/mailpeek" rel="noopener noreferrer"&gt;github.com/mailpeek/mailpeek&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Live demo&lt;/strong&gt;: &lt;a href="https://mailpeek.dev/demo" rel="noopener noreferrer"&gt;mailpeek.dev/demo&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;I need your help:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Star the Repo:&lt;/strong&gt; If you've ever struggled with email CSS, &lt;a href="https://github.com/mailpeek/mailpeek" rel="noopener noreferrer"&gt;check out Mailpeek on GitHub.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Templates:&lt;/strong&gt; I’ve just finished a library of 40+ "Dark Mode Ready" patterns and templates (Pricing tables, Newsletters, etc.). They are currently listed as "Coming Soon" on the mailpeek.dev (&lt;a href="https://mailpeek.dev" rel="noopener noreferrer"&gt;https://mailpeek.dev&lt;/a&gt;), but I’ll be giving early access to GitHub stargazers first.&lt;/p&gt;

&lt;p&gt;Let’s swap dark-mode horror stories in the comments!&lt;/p&gt;

</description>
      <category>vue</category>
      <category>webdev</category>
      <category>email</category>
      <category>showdev</category>
    </item>
    <item>
      <title>Why Gmail Breaks Your Email CSS (and How to Catch It Before Your Users Do)</title>
      <dc:creator>Mailpeek</dc:creator>
      <pubDate>Wed, 25 Feb 2026 12:12:07 +0000</pubDate>
      <link>https://dev.to/mailpeek/why-gmail-breaks-your-email-css-and-how-to-catch-it-before-your-users-do-3mlk</link>
      <guid>https://dev.to/mailpeek/why-gmail-breaks-your-email-css-and-how-to-catch-it-before-your-users-do-3mlk</guid>
      <description>&lt;p&gt;You spent an afternoon building a beautiful transactional email. The header gradient is perfect. The buttons have rounded corners and a subtle shadow. The layout uses flexbox so everything centres nicely. You send a test to yourself, open it in Gmail, and half the design is gone.&lt;/p&gt;

&lt;p&gt;This is not a bug. This is email in 2026.&lt;/p&gt;

&lt;p&gt;Email rendering is stuck somewhere around 2004. Gmail runs every incoming email through a custom HTML sanitizer that aggressively strips CSS it considers unsafe or unnecessary. Outlook desktop (2016 through 2021) goes further - it literally uses Microsoft Word as its HTML rendering engine. Word. The word processor. For rendering HTML.&lt;/p&gt;

&lt;p&gt;The result is that the CSS you write and the CSS your recipients see are often two completely different things. And unlike web development, there are no DevTools, no error messages, and no way to know what broke until someone screenshots it and sends it to you.&lt;/p&gt;

&lt;p&gt;Let's fix that.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Gmail Actually Strips
&lt;/h2&gt;

&lt;p&gt;Gmail's HTML sanitizer is not documented in full, but between Google's own developer docs, community testing, and resources like caniemail.com, we have a clear picture. Here is what it removes.&lt;/p&gt;

&lt;h3&gt;
  
  
  @font-face - gone entirely
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="c"&gt;/* What you wrote */&lt;/span&gt;
&lt;span class="k"&gt;@font-face&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;font-family&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;'BrandSans'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;src&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sx"&gt;url('https://fonts.example.com/brand-sans.woff2')&lt;/span&gt; &lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;'woff2'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nt"&gt;h1&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;font-family&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;'BrandSans'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;sans-serif&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;/* What Gmail renders */&lt;/span&gt;
&lt;span class="nt"&gt;h1&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;font-family&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;'BrandSans'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;sans-serif&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c"&gt;/* Falls back to Arial/Helvetica */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;@font-face&lt;/code&gt; rule is stripped. The &lt;code&gt;font-family&lt;/code&gt; declaration stays, but since the font was never loaded, you get whatever sans-serif fallback the system provides. On most machines, that is Arial.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a class="mentioned-user" href="https://dev.to/media"&gt;@media&lt;/a&gt; queries - ignored on desktop
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="c"&gt;/* What you wrote */&lt;/span&gt;
&lt;span class="k"&gt;@media&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;600px&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nc"&gt;.container&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt; &lt;span class="cp"&gt;!important&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nc"&gt;.column&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;block&lt;/span&gt; &lt;span class="cp"&gt;!important&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;/* What Gmail desktop renders */&lt;/span&gt;
&lt;span class="c"&gt;/* Nothing. The entire @media block is removed. */&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Gmail desktop strips all &lt;code&gt;@media&lt;/code&gt; rules. Your responsive email is not responsive in Gmail desktop. Gmail mobile has partial support, but desktop - which is where a huge percentage of business email is read - ignores media queries completely.&lt;/p&gt;

&lt;h3&gt;
  
  
  Positioning properties - all stripped
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="c"&gt;/* What you wrote */&lt;/span&gt;
&lt;span class="nc"&gt;.tooltip&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;absolute&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;-30px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;50%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;z-index&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;/* What Gmail renders */&lt;/span&gt;
&lt;span class="nc"&gt;.tooltip&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c"&gt;/* Empty. Every property was stripped. */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;position&lt;/code&gt;, &lt;code&gt;top&lt;/code&gt;, &lt;code&gt;right&lt;/code&gt;, &lt;code&gt;bottom&lt;/code&gt;, &lt;code&gt;left&lt;/code&gt;, and &lt;code&gt;z-index&lt;/code&gt; are all removed. Any layout that depends on positioned elements will collapse.&lt;/p&gt;

&lt;h3&gt;
  
  
  CSS Grid - stripped (but flexbox partially survives)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="c"&gt;/* What you wrote */&lt;/span&gt;
&lt;span class="nc"&gt;.grid-layout&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;grid&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;grid-template-columns&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="n"&gt;fr&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="n"&gt;fr&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="n"&gt;fr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;gap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;16px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.flex-layout&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;align-items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;center&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;justify-content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;space-between&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;/* What Gmail renders */&lt;/span&gt;
&lt;span class="nc"&gt;.grid-layout&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c"&gt;/* display: grid is removed entirely */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.flex-layout&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c"&gt;/* Kept! */&lt;/span&gt;
  &lt;span class="c"&gt;/* But align-items and justify-content are stripped */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a subtle one. Gmail does support &lt;code&gt;display: flex&lt;/code&gt;, but it strips the sub-properties that make flexbox useful -- &lt;code&gt;align-items&lt;/code&gt;, &lt;code&gt;justify-content&lt;/code&gt;, &lt;code&gt;flex-direction&lt;/code&gt;, &lt;code&gt;flex-wrap&lt;/code&gt;, and &lt;code&gt;flex&lt;/code&gt;. So your flex container exists, but you have no control over how its children are laid out.&lt;/p&gt;

&lt;h3&gt;
  
  
  Visual effects - stripped
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="c"&gt;/* What you wrote */&lt;/span&gt;
&lt;span class="nc"&gt;.card&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;box-shadow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;2px&lt;/span&gt; &lt;span class="m"&gt;8px&lt;/span&gt; &lt;span class="n"&gt;rgba&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nl"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;translateY&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;-2px&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nl"&gt;transition&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;transform&lt;/span&gt; &lt;span class="m"&gt;0.2s&lt;/span&gt; &lt;span class="n"&gt;ease&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;/* What Gmail renders */&lt;/span&gt;
&lt;span class="nc"&gt;.card&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c"&gt;/* All three properties removed */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;box-shadow&lt;/code&gt;, &lt;code&gt;transform&lt;/code&gt;, &lt;code&gt;animation&lt;/code&gt;, and &lt;code&gt;transition&lt;/code&gt; are all stripped. Your hover effects, entrance animations, and depth cues will not render.&lt;/p&gt;

&lt;h3&gt;
  
  
  External stylesheets and &lt;a class="mentioned-user" href="https://dev.to/import"&gt;@import&lt;/a&gt; - stripped
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- What you wrote --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"stylesheet"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://example.com/email-styles.css"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;style&amp;gt;&lt;/span&gt;
  &lt;span class="k"&gt;@import&lt;/span&gt; &lt;span class="sx"&gt;url('https://fonts.googleapis.com/css2?family=Inter')&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/style&amp;gt;&lt;/span&gt;

&lt;span class="c"&gt;&amp;lt;!-- What Gmail renders --&amp;gt;&lt;/span&gt;
&lt;span class="c"&gt;&amp;lt;!-- The &amp;lt;link&amp;gt; tag is removed entirely --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;style&amp;gt;&lt;/span&gt;
  &lt;span class="c"&gt;/* @import rule is removed */&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/style&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Gmail strips all external &lt;code&gt;&amp;lt;link&amp;gt;&lt;/code&gt; stylesheet references and all &lt;code&gt;@import&lt;/code&gt; rules inside &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; blocks. Every style must be in an inline &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; block or in a &lt;code&gt;style&lt;/code&gt; attribute.&lt;/p&gt;

&lt;h3&gt;
  
  
  The 8,192-character style block limit
&lt;/h3&gt;

&lt;p&gt;This is the one that catches people off guard. If a single &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; block in your email exceeds 8,192 characters, Gmail removes the &lt;strong&gt;entire block&lt;/strong&gt;. Not the overflow - the whole thing. Every rule in that block disappears.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- What you wrote --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;style&amp;gt;&lt;/span&gt;
  &lt;span class="c"&gt;/* ... 8,193 characters of perfectly valid CSS ... */&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/style&amp;gt;&lt;/span&gt;

&lt;span class="c"&gt;&amp;lt;!-- What Gmail renders --&amp;gt;&lt;/span&gt;
&lt;span class="c"&gt;&amp;lt;!-- The entire &amp;lt;style&amp;gt; block is gone. --&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Most email developers do not know this limit exists. If you are using a CSS framework, a design system, or generating styles programmatically, it is easy to hit. The fix is to split your styles across multiple &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; blocks, each under the limit.&lt;/p&gt;

&lt;h2&gt;
  
  
  Outlook Is Even Worse
&lt;/h2&gt;

&lt;p&gt;If Gmail's sanitizer is aggressive, Outlook's Word rendering engine is hostile. Outlook desktop (2016, 2019, 2021) strips everything Gmail strips, plus:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;border-radius&lt;/code&gt;&lt;/strong&gt; - no rounded corners, period. Your carefully rounded buttons become rectangles.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;display: flex&lt;/code&gt;&lt;/strong&gt; - Gmail keeps it, Outlook removes it. Flexbox does not exist in Word.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;background-size&lt;/code&gt;&lt;/strong&gt; - background images render, but you cannot control their sizing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;max-width&lt;/code&gt; and &lt;code&gt;min-width&lt;/code&gt;&lt;/strong&gt; - width constraints are unreliable. Use fixed &lt;code&gt;width&lt;/code&gt; instead.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;float&lt;/code&gt;&lt;/strong&gt; - the traditional CSS layout fallback does not work either.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The reason is straightforward: Microsoft Word's HTML renderer was built for converting Word documents to HTML, not for rendering modern web pages. It supports a subset of HTML 4 and CSS 2, and that is roughly where it stops.&lt;/p&gt;

&lt;p&gt;There is good news on the horizon. Microsoft is rolling out a new Chromium-based Outlook that uses a proper browser engine. But the transition is gradual - classic Word-engine Outlook and new Chromium Outlook coexist, and you need to support both for the foreseeable future.&lt;/p&gt;

&lt;h2&gt;
  
  
  Dark Mode: The Wild West
&lt;/h2&gt;

&lt;p&gt;If cross-client CSS support is a mess, dark mode is where it becomes genuinely chaotic. Every email client handles dark mode differently, and there is no single approach that works everywhere.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Gmail web desktop&lt;/strong&gt;: Does NOT transform email content at all. It darkens the surrounding Gmail UI chrome, but your email renders exactly as written - light background and all.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Gmail iOS&lt;/strong&gt;: Full colour inversion. It applies a CSS filter that inverts colours across the entire email body, then re-inverts images so photos do not look like negatives.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Outlook desktop (Word engine)&lt;/strong&gt;: Partial inversion with per-element colour replacement. It selectively replaces background and text colours, sometimes producing unexpected combinations.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Outlook.com / New Outlook&lt;/strong&gt;: Partial inversion at a lower intensity, closer to a colour remap than a true invert.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The practical consequence is that an email can look correct in Gmail web dark mode (because nothing changes), broken in Gmail iOS dark mode (because your dark-on-dark section disappears), and weird in Outlook (because it replaced your brand green with a shade of teal it thought was more readable).&lt;/p&gt;

&lt;p&gt;There is no CSS property or meta tag that reliably controls dark mode across all clients. You have to test each one.&lt;/p&gt;

&lt;h2&gt;
  
  
  Gmail vs Outlook: CSS Property Comparison
&lt;/h2&gt;

&lt;p&gt;Here is a quick reference for what survives and what does not in the two most common email clients:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;CSS Property&lt;/th&gt;
&lt;th&gt;Gmail Web&lt;/th&gt;
&lt;th&gt;Outlook (Word Engine)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;color&lt;/code&gt;, &lt;code&gt;background-color&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Supported&lt;/td&gt;
&lt;td&gt;Supported&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;font-size&lt;/code&gt;, &lt;code&gt;font-weight&lt;/code&gt;, &lt;code&gt;font-family&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Supported&lt;/td&gt;
&lt;td&gt;Supported&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;margin&lt;/code&gt;, &lt;code&gt;padding&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Supported&lt;/td&gt;
&lt;td&gt;Supported&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;border&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Supported&lt;/td&gt;
&lt;td&gt;Supported&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;width&lt;/code&gt;, &lt;code&gt;height&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Supported&lt;/td&gt;
&lt;td&gt;Supported&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;text-align&lt;/code&gt;, &lt;code&gt;vertical-align&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Supported&lt;/td&gt;
&lt;td&gt;Supported&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;display: block/inline/none&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Supported&lt;/td&gt;
&lt;td&gt;Supported&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;display: flex&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Supported&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Stripped&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;align-items&lt;/code&gt;, &lt;code&gt;justify-content&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Stripped&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Stripped&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;display: grid&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Stripped&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Stripped&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;border-radius&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Supported&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Stripped&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;max-width&lt;/code&gt;, &lt;code&gt;min-width&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Supported&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Stripped&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;float&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Supported&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Stripped&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;background-size&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Supported&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Stripped&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;box-shadow&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Stripped&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Stripped&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;position&lt;/code&gt; / offsets / &lt;code&gt;z-index&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Stripped&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Stripped&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;transform&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Stripped&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Stripped&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;animation&lt;/code&gt;, &lt;code&gt;transition&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Stripped&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Stripped&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@font-face&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Stripped&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Stripped&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;@media&lt;/code&gt; queries&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Stripped&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Stripped&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;@import&lt;/code&gt; / external stylesheets&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Stripped&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Stripped&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The pattern is clear: Outlook strips everything Gmail strips, plus &lt;code&gt;border-radius&lt;/code&gt;, &lt;code&gt;display: flex&lt;/code&gt;, &lt;code&gt;background-size&lt;/code&gt;, &lt;code&gt;max-width&lt;/code&gt;/&lt;code&gt;min-width&lt;/code&gt;, and &lt;code&gt;float&lt;/code&gt;. If it works in Outlook, it works everywhere.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Catch This Before Your Users Do
&lt;/h2&gt;

&lt;p&gt;I got tired of sending test emails to myself, opening them in six different clients, and squinting at screenshots. So I built a tool to solve it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.npmjs.com/package/@mailpeek/preview" rel="noopener noreferrer"&gt;&lt;code&gt;@mailpeek/preview&lt;/code&gt;&lt;/a&gt; is a Vue component that previews how Gmail and Outlook render your email HTML. It runs the same kind of CSS filtering those clients perform - stripping unsupported properties, removing &lt;code&gt;@media&lt;/code&gt; queries, enforcing the style block character limit - and shows you the result in real time.&lt;/p&gt;

&lt;p&gt;Install it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; @mailpeek/preview
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Drop it into any Vue 3 app:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight vue"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt; &lt;span class="na"&gt;setup&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"ts"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;EmailPreview&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@mailpeek/preview&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@mailpeek/preview/style.css&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;emailHtml&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`
  &amp;lt;html&amp;gt;
    &amp;lt;head&amp;gt;
      &amp;lt;style&amp;gt;
        .hero { border-radius: 8px; box-shadow: 0 2px 8px rgba(0,0,0,0.1); }
        .cta { display: flex; align-items: center; justify-content: center; }
      &amp;lt;/style&amp;gt;
    &amp;lt;/head&amp;gt;
    &amp;lt;body&amp;gt;
      &amp;lt;div class="hero"&amp;gt;
        &amp;lt;h1&amp;gt;Welcome aboard&amp;lt;/h1&amp;gt;
        &amp;lt;div class="cta"&amp;gt;
          &amp;lt;a href="#"&amp;gt;Get Started&amp;lt;/a&amp;gt;
        &amp;lt;/div&amp;gt;
      &amp;lt;/div&amp;gt;
    &amp;lt;/body&amp;gt;
  &amp;lt;/html&amp;gt;
`&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="c"&gt;&amp;lt;!-- Gmail mode: box-shadow stripped, flex sub-properties stripped --&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;EmailPreview&lt;/span&gt; &lt;span class="na"&gt;:html=&lt;/span&gt;&lt;span class="s"&gt;"emailHtml"&lt;/span&gt; &lt;span class="na"&gt;client=&lt;/span&gt;&lt;span class="s"&gt;"gmail"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;

  &lt;span class="c"&gt;&amp;lt;!-- Outlook mode: all of the above PLUS border-radius stripped --&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;EmailPreview&lt;/span&gt; &lt;span class="na"&gt;:html=&lt;/span&gt;&lt;span class="s"&gt;"emailHtml"&lt;/span&gt; &lt;span class="na"&gt;client=&lt;/span&gt;&lt;span class="s"&gt;"outlook"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Switching between &lt;code&gt;client="gmail"&lt;/code&gt; and &lt;code&gt;client="outlook"&lt;/code&gt; shows you exactly what each client strips. The component also exposes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;strong&gt;compatibility score&lt;/strong&gt; (0-100) that tells you how much of your CSS survives in each client. A score of 100 means nothing is stripped. A score of 60 means you have problems.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Console warnings&lt;/strong&gt; that name every stripped property, where it was found, and why it was removed.&lt;/li&gt;
&lt;li&gt;A built-in &lt;strong&gt;accessibility checker&lt;/strong&gt; running 10 WCAG-relevant checks - missing alt text on images, broken heading hierarchy, vague link text, and more.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dark mode preview&lt;/strong&gt; that simulates Gmail's no-op behaviour, Outlook's partial inversion, and full colour inversion for iOS clients.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Device width toggles&lt;/strong&gt; between mobile, tablet, and desktop viewports.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Disclaimer: It is not a pixel-perfect simulation - it approximates client behaviour based on documented CSS restriction databases and community testing from caniemail.com. But it catches the vast majority of issues before you send.&lt;/p&gt;

&lt;h2&gt;
  
  
  Build Emails That Survive Gmail
&lt;/h2&gt;

&lt;p&gt;Previewing is half the problem. The other half is writing email HTML that works in the first place.&lt;/p&gt;

&lt;p&gt;If you have used React Email, you know the idea: instead of writing raw &lt;code&gt;&amp;lt;table&amp;gt;&lt;/code&gt; HTML with inline styles by hand, you use components that output email-safe markup. &lt;a href="https://www.npmjs.com/package/@mailpeek/components" rel="noopener noreferrer"&gt;&lt;code&gt;@mailpeek/components&lt;/code&gt;&lt;/a&gt; is the Vue equivalent.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; @mailpeek/components
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is a welcome email built with mailpeek components:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight vue"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt; &lt;span class="na"&gt;setup&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;EmailHtml&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;EmailHead&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;EmailBody&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;EmailContainer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;EmailHeading&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;EmailText&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;EmailButton&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;EmailImage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@mailpeek/components&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;EmailHtml&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;EmailHead&lt;/span&gt; &lt;span class="na"&gt;title=&lt;/span&gt;&lt;span class="s"&gt;"Welcome to Acme"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;EmailBody&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;EmailContainer&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;EmailImage&lt;/span&gt;
          &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://acme.com/logo.png"&lt;/span&gt;
          &lt;span class="na"&gt;alt=&lt;/span&gt;&lt;span class="s"&gt;"Acme logo"&lt;/span&gt;
          &lt;span class="na"&gt;width=&lt;/span&gt;&lt;span class="s"&gt;"120"&lt;/span&gt;
        &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;EmailHeading&lt;/span&gt; &lt;span class="na"&gt;as=&lt;/span&gt;&lt;span class="s"&gt;"h1"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Welcome aboard&lt;span class="nt"&gt;&amp;lt;/EmailHeading&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;EmailText&amp;gt;&lt;/span&gt;
          Thanks for creating your account. We are excited to have you.
        &lt;span class="nt"&gt;&amp;lt;/EmailText&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;EmailButton&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://acme.com/dashboard"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
          Go to Dashboard
        &lt;span class="nt"&gt;&amp;lt;/EmailButton&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/EmailContainer&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/EmailBody&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/EmailHtml&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every component outputs table-based HTML with inline styles - the format that survives every email client. &lt;code&gt;EmailContainer&lt;/code&gt; becomes a centred &lt;code&gt;&amp;lt;table&amp;gt;&lt;/code&gt;, &lt;code&gt;EmailButton&lt;/code&gt; becomes a padded &lt;code&gt;&amp;lt;a&amp;gt;&lt;/code&gt; inside a &lt;code&gt;&amp;lt;td&amp;gt;&lt;/code&gt; with a background colour, &lt;code&gt;EmailColumn&lt;/code&gt; becomes table cells with proper &lt;code&gt;valign&lt;/code&gt; attributes. You write clean Vue templates; the components handle the ugly-but-compatible output.&lt;/p&gt;

&lt;p&gt;When you are ready to send, render the template to an HTML string on the server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;render&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@mailpeek/components&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;WelcomeEmail&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./WelcomeEmail.vue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;html&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;WelcomeEmail&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Jane&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="c1"&gt;// html is a complete email HTML string&lt;/span&gt;
&lt;span class="c1"&gt;// Send with Resend, SendGrid, Nodemailer, or whatever you use&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;render&lt;/code&gt; function uses Vue's server-side rendering pipeline to produce a complete HTML document. Pass it to any email sending service as the HTML body.&lt;/p&gt;

&lt;p&gt;There are 14 components in total: &lt;code&gt;EmailHtml&lt;/code&gt;, &lt;code&gt;EmailHead&lt;/code&gt;, &lt;code&gt;EmailBody&lt;/code&gt;, &lt;code&gt;EmailContainer&lt;/code&gt;, &lt;code&gt;EmailSection&lt;/code&gt;, &lt;code&gt;EmailRow&lt;/code&gt;, &lt;code&gt;EmailColumn&lt;/code&gt;, &lt;code&gt;EmailHeading&lt;/code&gt;, &lt;code&gt;EmailText&lt;/code&gt;, &lt;code&gt;EmailButton&lt;/code&gt;, &lt;code&gt;EmailImage&lt;/code&gt;, &lt;code&gt;EmailLink&lt;/code&gt;, &lt;code&gt;EmailDivider&lt;/code&gt;, and &lt;code&gt;EmailPreviewText&lt;/code&gt;. Enough to build most transactional and marketing emails without dropping down to raw table markup.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrap-up
&lt;/h2&gt;

&lt;p&gt;Email client rendering is genuinely difficult. The CSS restrictions are inconsistent, poorly documented, and changing as Microsoft transitions Outlook to a new engine. Dark mode multiplies the problem. Testing has historically meant sending real emails and checking them in real clients.&lt;/p&gt;

&lt;p&gt;Both &lt;code&gt;@mailpeek/preview&lt;/code&gt; and &lt;code&gt;@mailpeek/components&lt;/code&gt; are free, open source, and MIT licensed. Vue 3 only, zero runtime dependencies, full TypeScript support.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;npm&lt;/strong&gt;: &lt;a href="https://www.npmjs.com/package/@mailpeek/preview" rel="noopener noreferrer"&gt;@mailpeek/preview&lt;/a&gt; and &lt;a href="https://www.npmjs.com/package/@mailpeek/components" rel="noopener noreferrer"&gt;@mailpeek/components&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Docs&lt;/strong&gt;: &lt;a href="https://mailpeek.dev" rel="noopener noreferrer"&gt;mailpeek.dev&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GitHub&lt;/strong&gt;: &lt;a href="https://github.com/mailpeek/mailpeek" rel="noopener noreferrer"&gt;github.com/mailpeek/mailpeek&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Live demo&lt;/strong&gt;: &lt;a href="https://mailpeek.dev/demo" rel="noopener noreferrer"&gt;mailpeek.dev/demo&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you have ever sent an email that looked perfect in your browser and broken in your inbox, give it a try. And if you find CSS restrictions that the filtering misses, open an issue - the restriction databases are just TypeScript objects and they are straightforward to contribute to.&lt;/p&gt;

</description>
      <category>email</category>
      <category>vue</category>
      <category>css</category>
      <category>webdev</category>
    </item>
    <item>
      <title>The Complete Guide to Email Client Rendering Differences in 2026</title>
      <dc:creator>Mailpeek</dc:creator>
      <pubDate>Sun, 22 Feb 2026 20:05:10 +0000</pubDate>
      <link>https://dev.to/mailpeek/the-complete-guide-to-email-client-rendering-differences-in-2026-243f</link>
      <guid>https://dev.to/mailpeek/the-complete-guide-to-email-client-rendering-differences-in-2026-243f</guid>
      <description>&lt;p&gt;&lt;em&gt;If you've ever sent a perfectly crafted HTML email only to discover it looks like abstract art in Outlook, you're not alone. Every email client renders HTML and CSS differently, and in 2026 the landscape is more fragmented than ever.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This guide covers what actually works (and what breaks) across Gmail, Outlook, Apple Mail, Yahoo Mail, and Samsung Mail, with specific CSS support data, dark mode behaviour, and the workarounds that actually solve these problems.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The Core Problem: There Is No Standard
&lt;/h2&gt;

&lt;p&gt;Web browsers have largely converged on standards. Email clients have not.&lt;/p&gt;

&lt;p&gt;The reason is simple: email clients are not browsers. Gmail renders your HTML inside its own DOM and aggressively sanitises your CSS. Outlook desktop uses Microsoft Word's HTML engine to render emails. Apple Mail uses WebKit. Yahoo Mail has its own quirks that have persisted for years.&lt;/p&gt;

&lt;p&gt;The result is that an email with perfectly valid HTML and CSS will render differently across every client. There is no "write once, render everywhere" for email.&lt;/p&gt;

&lt;p&gt;Here's the current state of play.&lt;/p&gt;




&lt;h2&gt;
  
  
  Gmail: The Strict Sanitiser
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Market share:&lt;/strong&gt; ~31% globally (2025)&lt;/p&gt;

&lt;p&gt;Gmail is the second largest email client and arguably the most frustrating to develop for. It aggressively strips CSS it doesn't like, and it "doesn't like" a lot.&lt;/p&gt;

&lt;h3&gt;
  
  
  What Gmail Strips
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;External stylesheets&lt;/strong&gt; (&lt;code&gt;&amp;lt;link rel="stylesheet"&amp;gt;&lt;/code&gt;) are completely removed&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;@import&lt;/code&gt; rules&lt;/strong&gt; are completely removed&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;@font-face&lt;/code&gt;&lt;/strong&gt; is removed (except Roboto and Google Sans)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CSS positioning&lt;/strong&gt; (&lt;code&gt;position&lt;/code&gt;, &lt;code&gt;top&lt;/code&gt;, &lt;code&gt;right&lt;/code&gt;, &lt;code&gt;bottom&lt;/code&gt;, &lt;code&gt;left&lt;/code&gt;, &lt;code&gt;z-index&lt;/code&gt;) is stripped&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Flexbox sub-properties&lt;/strong&gt; (&lt;code&gt;align-items&lt;/code&gt;, &lt;code&gt;justify-content&lt;/code&gt;, &lt;code&gt;flex-direction&lt;/code&gt;, &lt;code&gt;flex-wrap&lt;/code&gt;) are stripped, though &lt;code&gt;display: flex&lt;/code&gt; itself is kept&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CSS Grid&lt;/strong&gt; (&lt;code&gt;display: grid&lt;/code&gt;, &lt;code&gt;grid-template-columns&lt;/code&gt;, etc.) is stripped entirely&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Transforms, animations, transitions&lt;/strong&gt; are all stripped&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;box-shadow&lt;/code&gt;&lt;/strong&gt;, &lt;strong&gt;&lt;code&gt;filter&lt;/code&gt;&lt;/strong&gt;, &lt;strong&gt;&lt;code&gt;clip-path&lt;/code&gt;&lt;/strong&gt;, &lt;strong&gt;&lt;code&gt;backdrop-filter&lt;/code&gt;&lt;/strong&gt; are stripped&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;background-image&lt;/code&gt;&lt;/strong&gt; inside &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; blocks strips the &lt;strong&gt;entire style block&lt;/strong&gt;, not just that rule&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  What Gmail Keeps
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Standard box model: &lt;code&gt;margin&lt;/code&gt;, &lt;code&gt;padding&lt;/code&gt;, &lt;code&gt;width&lt;/code&gt;, &lt;code&gt;height&lt;/code&gt;, &lt;code&gt;border&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;background-color&lt;/code&gt;, &lt;code&gt;color&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;font-family&lt;/code&gt;, &lt;code&gt;font-size&lt;/code&gt;, &lt;code&gt;font-weight&lt;/code&gt;, &lt;code&gt;font-style&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;text-align&lt;/code&gt;, &lt;code&gt;text-decoration&lt;/code&gt;, &lt;code&gt;line-height&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;display: block&lt;/code&gt;, &lt;code&gt;display: inline-block&lt;/code&gt;, &lt;code&gt;display: flex&lt;/code&gt; (the property, not sub-properties)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;border-radius&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;max-width&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The Style Block Gotchas
&lt;/h3&gt;

&lt;p&gt;Gmail's &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; block handling has several traps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;One syntax error nukes everything.&lt;/strong&gt; A single unclosed bracket or invalid property causes Gmail to strip your entire &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; block. It does not partially parse; it's all or nothing.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;8,192 character limit.&lt;/strong&gt; If your CSS exceeds 8,192 characters, the entire block is removed. Minify aggressively.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;background-image&lt;/code&gt; contamination.&lt;/strong&gt; If &lt;em&gt;any&lt;/em&gt; rule in your &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; block contains &lt;code&gt;background-image: url(...)&lt;/code&gt;, Gmail strips the &lt;em&gt;entire&lt;/em&gt; block. Use inline styles for background images.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Class name rewriting.&lt;/strong&gt; Gmail prefixes your class names with its own namespace. This doesn't break anything by itself, but it means you can't rely on JavaScript-based class targeting.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Gmail's 102KB Clipping
&lt;/h3&gt;

&lt;p&gt;When the raw HTML source exceeds approximately 102KB, Gmail clips the email and shows a "Message clipped" link. Everything beyond the threshold is hidden.&lt;/p&gt;

&lt;p&gt;This includes all HTML, inline CSS, text content, and tracking URLs, but not externally hosted images. Keep total HTML under 100KB.&lt;/p&gt;

&lt;h3&gt;
  
  
  Media Queries
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Gmail Web (desktop):&lt;/strong&gt; Does NOT support media queries&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Gmail Mobile (iOS/Android):&lt;/strong&gt; Supports media queries&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This means responsive breakpoints only work on mobile Gmail. Desktop Gmail ignores them entirely.&lt;/p&gt;




&lt;h2&gt;
  
  
  Outlook: The Word Processor That Renders Email
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Market share:&lt;/strong&gt; ~4% globally, but dominant in enterprise environments&lt;/p&gt;

&lt;p&gt;Outlook is actually four different rendering engines wearing the same brand name. In 2026, you need to care about all of them because Microsoft is in the middle of a multi-year migration.&lt;/p&gt;

&lt;h3&gt;
  
  
  Classic Desktop Outlook (Word Engine: 2007-2021+)
&lt;/h3&gt;

&lt;p&gt;This is the notorious one. Classic Outlook uses Microsoft Word's HTML rendering engine, the same Word that renders your &lt;code&gt;.docx&lt;/code&gt; files. It was never designed for modern CSS.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What does NOT work:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;display: flex&lt;/code&gt; and &lt;code&gt;display: grid&lt;/code&gt; are completely ignored&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;border-radius&lt;/code&gt; is ignored (everything is square)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;background-image&lt;/code&gt; via CSS is ignored (requires VML workaround)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;max-width&lt;/code&gt; and &lt;code&gt;min-width&lt;/code&gt; are ignored&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;position&lt;/code&gt;, &lt;code&gt;float&lt;/code&gt; are ignored&lt;/li&gt;
&lt;li&gt;CSS animations, transforms, transitions are ignored&lt;/li&gt;
&lt;li&gt;Media queries are ignored&lt;/li&gt;
&lt;li&gt;Web fonts &lt;strong&gt;fall back to Times New Roman&lt;/strong&gt; (not your next font in the stack; it skips everything)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;padding&lt;/code&gt; on &lt;code&gt;&amp;lt;p&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; is inconsistent&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; width/height is ignored (divs become 100% width)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;What DOES work:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Table-based layouts (&lt;code&gt;&amp;lt;table&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;tr&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;td&amp;gt;&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;width&lt;/code&gt; and &lt;code&gt;height&lt;/code&gt; as &lt;strong&gt;HTML attributes&lt;/strong&gt; on tables/cells&lt;/li&gt;
&lt;li&gt;&lt;code&gt;background-color&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;border&lt;/code&gt; (solid only)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;text-align&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;color&lt;/code&gt; and basic font properties (system fonts only)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The DPI Scaling Bug
&lt;/h3&gt;

&lt;p&gt;On Windows machines with display scaling above 100% (common on high-resolution laptops), Outlook converts CSS &lt;code&gt;px&lt;/code&gt; values to &lt;code&gt;pt&lt;/code&gt; inconsistently. HTML attributes stay as pixels, but CSS pixels get distorted, causing layouts to break.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; Add this to your &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!--[if gte mso 9]&amp;gt;
&amp;lt;xml&amp;gt;
  &amp;lt;o:OfficeDocumentSettings&amp;gt;
    &amp;lt;o:AllowPNG/&amp;gt;
    &amp;lt;o:PixelsPerInch&amp;gt;96&amp;lt;/o:PixelsPerInch&amp;gt;
  &amp;lt;/o:OfficeDocumentSettings&amp;gt;
&amp;lt;/xml&amp;gt;
&amp;lt;![endif]--&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Conditional Comments and Ghost Tables
&lt;/h3&gt;

&lt;p&gt;MSO conditional comments (&lt;code&gt;&amp;lt;!--[if mso]&amp;gt;...&amp;lt;![endif]--&amp;gt;&lt;/code&gt;) let you write HTML that &lt;strong&gt;only&lt;/strong&gt; classic Outlook sees. Other clients treat them as regular comments and ignore them.&lt;/p&gt;

&lt;p&gt;The most common use is &lt;strong&gt;ghost tables&lt;/strong&gt;: table structures that wrap your &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt;-based layout so Outlook doesn't collapse it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!--[if mso]&amp;gt;
&amp;lt;table role="presentation" width="600"&amp;gt;&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;
&amp;lt;![endif]--&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"max-width: 600px;"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="c"&gt;&amp;lt;!-- Your modern HTML here --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

&lt;span class="c"&gt;&amp;lt;!--[if mso]&amp;gt;
&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;lt;/table&amp;gt;
&amp;lt;![endif]--&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  VML Background Images
&lt;/h3&gt;

&lt;p&gt;Since Outlook ignores CSS &lt;code&gt;background-image&lt;/code&gt;, you need VML (Vector Markup Language) for background images:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!--[if mso]&amp;gt;
&amp;lt;v:rect xmlns:v="urn:schemas-microsoft-com:vml" fill="true" stroke="false"
  style="width:600px;height:400px;"&amp;gt;
  &amp;lt;v:fill type="tile" src="https://example.com/bg.jpg" /&amp;gt;
  &amp;lt;v:textbox&amp;gt;
&amp;lt;![endif]--&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"background-image: url('https://example.com/bg.jpg');"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    Content here
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="c"&gt;&amp;lt;!--[if mso]&amp;gt;
  &amp;lt;/v:textbox&amp;gt;
&amp;lt;/v:rect&amp;gt;
&amp;lt;![endif]--&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  New Outlook for Windows (Chromium-Based)
&lt;/h3&gt;

&lt;p&gt;The new Outlook uses a Chromium-based rendering engine and is essentially the same as Outlook.com. It supports flexbox, media queries, background images, border-radius, web fonts, and modern CSS.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Crucially, it does NOT support MSO conditional comments.&lt;/strong&gt; They're treated as regular comments and ignored. This means your ghost tables and VML workarounds for classic Outlook are harmlessly invisible in the new Outlook.&lt;/p&gt;

&lt;h3&gt;
  
  
  The 2025-2026 Transition Timeline
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;January 2025:&lt;/strong&gt; Business Standard/Premium users auto-migrated to new Outlook&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;April 2026:&lt;/strong&gt; Enterprise users begin migration&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;October 2026:&lt;/strong&gt; End of support for Word rendering engine&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;2029+:&lt;/strong&gt; Classic Outlook still supported for non-rendering features&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;This means 2025-2026 is peak dual-Outlook pain.&lt;/strong&gt; You must code for both the Word engine AND Chromium simultaneously. The good news: conditional comments make this possible without either version seeing the other's workarounds.&lt;/p&gt;




&lt;h2&gt;
  
  
  Apple Mail: The Gold Standard
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Market share:&lt;/strong&gt; 48-53% globally (the largest email client)&lt;/p&gt;

&lt;p&gt;Apple Mail uses &lt;strong&gt;WebKit&lt;/strong&gt; (Safari's engine) and is by far the most standards-compliant email client. If it works in a browser, it probably works in Apple Mail.&lt;/p&gt;

&lt;h3&gt;
  
  
  What's Supported
&lt;/h3&gt;

&lt;p&gt;Essentially everything: flexbox, grid, media queries, border-radius, background images, transforms, CSS animations (with &lt;code&gt;-webkit-&lt;/code&gt; prefix), &lt;code&gt;@font-face&lt;/code&gt; web fonts, &lt;code&gt;&amp;lt;link&amp;gt;&lt;/code&gt; external stylesheets, &lt;code&gt;@import&lt;/code&gt;, and even inline SVG (partial).&lt;/p&gt;

&lt;p&gt;Apple Mail scores highest on the &lt;a href="https://www.caniemail.com/scoreboard/" rel="noopener noreferrer"&gt;Can I email scoreboard&lt;/a&gt; across 303 tested features.&lt;/p&gt;

&lt;h3&gt;
  
  
  Dark Mode Done Right
&lt;/h3&gt;

&lt;p&gt;Apple Mail is the only major client that gives developers full control over dark mode:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"color-scheme"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"light dark"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"supported-color-schemes"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"light dark"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nd"&gt;:root&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;color-scheme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;light&lt;/span&gt; &lt;span class="n"&gt;dark&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;@media&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prefers-color-scheme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;dark&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nc"&gt;.email-body&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#1a1a1a&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#e0e0e0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Without the &lt;code&gt;color-scheme&lt;/code&gt; declarations, Apple Mail won't apply your custom dark mode styles and may apply its own inversion instead.&lt;/p&gt;




&lt;h2&gt;
  
  
  Yahoo Mail: The Quirky One
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The &lt;code&gt;height&lt;/code&gt; to &lt;code&gt;min-height&lt;/code&gt; Bug
&lt;/h3&gt;

&lt;p&gt;Yahoo Mail converts &lt;code&gt;height&lt;/code&gt; to &lt;code&gt;min-height&lt;/code&gt;. This breaks responsive image patterns where you set &lt;code&gt;height: auto&lt;/code&gt; in a media query. It becomes &lt;code&gt;min-height: auto&lt;/code&gt;, which is invalid CSS and does nothing.&lt;/p&gt;

&lt;h3&gt;
  
  
  The &lt;code&gt;!important&lt;/code&gt; Spacing Bug
&lt;/h3&gt;

&lt;p&gt;This one is subtle:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="c"&gt;/* This works: */&lt;/span&gt;
&lt;span class="nt"&gt;display&lt;/span&gt;&lt;span class="nd"&gt;:none&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nt"&gt;important&lt;/span&gt;

&lt;span class="c"&gt;/* This does NOT work (note the space): */&lt;/span&gt;
&lt;span class="nt"&gt;display&lt;/span&gt;&lt;span class="nd"&gt;:block&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nt"&gt;important&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If there's a space before &lt;code&gt;!important&lt;/code&gt;, Yahoo strips it. No space = keeps it.&lt;/p&gt;

&lt;h3&gt;
  
  
  CSS Under Comments
&lt;/h3&gt;

&lt;p&gt;Yahoo Mail removes any CSS declarations that appear below HTML comments in your &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; blocks. Keep your comments out of your stylesheets.&lt;/p&gt;

&lt;h3&gt;
  
  
  Dark Mode
&lt;/h3&gt;

&lt;p&gt;Yahoo Mail applies dark mode to its &lt;strong&gt;UI&lt;/strong&gt; but does &lt;strong&gt;not&lt;/strong&gt; invert email content colours. Your email renders as-is regardless of the user's dark mode setting. This is actually the easiest client to deal with for dark mode.&lt;/p&gt;




&lt;h2&gt;
  
  
  Samsung Mail: The Wild Card
&lt;/h2&gt;

&lt;p&gt;Samsung Mail's biggest quirk is its &lt;strong&gt;auto-fit content&lt;/strong&gt; feature. It uses HTML &lt;code&gt;width&lt;/code&gt; attributes (not CSS &lt;code&gt;width&lt;/code&gt;) to determine the email's viewport width. An image with &lt;code&gt;width="600"&lt;/code&gt; will force Samsung to render at 600px wide, potentially breaking your responsive layout.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; Always use &lt;code&gt;width="100%"&lt;/code&gt; as the HTML attribute on images and wrapper tables.&lt;/p&gt;

&lt;p&gt;Other gotchas:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;margin&lt;/code&gt; works on &lt;code&gt;&amp;lt;p&amp;gt;&lt;/code&gt; and headings but &lt;strong&gt;not&lt;/strong&gt; on &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; or &lt;code&gt;&amp;lt;table&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;max-width&lt;/code&gt; works on &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;table&amp;gt;&lt;/code&gt; but &lt;strong&gt;not&lt;/strong&gt; on &lt;code&gt;&amp;lt;td&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;CSS transitions work; keyframe animations do not&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Account type matters:&lt;/strong&gt; If the Samsung Email app is configured with an Outlook/Exchange account, rendering changes significantly, including loss of media query support&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The Dark Mode Matrix
&lt;/h2&gt;

&lt;p&gt;Dark mode is the single most inconsistent feature across email clients. Here's the reality:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Client&lt;/th&gt;
&lt;th&gt;What Happens&lt;/th&gt;
&lt;th&gt;Can You Control It?&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Apple Mail&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Respects &lt;code&gt;@media (prefers-color-scheme: dark)&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Yes, full control&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Outlook Mac&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Respects &lt;code&gt;@media (prefers-color-scheme: dark)&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Yes, full control&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Thunderbird&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Respects &lt;code&gt;@media (prefers-color-scheme: dark)&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Yes, full control&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Gmail Web&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Does nothing to email content&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Gmail iOS&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Full automatic colour inversion&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Gmail Android&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Partial automatic colour inversion&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Outlook Desktop (Word)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Full automatic inversion with &lt;code&gt;!important&lt;/code&gt; overrides&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Outlook.com / New Outlook&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Partial inversion with injected overrides&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Yahoo Mail&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Does not invert email content&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Practical advice:&lt;/strong&gt; Use off-whites (&lt;code&gt;#FAFAFA&lt;/code&gt;) instead of pure white and dark greys (&lt;code&gt;#222222&lt;/code&gt;) instead of pure black. Pure &lt;code&gt;#FFFFFF&lt;/code&gt; and &lt;code&gt;#000000&lt;/code&gt; trigger the most aggressive inversion in Gmail and Outlook. Slight variations reduce the visual impact of forced inversion.&lt;/p&gt;




&lt;h2&gt;
  
  
  The CSS Support Cheat Sheet
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Gmail&lt;/th&gt;
&lt;th&gt;Outlook (Word)&lt;/th&gt;
&lt;th&gt;Apple Mail&lt;/th&gt;
&lt;th&gt;Yahoo&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; blocks&lt;/td&gt;
&lt;td&gt;Yes (with limits)&lt;/td&gt;
&lt;td&gt;Limited&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;External stylesheets&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Media queries&lt;/td&gt;
&lt;td&gt;Mobile only&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Partial&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Flexbox&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;display:flex&lt;/code&gt; only&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Grid&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;border-radius&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Partial&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;background-image&lt;/code&gt; (CSS)&lt;/td&gt;
&lt;td&gt;Inline only&lt;/td&gt;
&lt;td&gt;No (needs VML)&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;max-width&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Web fonts&lt;/td&gt;
&lt;td&gt;Roboto only&lt;/td&gt;
&lt;td&gt;No (Times New Roman fallback)&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Partial&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CSS animations&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes (&lt;code&gt;-webkit-&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;box-shadow&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Practical Recommendations
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Start with Tables, Enhance with CSS
&lt;/h3&gt;

&lt;p&gt;The only layout method that works &lt;strong&gt;everywhere&lt;/strong&gt; is &lt;code&gt;&amp;lt;table&amp;gt;&lt;/code&gt;. Build your core layout with tables, then use CSS for enhancement:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;table&lt;/span&gt; &lt;span class="na"&gt;role=&lt;/span&gt;&lt;span class="s"&gt;"presentation"&lt;/span&gt; &lt;span class="na"&gt;width=&lt;/span&gt;&lt;span class="s"&gt;"100%"&lt;/span&gt; &lt;span class="na"&gt;cellpadding=&lt;/span&gt;&lt;span class="s"&gt;"0"&lt;/span&gt; &lt;span class="na"&gt;cellspacing=&lt;/span&gt;&lt;span class="s"&gt;"0"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;tr&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;td&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"padding: 20px; background-color: #ffffff;"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="c"&gt;&amp;lt;!-- Content here --&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/tr&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/table&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Inline Your Critical Styles
&lt;/h3&gt;

&lt;p&gt;Gmail, Outlook, and Yahoo all handle &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; blocks differently. The only CSS delivery method that works identically everywhere is inline styles. Use a CSS inliner tool as part of your build process.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Test Before You Send
&lt;/h3&gt;

&lt;p&gt;The rendering differences are too numerous to catch by eyeballing HTML in a browser. Use a preview tool that simulates how each client will actually render your email, including CSS stripping, dark mode inversion, and responsive behaviour.&lt;/p&gt;

&lt;p&gt;Tools like &lt;a href="https://github.com/mailpeek/mailpeek" rel="noopener noreferrer"&gt;mailpeek&lt;/a&gt; let you preview Gmail and Outlook rendering directly in your development environment, with dark mode simulation and compatibility scoring that flags unsupported CSS before you send.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Design for Degradation
&lt;/h3&gt;

&lt;p&gt;Your email should look good in Apple Mail and acceptable in Outlook. Don't fight Outlook's limitations; design around them:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use solid colour backgrounds instead of gradients&lt;/li&gt;
&lt;li&gt;Use border-based buttons instead of &lt;code&gt;border-radius&lt;/code&gt; pills&lt;/li&gt;
&lt;li&gt;Include &lt;code&gt;alt&lt;/code&gt; text styled with &lt;code&gt;font-size&lt;/code&gt;, &lt;code&gt;color&lt;/code&gt;, and &lt;code&gt;font-family&lt;/code&gt; so emails are readable with images blocked&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  5. Watch the Outlook Transition
&lt;/h3&gt;

&lt;p&gt;2026 is the last year you need to worry about the Word rendering engine in new Outlook deployments. But enterprise environments move slowly, so expect to support it through 2027-2028 at minimum. Keep your ghost tables and VML workarounds in place for now.&lt;/p&gt;




&lt;h2&gt;
  
  
  Further Reading
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.caniemail.com/" rel="noopener noreferrer"&gt;Can I email&lt;/a&gt;: The definitive compatibility tables for HTML/CSS in email&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.caniemail.com/scoreboard/" rel="noopener noreferrer"&gt;Can I email Scoreboard&lt;/a&gt;: Client support rankings across 303 features&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.campaignmonitor.com/css/" rel="noopener noreferrer"&gt;Campaign Monitor CSS Guide&lt;/a&gt;: Property-by-property CSS support&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.litmus.com/blog/the-ultimate-guide-to-dark-mode-for-email-marketers" rel="noopener noreferrer"&gt;Litmus Dark Mode Guide&lt;/a&gt;: Comprehensive dark mode reference&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/hteumeuleu/email-bugs" rel="noopener noreferrer"&gt;HTeuMeuLeu Email Bugs&lt;/a&gt;: Crowdsourced email client bug tracker&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://developers.google.com/workspace/gmail/design/css" rel="noopener noreferrer"&gt;Google Developers Gmail CSS Support&lt;/a&gt;: Official Gmail supported properties&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;This post is maintained by the team behind &lt;a href="https://github.com/mailpeek/mailpeek" rel="noopener noreferrer"&gt;mailpeek&lt;/a&gt;, an open-source Vue 3 email preview component with Gmail/Outlook simulation, dark mode preview, compatibility scoring, and accessibility checking. Star us on GitHub if this guide was useful.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>html</category>
      <category>wecoded</category>
      <category>css</category>
    </item>
    <item>
      <title>Building a Gmail/Outlook email preview component for Vue</title>
      <dc:creator>Mailpeek</dc:creator>
      <pubDate>Fri, 20 Feb 2026 07:56:21 +0000</pubDate>
      <link>https://dev.to/mailpeek/building-a-gmailoutlook-email-preview-component-for-vue-29kk</link>
      <guid>https://dev.to/mailpeek/building-a-gmailoutlook-email-preview-component-for-vue-29kk</guid>
      <description>&lt;p&gt;If you have ever built transactional emails in a Vue app, you have probably hit the same wall. You write the HTML, send a test, open it in Gmail, and half your styling is gone. Then you open it in Outlook and it looks like it was rendered in 2003.&lt;/p&gt;

&lt;p&gt;The React ecosystem has react.email, which gives you components, a preview server, and a whole development workflow. Vue has nothing equivalent. There is a thin wrapper around Unlayer that requires an account, an abandoned MJML builder with zero dependents on npm, and a lot of Stack Overflow threads telling you to just use Litmus.&lt;/p&gt;

&lt;p&gt;I'm on a mission to improve this workflow for Vue developers, starting with &lt;a class="mentioned-user" href="https://dev.to/mailpeek"&gt;@mailpeek&lt;/a&gt;/preview.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it does
&lt;/h2&gt;

&lt;p&gt;It is a single Vue 3 component that renders your email HTML in an isolated iframe, simulating how Gmail and Outlook actually handle it, including their CSS restrictions.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;npm install @mailpeek/preview&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;
  &amp;lt;script setup &lt;span class="nv"&gt;lang&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"ts"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  import &lt;span class="o"&gt;{&lt;/span&gt; EmailPreview &lt;span class="o"&gt;}&lt;/span&gt; from &lt;span class="s1"&gt;'@mailpeek/preview'&lt;/span&gt;
  import &lt;span class="s1"&gt;'@mailpeek/preview/style.css'&lt;/span&gt;
  &amp;lt;/script&amp;gt;

  &amp;lt;template&amp;gt;
    &amp;lt;EmailPreview :html&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"yourEmailHtml"&lt;/span&gt; /&amp;gt;
  &amp;lt;/template&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You get a Gmail chrome preview, a client switcher, a mobile and desktop toggle, and metadata extraction including subject line, preheader text, and file size, all out of the box.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How the CSS filtering works&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The biggest challenge was simulating what each email client actually does to your CSS.&lt;/p&gt;

&lt;p&gt;Gmail strips a surprising amount. No position, no transform, no animation, no box-shadow, no CSS Grid, no flexbox sub-properties. It also strips external stylesheets and &lt;a class="mentioned-user" href="https://dev.to/import"&gt;@import&lt;/a&gt; rules entirely.&lt;/p&gt;

&lt;p&gt;Outlook desktop (the Word engine versions from 2016 to 2021) is worse. Everything Gmail strips, plus border-radius, display: flex, background-size, max-width, and min-width.&lt;/p&gt;

&lt;p&gt;Mailpeek processes your HTML through a CSS filter before rendering it in the iframe. If your email uses border-radius: 8px and you switch to Outlook mode, the property is stripped and a warning appears in the console:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;[mailpeek] Outlook: removed "border-radius: 8px" — Outlook Word renderer does not support border-radius&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The filter is regex-based with no DOM dependency, making it fully SSR-safe and compatible with Nuxt 3. The restriction lists are sourced from the Google Developers Gmail CSS documentation and caniemail.com.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What it does not do yet&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It is not a replacement for Litmus or Email on Acid. It will not catch every rendering quirk, it does not simulate VML for Outlook, and it does not account for every edge case in every client version. Think of it as a linter that catches obvious issues during development rather than a screenshot testing service.&lt;/p&gt;

&lt;p&gt;Dark mode simulation for Gmail, Apple Mail, and Outlook is the next thing on the roadmap.&lt;/p&gt;

&lt;p&gt;Try it out!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://mailpeek.dev/demo" rel="noopener noreferrer"&gt;Live Demo&lt;/a&gt; · &lt;a href="https://github.com/mailpeek/mailpeek" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;npm install @mailpeek/preview&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Zero runtime dependencies beyond Vue 3. TypeScript types included.&lt;/p&gt;

&lt;p&gt;If you build HTML emails in Vue and have thoughts on what is missing, I would love to hear them in the comments or as a GitHub issue.&lt;/p&gt;

</description>
      <category>showdev</category>
      <category>tooling</category>
      <category>vue</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Instagram #pride rainbow tag - how did they do it?</title>
      <dc:creator>Mailpeek</dc:creator>
      <pubDate>Fri, 28 Jun 2019 17:22:20 +0000</pubDate>
      <link>https://dev.to/mailpeek/instagram-pride-rainbow-tag-how-did-they-do-it-2hhn</link>
      <guid>https://dev.to/mailpeek/instagram-pride-rainbow-tag-how-did-they-do-it-2hhn</guid>
      <description>&lt;p&gt;I noticed this nice touch for pride week and wanted to see if there was anything special about how it was done by the devs at instagram. Turns out there wasn't anything special but it looks great and is really simple to do so decided to write it up anyway!&lt;/p&gt;

&lt;p&gt;The main property required here is &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/background-clip" rel="noopener noreferrer"&gt;&lt;strong&gt;background-clip&lt;/strong&gt;&lt;/a&gt;. It allows you to clip (or mask) an element or text to it's background image or colour. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Important&lt;/strong&gt; - in order to apply it to text it still requires the &lt;em&gt;-webkit&lt;/em&gt; prefix for Chrome, Firefox, Safari and Edge. In Safari it can't be applied to the button tag but you could get around this with a span!&lt;/p&gt;

&lt;p&gt;The rainbow is just a simple &lt;a href="https://www.instagram.com/static/images/rainbowGradient.png/558818d23695.png" rel="noopener noreferrer"&gt;png&lt;/a&gt; repeated as a background image on the element with the background size increased.&lt;/p&gt;

&lt;p&gt;Finally they've set the color of the text to transparent to allow the rainbow background to be visible. Interestingly they used the &lt;strong&gt;-webkit-text-fill-color&lt;/strong&gt; property to do this as the standard color property doesn't allow for the transparent value. The two properties are essentially the same, but &lt;strong&gt;-webkit-text-fill-color&lt;/strong&gt; will take precedence over &lt;strong&gt;color&lt;/strong&gt; if the two have different values. This is useful as it means it will just gracefully fallback to the default colour of the text if not supported.&lt;/p&gt;

&lt;p&gt;Here's a quick &lt;a href="https://codepen.io/anon/pen/jjYZEL" rel="noopener noreferrer"&gt;codepen&lt;/a&gt; of the final effect. Anyone got any other more advanced examples of background-clip?&lt;/p&gt;

</description>
      <category>css</category>
      <category>frontend</category>
      <category>codepen</category>
    </item>
    <item>
      <title>Headless CMS - what are the cool kids using?</title>
      <dc:creator>Mailpeek</dc:creator>
      <pubDate>Sun, 23 Jun 2019 17:50:58 +0000</pubDate>
      <link>https://dev.to/mailpeek/headless-cms-what-are-the-cool-kids-using-30gk</link>
      <guid>https://dev.to/mailpeek/headless-cms-what-are-the-cool-kids-using-30gk</guid>
      <description>&lt;p&gt;I'm going to make use of my "time" on maternity and finally get a programming blog together, mainly as a way for me to keep up by writing articles on topics I want to brush up on. I've used Kentico cloud for large scale multisites and find it great but are there any others that might suit a personal blog better? I've been looking at Sanity which looks great and really easy to set up, anyone any experience with it?&lt;/p&gt;

</description>
      <category>cms</category>
      <category>headless</category>
      <category>content</category>
    </item>
  </channel>
</rss>
