Skip to main content

Minimal Python static site generator. Markdown + Jinja2, Tailwind CSS built-in, zero configuration, no frontmatter.

Project description

medusa-ssg

Minimal Python static site generator. Markdown + Jinja2, Tailwind CSS built-in, zero configuration, no frontmatter.

Quick Start

pip install medusa-ssg
medusa new mysite
cd mysite
medusa serve

Open http://localhost:4000 to see your site with live reload.

Features

  • Zero frontmatter — title, date, tags, and description derived from filenames and content
  • Markdown + Jinja2 — write content in Markdown, layouts in Jinja2
  • Tailwind CSS — built-in pipeline with typography plugin
  • Live reload — dev server with WebSocket-based hot reload
  • Pretty URLs/posts/hello/ not /posts/hello.html
  • Automatic sitemap & RSS — generated on build
  • Custom 404 pages — static HTML served with proper status codes

Requirements

  • Python 3.10+
  • Node.js 16+ (for Tailwind CSS)

Installation

# Install Medusa
pip install medusa-ssg

# Or install from source
git clone https://github.com/yourname/medusa.git
cd medusa
pip install -e .

CLI Commands

medusa new NAME          # Create a new project
medusa build             # Build site to output/
medusa build --drafts    # Include draft content
medusa serve             # Dev server at localhost:4000
medusa serve --port 3000 # Custom port
medusa serve --drafts    # Include drafts in dev

Project Structure

mysite/
├── site/                    # Content and templates
│   ├── _layouts/            # Page layouts
│   │   └── default.html.jinja
│   ├── _partials/           # Reusable components
│   │   ├── header.html.jinja
│   │   └── footer.html.jinja
│   ├── posts/               # Blog posts
│   │   └── 2024-01-15-hello-world.md
│   ├── index.jinja          # Home page
│   ├── about.md             # Static page
│   └── 404.html             # Custom error page
├── assets/
│   ├── css/main.css         # Tailwind entry point
│   ├── js/main.js
│   └── images/
├── data/                    # YAML data files
│   ├── site.yaml            # Site metadata
│   └── nav.yaml             # Navigation links
├── output/                  # Generated site (git-ignored)
├── medusa.yaml              # Configuration
├── tailwind.config.js
└── package.json

Configuration

Create medusa.yaml in your project root:

# Output directory (default: output)
output_dir: output

# Base URL used to absolutize all links in output (default: empty).
# medusa serve always uses http://localhost:<port> for dev builds.
root_url: https://example.com

# Dev server port (default: 4000)
port: 4000

# WebSocket port for live reload (default: port + 1)
ws_port: 4001

Writing Content

Markdown Pages

Create .md files anywhere in site/:

# My Page Title

Content goes here. The first `# heading` becomes the page title.

Use #hashtags for tags. #python #tutorial

Dated Posts

Name files with a date prefix for automatic date extraction:

site/posts/2024-01-15-my-post.md  →  /posts/my-post/
site/posts/2024-02-20-another.md  →  /posts/another/

Custom Layouts

Specify a layout with brackets in the filename:

site/about[minimal].md  →  Uses _layouts/minimal.html.jinja
site/contact.md         →  Uses _layouts/default.html.jinja

Drafts

Prefix files or folders with _ to mark as draft:

site/posts/_work-in-progress.md   # Draft post
site/_experiments/test.md          # Draft folder

Drafts are excluded from builds unless you use --drafts.

Templates

Available Variables

Variable Description
current_page The current page object
pages Collection of all pages
tags Map of tag names to page collections
data Merged YAML from data/ directory
url_for(path) Generate URLs with base path

Page Object

current_page.title        # Page title
current_page.content      # Rendered HTML
current_page.description  # First paragraph
current_page.url          # URL path (/posts/hello/)
current_page.date         # Publication date
current_page.tags         # List of tags
current_page.draft        # Is draft?
current_page.layout       # Layout name
current_page.group        # Folder group (posts, pages, etc.)

Collections API

{# Get posts #}
{% for post in pages.group("posts").sorted() %}
  <a href="{{ post.url }}">{{ post.title }}</a>
{% endfor %}

{# Filter by tag #}
{% for post in pages.with_tag("python").latest(5) %}
  {{ post.title }}
{% endfor %}

{# Published only (excludes drafts) #}
{% for page in pages.published() %}
  {{ page.title }}
{% endfor %}

Partials

{# site/_partials/card.html.jinja #}
<div class="card">
  <h3>{{ title }}</h3>
  <p>{{ description }}</p>
</div>

{# Include in any template #}
{% include "card.html.jinja" %}

Data Files

YAML files in data/ are available in templates:

# data/site.yaml - merged into data root
title: My Site
author: Jane Doe

# data/social.yaml - available as data.social
- platform: GitHub
  url: https://github.com/username
- platform: Twitter
  url: https://twitter.com/username
<h1>{{ data.title }}</h1>
<p>By {{ data.author }}</p>

{% for link in data.social %}
  <a href="{{ link.url }}">{{ link.platform }}</a>
{% endfor %}

Static Files

404 Page

Create site/404.html for a custom error page. This file is copied directly to output without layout processing and served with a 404 status code by the dev server.

Other Static HTML

Any .html file (not .html.jinja) in site/ is copied as-is to output, preserving the path structure.

Deployment

Netlify

Create netlify.toml:

[build]
  command = "pip install -e . && medusa build"
  publish = "output"

[[redirects]]
  from = "/*"
  to = "/404.html"
  status = 404

Vercel

Create vercel.json:

{
  "buildCommand": "pip install -e . && medusa build",
  "outputDirectory": "output"
}

GitHub Pages

Create .github/workflows/deploy.yml:

name: Deploy
on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: '3.12'
      - uses: actions/setup-node@v4
        with:
          node-version: '20'
      - run: pip install medusa-ssg
      - run: npm install
      - run: medusa build
      - uses: peaceiris/actions-gh-pages@v3
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}
          publish_dir: ./output

Manual / Any Host

medusa build
# Upload contents of output/ to your server

Development

# Clone and install
git clone https://github.com/yourname/medusa.git
cd medusa
pip install -e ".[dev]"

# Run tests
pytest

# Run with coverage
pytest --cov=medusa --cov-report=term-missing

License

MIT License. See LICENSE for details.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

medusa_ssg-0.1.1.tar.gz (37.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

medusa_ssg-0.1.1-py3-none-any.whl (34.5 kB view details)

Uploaded Python 3

File details

Details for the file medusa_ssg-0.1.1.tar.gz.

File metadata

  • Download URL: medusa_ssg-0.1.1.tar.gz
  • Upload date:
  • Size: 37.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.0

File hashes

Hashes for medusa_ssg-0.1.1.tar.gz
Algorithm Hash digest
SHA256 41b53e59cbb6de5fc3192cc42556778e9b621adb7e6c7052f038c70680e843f1
MD5 e04495daf3b1ae49514a7f916ba0169b
BLAKE2b-256 45f09766094b876fa9f3c7b25e075eabd0a67492535a306105b8893cfd7190f5

See more details on using hashes here.

File details

Details for the file medusa_ssg-0.1.1-py3-none-any.whl.

File metadata

  • Download URL: medusa_ssg-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 34.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.0

File hashes

Hashes for medusa_ssg-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 862b9008e161c7779772c903be9ba8b5b8b457fdd9299fcc2d05378b497ba8fd
MD5 508befc7e52c4b35e8e18d6efcbab902
BLAKE2b-256 6bfbd935eda5bfbfa2686b6f535eeb4360366ce46440fdc70e77dc737da3e9dc

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page