Files

1.5 KiB

HTML Email Rendering Order

CRITICAL: Regex substitution order

When converting Markdown email body to HTML, the order of operations matters:

WRONG (produces literal ## Title in HTML):

  1. Convert \n to <br> FIRST
  2. Then try to match ^## (.+)$ — FAILS because lines are now <br>-delimited

RIGHT (renders styled <h2> in HTML):

  1. Regex-substitute headers FIRST (^## (.+)$<h2>\1</h2> with re.MULTILINE)
  2. Regex-substitute horizontal rules (^---$<hr>)
  3. Convert bold (**text**<strong>)
  4. Convert newlines LAST (\n<br>)
import re

html = body
html = re.sub(r'^## (.+)$', r'<h2 style="...">\1</h2>', html, flags=re.MULTILINE)
html = re.sub(r'^### (.+)$', r'<h3 style="...">\1</h3>', html, flags=re.MULTILINE)
html = re.sub(r'^---$', r'<hr style="...">', html, flags=re.MULTILINE)
html = re.sub(r'\*\*(.+?)\*\*', r'<strong>\1</strong>', html)
html = html.replace('\n', '<br>')

Always use multipart/alternative

  • Plain text part first (keeps raw markdown — degrades gracefully)
  • HTML part second (email clients render the last part they understand)
  • Wrapped in full <!DOCTYPE html> with inline <style> block

Common failure modes

  • Literal ## in output — newlines converted to <br> before header regex. Fix: reorder steps.
  • Raw --- visible^---$ regex didn't match because line is not at line start after <br> conversion.
  • **text** visible — bold regex ran before or without proper newline handling.