39 lines
1.5 KiB
Markdown
39 lines
1.5 KiB
Markdown
# 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>`)
|
|
|
|
```python
|
|
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.
|