Skip to main content

Stapler

A simple static site generator built with Jinja and Markdown.

Installation

Clone the repo:

git clone https://github.com/gijs6/stapler.git
cd stapler

Create a virtual environment (recommended):

python -m venv .venv
source .venv/bin/activate

Install:

pip install -e .

Or with dev dependencies:

pip install -e ".[dev]"

Quick start

  1. Create a stapler.toml in your project root:
[site]
url = "https://yoursite.com"
title = "Your site"
  1. Put your content in a site/ directory (the default). Templates go in site/templates/.

  2. Run:

stapler serve   # local dev server on port 8000
stapler build   # production build

Configuration

Required

[site]
url = "https://yoursite.com"
title = "Your site"

Optional

Site metadata

[site]
description = "About your site"
base_path = "/blog"                # Deploy to example.com/blog instead of the root

Author info

Used in RSS/Atom feeds.

[site.author]
name = "Your name"
email = "you@example.com"

Directories

All paths are relative to where you run stapler.

[directories]
site = "site"                      # Content directory (default: "site")
build = "build"                    # Production output (default: "build")
build_dev = "build-dev"            # Dev server output (default: "build-dev")
templates = "templates"            # Templates folder inside the site directory (default: "templates")
blog = "blog"                      # Blog posts folder inside the site directory (default: "blog")

Default template

The template used for HTML pages that have front matter but no template field.

[templates]
default = "base.html"

Blog

[features.blog]
enabled = true                     # Enable blog functionality (default: false)
template = "blog_post.html"        # Template for individual posts
index_template = "blog_index.html" # Template for the blog index page

Sitemap and feeds

[features]
sitemap = true                     # Generate sitemap.xml (default: true)

[features.feeds]
rss = true                         # Generate rss.xml (default: true)
atom = true                        # Generate atom.xml (default: true)

Feeds are only generated when the blog feature is enabled.

Markdown extensions

[markdown]
extensions = ["meta", "tables", "fenced_code"]  # Python-Markdown extensions

How it works

Pages

Any .html or .md file in your site directory (excluding the templates and blog folders) becomes a page.

Markdown files

Markdown files are always rendered to HTML. If the front matter includes a template field, the result is passed to that template as page.content. Without a template field (or without front matter entirely), the raw HTML is written directly.

---
template: base.html
title: My page
---

# Content

Regular markdown here

HTML files

HTML files with front matter are rendered through a template. The template field in front matter takes precedence; if omitted, the default template from [templates].default is used.

---
title: My page
---
<h1>Content here</h1>

HTML files without front matter are treated as Jinja templates directly:

{% extends "base.html" %}
{% block content %}
<h1>Hello</h1>
{% endblock %}

Front matter is YAML. All fields are available as page.metadata.<field> in your templates.

Static files

Anything that's not a .html or .md file (and not in your templates or blog folder) is copied as-is to the output directory.

Blog

Enable the blog feature in your config, then put .md files in your blog directory.

---
title: My post
date: 2025-01-15
---

Post content here

The date field is optional. If omitted, stapler tries to infer it from the file's git history.

Templates

Templates live in the directory you configured (default: site/templates/).

Available in all templates

  • data: build info
    • data.now: current build time
      • data.now.date.long: date as %B %d, %Y (e.g. April 12, 2026)
      • data.now.date.short: date as %Y-%m-%d (e.g. 2026-04-12)
      • data.now.time: time as %H:%M:%S
      • data.now.iso: datetime as ISO 8601
    • data.last_commit: last git commit info (None if not in a git repo)
      • data.last_commit.hash.short: short 7-character commit hash
      • data.last_commit.hash.long: full commit hash
      • data.last_commit.dt.date.long: date as %B %d, %Y (e.g. April 12, 2026)
      • data.last_commit.dt.date.short: date as %Y-%m-%d (e.g. 2026-04-12)
      • data.last_commit.dt.time: time as %H:%M:%S
      • data.last_commit.dt.iso: datetime as ISO 8601

Regular page templates

  • page: the current page
    • page.active_page: identifier derived from the filename (e.g. about for about.html, home for index.html), useful for highlighting the active nav item
    • page.canonical_path: URL path of the page (e.g. /about)
    • page.content: page content as HTML (only present if the page has front matter)
    • page.metadata.<field>: any front matter field (e.g. page.metadata.title)

Blog post template

  • page: navigation info
    • page.active_page: name of the blog directory (e.g. blog)
    • page.canonical_path: URL path of the post (e.g. /blog/my-post)
  • post: the current blog post
    • post.title: post title (from front matter, or derived from the filename)
    • post.slug: URL slug (filename without .md)
    • post.content: post content as HTML
    • post.date: date as %Y-%m-%d (e.g. 2026-04-12), only set if a date is available
    • post.date_iso: date as ISO 8601, only set if a date is available
  • data: same as above

Blog index template

  • page: navigation info
    • page.active_page: name of the blog directory (e.g. blog)
    • page.canonical_path: URL path of the blog index (e.g. /blog)
  • posts: list of all blog posts sorted newest first; each item has the same fields as post above
  • data: same as above

CLI

# Build with default config (stapler.toml)
stapler build

# Build with custom config
stapler build -c myconfig.toml

# Serve on default port (8000)
stapler serve

# Serve on custom port
stapler serve -p 3000

# Serve with custom config and port
stapler serve -c myconfig.toml -p 3000

# Show version
stapler --version

Metadata

Release files for stapler-ssg 0.1.3

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for stapler-ssg 0.1.3
File Size Uploaded
stapler_ssg-0.1.3.tar.gz 12.2 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for stapler-ssg 0.1.3
File Interpreter ABI Platform
stapler_ssg-0.1.3-py3-none-any.whl Python 3 none any Details

Total release size: 26.2 kB

Release files / stapler_ssg-0.1.3.tar.gz

Download URL stapler_ssg-0.1.3.tar.gz
Size 12.2 kB
Tags Source
SHA-256 checksum
How to use checksums
4e4a61b1d9337c325b3619fdc385c174f3d8293c6e0436495dfaadb843e0d5d1
BLAKE2b-256 checksum
How to use checksums
5c4b64422f529f991cb913875a672a8e4f48a33692625e2c2241238afd566898
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 13, 2026.

Transparency log

Release files / stapler_ssg-0.1.3-py3-none-any.whl

Download URL stapler_ssg-0.1.3-py3-none-any.whl
Size 14.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
214b9b7d826c0ceef05bd31c5690c011aaf4c63919df74e43e64d7c058b13c44
BLAKE2b-256 checksum
How to use checksums
23e62337c80451703fc18aa6429b0f6afccf323c236762aac13f4e93e814d8ca
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 13, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.1.3 This release

2 release files

0.1.2

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page