Full-stack software engineer building useful tools. Constantly learning, with a habit of connecting dots across different domains.
A friend asked me if I could help him print an HTML file. He said he had already tried everything online, but nothing worked. I figured, why not give it a shot?
The first thing I tried was Chrome’s built-in print option. It only printed the first page. Then I moved on to online tools like iLovePDF, CloudConvert, and a bunch of others, but they all had the same issue: only the first page was getting printed.
Since we’re in the era of LLMs, I turned to them next. Unsurprisingly, they suggested the same tools I had already tried. I experimented with a few lesser-known ones as well, but the result didn’t change.
So, I decided to lean into my dev skills. I opened AI-assisted IDEs like Cursor and Google AntiGravity and asked for help. I tested this across several models and harnesses: Gemini 3.1 Pro and Gemini 3 Flash using AntiGravity, and Opus 4.6 using both Cursor and AntiGravity, as well as Composer inside Cursor.
Their first approach was predictable: use Puppeteer to spin up a headless browser and trigger printing. That didn’t work either. Then they pivoted to taking full-page screenshots and converting those into PDFs. This involved quite a bit of code. Initially, the output wasn’t even a proper PDF, and the layout was totally broken. After multiple iterations, the alignment improved, but it still wasn’t right.
Next, they suggested DocRaptor. I even signed up and got an API key, expecting that a paid service would finally solve it. But again, same issue.
Up to this point, I hadn’t really given the models any meaningful direction. Then I had a realisation. I opened the browser’s developer tools, inspected the DOM, and noticed the root div was locked tightly to the viewport. Puppeteer wasn’t failing; it was just accurately capturing a layout that was fundamentally hostile to pagination.
I told the LLMs to stop writing Python and Node scripts and focus entirely on fixing the CSS styling, since the app behaved exactly like a Single Page Application (SPA).
That’s when things finally clicked. Cursor’s Composer was the first model to give me an HTML file that finally printed all the pages correctly.
Modern web apps are built to behave like native desktop applications, not like documents meant for printing. To achieve this, developers rely on CSS techniques that don’t play well with traditional paginated layouts:
height: 100vh) keeps the app constrained to a single screen height.overflow: hidden) ensures clean layouts by clipping anything outside containers.position: absolute) is often used for performance, especially in dynamic layouts.When the browser’s print engine processes such a page, it sees a container that explicitly says: “I am exactly one screen tall, and anything beyond that should be hidden.” So naturally, it prints just one page.
If you try to simply remove these constraints, another issue appears: elements that rely on absolute positioning lose their reference points and collapse on top of each other.
The fix is to inject print-specific CSS overrides using @media print.
Step 1: Break the single-page constraint. Force the main layout containers to behave like normal block elements and allow content to flow naturally across pages:
@media print {
#wrapper, .main-container, .app-root {
display: block !important;
width: 100% !important;
height: auto !important;
max-height: none !important;
min-height: 0 !important;
overflow: visible !important;
}
}
This removes the height restrictions and allows the content to span multiple pages.
Step 2: Fix overlapping elements. Reset positioning so elements fall back into the natural document flow:
@media print {
#wrapper, .main-container {
position: static !important;
transform: none !important;
}
}
The key here is position: static. It neutralizes absolute, fixed, and sticky positioning, preventing layout collapse.
If you ever run into this problem, prompting an LLM with these strict constraints can save you hours of trial and error. Just copy and paste this:
I have a saved HTML file from a web app (Single Page Application). When I print it to PDF in the browser, the output is broken—it either cuts off after one page, has overlapping sidebars, loses background colors, or layouts shift.
Inspect the HTML and inject a single
<style>block before</head>that fixes all print issues. Follow these rules strictly:
- CSS only: Inside
@media print { }, no JavaScript.- Unlock page flow: Set
height: auto,min-height: 0,max-height: none,overflow: visibleonhtml,body,main,article,section, and any app-specific root wrapper IDs/classes you find in the HTML.- Unlock scroll containers: Override any element with inline
overflow: hiddenoroverflow: autostyles.- Hide UI clutter: Use
display: noneonnav,aside,header, and any sidebar-specific selectors you find. Do NOT useposition: staticfor this—that causes overlap. Do NOT use broad wildcard attribute selectors like[class*="nav"], as they will accidentally hide the main content. Hide all modals, tooltips, and dialogs.- Preserve colors: Apply
print-color-adjust: exactand-webkit-print-color-adjust: exactglobally.- Prevent bad page breaks: Apply
page-break-inside: avoidonh1–h6,img,figure,table,tr,td,li. Do NOT apply it topordiv, as that creates huge whitespace gaps.- Target carefully: Do NOT apply
height: autoto alldivelements, as it breaks icons, flex items, progress bars, and small UI components. Only target the top-level layout wrappers.Output the modified HTML file with only the CSS change, nothing else altered.
This entire debugging session highlighted something fascinating: different LLMs and even different harnesses for the same model have vastly different problem-solving defaults. Some try to solve everything internally, while others immediately reach for external scripts and tooling.
I plan to explore this specific behaviour in a future write-up, but for now, this small printing task turned out to be a surprisingly deep dive into both CSS constraints and AI reasoning.