PDF / Print optimization — current state & uplift options

Date: 2026-05-20 · Surface: pa_claim_cockpit_render.py (claim cockpit at reports.dare.co.uk/dare_claim_cockpit_*) · Status: Parked sketch — current implementation shipped, uplift options documented for future return

Dan’s question, 2026-05-20:

“Let me ask a question, the print-to-pdf has some adjusted code within it, parameters etc, or is it just a straight-up convert (vanilla)?”

This report is the honest answer: the button is vanilla, the CSS is custom — but lightly so. It documents what’s there, what’s not, and what to add when paper output becomes load-bearing.


What’s currently in the cockpit (shipped 2026-05-20)

The button (vanilla)

<button type="button" class="print-btn" onclick="window.print()">🖨 Print this cockpit</button>

Triggers the browser’s native print dialog. Same code path that produces “Save as PDF” in macOS Print → PDF → Save. No JavaScript library, no PDF generator, no headless Chrome. Just window.print().

The CSS (custom — but the light end of what’s possible)

22 lines of @media print rules layered into the existing stylesheet:

STASH5

What this actively rewrites for paper

Aspect Screen Print
Background Cream --bg White (ink-economical)
Deadline bar Sticky at top of viewport Static, one-time at top of page 1
Print button Visible Hidden
Continue-in-claude link Cream-red accent CTA Hidden (paper recipient can’t click)
“Mark done” buttons on actions Visible Hidden
Action cards Free flow break-inside: avoid — no card split across pages
Heading at page bottom Possible orphan break-after: avoid — heading sticks to its content
Folder grid Flex layout Kept together
Anchors Cream-red, no underline Black, underlined
em italic accent Cream-red Black italic

So: the layout is meaningfully restructured for paper. Not vanilla.


What’s NOT customized — uplift options for later

These are the levers I deliberately did NOT pull, in order of impact:

1. @page rules (no margins, no size, no named layouts)

Currently the browser uses default margins (~0.5in on macOS Chrome, ~0.75in on Safari, variable on Firefox). For more control:

@page {
  size: letter portrait;  /* or A4 portrait */
  margin: 0.6in 0.5in;
}
@page :first {
  margin-top: 0.4in;  /* tighter for the first page so deadlines stay above the fold */
}

When to add: if paper output gets shared across geographies (UK vs US default paper differs), or if Dan needs predictable page breaks for binding/filing.

2. Page numbers + running headers

@page {
  @top-right { content: counter(page) " of " counter(pages); font: 9pt sans-serif; color: #666; }
  @bottom-left { content: "Claim 046414618 · Liberty Mutual"; font: 9pt sans-serif; color: #666; }
}

When to add: when the cockpit gets long enough that page numbers matter (currently fits on ~3 pages so probably overkill — until you start including all the analyst sections too).

Caveat: @page margin boxes have spotty browser support. Chrome + Safari yes; Firefox limited. If Audrey prints from a non-Chrome browser, this might not render.

A paper-specific best practice — show the URL next to each link so the recipient knows where it goes:

@media print {
  a[href]::after {
    content: " (" attr(href) ")";
    font-size: 0.85em;
    color: #666;
    word-break: break-all;
  }
  /* Suppress for internal anchors and short hrefs */
  a[href^="#"]::after,
  a[href^="javascript"]::after { content: ""; }
}

When to add: if the cockpit starts including more outbound links (to specific emails, claim documents, etc.) that the paper recipient would want to type or search later.

4. Print-tuned typography

Screen vs paper readability differs. Paper wants slightly larger body text, tighter line height, more contrast:

@media print {
  body { font-size: 11pt; line-height: 1.45; }
  h1 { font-size: 22pt; margin-bottom: 0.3em; }
  h2 { font-size: 15pt; margin-top: 1em; }
  .deadline-pill { font-size: 9pt; }
}

When to add: if Audrey says the paper version is hard to read at default sizes. Right now we’re inheriting screen sizes which print at ~12-14pt depending on browser zoom.

5. Force page break before specific sections

If you want each major section to start on a fresh page:

@media print {
  h2 { break-before: page; }
  /* Or selectively */
  .actions-section { break-before: page; }
}

When to add: if the cockpit grows enough that each section deserves its own page. Currently it’s compact enough that forced breaks would waste paper.

6. Hide-on-print marker attribute

A scaling pattern as more interactive elements get added: use a data-print attribute to opt-in/opt-out rather than CSS selector lists:

<button data-print="hide">Mark done</button>
<a data-print="show" href="...">Continue in claude.ai</a>
@media print {
  [data-print="hide"] { display: none !important; }
  [data-print="show"] { display: inline-flex !important; }
}

When to add: when the action-row picks up 3+ buttons and the current “list everything in .print-btn, .claude-link” CSS selector gets unwieldy.

7. PDF metadata (title, author, subject)

Browser print dialog doesn’t expose this directly, but if we wanted programmatic PDF generation (Chrome headless, Puppeteer, Playwright):

chrome --headless --print-to-pdf=cockpit.pdf \
       --print-to-pdf-no-header \
       --no-pdf-header-footer \
       https://reports.dare.co.uk/dare_claim_cockpit_046414618_2026-05-20

When to add: when the cockpit needs to be auto-PDFed for archival (e.g. a snapshot on every analyst-pass run), not just user-triggered print.


Architecture decision: why no JS-based PDF generator?

Considered + rejected: libraries like jsPDF, html2pdf.js, pdfmake. Reasons:

Approach Pros Cons Verdict
window.print() + @media print CSS (shipped) Zero dependencies. Uses browser’s renderer (correct fonts, layout, links live as clickable PDF links). User picks paper-or-PDF in their print dialog. Less programmatic control. PDF metadata limited. Chosen — simplest + leverages existing browser capability
html2pdf.js (HTML2Canvas + jsPDF) Pure browser, no server. Programmatic. Renders to canvas → rasterized output (no live links, no text selection, blurry). Bundle adds ~200KB. Rejected — quality regression
pdfmake Vector output. Programmatic. Requires re-writing layout in pdfmake’s DSL (not HTML). Bundle ~700KB. Rejected — too much rebuild cost for one page
Headless Chrome / Puppeteer (server-side) Pixel-perfect, full CSS support, vector output. Needs a server/Worker with browser runtime. Currently Cloudflare Workers don’t have one (Browser Rendering API exists but adds infra). Park for later — if auto-PDF archival becomes a need

Verdict: window.print() + @media print is the right primitive. It’s what the platform already does well, and the customization knobs are CSS — same vocabulary as the screen styles, no new toolchain.


When to come back to this

Resume conditions:

  1. Audrey reports the printed cockpit is hard to read → add point 4 (typography)
  2. Paper recipient asks “where does this link go?” → add point 3 (URLs beside links)
  3. The cockpit grows past ~5 pages → add points 1 + 2 (@page rules + page numbers)
  4. You want auto-PDF on every analyst pass → add point 7 (headless Chrome via Browser Rendering API on Worker)
  5. The action-row picks up 3+ buttons → refactor to data-print attribute (point 6)

None of these is needed today. The current 22-line @media print block carries decision-grade content from screen to paper without losing structure, hides the operator-only chrome, and prevents card-splitting at page breaks. Good enough for “print for Audrey.”


Sibling memories

The aphorism

The browser already knows how to print. Don’t bring a PDF library — bring a @media print block. The platform pays you back when you work with it.

Source: parked_sketch_pdf_print_optimization_2026-05-20.md · Rendered 2026-05-20 11:40