May 24, 2026 · Developer Guide
Styling PDFs with Markdown: Layouts and CSS for E-Signatures (2026)
How to use markdown and custom CSS classes to generate beautiful, professional PDFs via API. Managing page breaks, margins, and headers.
Founder, Signbee
TL;DR
Generating professional PDFs from Markdown requires a mix of markup structure, custom CSS, and API options. By injecting custom CSS into the Signbee /v1/generate API, you can control document styling, handle page-break rules (like page-break-inside: avoid), and configure margins, headers, and footers. Read on to see payload examples, print-specific CSS patterns, and a full integration script.
Why Markdown alone isn't enough for PDFs
Markdown is the perfect format for writing digital agreements, NDAs, and proposals. It is human-readable, version-control friendly, and lightweight. However, markdown was built for the continuous scroll of web browsers. It has no concept of page dimensions, margin limits, headers, footers, or page-break boundaries.
When you convert raw markdown to a PDF using a typical API, you often end up with awkward results: headings stranded at the bottom of a page, text overlapping with headers, or a signature block split across a page crease. To turn markdown into a client-ready contract, you need to combine the simplicity of markdown with custom CSS layout rules and precise API controls.
The Signbee generate API payload
The Signbee /v1/generate endpoint allows you to send markdown, inject custom CSS, and set page layout options in a single API call. This separation of concerns lets your application logic focus on generating content while keeping style and document rules clean.
Here is what a typical payload looks like when configuring page sizes, custom margins, and headers/footers with dynamic page numbering:
{
"markdown": "# Service Agreement\n\nThis agreement is made between...",
"css": "body { font-family: 'Helvetica', sans-serif; color: #111; }\nh1 { font-size: 24pt; }\n.signature-block { page-break-inside: avoid; }",
"options": {
"format": "Letter",
"margin": {
"top": "20mm",
"bottom": "20mm",
"left": "15mm",
"right": "15mm"
},
"displayHeaderFooter": true,
"headerTemplate": "<div style='font-size: 8px; color: #888; width: 100%; text-align: right; padding-right: 15mm;'>CONFIDENTIAL</div>",
"footerTemplate": "<div style='font-size: 8px; color: #888; width: 100%; text-align: center;'>Page <span class='pageNumber'></span> of <span class='totalPages'></span></div>"
}
}Mastering CSS page breaks in PDFs
Controlling how elements split across pages is critical for legal agreements. You don't want your sign-off details floating alone on page five, nor do you want a table row cut in half. The PDF generation engine supports standard CSS paged media rules:
1. Forced Page Breaks (page-break-before: always / break-before: page): Use this to begin a new major section on a clean page. You can insert an HTML element directly into your markdown to trigger this:
<div class="page-break"></div>
2. Avoiding Inside Breaks (page-break-inside: avoid / break-inside: avoid): Apply this to groups of elements that must stay together, such as signature fields, callout boxes, or small tables.
3. Preventing Headings Orphans (page-break-after: avoid / break-after: avoid): Prevent headings from sitting alone at the bottom of a page by ensuring they always stay attached to the paragraph that follows.
Styling tables, lists, and images
Because markdown compiles into standard HTML tags, you can target them directly using your custom CSS block. Use this table as a reference for print-specific rules:
| Element | CSS Property | Recommended Value |
|---|---|---|
Table Rows (tr) | page-break-inside | avoid |
Table Header (thead) | display | table-header-group |
Tables (table) | border-collapse | collapse |
Signature Box (.sig) | page-break-inside | avoid |
Images (img) | max-width, height | 100%, auto |
A complete generation example (Node.js)
Here is a complete integration script. It defines a document with a forced page break using an inline HTML spacer, customizes CSS fonts and margins, makes a POST request to Signbee, and saves the generated PDF buffer directly to the local filesystem.
import fs from 'fs/promises';
async function generateStyledPdf() {
const payload = {
markdown: `# Master Services Agreement
This Agreement is entered into as of May 24, 2026.
## 1. Services and Deliverables
The Consultant will provide styling engine services as detailed in Exhibit A.
<div class="page-break"></div>
## 2. Payment Terms
Invoices are payable within 30 days of receipt.
<div class="signature-block">
<h3>Signatures</h3>
<table style="width: 100%; border: none;">
<tr>
<td style="width: 50%; border: none; padding: 20px 0;">
___________________________<br/>
<strong>Client Signature</strong>
</td>
<td style="width: 50%; border: none; padding: 20px 0;">
___________________________<br/>
<strong>Consultant Signature</strong>
</td>
</tr>
</table>
</div>`,
css: `
body {
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
color: #333;
font-size: 11pt;
line-height: 1.6;
}
h1 { font-size: 22pt; margin-bottom: 20px; color: #111; }
h2 { font-size: 16pt; margin-top: 30px; color: #222; }
h3 { font-size: 13pt; margin-top: 20px; color: #333; }
.page-break { page-break-before: always; }
.signature-block { page-break-inside: avoid; margin-top: 50px; }
`,
options: {
format: "A4",
margin: {
top: "25mm",
bottom: "25mm",
left: "20mm",
right: "20mm"
},
displayHeaderFooter: true,
headerTemplate: `<div style="font-size: 9px; color: #888; width: 100%; text-align: right; padding-right: 20mm; font-family: sans-serif;">Confidential - MSA</div>`,
footerTemplate: `<div style="font-size: 9px; color: #888; width: 100%; text-align: center; font-family: sans-serif;">Page <span class="pageNumber"></span> of <span class="totalPages"></span></div>`
}
};
const response = await fetch("https://signb.ee/api/v1/generate", {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": "Bearer YOUR_API_KEY"
},
body: JSON.stringify(payload)
});
if (!response.ok) {
const errorText = await response.text();
throw new Error(`Failed to generate PDF: ${response.status} ${errorText}`);
}
// API returns the raw PDF stream
const arrayBuffer = await response.arrayBuffer();
await fs.writeFile("agreement.pdf", Buffer.from(arrayBuffer));
console.log("agreement.pdf generated successfully!");
}
generateStyledPdf().catch(console.error);Advanced Typography: Custom Web Fonts & Base64 Embedding
One of the most frequent styling hurdles developers face is loading custom brand typography (such as Inter, Roboto, or bespoke corporate typefaces) in generated PDFs. In headless browser rendering environments, loading external web fonts via standard @import url(...) or <link rel="stylesheet"> tags introduces network latency and intermittent render timeouts—often leading to Flash of Unstyled Text (FOUT) or blank glyphs.
The recommended pattern for bulletproof PDF typography is inlining base64-encoded WOFF2 or TTF font files directly into your custom CSS string:
@font-face {
font-family: 'Inter';
src: url('data:font/woff2;base64,d09GMgABAAAAAA...') format('woff2');
font-weight: 400;
font-style: normal;
font-display: block;
}
body {
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
font-size: 10.5pt;
line-height: 1.55;
color: #1a1a1a;
}Cover Page Styling & Dynamic Watermarks
When generating formal enterprise proposals or multi-page Master Service Agreements, you often want a prominent cover page that omits the running header and footer, followed by subsequent pages that display confidentiality notices and running page counters.
You can achieve this layout cleanly using CSS paged media rules and the @page:first pseudo-class selector:
Cover Page & Watermark Patterns:
- Suppress Header/Footer on Cover: While the global header template applies to all pages, you can set the cover page container to full viewport height (
min-height: 100vh) and style subsequent content blocks withpage-break-before: always. - Diagonal "DRAFT" Watermark: Apply a fixed-position background watermark using CSS:
.watermark { position: fixed; top: 40%; left: 15%; transform: rotate(-45deg); font-size: 72pt; color: rgba(200, 200, 200, 0.15); pointer-events: none; z-index: -1; }. Because it is positioned fixed within paged media, it stamps across every page automatically. - Bleed & Crop Marks: When printing physical booklets, define
@page { size: A4; marks: crop cross; bleed: 3mm; }to guide commercial print finishing.
| Mistake | Consequence |
|---|---|
| Using Flexbox / Grid wrapping | Columns clip or break poorly across pages |
Relative font sizes (rem / em) | Font scaling depends on viewport size, leading to unpredictable sizing |
| Ignoring background color rules | Table headers or callouts have missing background fills in PDF |
| Orphaned Headings | H2/H3 titles show at bottom of page; text starts on next page |
| No margin space in footer/header | Headers/footers overlap document text |
Frequently Asked Questions
Can I use standard CSS rules like page-break-before in markdown to PDF conversion?
Yes. When converting markdown to PDF using Signbee's API, you can embed standard CSS style tags or inline styles. To force a page break, use rules like page-break-before: always or break-before: page inside HTML tags embedded in your markdown, such as <div class="page-break"></div> or via custom CSS class definitions.
How do I manage margins, headers, and footers when generating PDFs?
Margins, headers, and footers are controlled via the Signbee /v1/generate API payload options. You can specify top, bottom, left, and right margins in inches, millimeters, or pixels. Custom headers and footers can contain HTML with special classes like date, title, url, pageNumber, and totalPages to dynamically print page info.
How do I style markdown tables and lists for PDF outputs?
Since markdown tables and lists compile to standard HTML tags (<table>, <ul>, <ol>), you can style them by passing a custom CSS stylesheet in the API payload. You should set border-collapse: collapse and define borders, padding, and alternating row backgrounds for tables to ensure they render beautifully and remain readable on a printed page.
Create beautifully styled agreements instantly — 5 free docs/month, 100 req/min.
Last updated: May 29, 2026 · Michael Beckett is the founder of Signbee and B2bee Ltd.