1.5 KiB
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):
- Convert
\nto<br>FIRST - Then try to match
^## (.+)$— FAILS because lines are now<br>-delimited
RIGHT (renders styled <h2> in HTML):
- Regex-substitute headers FIRST (
^## (.+)$→<h2>\1</h2>withre.MULTILINE) - Regex-substitute horizontal rules (
^---$→<hr>) - Convert bold (
**text**→<strong>) - 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.