Newsletter HTML Builder
Write your newsletter as one section per line and get a complete, responsive HTML email back — table-based layout, inline CSS, an Outlook ghost table, a hidden preheader and optional dark mode. Runs entirely in your browser, no server, no sign-up.
About this tool
Email clients are not browsers. Gmail strips <style> blocks in forwarded messages, Outlook renders
with Word's layout engine, and flexbox or grid simply do not exist in most inboxes. This builder
takes a plain list of sections and returns the markup those clients actually understand: nested
role="presentation" tables, every declaration inlined on the element it styles, an Outlook
ghost-table wrapper in a conditional comment, mso-table-lspace resets, and a single mobile media
query that drops the fixed-width card to 100%.
You write content, not markup:
heading | What shipped in March
text | Hi {{first_name}}, three things went live this month.
columns | **Faster exports** — 4x quicker. | **New API keys** — scoped and revocable.
button | Read the release notes | https://example.com/changelog
divider
footer | [Unsubscribe]({{unsubscribe_url}})
Worked example
Input — one section, defaults everywhere else:
button | Read more | https://example.com
Output (the button row inside the generated document):
<tr>
<td align="center" style="padding:24px 32px;">
<table role="presentation" cellpadding="0" cellspacing="0" border="0">
<tr>
<td align="center" bgcolor="#2563eb" style="border-radius:6px;background-color:#2563eb;">
<a href="https://example.com" style="display:inline-block;padding:14px 28px;font-family:-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;font-size:16px;line-height:20px;color:#ffffff; font-weight:600;color:#ffffff;text-decoration:none;border-radius:6px;">Read more</a>
</td>
</tr>
</table>
</td>
</tr>
That is the "bulletproof button" pattern — a background-coloured table cell with a padded
display:inline-block link inside it, so the whole rectangle stays clickable in clients that ignore
padding on anchors, and it degrades to a plain coloured cell where border-radius is unsupported.
Section types
| Type | Line | Notes |
|---|---|---|
heading | heading | Big title | 28px, bold |
subheading | subheading | Smaller title | 20px, semi-bold |
text | text | A paragraph | 16px/26px body copy |
button | button | Label | https://… | bulletproof button, accent-coloured |
image | image | https://…png | Alt | https://… | full-bleed, fluid; 4th part is an optional link |
columns | columns | Left | Right | two 50% cells that stack on mobile |
divider | divider | 1px rule |
spacer | spacer | 24 | blank vertical space, px |
footer | footer | Small print | 12px, muted, centred |
html | html | <p>…</p> | raw markup, passed through verbatim |
Inside any text you can use **bold**, *italic*, [label](https://example.com) and \n for a
line break. Merge tags such as {{first_name}}, {{unsubscribe_url}} or *|FNAME|* pass through
untouched, so the output can be pasted straight into Mailchimp, Klaviyo, Brevo, Postmark or your own
sending code. Lines beginning with # are comments.
Limits and edge cases
- Max 200 sections per newsletter; the 201st is an error rather than a truncated document.
- Content width 320–900px, default 600 — the width every desktop client renders without a horizontal scrollbar. Below that width the card goes fluid.
- Spacer height is capped at 200px.
- Only
https://,http://,mailto:,tel:,#anchorand merge-tag URLs are accepted. Ajavascript:URL is rejected, not silently escaped. - Colours must be hex (
#f4f4f5, 3/4/6/8 digits) or a plain CSS colour name — anything else is rejected so a broken value can't leak into an inlinestyleattribute. - Text is HTML-escaped; the
htmlsection type is the deliberate escape hatch and is not sanitised — only put markup you trust there. - No web fonts, no background images, no
<script>: all three are ignored or blocked by major clients, so the builder does not emit them.
FAQ
Why is all the CSS inline instead of a style block?
Because several clients delete <style> blocks. Gmail's web client strips them when a message is
forwarded, and some corporate filters remove them outright. Anything that must survive — colours,
fonts, padding, widths — is written on the element itself. The <style> block is used only for the
things that cannot be inlined: the mobile media query and the dark-mode block. If both are
stripped, the email still renders correctly at its fixed width.
Will this render correctly in Outlook?
Outlook on Windows renders with Word, which ignores max-width, border-radius and most modern
CSS. The output handles that with a conditional-comment ghost table (<!--[if mso]>) that pins the
content to a real fixed-width table, an o:PixelsPerInch setting so images are not scaled up, and
mso-table-lspace/mso-table-rspace resets that remove Word's phantom table gutters. Buttons fall
back to square corners there — that is expected, not a bug.
What is a preheader, and why is it padded with strange characters?
The preheader is the short line most inboxes show after the subject. It is rendered as a
zero-height, transparent, mso-hide:all div at the top of the body. Without padding, the client
would continue pulling in whatever comes next — usually "View in browser" or your first heading —
and append it to the preview. The run of ͏‌   characters is
invisible filler that consumes the rest of the preview slot.
How does the dark-mode option work?
With the box ticked, the document gets color-scheme / supported-color-schemes meta tags and a
@media (prefers-color-scheme: dark) block that swaps the page, card, text, muted and rule colours
for dark equivalents. Apple Mail, Outlook on macOS/iOS and several others honour it. Gmail and
Outlook.com apply their own colour inversion regardless — the meta tags make that inversion less
aggressive. Untick the box if you want the light palette everywhere.
Can I use merge tags and personalisation variables?
Yes. Text is escaped for HTML but {{first_name}}, *|FNAME|* and similar tags contain no
HTML-special characters, so they survive verbatim. Merge tags are also accepted as URLs — for
example [Unsubscribe]({{unsubscribe_url}}) — because most sending platforms substitute the real
link at send time.
Do the two-column sections stack on phones?
Yes. The columns are 50%-width table cells carrying an sm-stack class; the media query switches
them to display:block; width:100% below your chosen content width. Clients that drop the media
query (older Gmail app versions on some Android builds) show them side by side, which is why each
column is kept narrow enough to remain readable at half width.
How do I preview the result before sending?
Copy the output, save it as a .html file and open it in a browser for the layout, then send
yourself a real test message through your sending platform — a browser cannot reproduce Outlook's
Word engine or Gmail's CSS stripping. Sending one test to a Gmail address, one to Outlook and one
to Apple Mail covers most of the market.
Developer & Automation Access
Run it from the terminal
Same engine as this page, headless — via the gizza CLI:
gizza tool newsletter-html-builder "heading | Product update
text | Hi {{first_name}}, here is what shipped this month.
button | Read the notes | https://example.com/changelog
divider
footer | [Unsubscribe](https://example.com/u)"New to the CLI? Get gizza →
Open it by URL
Pre-fill and auto-run this tool with query parameters — the names match the API/CLI:
https://gizza.ai/tools/newsletter-html-builder/?sections=heading%20%7C%20Product%20update%0Atext%20%7C%20Hi%20%7B%7Bfirst_name%7D%7D%2C%20here%20is%20what%20shipped%20this%20month.%0Abutton%20%7C%20Read%20the%20notes%20%7C%20https%3A%2F%2Fexample.com%2Fchangelog%0Adivider%0Afooter%20%7C%20%5BUnsubscribe%5D%28https%3A%2F%2Fexample.com%2Fu%29&subject=March%20newsletter&preheader=Three%20new%20features%20and%20a%20discount%20inside&width=600&background=%23f4f4f5&content_background=%23ffffff&text_color=%231f2937&accent=%232563eb&font=system&dark_mode=trueMachine-readable descriptor: tool.json — title + parameters JSON Schema for agents.
