README
ΒΆ
SSG - Static Site Generator
π SSG - A simple static site generator written in Go. Converts content from WordPress exports (Markdown format with YAML frontmatter) to static HTML, CSS, and JS files.
π Table of Contents
- Features
- Requirements
- Installation
- Usage
- GitHub Actions
- Project Structure
- Templates
- Styles/Colors
- Architecture
- Testing
- Development
β¨ Features
- π Fast static site generation
- π Markdown support with YAML frontmatter
- π¨ Two templates: simple (dark theme) and krowy (green/natural theme)
- π± Responsive design
- βΏ WCAG 2.2 compliant
- π SEO-friendly URLs (clean addresses)
- π Automatic media file copying
- π·οΈ Category support
- π Config file support (YAML, TOML, JSON)
- π§ Multiple template engines - Go, Pongo2 (Jinja2), Mustache, Handlebars
- π Online theme download - use Hugo themes from GitHub/GitLab
- π Built-in HTTP server (
--httpflag) - π Watch mode - auto-rebuild on file changes (
--watchflag) - πΌοΈ WebP conversion (
--webpflag) - ποΈ Minification - HTML, CSS, JS (
--minify-allflag) - π§Ή Clean builds (
--cleanflag) - π¦ Cloudflare Pages deployment package (
--zipflag) - π³ Docker support - minimal Alpine image (~15MB)
- π¬ GitHub Actions integration - Use as a step in CI/CD pipelines
π¦ Requirements
- Go 1.25 or later
- Make (optional, for Makefile)
cwebp(optional, for WebP conversion)
π Installation
Quick Install (Linux/macOS)
curl -sSL https://raw.githubusercontent.com/spagu/ssg/main/install.sh | bash
Package Managers
| Platform | Command |
|---|---|
| Homebrew (macOS/Linux) | brew install spagu/tap/ssg |
| Snap (Ubuntu) | snap install ssg |
| Debian/Ubuntu | wget https://github.com/spagu/ssg/releases/download/v1.3.0/ssg_1.3.0_amd64.deb && sudo dpkg -i ssg_1.3.0_amd64.deb |
| Fedora/RHEL | sudo dnf install https://github.com/spagu/ssg/releases/download/v1.3.0/ssg-1.3.0-1.x86_64.rpm |
| FreeBSD | pkg install ssg or from ports |
| OpenBSD | From ports: /usr/ports/www/ssg |
Binary Downloads
Download pre-built binaries from GitHub Releases:
| Platform | AMD64 | ARM64 |
|---|---|---|
| Linux | ssg-linux-amd64.tar.gz | ssg-linux-arm64.tar.gz |
| macOS | ssg-darwin-amd64.tar.gz | ssg-darwin-arm64.tar.gz |
| FreeBSD | ssg-freebsd-amd64.tar.gz | ssg-freebsd-arm64.tar.gz |
| Windows | ssg-windows-amd64.zip | ssg-windows-arm64.zip |
From Source
git clone https://github.com/spagu/ssg.git
cd ssg
make build
sudo make install
Docker
# Pull image from GitHub Container Registry
docker pull ghcr.io/spagu/ssg:latest
# Run SSG in container
docker run --rm -v $(pwd):/site ghcr.io/spagu/ssg:latest \
my-content krowy example.com --webp
# Or use docker-compose
docker compose run --rm ssg my-content krowy example.com
# Development server with watch mode
docker compose up dev
π Full installation guide: docs/INSTALL.md
π» Usage
Syntax
ssg <source> <template> <domain> [options]
Arguments
| Argument | Description |
|---|---|
source |
Source folder name (inside content-dir) |
template |
Template name (inside templates-dir) |
domain |
Target domain for the generated site |
Configuration File
SSG supports configuration files in YAML, TOML, or JSON format. Auto-detects: .ssg.yaml, .ssg.toml, .ssg.json
# Use explicit config file
ssg --config .ssg.yaml
# Or just create .ssg.yaml and run ssg (auto-detected)
ssg
Example .ssg.yaml:
source: "my-content"
template: "krowy"
domain: "example.com"
http: true
watch: true
clean: true
webp: true
webp_quality: 80
minify_all: true
See .ssg.yaml.example for all options.
Options
Configuration:
| Option | Description |
|---|---|
--config=FILE |
Load config from YAML/TOML/JSON file |
Server & Development:
| Option | Description |
|---|---|
--http |
Start built-in HTTP server (default port: 8888) |
--port=PORT |
HTTP server port (default: 8888) |
--watch |
Watch for changes and rebuild automatically |
--clean |
Clean output directory before build |
Output Control:
| Option | Description |
|---|---|
--sitemap-off |
Disable sitemap.xml generation |
--robots-off |
Disable robots.txt generation |
--minify-all |
Minify HTML, CSS, and JS |
--minify-html |
Minify HTML output |
--minify-css |
Minify CSS output |
--minify-js |
Minify JS output |
--sourcemap |
Include source maps in output |
Image Processing (Native Go - no external tools needed):
| Option | Description |
|---|---|
--webp |
Convert images to WebP format (requires cwebp) |
--webp-quality=N |
WebP compression quality 1-100 (default: 60) |
Deployment:
| Option | Description |
|---|---|
--zip |
Create ZIP file for Cloudflare Pages |
Paths:
| Option | Description |
|---|---|
--content-dir=PATH |
Content directory (default: content) |
--templates-dir=PATH |
Templates directory (default: templates) |
--output-dir=PATH |
Output directory (default: output) |
Template Engine:
| Option | Description |
|---|---|
--engine=ENGINE |
Template engine: go (default), pongo2, mustache, handlebars |
--online-theme=URL |
Download theme from URL (GitHub, GitLab, or direct ZIP) |
Other:
| Option | Description |
|---|---|
--quiet, -q |
Suppress output (only exit codes) |
--version, -v |
Show version |
--help, -h |
Show help |
Examples
# Development mode: HTTP server + auto-rebuild on changes
./build/ssg my-content krowy example.com --http --watch
# HTTP server on custom port
./build/ssg my-content krowy example.com --http --port=3000
# Generate site with krowy template
./build/ssg krowy.net.2026-01-13110345 krowy krowy.net
# Generate with simple template (dark theme)
./build/ssg krowy.net.2026-01-13110345 simple krowy.net
# Generate with WebP conversion and ZIP package
./build/ssg krowy.net.2026-01-13110345 krowy krowy.net --webp --zip
# Use custom directories
./build/ssg my-content my-template example.com \
--content-dir=/data/content \
--templates-dir=/data/templates \
--output-dir=/var/www/html
# Or using Makefile
make generate # krowy template
make generate-simple # simple template
make serve # generate and run local server
make deploy # generate with WebP + ZIP for Cloudflare Pages
Output
Generated files will be in the output/ folder:
output/
βββ index.html # Homepage
βββ css/
β βββ style.css # Stylesheet
βββ js/
β βββ main.js # JavaScript
βββ media/ # Media files
βββ {slug}/ # Pages and posts (SEO URLs)
β βββ index.html
βββ category/
β βββ {category-slug}/
β βββ index.html
βββ sitemap.xml # Sitemap for search engines
βββ robots.txt # Robots file
βββ _headers # Cloudflare Pages headers
βββ _redirects # Cloudflare Pages redirects
π§ Template Engines
SSG supports multiple template engines. By default, Go templates are used, but you can switch to other engines:
Available Engines
| Engine | Flag | Syntax Style |
|---|---|---|
| Go (default) | --engine=go |
{{"{{"}}.Variable}}, {{"{{"}}.range .Items}} |
| Pongo2 | --engine=pongo2 |
Jinja2/Django: {{"{{"}}.variable}}, {%raw%}{% for item in items %}{%endraw%} |
| Mustache | --engine=mustache |
{{"{{"}}.variable}}, {{"{{#"}} items}} |
| Handlebars | --engine=handlebars |
{{"{{"}}.variable}}, {{"{{#each"}} items}} |
Usage Examples
# Use Pongo2 (Jinja2/Django syntax)
ssg my-content mytheme example.com --engine=pongo2
# Use Mustache
ssg my-content mytheme example.com --engine=mustache
# Use Handlebars
ssg my-content mytheme example.com --engine=handlebars
Online Themes
Download themes directly from GitHub, GitLab, or any ZIP URL:
# Download Hugo theme from GitHub
ssg my-content bearblog example.com --online-theme=https://github.com/janraasch/hugo-bearblog
# Download from any URL
ssg my-content mytheme example.com --online-theme=https://example.com/theme.zip
The theme will be downloaded and extracted to templates/{template-name}/.
Template Syntax Comparison
Go Templates:
{% raw %}
{{ range .Posts }}
<h2>{{ .Title }}</h2>
<p>{{ .Content }}</p>
{{ end }}
{% endraw %}
Pongo2 (Jinja2):
{% raw %}
{% for post in Posts %}
<h2>{{ post.Title }}</h2>
<p>{{ post.Content }}</p>
{% endfor %}
{% endraw %}
Mustache:
{% raw %}
{{#Posts}}
<h2>{{Title}}</h2>
<p>{{Content}}</p>
{{/Posts}}
{% endraw %}
Handlebars:
{% raw %}
{{#each Posts}}
<h2>{{Title}}</h2>
<p>{{Content}}</p>
{{/each}}
{% endraw %}
π¬ GitHub Actions
Use SSG as a GitHub Action in your CI/CD pipeline:
Versioning
| Reference | Description |
|---|---|
spagu/ssg@main |
Latest from main branch (development) |
spagu/ssg@v1 |
Latest stable v1.x release |
spagu/ssg@v1.3.0 |
Specific version |
Note: Use
@mainuntil a stable release is published.
Basic Usage
- name: Generate static site
uses: spagu/ssg@main # or @v1 after release
with:
source: 'my-content'
template: 'krowy'
domain: 'example.com'
Full Configuration
{% raw %}
- name: Generate static site
id: ssg
uses: spagu/ssg@v1
with:
source: 'my-content' # Content folder (inside content/)
template: 'krowy' # Template: 'simple' or 'krowy'
domain: 'example.com' # Target domain
version: 'latest' # Optional: SSG version (default: latest)
content-dir: 'content' # Optional: content directory path
templates-dir: 'templates' # Optional: templates directory path
output-dir: 'output' # Optional: output directory path
webp: 'true' # Optional: convert images to WebP
webp-quality: '80' # Optional: WebP quality 1-100 (default: 60)
zip: 'true' # Optional: create ZIP for deployment
minify: 'true' # Optional: minify HTML/CSS/JS
clean: 'true' # Optional: clean output before build
- name: Show outputs
run: |
echo "Output path: ${{ steps.ssg.outputs.output-path }}"
echo "ZIP file: ${{ steps.ssg.outputs.zip-file }}"
echo "ZIP size: ${{ steps.ssg.outputs.zip-size }} bytes"
{% endraw %}
Action Inputs
| Input | Description | Required | Default |
|---|---|---|---|
source |
Content source folder name | β | - |
template |
Template name | β | simple |
domain |
Target domain | β | - |
version |
SSG version to download | β | latest |
content-dir |
Path to content directory | β | content |
templates-dir |
Path to templates directory | β | templates |
output-dir |
Path to output directory | β | output |
webp |
Convert images to WebP | β | false |
webp-quality |
WebP compression quality 1-100 | β | 60 |
zip |
Create ZIP file | β | false |
minify |
Minify HTML, CSS, and JS | β | false |
clean |
Clean output directory before build | β | false |
Action Outputs
| Output | Description |
|---|---|
output-path |
Path to generated site directory |
zip-file |
Path to ZIP file (if --zip used) |
zip-size |
Size of ZIP file in bytes |
Deploy to Cloudflare Pages
{% raw %}
name: Deploy
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Generate site
id: ssg
uses: spagu/ssg@v1
with:
source: 'my-content'
template: 'krowy'
domain: 'example.com'
webp: 'true'
- name: Deploy to Cloudflare
uses: cloudflare/pages-action@v1
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
projectName: 'my-site'
directory: ${{ steps.ssg.outputs.output-path }}
{% endraw %}
π Project Structure
ssg/
βββ cmd/
β βββ ssg/
β βββ main.go # CLI entry point
βββ internal/
β βββ generator/
β β βββ generator.go # Generator logic
β β βββ generator_test.go # Generator tests
β β βββ templates.go # Default HTML templates
β βββ models/
β β βββ content.go # Data models
β βββ parser/
β βββ markdown.go # Markdown parser
β βββ markdown_test.go # Parser tests
βββ content/ # Source data
β βββ {source}/
β βββ metadata.json
β βββ media/
β βββ pages/
β βββ posts/
βββ templates/ # Templates
β βββ simple/
β β βββ css/
β β βββ js/
β βββ krowy/
β βββ css/
β βββ js/
βββ output/ # Generated site (gitignored)
βββ go.mod
βββ go.sum
βββ Makefile
βββ README.md
βββ CHANGELOG.md
βββ .gitignore
βββ .dockerignore
π¨ Templates
simple - Modern Dark Theme
Elegant dark theme with glassmorphism and gradients:
- Dark background:
#0f0f0f - Cards:
#222222 - Accent: purple gradient
#6366f1β#a855f7 - Hover animations and micro-interactions
krowy - Green Farm Theme
Natural light theme inspired by krowy.net:
- Light background:
#f8faf5 - Cards:
#ffffff - Accent: green
#2d7d32 - Cow icon π in logo
- Nature and ecology focus
π¨ Styles/Colors
Color Guidelines (WCAG 2.2 Compliant)
Simple Template (Dark)
/* Background */
--color-bg-primary: #0f0f0f;
--color-bg-secondary: #1a1a1a;
--color-bg-card: #222222;
/* Text (minimum contrast 4.5:1) */
--color-text-primary: #ffffff;
--color-text-secondary: #b3b3b3;
--color-text-muted: #808080;
/* Accent */
--color-accent: #6366f1;
--gradient-primary: linear-gradient(135deg, #6366f1 0%, #8b5cf6 50%, #a855f7 100%);
Krowy Template (Light)
/* Background */
--color-bg-primary: #f8faf5;
--color-bg-secondary: #ffffff;
--color-bg-card: #ffffff;
/* Text (minimum contrast 4.5:1) */
--color-text-primary: #1a2e1a;
--color-text-secondary: #3d5a3d;
--color-text-muted: #6b8a6b;
/* Accent */
--color-accent: #2d7d32;
--gradient-primary: linear-gradient(135deg, #2d7d32 0%, #43a047 50%, #66bb6a 100%);
Detailed style documentation: docs/STYLES.md
ποΈ Architecture
flowchart TB
subgraph Input["π₯ Input"]
A[content/source] --> B[metadata.json]
A --> C[pages/*.md]
A --> D[posts/**/*.md]
A --> E[media/*]
end
subgraph Processing["βοΈ Processing"]
F[Parser] --> G[Models]
G --> H[Generator]
T[Templates] --> H
end
subgraph Output["π€ Output"]
H --> I[output/]
I --> J[index.html]
I --> K[pages/]
I --> L[posts/]
I --> M[category/]
I --> N[css/]
I --> O[js/]
I --> P[media/]
end
B --> F
C --> F
D --> F
E --> P
π§ͺ Testing
# Run all tests
make test
# Tests with coverage
make test-coverage
# Open coverage report
open coverage.html
π οΈ Development
Available Make Commands
make help # Show all commands
make all # deps + lint + test + build
make build # Build binary
make test # Run tests
make lint # Check code
make run # Build and run
make generate # Generate site (krowy template)
make generate-simple # Generate site (simple template)
make serve # Generate and serve locally
make deploy # Generate with WebP + ZIP for Cloudflare Pages
make clean # Clean artifacts
make install # Install binary to /usr/local/bin
Creating Your Own Template
- Create a folder in
templates/your-template-name/ - Add files:
css/style.cssjs/main.js(optional)index.html,page.html,post.html,category.html(optional)
- HTML templates are generated automatically if missing
π License
BSD 3-Clause License - see LICENSE
π₯ Authors
- spagu - GitHub
Directories
ΒΆ
| Path | Synopsis |
|---|---|
|
cmd
|
|
|
ssg
command
Package main provides the entry point for the SSG (Static Site Generator) CLI tool.
|
Package main provides the entry point for the SSG (Static Site Generator) CLI tool. |
|
internal
|
|
|
config
Package config handles SSG configuration file parsing
|
Package config handles SSG configuration file parsing |
|
engine
Package engine provides multiple template engine implementations
|
Package engine provides multiple template engine implementations |
|
generator
Package generator handles static site generation
|
Package generator handles static site generation |
|
models
Package models defines data structures for content parsing
|
Package models defines data structures for content parsing |
|
parser
Package parser handles parsing of content files (Markdown with YAML frontmatter)
|
Package parser handles parsing of content files (Markdown with YAML frontmatter) |
|
theme
Package theme provides theme downloading and management
|
Package theme provides theme downloading and management |
|
webp
Package webp provides WebP image conversion using the cwebp command-line tool.
|
Package webp provides WebP image conversion using the cwebp command-line tool. |