Skip to content

SOPs

Build It Light, Not Dark

Anything rendered by something we do not control — emails above all — gets a light background. White card, dark text. No navy, no #1a1a2e, no dark blue.

This is not a style preference. It is a correctness rule. Dark-background HTML breaks in dark-mode renderers, and we do not get to choose the recipient’s renderer.

Dark-mode renderers — Gmail’s dark theme, Apple Mail’s dark theme, Chrome’s “Auto Dark Mode for Web Contents” — color-invert HTML they decide is light-themed. The inversion is dumb: it flips light text toward black but leaves an already-dark background dark. The result is dark text on a dark background. Unreadable.

You cannot win this by making the text “whiter.” Every color you set gets inverted right back. The renderer is downstream of your CSS.

If text renders dark but your source clearly says light, check the borders. In a normal render, dark 1px solid #333 dividers are nearly invisible on a dark card. If those dividers show up light in a screenshot while the text shows up dark, the whole page has been inverted. That is the fingerprint.

This cost three rebuild rounds on the Facebook follower report email on 2026-05-21 before the inversion was spotted — each round “made the text whiter,” each round got inverted straight back.

The fixed fb-follower-tracker daily email is the canonical light template. build_email_html() in track.py:

ElementValue
Card background#ffffff
Card border1px solid #e2e8f0
Body text#1e293b
Headings#0f172a
Secondary text#64748b
Row / table panel#f8fafc
Borders / dividers#e2e8f0
Positive metric#15803d (green)
Negative metric#b91c1c (red)
Root style hintcolor-scheme: light

color-scheme: light on the root tells conforming renderers the page is already finished — stop force-inverting it.

Light emails survive dark-mode clients because the client either leaves them alone or applies one coherent dark transform. Either way they stay readable. Dark emails get the broken half-transform.

  1. Card background is #ffffff (or a very light gray). Never navy, never #1a1a2e.
  2. Body text is a dark slate (#1e293b), headings darker (#0f172a).
  3. Borders are light and visible on white (#e2e8f0).
  4. Status colors are dark enough to read on white — #15803d green, #b91c1c red. The bright #4ade80 / #f87171 shades read fine on dark but wash out on white.
  5. color-scheme: light is on the root element style.
  6. Preview it, then send one real test to yourself and open it in your actual email client — that is the only renderer that matters.
  • Emails: light, always. No exceptions.
  • HTML reports, exported docs, anything that lands in a file someone opens elsewhere: light.
  • Dashboards opened directly in our own Chrome (the #1a1a2e dashboard theme from the Markdown-to-HTML sync pattern): the dark theme stays — we control that renderer and force-dark is off there. If Chrome’s auto dark mode ever gets turned on, those invert too, and they move to light.