export-to-html | Skill Performance & Reviews | TopRankSkills

TopRank Skills

Home / Skills / tools / export-to-html

export-to-html

maintained by johncarpenter

star 0 account_tree 0 verified_user MIT License
bolt View GitHub

name: export-to-html description: > Export a markdown file to a self-contained HTML folder using Jinja templates. Creates an output folder with HTML, CSS, and image assets from the template. Supports metadata like title, author, client, date via YAML frontmatter or arguments. Triggers: "export to html", "render markdown", "convert to html", "make html", "export document", "render document", "html export".

Export to HTML

Export markdown files to self-contained HTML folders with all assets included.

Usage

/export-to-html <markdown-file> [template] [options]

Arguments

Argument Required Default Description
markdown-file Yes - Path to the source markdown file
template No 2lines-external Template directory name

Options (passed as context)

Option Description
--title Document title (overrides frontmatter)
--subtitle Document subtitle or one-line description
--author Author name (default: "John Carpenter, 2Lines Software")
--client Client name (for external docs)
--date Document date (default: today, e.g., "February 2026")
--version Document version (default: "1.0")
--classification Classification level (default: "Confidential")
--output Output folder path (default: same location as source, named after file)

Examples

/export-to-html clients/Circuit/proposal.md
/export-to-html research/technical-spike.md --title "Technical Analysis" --client "Acme Corp"
/export-to-html pipeline/rfp-response.md 2lines-external --output exports/rfp

Output Structure

The skill creates a self-contained folder with all necessary assets:

<document-name>/
├── index.html      # Rendered HTML document
├── style.css       # Stylesheet from template
└── logo.svg        # Logo and other images from template

Example: For clients/Circuit/proposal.md, output is:

clients/Circuit/proposal/
├── index.html
├── style.css
└── logo.svg

Available Templates

Template Directory Purpose
2lines-external _templates/2lines-external/ External documents (proposals, reports, deliverables)

Template Structure

Each template directory contains:

_templates/<template-name>/
├── base.html       # Main template with cover page, TOC, and body structure
├── style.css       # Stylesheet (copied to output folder)
└── logo.svg        # 2Lines logo (copied to output folder)

The 2lines-external template includes:

  • Cover page with logo, title, subtitle, client/author metadata
  • Table of contents section (optional)
  • Running page headers with logo and document title
  • Callout boxes (info, warning, success) for highlighting key information
  • Print-optimized styles for PDF generation via browser

Workflow

Step 1: Parse Arguments

Extract the markdown file path, template name, and any metadata options from the user's request.

# Example parsing
markdown_file = "clients/Circuit/proposal.md"
template_name = "2lines-external"  # default
options = {
    "title": "Project Proposal",
    "client": "Circuit",
    "classification": "Confidential"
}

Step 2: Read Markdown File

from pathlib import Path

# Read the source markdown
md_path = Path(markdown_file)
if not md_path.is_absolute():
    md_path = Path("/Users/john/Documents/Workspace/2Lines/knowledge-base") / md_path

md_content = md_path.read_text()

Step 3: Parse YAML Frontmatter (if present)

If the markdown file has YAML frontmatter, extract metadata:

import re

frontmatter = {}
content = md_content

# Check for YAML frontmatter
if md_content.startswith('---'):
    match = re.match(r'^---\n(.*?)\n---\n', md_content, re.DOTALL)
    if match:
        import yaml
        frontmatter = yaml.safe_load(match.group(1)) or {}
        content = md_content[match.end():]

Step 4: Convert Markdown to HTML

import markdown

md = markdown.Markdown(extensions=[
    'tables',
    'fenced_code',
    'codehilite',
    'toc',      # Generates md.toc with table of contents HTML
    'meta',
])

html_content = md.convert(content)

# Extract table of contents (generated by toc extension)
toc_html = md.toc  # Contains <div class="toc">...</div>

Step 5: Build Template Context

Merge frontmatter with command-line options (options take precedence):

from datetime import date

context = {
    **frontmatter,  # From YAML frontmatter
    **options,      # From command arguments
    'content': html_content,
    'toc': toc_html,  # Table of contents
}

# Set defaults
if 'title' not in context:
    context['title'] = md_path.stem.replace('-', ' ').title()
if 'date' not in context:
    context['date'] = date.today().isoformat()

Step 6: Create Output Folder

import shutil

# Determine output folder path
if 'output' in options:
    output_dir = Path(options['output'])
else:
    output_dir = md_path.parent / md_path.stem  # Same location, named after file

if not output_dir.is_absolute():
    output_dir = BASE_DIR / output_dir

# Create output folder
output_dir.mkdir(parents=True, exist_ok=True)

Step 7: Render Template and Copy Assets

from jinja2 import Environment, FileSystemLoader

templates_dir = Path("/Users/john/Documents/Workspace/2Lines/knowledge-base/_templates")
template_path = templates_dir / template_name

env = Environment(loader=FileSystemLoader(str(template_path)))
template = env.get_template('base.html')

output_html = template.render(**context)

# Write HTML
(output_dir / 'index.html').write_text(output_html)

# Copy CSS and image assets from template
for asset in template_path.glob('*'):
    if asset.suffix in ['.css', '.svg', '.png', '.jpg', '.jpeg', '.gif', '.ico']:
        shutil.copy2(asset, output_dir / asset.name)

Complete Render Function

#!/usr/bin/env python3
"""Render markdown to HTML folder with assets."""

import re
import shutil
from pathlib import Path
from datetime import date

import markdown
from jinja2 import Environment, FileSystemLoader

BASE_DIR = Path("/Users/john/Documents/Workspace/2Lines/knowledge-base")
TEMPLATES_DIR = BASE_DIR / "_templates"

# Asset file extensions to copy from template
ASSET_EXTENSIONS = {'.css', '.svg', '.png', '.jpg', '.jpeg', '.gif', '.ico', '.woff', '.woff2', '.ttf'}


def export_to_html(
    markdown_file: str,
    template_name: str = "2lines-external",
    output_dir: str = None,
    **options
) -> Path:
    """
    Export a markdown file to an HTML folder with assets.

    Args:
        markdown_file: Path to markdown file (relative to BASE_DIR or absolute)
        template_name: Template directory name
        output_dir: Output folder path (optional)
        **options: Metadata options (title, author, client, etc.)

    Returns:
        Path to the output folder
    """
    # Resolve markdown path
    md_path = Path(markdown_file)
    if not md_path.is_absolute():
        md_path = BASE_DIR / md_path

    if not md_path.exists():
        raise FileNotFoundError(f"Markdown file not found: {md_path}")

    # Read markdown content
    md_content = md_path.read_text()

    # Parse YAML frontmatter if present
    frontmatter = {}
    content = md_content

    if md_content.startswith('---'):
        match = re.match(r'^---\n(.*?)\n---\n', md_content, re.DOTALL)
        if match:
            try:
                import yaml
                frontmatter = yaml.safe_load(match.group(1)) or {}
            except:
                pass
            content = md_content[match.end():]

    # Convert markdown to HTML
    md = markdown.Markdown(extensions=[
        'tables',
        'fenced_code',
        'codehilite',
        'toc',
    ])
    html_content = md.convert(content)

    # Extract table of contents
    toc_html = md.toc

    # Build context (frontmatter, then options override)
    context = {
        **frontmatter,
        **{k: v for k, v in options.items() if v is not None},
        'content': html_content,
        'toc': toc_html,
    }

    # Set defaults
    if 'title' not in context:
        context['title'] = md_path.stem.replace('-', ' ').title()
    if 'date' not in context:
        context['date'] = date.today().isoformat()

    # Determine output folder
    if output_dir:
        out_dir = Path(output_dir)
        if not out_dir.is_absolute():
            out_dir = BASE_DIR / out_dir
    else:
        out_dir = md_path.parent / md_path.stem

    # Create output folder
    out_dir.mkdir(parents=True, exist_ok=True)

    # Load and render template
    template_path = TEMPLATES_DIR / template_name
    if not template_path.exists():
        raise FileNotFoundError(f"Template not found: {template_path}")

    env = Environment(loader=FileSystemLoader(str(template_path)))
    template = env.get_template('base.html')
    output_html = template.render(**context)

    # Write HTML
    (out_dir / 'index.html').write_text(output_html)

    # Copy assets from template
    assets_copied = []
    for asset in template_path.iterdir():
        if asset.is_file() and asset.suffix.lower() in ASSET_EXTENSIONS:
            shutil.copy2(asset, out_dir / asset.name)
            assets_copied.append(asset.name)

    return out_dir, assets_copied

Execution Steps

When the user invokes /export-to-html, execute:

  1. Parse the request - Extract file path, template name, and options
  2. Verify dependencies - Check that markdown and jinja2 are installed
  3. Run the export - Use Bash to execute Python with the render logic
  4. Report results - Show the output folder path and list of files created

Error Handling

Error Response
File not found Report error with suggestions for correct path
Template not found List available templates
Missing dependencies Show pip install markdown jinja2 pyyaml
Render error Show Jinja2 error message

Output Format

After successful export:

Export Complete

Source: clients/Circuit/proposal.md
Template: 2lines-external
Output folder: clients/Circuit/proposal/

Files created:
  - index.html
  - style.css
  - logo.svg

Metadata:
  Title: Project Proposal
  Client: Circuit
  Date: 2026-02-12
  Classification: Confidential

Open in browser: open clients/Circuit/proposal/index.html

Tips

  • YAML Frontmatter: Add metadata directly in the markdown file:
    ---
    title: My Document
    author: John Smith
    client: Acme Corp
    ---
    
  • Preview: Open the index.html file in a browser to preview
  • PDF: Use browser print-to-PDF for quick PDF generation (templates have print styles)
  • Sharing: The output folder is self-contained and can be zipped and shared
  • Custom templates: Create new template directories under _templates/ for different styles

chat Comments (0)

chat_bubble_outline

No comments yet. Be the first to share your thoughts!

Skill Details

GitHub Stars 0
GitHub Forks 0
Created Mar 2026
Last Updated 3 months ago
tools tools ide plugins

Related Skills

writing-skills
chevron_right
codex
chevron_right
smart-illustrator
chevron_right
collaborating-with-codex
chevron_right
code-review-router
chevron_right

Build your own?

Join 12,000+ developers contributing to the Claude ecosystem.