A lightweight, Python-based static site generator that converts Markdown content into clean, fast HTML websites. Built from scratch with zero external dependencies.
This SSG transforms Markdown files into a complete static website with:
- Markdown to HTML conversion - Write content in Markdown, get semantic HTML
- Directory-based routing - File structure becomes URL structure (
content/blog/post.mdβ/blog/post/) - Template system - Single HTML template for consistent styling across all pages
- Asset management - Automatic copying of CSS, images, and static files
- Clean URLs - No
.htmlextensions, just clean paths
Dev Adventure - An interactive choose-your-own-adventure story about a production deployment gone wrong. This demonstrates the SSG's capabilities:
- Branching narrative with multiple paths
- 8 different endings based on choices
- Professional "calm incident room" UI aesthetic
- Collapsible sections and spoiler protection
- Fully keyboard-accessible navigation
The entire adventure site is generated from Markdown files in the content/ directory.
- Write in Markdown, no HTML knowledge required
- Organize content with folders (becomes navigation structure)
- Single template controls entire site appearance
- Fast builds - regenerate entire site in milliseconds
- Zero dependencies - Pure Python 3, no pip installs needed
- Custom parser - Hand-built Markdown parser, not a library wrapper
- Test coverage - 99 unit tests ensuring reliability
- Professional architecture - Package-based structure with clear separation of concerns
- Modular design - Models, parsers, converters, and generators in separate packages
- Extensible - Easy to add new Markdown features or HTML elements
static-site-generator/
βββ src/
β βββ main.py # Entry point
β βββ models/ # Domain models
β β βββ html_node.py # HTML node base class
β β βββ leaf_node.py # HTML leaf nodes (no children)
β β βββ parent_node.py # HTML parent nodes (with children)
β β βββ text_node.py # Text node representation
β β βββ enums.py # Type definitions (BlockType, TextType)
β βββ parsers/ # Markdown parsing
β β βββ markdown.py # Block-level parsing
β β βββ inline.py # Inline text parsing (delimiters, links, images)
β βββ converters/ # HTML conversion
β β βββ text_to_html.py # TextNode β HTMLNode conversion
β β βββ block_to_html.py # Block β HTMLNode conversion
β βββ generators/ # Page generation
β β βββ page_generator.py # Page generation & routing
β βββ utils/ # Utilities
β β βββ file_utils.py # File operations
β βββ tests/ # Test suite (99 unit tests)
β βββ test_htmlnode.py
β βββ test_leafnode.py
β βββ test_parentnode.py
β βββ test_textnode.py
β βββ test_inline.py
β βββ test_converters.py
β βββ test_file_utils.py
βββ content/ # Markdown source files
βββ static/ # CSS, images, favicon
βββ template.html # HTML template
βββ docs/ # Generated output (GitHub Pages ready)
- Parse Markdown - Custom parser converts Markdown to intermediate representation
- Build HTML Nodes - Intermediate representation becomes HTML node tree
- Apply Template - Content injected into HTML template
- Generate Files - HTML files written to
docs/directory - Copy Assets - Static files (CSS, images) copied to output
- Headings (
#,##,###) - Paragraphs and line breaks
- Bold and italic text
Inline codeand code blocks- Links
[text](url) - Images
 - Unordered and ordered lists
- Blockquotes
- Horizontal rules
- Semantic HTML5 elements
- Clean, readable source code
- No inline styles (CSS in separate file)
- Accessible markup (ARIA-friendly)
- Python 3.10 or higher
- No external dependencies required
# Generate site
python3 src/main.py
# Or use the build script
./build.sh# Build and serve locally
./main.sh
# Site available at http://localhost:8888./test.sh
# Runs 99 unit testsCreate content/about/index.md:
# About Me
I'm a developer who builds things.
## Skills
- Python
- Web Development
- System DesignGenerates: docs/about/index.html β accessible at /about/
content/
βββ blog/
β βββ index.md β /blog/
β βββ post-1/
β β βββ index.md β /blog/post-1/
β βββ post-2/
β βββ index.md β /blog/post-2/
[Read my blog](/blog/)
[Check out this post](/blog/post-1/)Edit static/index.css to change the appearance. The Dev Adventure example uses:
- CSS custom properties for theming
- Responsive design
- Keyboard-accessible focus states
- Dark mode optimized colors
Modify template.html to change the HTML structure:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>{{ Title }} | Dev Adventure</title>
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<link href="/index.css" rel="stylesheet" />
<meta name="description" content="An interactive developer adventure - your choices matter" />
</head>
<body>
<article>{{ Content }}</article>
</body>
</html>The project includes comprehensive test coverage:
- Markdown parsing - Ensures correct conversion of all Markdown features
- HTML generation - Validates proper HTML node creation
- File operations - Tests recursive copying and file handling
- Edge cases - Empty files, special characters, nested structures
./test.sh
# Expected: Ran 99 tests in 0.006s - OK- Push to GitHub
- Enable GitHub Pages in repository settings
- Set source to
docs/folder - Site live at
https://username.github.io/repo-name/
Add CNAME file to static/ directory:
yourdomain.com
This project demonstrates:
- Parser design - Building a Markdown parser from scratch
- Package architecture - Professional Python package organization with clear module boundaries
- Object-oriented programming - Clean class hierarchy for HTML nodes
- Separation of concerns - Models, parsers, converters, generators in isolated packages
- Recursive algorithms - Directory traversal and tree building
- Comprehensive testing - 99 unit tests ensuring code reliability and maintainability
- File I/O operations - Reading, writing, and copying files
- Template systems - Simple but effective content injection
- Web fundamentals - HTML generation, routing, static hosting
The codebase follows Python package organization:
# Clean imports from organized packages
from models import HTMLNode, LeafNode, ParentNode, TextNode
from parsers import markdown_to_blocks, block_to_block_type
from converters import markdown_to_html_node
from generators import generate_pages_recursive
from utils import copy_directory_recursive# parsers/markdown.py - Block-level parsing
def markdown_to_html_node(markdown: str) -> ParentNode:
blocks = markdown_to_blocks(markdown)
children = []
for block in blocks:
block_type = block_to_block_type(block)
children.append(block_to_html_node(block, block_type))
return ParentNode("div", children)# generators/page_generator.py - Generates entire directory tree
def generate_pages_recursive(dir_path_content, template_path, dest_dir_path, basepath='/'):
for entry in os.listdir(dir_path_content):
if os.path.isfile(from_path) and entry.endswith('.md'):
generate_page(from_path, template_path, dest_path, basepath)
elif os.path.isdir(from_path):
generate_pages_recursive(from_path, template_path, dest_path, basepath)This isn't just another SSG - it's a demonstration of:
- Problem-solving - Building complex functionality from simple primitives
- Code quality - Clean, testable, maintainable code
- Engineering discipline - Proper testing, documentation, version control
- Product thinking - Not just code, but a complete, usable product (Dev Adventure)
- Attention to detail - From UX polish to accessibility considerations
- Lines of Code: ~1,500 (excluding tests)
- Test Coverage: 99 unit tests
- Dependencies: 0 external packages
- Build Time: <100ms for full site
- Supported Markdown Features: 12+
- Personal portfolios - Showcase your work
- Documentation sites - Technical documentation
- Blogs - Simple, fast blogging platform
- Interactive stories - Like the Dev Adventure example
- Landing pages - Quick marketing sites
This project is open source and available for educational purposes.
This SSG was built to demonstrate:
- Strong Python fundamentals
- Software engineering best practices
- Ability to build complete products from scratch
- Attention to UX and accessibility
For technical questions or to discuss the implementation, feel free to reach out!
Built with Python 3 | No frameworks, no dependencies, just code