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 viaprefers-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
@mediain<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.