Agent skill
export-html
Convert a published or drafted article markdown file into a beautiful self-contained HTML file with all images embedded as base64. Use after write-article or publish is complete, when user wants a single HTML file for easy copy-paste to Substack or Medium.
Install this agent skill to your Project
npx add-skill https://github.com/majiayu000/claude-skill-registry/tree/main/skills/other/other/export-html
SKILL.md
Export to Self-Contained HTML Skill
Input
$ARGUMENTS = topic name (must have drafts/.md or published/.md)
Process
Step 1: Find the Article
Check for the article in this order:
- published/.md (preferred, final version)
- drafts/.md (fallback)
Read the markdown file completely.
Step 2: Collect All Images
Find every image reference in the markdown:
- Pattern:
 - Extract all image file paths
- Read each image from figures/output/ and convert to base64
Use this bash command for each image:
base64 -w 0 figures/output/<fig_name>.png
On Windows (if base64 command fails), use Python:
python -c "import base64; print(base64.b64encode(open('figures/output/<fig_name>.png','rb').read()).decode())"
Step 3: Generate the HTML File
Create a single .html file with ALL images embedded as base64 data URIs.
The HTML must follow this exact template structure:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{ARTICLE TITLE}</title>
<style>
/* Import clean fonts */
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap');
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
font-size: 18px;
line-height: 1.75;
color: #1a1a1a;
background: #ffffff;
max-width: 760px;
margin: 0 auto;
padding: 40px 24px 80px;
}
/* Title */
h1 {
font-size: 2.2em;
font-weight: 700;
line-height: 1.2;
margin-bottom: 8px;
color: #111111;
letter-spacing: -0.02em;
}
/* Subtitle */
.subtitle {
font-size: 1.2em;
color: #555555;
margin-bottom: 32px;
line-height: 1.5;
}
/* Section headings */
h2 {
font-size: 1.6em;
font-weight: 700;
margin-top: 48px;
margin-bottom: 16px;
color: #111111;
letter-spacing: -0.01em;
}
h3 {
font-size: 1.25em;
font-weight: 600;
margin-top: 32px;
margin-bottom: 12px;
color: #222222;
}
/* Body text */
p {
margin-bottom: 16px;
}
/* Bold text */
strong {
font-weight: 600;
color: #111111;
}
/* Links */
a {
color: #2563eb;
text-decoration: underline;
text-underline-offset: 2px;
}
/* Bullet lists */
ul, ol {
margin-bottom: 16px;
padding-left: 28px;
}
li {
margin-bottom: 8px;
}
/* Figures and images */
figure {
margin: 32px 0;
text-align: center;
}
figure img {
max-width: 100%;
height: auto;
border: 1px solid #e5e5e5;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0,0,0,0.06);
}
figcaption {
font-size: 0.88em;
color: #666666;
margin-top: 12px;
line-height: 1.5;
text-align: center;
font-style: italic;
}
/* Callout boxes */
blockquote {
background: #f0f7ff;
border-left: 4px solid #2563eb;
padding: 20px 24px;
margin: 24px 0;
border-radius: 0 8px 8px 0;
font-size: 0.95em;
}
blockquote p {
margin-bottom: 8px;
}
blockquote p:last-child {
margin-bottom: 0;
}
/* Code blocks */
pre {
background: #1e1e2e;
color: #cdd6f4;
padding: 24px;
border-radius: 8px;
overflow-x: auto;
margin: 24px 0;
font-family: 'JetBrains Mono', 'Fira Code', monospace;
font-size: 0.85em;
line-height: 1.6;
}
code {
font-family: 'JetBrains Mono', 'Fira Code', monospace;
font-size: 0.88em;
}
/* Inline code */
p code, li code {
background: #f3f4f6;
padding: 2px 6px;
border-radius: 4px;
color: #d63384;
font-size: 0.85em;
}
/* Horizontal rule (section divider) */
hr {
border: none;
border-top: 1px solid #e5e5e5;
margin: 48px 0;
}
/* Equation blocks */
.equation {
text-align: center;
font-size: 1.1em;
margin: 24px 0;
padding: 16px;
background: #fafafa;
border-radius: 8px;
}
/* "This article covers" box */
.covers-box {
background: #f8fafc;
border: 1px solid #e2e8f0;
border-radius: 8px;
padding: 24px;
margin: 24px 0;
}
.covers-box strong {
font-size: 1.05em;
}
/* CTA at the end */
.cta {
background: #f0f7ff;
border-radius: 12px;
padding: 32px;
margin-top: 48px;
text-align: center;
}
.cta p {
margin-bottom: 0;
font-size: 1.05em;
}
/* Further reading */
.further-reading {
background: #fafafa;
border-radius: 8px;
padding: 24px;
margin-top: 32px;
}
.further-reading h2 {
margin-top: 0;
font-size: 1.3em;
}
/* Print styles */
@media print {
body {
max-width: 100%;
padding: 0;
font-size: 14px;
}
pre {
white-space: pre-wrap;
}
}
</style>
</head>
<body>
{CONVERTED HTML CONTENT WITH BASE64 IMAGES}
</body>
</html>
Step 4: Convert Markdown to HTML Content
Process the markdown into HTML:
- Title (# heading) becomes
<h1>at the top - Section headings (## heading) become
<h2> - Subsection headings (### heading) become
<h3> - Paragraphs become
<p>tags - Bold text (text) becomes
<strong> - Inline code (
code) becomes<code> - Code blocks (```) become
<pre><code>with dark theme - Bullet lists become
<ul><li> - Numbered lists become
<ol><li> - Blockquotes (> text) become
<blockquote> - Images become
<figure>with embedded base64:
<figure>
<img src="data:image/png;base64,{BASE64_DATA}" alt="{caption}">
<figcaption>{caption}</figcaption>
</figure>
- Horizontal rules (---) become
<hr>
Step 5: Write the HTML Using Python
Since the HTML will be large (base64 images), use a Python script to:
- Read the markdown
- Convert to HTML (use Python's markdown library if available, or do manual conversion)
- Replace image paths with base64 data URIs
- Wrap in the HTML template
- Save to published/.html
pip install markdown --break-system-packages 2>/dev/null
python build_html.py
If the markdown library is not available, do manual regex-based conversion. The most important thing is that images are base64 embedded and the styling is clean and professional.
Step 6: Verify the Output
- Check the HTML file size (should be large due to base64 images)
- Count the number of embedded images (should match figure count)
- Open the HTML file in a browser to visually verify rendering
Report to the user:
HTML Export Complete
=====================
File: published/<topic-slug>.html
Size: X MB
Images embedded: 35 of 35
Word count: X words
To use:
1. Open the HTML file in your browser
2. Ctrl+A to select all content
3. Ctrl+C to copy
4. Paste into Substack editor
OR
Open in browser and use as standalone article
Important Rules
- ALL images must be embedded as base64. No external file references.
- The HTML must be self-contained. Opening it in any browser should render the complete article with all images, no internet needed.
- Use clean, modern styling that looks good on Substack.
- Maintain the exact order of content from the markdown.
- Every figure must have a visible caption below it.
- Code blocks must use dark theme for readability.
- The file will be large (likely 10-30 MB) due to base64 images. This is expected and fine.
Output
Save to published/.html Report file size and image count to user.
Recommended Agent Skills
Expand your agent's capabilities with these related and highly-rated skills.
agent-ops-spec
Manage specification documents in .agent/specs/. Use when user provides requirements, acceptance criteria, or feature descriptions that need to be tracked and validated against implementation.
agent-ops-state
Maintain .agent state files. Use at session start, after meaningful steps, and before concluding: read/update constitution/memory/focus/issues/baseline consistently.
agent-ops-spec
Manage specification documents in .agent/specs/. Use when user provides requirements, acceptance criteria, or feature descriptions that need to be tracked and validated against implementation.
agent-ops-testing
Test strategy, execution, and coverage analysis. Use when designing tests, running test suites, or analyzing test results beyond baseline checks.
agent-ops-testing
Test strategy, execution, and coverage analysis. Use when designing tests, running test suites, or analyzing test results beyond baseline checks.
agent-ops-state
Maintain .agent state files. Use at session start, after meaningful steps, and before concluding: read/update constitution/memory/focus/issues/baseline consistently.
Didn't find tool you were looking for?