Create a Claude SKILL (a reusable system-prompt / instruction file) that, when given Markdown content, produces a branded PDF document using a standalone Python tool. Claude generates styled HTML, and a Python script powered by Playwright renders it to PDF via headless Chromium — no manual browser interaction required.
pdf-creation.html is a single-page browser app:
- Input: User pastes or drag-drops a
.mdfile into a textarea. - Parsing:
marked.jsconverts Markdown → HTML client-side;mermaid.jsrenders diagrams. - Styling: CSS applies Improving brand identity:
- CSS variables:
--improving-blue: #0054a6,--improving-teal: #0097a7,--improving-light-blue: #e8f4fc H1→ large blue document title (doc-titleclass)- First
H2immediately afterH1→ teal subtitle (doc-subtitleclass) - Subsequent
H2s → white text on a gradient background image, uppercase H3→ teal, uppercase;H4→ dark gray- Tables with blue header row, alternating row stripes
- Blockquotes with teal left border
- Code blocks in light gray
- CSS variables:
- Brand images (in
images/):new-header.png— full-width header graphic on every pagenew-footer.png— full-width footer graphic on every pageh2-background.png— stretched behind every H2 section headerbg-image.png— tiled decorative background at page bottom (50% opacity)
- Print layout:
@pagewith letter size, zero margin; repeating header via<table><thead>, fixed-position footer, page-break avoidance rules.
The SKILL eliminates the client-side editor and runtime Markdown parsing. Claude performs the Markdown → HTML conversion itself, and a standalone Python tool handles the HTML → PDF step automatically via Playwright's headless Chromium.
| Aspect | Current (HTML app) | SKILL + Python Tool |
|---|---|---|
| Markdown parsing | marked.js at runtime |
Claude converts MD → HTML directly |
| Mermaid diagrams | mermaid.js at runtime |
Playwright renders mermaid.js in headless Chromium before PDF capture |
| Editor UI | Textarea + drag-drop | Not needed — removed entirely |
| Brand images | Relative images/ paths |
Base64 data-URIs embedded inline (self-contained) |
| PDF generation | Manual: open browser → Print → Save as PDF | Automatic: Playwright page.pdf() in headless Chromium |
| Output | Live preview + manual print | .pdf file written directly to disk |
IMPLEMENTATION DISCIPLINE — READ THIS FIRST
Follow these rules strictly when executing this plan:
- One checkbox at a time. Complete each
- [ ]item individually. Do NOT combine or batch multiple checkboxes into a single action.- Commit after every checkbox. After completing each checkbox, stage and commit the changes with a descriptive message referencing the phase and step (e.g.,
phase-1: base64-encode brand images).- Push after every phase. After all checkboxes in a phase are complete and committed, push to origin.
- Self-review after every phase. After pushing, carefully examine all code and files produced during that phase. Verify they meet the expectations described in this plan. Check for:
- Correctness: Does the output match what the plan describes?
- Completeness: Are any details from the plan missing?
- Consistency: Does the new code integrate properly with prior phases?
- Quality: Is the code clean, well-structured, and free of obvious issues?
- Fix before moving on. If the self-review identifies any concerns, commit a follow-up fix with the message
phase-N: code review fix — <description>and push again. Do NOT proceed to the next phase until the current phase passes review.- Mark checkboxes as you go. Update this file to change
- [ ]to- [x]for each completed item, included in that item's commit.
- Base64-encode all four brand images (
new-header.png,new-footer.png,h2-background.png,bg-image.png) as data-URIs for self-contained output. - Store these as constants in
src/improving_pdf_tool/assets/images.py.
Create a canonical HTML template that the SKILL instructs Claude to populate:
- Create
<head>with embedded<style>block: brand colors, typography, heading styles, table styles, print rules. - Add
@page { size: letter; margin: 0; }and print-specific rules. - Wire H2 background and page decoration via base64 data-URIs.
- Add conditional Mermaid CDN
<script>tag (only when mermaid blocks are present). - Create
<body>with print-ready structure:<table class="print-table">with<thead>header image,<tbody>content, fixed footer, background decoration. - Add screen preview styles (box shadow, centered page) so the file looks reasonable in a browser.
- Convert all Tailwind utility classes to equivalent plain CSS.
Create the core generator.py and cli.py modules:
- Implement
generator.py: accept HTML file path, launch headless Chromium via Playwright, load the HTML. - Add Mermaid wait logic: poll for
[data-processed]attributes or use a timeout before PDF capture. - Call
page.pdf()withformat="Letter",print_background=True, zero margins (handled by CSS). - Implement
cli.pywith argparse:improving-pdf input.html -o output.pdf. - Add optional
.mdinput mode: convert Markdown using the embedded template, then render to PDF. - Add auto-install of Chromium on first run (
playwright install chromiumif missing).
Write skill.md — the Claude SKILL instruction file:
- Define role: document formatter that produces branded HTML and invokes the Python tool for PDF.
- Document Markdown → HTML conversion rules:
- Standard elements: headings, paragraphs, lists, tables, code blocks, blockquotes, HR, links, images, bold, italic.
- First
# H1→<h1 class="doc-title">. - First
## H2immediately after H1 →<h2 class="doc-subtitle">. - All other
## H2→ standard section header (gradient background via CSS). - Mermaid fenced code blocks →
<div class="mermaid">with unique IDs; include mermaid.js CDN + init script. - Strip HTML comments from Markdown.
- Document template assembly: insert converted HTML into the template.
- Document output format: save HTML to disk, invoke
improving-pdfto produce the PDF. - Document self-bootstrapping: check for package,
pip installfrom GitHub if missing. - Provide user-facing output guidance: path to generated PDF, optionally keep intermediate HTML.
Package the tool for pip install from GitHub:
- Create
pyproject.tomlwith metadata,playwrightdependency, andimproving-pdfCLI entry point. - Create
src/improving_pdf_tool/__init__.pywith__version__. - Bundle brand assets (base64 images) and HTML template as package data.
- Verify install works:
pip install git+https://github.com/improving/improving-pdf.git. - Verify CLI works after install:
improving-pdf input.html -o output.pdf. - Add self-bootstrapping logic to SKILL:
pip show improving-pdf-tool > /dev/null 2>&1 || pip install git+https://github.com/improving/improving-pdf.git. - Confirm Chromium auto-install triggers on first run.
- Tag initial release for reproducible installs (
pip install ...@v1.0.0).
- Verify page-break rules for long documents:
page-break-inside: avoidon paragraphs, list items, tables, code blocks;page-break-after: avoidon headings. - Handle no-content fallback: SKILL prompts for Markdown if none provided.
- Handle
image references in Markdown →<img>tags; note external URLs must be accessible. - Optional: support TOC generation from headings if user requests.
- Ensure SKILL handles being invoked repeatedly in one conversation.
Create the /release Claude command (.windsurf/workflows/release.md):
- Create
.windsurf/workflows/release.mdwith full release workflow. - Verify workflow prompts for bump type (
major,minor,patch). - Verify workflow reads current version from
src/improving_pdf_tool/__init__.py. - Verify workflow updates version in both
__init__.pyandpyproject.toml. - Verify workflow commits, tags, pushes, and creates GitHub release via
gh. - Verify workflow prints install command for new version.
- Verify workflow reminds to update pinned version in
skill.mdif applicable.
- Create
test/sample-input.mdwith the sample content fromloadSampleContent()in the current file. - Generate PDF from sample content and compare side-by-side with current tool's output.
- Verify header and footer appear on every printed page.
- Verify H2 gradient background renders correctly.
- Verify brand colors match (
#0054a6,#0097a7). - Verify page breaks don't split headings from their content.
- Verify Mermaid diagrams render (if present).
- Verify background decoration appears at page bottom.
- Test installed CLI:
improving-pdf test/sample-input.md -o test/result.pdf. - Test self-bootstrap flow: fresh venv → SKILL triggers install → generates PDF.
- Verify Playwright output matches manual Chrome Print to PDF.
- Test
/releaseworkflow: bump a patch version, verify tag and GitHub release are created.
pdf-skill/
├── .windsurf/
│ └── workflows/
│ └── release.md # /release command — cut a new versioned release
├── PLAN.md # This file
├── skill.md # The Claude SKILL instructions
├── pyproject.toml # Python package config + CLI entry point
├── README.md # Setup & usage instructions for coworkers
├── src/
│ └── improving_pdf_tool/
│ ├── __init__.py # Contains __version__
│ ├── cli.py # CLI entry point (improving-pdf command)
│ ├── generator.py # Core HTML→PDF logic (Playwright)
│ ├── template.html # Branded HTML template
│ └── assets/
│ └── images.py # Base64-encoded brand image constants
├── images/ # Original brand assets (source of truth)
│ ├── new-header.png
│ ├── new-footer.png
│ ├── h2-background.png
│ └── bg-image.png
└── test/
├── sample-input.md # Test Markdown input
├── sample-output.html # Expected HTML output for comparison
└── sample-output.pdf # Expected PDF output for comparison
- Base64 images vs. external references — Base64 makes the file self-contained but large (~80KB+ for the header image alone). Alternative: host images at a stable URL and reference them. Recommendation: Base64 for portability, since these are small images.
- Mermaid support — Playwright's headless Chromium executes JavaScript, so mermaid.js renders naturally. The Python tool waits for rendering to complete before capturing the PDF. Recommendation: Keep mermaid.js CDN reference, only included when mermaid blocks are detected.
- Tailwind CSS — The current file loads Tailwind via CDN for utility classes (
px-10,py-6, etc.). The SKILL output should replace these with plain CSS to avoid the CDN dependency. Recommendation: Convert all Tailwind utilities to equivalent inline/embedded CSS. - Screen vs. Print styling — The output file should look presentable when opened in a browser (not just when printed). Include a minimal screen layout that approximates the printed appearance.
- PDF generation engine — Playwright with headless Chromium. Uses the same rendering engine as Chrome's Print to PDF, ensuring pixel-identical output. Decided: Playwright.
- Tool invocation model — The SKILL instructs Claude to save the HTML to disk and then run
improving-pdfto produce the PDF. Claude can do this via a shell command in agentic environments (Windsurf, Claude Code) or provide the user with the command to run manually. - Distribution — Installable Python package from
pip install git+https://github.com/improving/improving-pdf.git. The SKILL self-bootstraps by checking for the package and installing it if absent. Brand assets are bundled inside the package so coworkers need nothing beyond thepip install. Decided: GitHub + pip. - Release process — Automated via
/releaseClaude command. Bumps version, commits, tags, pushes, and creates a GitHub release. RequiresghCLI. Decided: Windsurf workflow.