Convert markdown to a beautifully styled, shareable HTML page and upload to S3-compatible storage
npx skills add https://github.com/alexknowshtml/claude-skills --skill pretty-page
Convert markdown content into a beautifully styled, shareable HTML page using the JFDI design system (Risograph-inspired aesthetic). Renders locally and uploads to any S3-compatible host.
/pretty-page <file-path-or-inline-content> [--title "Page Title"] [--slug custom-slug] [--nav '<JSON>'] [--wide]
--wide — widens the content column from 720px to 1100px. Use for table-heavy pages so wide tables render without horizontal scrolling.
render.py and template.htmlupload.pyInstead of posting raw .md files or sending markdown attachments, use this to create a readable, styled page that anyone can open in a browser.
Good for:
The MD is always the source of truth. The pretty-page is a rendered artifact.
.md first, then regenerate..md files, always verify URLs exist before writing them.Install the Python dependency:
pip install boto3
Set these environment variables (add to your .env or shell profile):
# Required
export S3_BUCKET=your-bucket-name
export AWS_ACCESS_KEY_ID=your-key
export AWS_SECRET_ACCESS_KEY=your-secret
# Optional — for non-AWS providers (DO Spaces, Cloudflare R2, Backblaze B2)
export S3_ENDPOINT_URL=https://nyc3.digitaloceanspaces.com
export AWS_DEFAULT_REGION=nyc3
# Optional — prefix for uploaded files (default: "pretty-page/")
export S3_PREFIX=public/
# Optional — override public URL base (e.g. your CDN domain)
export S3_PUBLIC_BASE_URL=https://cdn.yourdomain.com
The template uses the JFDI design system:
Markdown - [ ] and - [x] render as styled, clickable HTML checkboxes. State persists in browser localStorage (keyed by page title slug).
A notice appears under each h2 section containing checkboxes: "Checkbox changes are stored locally on your device, not synced with other devices or people."
Floating button in the top-right corner copies the full raw markdown source to clipboard. Shows "Copied!" feedback with green styling for 2 seconds.
#section-name), the auto-TOC is suppressed and the manual version is used instead[+]/[-] toggle — starts open, state persists in localStorageOn wide-screen displays (>1300px), a fixed sidebar TOC appears on the left side of pages with 4+ h2 headings. It highlights the active section as you scroll.
All h3 headings are collapsible — click the heading to toggle the section. State persists in localStorage.
Pass --nav with a JSON array of {label, url, active} objects to add a fixed nav bar:
NAV='[{"label":"Home","url":"https://example.com/","active":true},{"label":"Docs","url":"https://example.com/docs"}]'
python3 render.py source.md --nav "$NAV"
All h2/h3/h4 headings get slug-based id attributes for in-page linking. scroll-margin-top provides breathing room when jumping to anchors.
Consecutive Bold: lines at the start of content are wrapped in a styled .metadata div. First line renders slightly larger for visual hierarchy. Lists following bold labels are included in the box.
Use blockquotes to create visually distinct cards — great for staff rosters, contact profiles, or any list of items that need their own box.
Pattern — header + bullets + connections paragraph:
> **Name** — Title
> - Career stop 1
> - Career stop 2 (current role)
> - Education
>
> *Connections:* Cross-reference notes as prose here.
How it renders:
Name — Title) → styled as a card-header (1.1em, semi-bold)<ul><li> list> line separates paragraphs within the same card>) between cards creates separate blockquote boxesEvery --- in the markdown generates a styled back-to-top link + <hr> in the rendered HTML on archive-style pages (3+ HRs). Use --- sparingly — only between major ## sections.
The render script derives its output filename from the document's H1 slug. Use --slug to control the name:
python3 render.py source.md --slug my-slug
# → writes /tmp/pretty-page-my-slug.html
Block-level lines starting with an HTML tag (<div ...>, </div>, etc.) pass through render.py unchanged, enabling template components to be embedded directly in markdown sources.
The template ships CSS for a Gmail-style email card (.gmail-card):
<div class="gmail-card">
<div class="gmail-header">
<div class="gmail-subject">Subject line here</div>
<div class="gmail-meta">
<div class="gmail-avatar">A</div>
<div class="gmail-sender-info">
<div class="gmail-sender-name">Sender Name</div>
<div class="gmail-sender-email">[email protected]</div>
<div class="gmail-to">to Recipient</div>
</div>
<div class="gmail-date">Apr 7, 2026</div>
</div>
</div>
<div class="gmail-body">
<p>Body paragraphs as HTML.</p>
</div>
</div>
Rules: every line of the block must start with < (passthrough is line-based); body content must be pre-converted to HTML (<p>, <ul>/<li>, <a>).
For system documentation, architecture explanations, or any page where the sequence or timing is the point, you can add interactive JS animations. These are hand-authored in HTML rather than generated from markdown — build the page as a standalone .html file, use the JFDI design system CSS variables (--paper, --ink, --red, --blue, --yellow, --green) and fonts, then upload directly.
Key JS rule: All choreographed animations use async/await + const sleep = ms => new Promise(r => setTimeout(r, ms)). Never callback chains.
The footer is set in render.py:
html = html.replace('{{FOOTER}}', 'Prepared by <a href="https://example.com">Your Name</a>')
Edit this line to personalize the footer for your deployment.
render.py to convert markdown to HTML using template.html python3 render.py source.md --slug my-slug
# Script writes to /tmp/pretty-page-my-slug.html and prints that path
grep -oP 'https?://[^)"\s]+' file.md | sort -u | while read url; do
code=$(curl -s -o /dev/null -w "%{http_code}" -L --max-time 10 "$url")
echo "$code $url"
done
upload.py to upload and get a public URLIntegration with protocols.io API for managing scientific protocols. This skill should be used when working with protocols.io to search, create, update, or publish protocols; manage protocol steps and materials; handle discussions and comments; organize workspaces; upload and manage files; or integrate protocols.io functionality into workflows. Applicable for protocol discovery, collaborative protocol development, experiment tracking, lab protocol management, and scientific documentation.
Analyzes job descriptions and generates tailored resumes that highlight relevant experience, skills, and achievements to maximize interview chances
Generate Excalidraw diagrams from natural language descriptions. Use when asked to "create a diagram", "make a flowchart", "visualize a process", "draw a system architecture", "create a mind map", or "generate an Excalidraw file". Supports flowcharts, relationship diagrams, mind maps, and system architecture diagrams. Outputs .excalidraw JSON files that can be opened directly in Excalidraw.
Build and distribute Expo development clients locally or via TestFlight
Use when you have a written implementation plan to execute in a separate session with review checkpoints
Data structure for annotated matrices in single-cell analysis. Use when working with .h5ad files or integrating with the scverse ecosystem. This is the data format skill—for analysis workflows use scanpy; for probabilistic models use scvi-tools; for population-scale queries use cellxgene-census.
Benchling R&D platform integration. Access registry (DNA, proteins), inventory, ELN entries, workflows via API, build Benchling Apps, query Data Warehouse, for lab data management automation.
Comprehensive molecular biology toolkit. Use for sequence manipulation, file parsing (FASTA/GenBank/PDB), phylogenetics, and programmatic NCBI/PubMed access (Bio.Entrez). Best for batch processing, custom bioinformatics pipelines, BLAST automation. For quick lookups use gget; for multi-service integration use bioservices.
Take alexknowshtml/pretty-page from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.
The instructions reference pip.
Without those the skill loads but fails at the first command.