Export HTML to PDF While Keeping Colors
Keep HTML backgrounds and colors in PDFs with browser settings, print CSS, Puppeteer, and reliable troubleshooting steps.
Short answer: enable Print backgrounds or Background graphics in the print dialog, and add print-color-adjust: exact to the elements whose colors must survive printing. For automated conversion, enable the renderer’s background option as well as the CSS rule. CSS requests exact output, but a user’s print preference or the converter’s settings can still override it.
1. Why colors disappear in a PDF
Browsers commonly optimize print output to save ink or improve legibility. That optimization can remove background colors, gradients, and background images even when the screen version is correct. The CSS default for print-color-adjust is economy, which allows the user agent to change the appearance; exact requests that authored colors and imagery be retained. MDN documents this behavior.
A PDF can also differ because the page has print-specific CSS, because the print dialog disables backgrounds, or because an automated renderer is using its own PDF options. Treat the page styles and the export settings as two separate controls.
2. Save a colorful HTML page to PDF manually
Firefox
- Open the page and choose Print.
- Select Save to PDF as the destination.
- Open More settings.
- Enable Print backgrounds.
- Keep Format set to Original. Firefox removes the background option when Simplified is selected.
- Inspect the preview, then save the PDF.
Firefox’s print help explains that web pages may deliberately use different print styles, so the PDF is not always identical to the screen rendering. See Mozilla’s printing instructions.
Other browsers
Look for a setting named Print backgrounds, Background graphics, or similar. Names and locations change between browsers and versions. Always verify the preview; if the preview has no colors, the saved PDF will not either.
3. Add print CSS when you control the HTML
Put this rule in the page stylesheet:
@media print {
.preserve-color {
print-color-adjust: exact;
-webkit-print-color-adjust: exact;
}
}
Apply the class to the elements whose backgrounds carry meaning:
<section class="preserve-color invoice-header">
<h1>Invoice</h1>
</section>
Use it selectively when possible. A page with large decorative backgrounds can produce a much larger PDF and consume more printer ink. The declaration is a request to the rendering engine; a print setting that disables backgrounds still wins.
Complete minimal example
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Color PDF example</title>
<style>
body { font-family: sans-serif; margin: 0; }
.hero {
background: #173b73;
color: white;
padding: 48px;
}
.card {
background: #e8f1ff;
border: 2px solid #4d83d1;
margin: 24px 48px;
padding: 24px;
}
@media print {
.preserve-color {
print-color-adjust: exact;
-webkit-print-color-adjust: exact;
}
}
</style>
</head>
<body>
<header class="hero preserve-color">
<h1>Quarterly report</h1>
</header>
<section class="card preserve-color">
Background colors should remain in the exported PDF.
</section>
</body>
</html>
4. Generate the PDF with Puppeteer
Puppeteer’s page.pdf() uses print CSS media. Set the PDF background option and keep the prefixed color-adjust rule in the document. The official API reference is Puppeteer’s page.pdf() documentation.
import puppeteer from 'puppeteer';
const browser = await puppeteer.launch({headless: true});
try {
const page = await browser.newPage();
await page.goto('http://localhost:3000/report.html', {
waitUntil: 'networkidle0'
});
await page.pdf({
path: 'report.pdf',
format: 'A4',
printBackground: true,
preferCSSPageSize: true
});
} finally {
await browser.close();
}
printBackground: true enables background painting in the PDF. preferCSSPageSize lets declared CSS page dimensions take precedence when you use @page. Choose margins, orientation, and page ranges according to your document; then inspect the actual PDF produced by your deployment pipeline.
Wait for fonts, images, and application data
await page.goto(url, {waitUntil: 'networkidle0'});
await page.evaluate(() => document.fonts.ready);
await page.waitForSelector('#report-ready');
await page.pdf({path: 'report.pdf', printBackground: true});
Without these waits, a page can be converted before web fonts, lazy images, or client-rendered colors exist.
5. Other conversion tools
Gotenberg’s Chromium conversion documentation identifies printBackground, omitBackground, and the document’s CSS as controls that affect the result. Ensure background printing is enabled and background omission is disabled in the request. See Gotenberg’s HTML-to-PDF documentation.
Adobe Acrobat’s web-page conversion settings include Retain page background, along with page size, orientation, margins, and wide-content scaling. See Adobe’s conversion settings.
6. A practical decision checklist
| Situation | Use | Color control |
|---|---|---|
| One-off export | Browser print dialog | Enable Print backgrounds and verify preview |
| You own the HTML | Print CSS plus browser or renderer | Add print-color-adjust: exact; enable backgrounds in the tool |
| Repeatable server workflow | Puppeteer, Gotenberg, or another Chromium pipeline | Set the renderer background option and wait for page readiness |
| Desktop conversion with layout controls | Acrobat web conversion | Enable Retain page background |
- Check the print preview before saving.
- Confirm the correct print media styles are active.
- Enable background graphics in the export tool.
- Wait for fonts, images, and client-side rendering.
- Open the resulting PDF in more than one viewer when color accuracy matters.
7. Troubleshooting missing colors
The preview is black and white or has white panels
Cause: background printing is disabled. Fix: enable Print backgrounds or Background graphics, then regenerate the preview.
The browser option is missing
Cause: Firefox is using Simplified format. Fix: change Format to Original; Mozilla documents that Simplified removes Print backgrounds.
CSS has print-color-adjust: exact, but colors are still absent
Cause: the user or renderer disabled backgrounds. Fix: enable the print-dialog or PDF-tool background option. CSS cannot override that preference.
Only some sections lose their colors
Cause: a print stylesheet overrides those selectors, or the class is applied to a parent that does not own the background. Fix: inspect computed styles under print media and apply the rule directly to the colored element.
Colors are present but images or gradients are missing
Cause: background images may be blocked, lazy-loaded, or omitted by the renderer. Fix: wait for the relevant selector or network activity, confirm the asset URL is reachable from the conversion environment, and keep background printing enabled.
The automated PDF is blank or incomplete
Cause: conversion began before client-side content finished rendering. Fix: wait for network idle, fonts, and an application-specific ready selector before calling page.pdf().
8. Performance, reliability, and cost considerations
Background-heavy pages require more rendering and can create larger PDFs. Limit decorative backgrounds when file size matters, use optimized assets, and avoid waiting for an unbounded network-idle condition on pages with analytics or long polling. For reliable automation, use a deterministic ready selector, fixed viewport and page size, and a retry policy for transient navigation failures. Compare the generated PDF in the same renderer used in production; output can vary between browser versions.
For manual browser exports, the cost is usually the time spent reviewing the preview. For server conversion, account for browser startup, memory, fonts, image downloads, and retries when sizing workers. No universal quality or speed benchmark applies across pages and renderers.
9. Or skip the browser setup
ScreenshotNeo provides a website capture API and MCP server. Its capture workflow can produce PDFs while handling the page-loading work for you; see the API documentation for PDF options.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
Cookie and consent banners, newsletter popups, and chat widgets are removed before the shot. Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed; the response identifies the verdict and billing status in X-Page-Verdict and X-Billed headers. An MCP server lets Claude, Cursor, and other MCP clients call take_screenshot, get_page_info, and capture_pdf. The Free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 shots.
Create a free ScreenshotNeo account to get started.
10. FAQ
Does print-color-adjust: exact guarantee identical screen and PDF colors?
No. It requests preservation, but print preferences, renderer behavior, color profiles, and print-specific CSS can still change output.
Should I use RGB or CMYK CSS colors?
For browser PDF export, use normal CSS colors and validate the resulting PDF. The main issue is usually background-printing settings, not the color notation.
Why do text colors survive while backgrounds do not?
Print optimization commonly keeps foreground text for readability while dropping decorative or large-area backgrounds.
Can I fix a third-party page I cannot edit?
Use the browser’s background-print option or a conversion tool that exposes one. You cannot add print CSS to a page you do not control unless your pipeline injects styles before conversion.
What should I verify before shipping a PDF feature?
Test representative pages with gradients, background images, lazy content, custom fonts, dark sections, and long tables in the exact browser or service version used in production.


