Responsive and dark-mode variants

Use sm: and dark: variants as progressive enhancement while keeping the baseline email correct everywhere.

SML supports exactly two class variants: sm: and dark:. Both follow the same posture, progressive enhancement, never foundation. Your unprefixed (base) classes must produce a fully correct email on their own; a variant only adjusts that baseline for clients that understand it. Neither variant may be the only place a piece of layout or color is defined.

This mirrors how the wider email ecosystem (including React Email) treats responsive styles: email clients vary wildly in which <style>/media-query features they honor, so the source of truth has to work with every variant mentally stripped out.

sm: progressive enhancement

sm: adjusts font size, padding, and column stacking for narrower viewports; never establishes layout on its own.

<Heading level={1} class="text-3xl font-bold sm:text-4xl">
  Welcome, {{firstName}}
</Heading>

Only sm: is supported, there's no md:/lg:/xl:/2xl: breakpoint ladder. Using one of those is a compile error suggesting sm: instead (see the error catalog).

sm: compiles to a <style> block in the document <head>, with !important. required to beat the inline style="" attributes that carry the base declarations for every other client. Clients that strip <head><style> (some webmail) simply never see the adjustment and fall back to the base styles, which is exactly the progressive-enhancement contract.

dark: color only

dark: adjusts color families only, bg-*, text-{color}, border-{color}. Applying dark: to a non-color utility (spacing, sizing, display, typography) is a compile error (dark-variant-non-color): dark mode in email is a palette swap, not a layout change.

<Text class="text-base text-gray-700 dark:text-gray-200">
  Thanks for joining Samva.
</Text>
<Button class="bg-blue-600 dark:bg-blue-400" href="{{ctaUrl}}">
  Open dashboard
</Button>

Like sm:, dark: compiles to head <style> with !important, inside a @media (prefers-color-scheme: dark) block, plus Outlook.com's [data-ogsc]/[data-ogsb] selector duplication (Outlook.com doesn't honor the standard media query). The compiler always emits color-scheme/ supported-color-schemes meta and CSS, even for documents with zero dark: classes, that hygiene is unconditional, not opt-in.

Client support is genuinely mixed

Dark mode is the one place client behavior really does fork into distinct categories, not just "supported or not":

  • Honoring clients (Apple Mail, Outlook desktop and new Outlook) apply your dark: styles as authored via prefers-color-scheme.
  • Gmail's mobile and web apps force-invert the entire message and ignore authored dark styles entirely, your dark: classes have no effect there. This is a hard client limitation, not a gap in SML.
  • Clients with no dark-mode concept at all just render the light baseline, same as any other client that ignores @media in <style>.

Because Gmail's forced inversion can't be authored around, the checks engine treats invert-safety as a separate concern from dark: authoring: it warns on patterns that look fine in light mode but invert badly (pure-black text, transparent-background logos, low contrast after inversion) regardless of whether the template uses dark: at all. Write dark: for the clients that honor it; let the checks engine catch force-invert hazards for the clients that don't.

Combining variants

A class carries at most one variant, sm:dark:bg-blue-400 (stacked variants) is a compile error (unsupported-variant); write separate sm: and dark: classes instead if you need both adjustments on the same element.

On this page