A static website generator powered by NUKE, Markdig, and Pico CSS. Write content in Markdown with YAML front-matter, and StaticWGen generates a complete static website with SEO metadata, syntax highlighting, diagrams, feeds, and more.
- Markdown to HTML — Convert
.mdfiles to styled HTML pages using Markdig - YAML Front-Matter — Define page metadata (title, description, author, date, keywords, image)
- Prism.js Syntax Highlighting — Automatic code block highlighting for 200+ languages
- Mermaid Diagrams — Embed flowcharts, sequence diagrams, and more using fenced code blocks
- Emoji Support — Use
:emoji:shortcodes in your content - LaTeX Mathematics — Inline
\( ... \)and display\[ ... \]math expressions - Table of Contents — Auto-generated TOC for pages with 3+ headings, configurable depth
- SEO Generation — Open Graph, Twitter Cards, sitemap.xml, robots.txt, JSON-LD schema
- Atom Feed — Automatic
feed.xmlgeneration for blog content - Blog Index — Paginated blog listing with configurable posts per page
- Tagging System — Tag index and per-tag pages from
keywordsmetadata - Search Index — Client-side search via generated
search-index.json - Content Lifecycle — Draft, scheduled, and archived content support
- Internationalization — Multi-language pages with hreflang links
- Image Optimization — Automatic resizing, WebP generation, lazy loading
- HTML Minification — Minify output HTML and fingerprint CSS/JS assets
- Output Validation — HTML structure, accessibility (WCAG), SEO, and link checking
- Analytics Integration — Plausible, Google Analytics (GA4), or custom scripts
- Theme System — Multiple themes with fallback to legacy template
- Development Server — Live-reload dev server with file watching
- Scaffolding — Quick-start commands for new posts, pages, and project init
- Theme Switching — Built-in light/dark/auto theme toggle
- Docker Deployment — One-command deployment with nginx
- CI/CD — GitHub Actions workflows for build, release, and GitHub Pages deployment
graph LR A[input/*.md] --> B[GenerateHtml] B --> C[output/*.html] B --> D[GenerateTagPages] B --> E[GenerateSitemap] B --> F[GenerateRobotsTxt] B --> G[GenerateFeed] B --> H[GenerateBlogIndex] D --> I[output/tags/*.html] E --> J[output/sitemap.xml] F --> K[output/robots.txt] G --> L[output/feed.xml] H --> M[output/blog.html] C & I & J & K & L & M --> N[BuildWebsite] N --> O[OptimizeImages] O --> P[OptimizeOutput] P --> Q[Validate] P --> R[CompressOutput] N --> S[BuildDockerImage] S --> T[DeployDockerImage] N --> U[GenerateBuildReport] N --> V[GenerateSearchIndex] - .NET 10 SDK (or later)
git clone https://github.com/Atypical-Consulting/StaticWGen.git cd StaticWGen./build.sh BuildWebsite \ --site-base-url "http://localhost:8080" \ --site-title "My Site"The generated site is in the output/ directory.
./build.sh Watch \ --site-base-url "http://localhost:3000" \ --site-title "My Site"Open http://localhost:3000 in your browser. Changes to input/ and template/ are automatically rebuilt with live reload.
cd output && python3 -m http.server 8080./build.sh DeployDockerImage \ --site-base-url "http://localhost:8080" \ --site-title "My Site" \ --image-name my-site \ --version-tag latest \ --container-name my-site \ --host-port 8080 \ --container-port 80All parameters can be set via command-line flags, .nuke/parameters.json, .env file, or NUKE_* environment variables.
| Parameter | Required | Default | Description |
|---|---|---|---|
SiteTitle | Yes | — | Title displayed in header, footer, and meta tags |
SiteBaseUrl | Yes | — | Base URL for absolute links, sitemap, and feed |
Theme | No | "" | Theme name (looks in themes/{name}/, falls back to template/) |
DefaultImageUrl | No | "" | Fallback Open Graph image URL |
IncludeDrafts | No | false | Include draft and scheduled pages in output |
ContinueOnError | No | false | Continue processing remaining files when one fails |
| Parameter | Required | Default | Description |
|---|---|---|---|
AnalyticsProvider | No | "" | Analytics provider: plausible, google, custom, or empty |
AnalyticsSiteId | No | "" | Domain (Plausible) or measurement ID (GA4) |
AnalyticsScriptUrl | No | "" | Custom analytics script URL |
| Parameter | Required | Default | Description |
|---|---|---|---|
FeedTitle | No | SiteTitle | Atom feed title |
FeedDescription | No | "" | Atom feed description |
FeedAuthor | No | "" | Default author for feed entries |
| Parameter | Required | Default | Description |
|---|---|---|---|
MinifyHtml | No | true | Enable HTML minification |
FingerprintAssets | No | true | Add content hashes to CSS/JS filenames |
ImageMaxWidth | No | 1200 | Maximum image width in pixels |
ImageQuality | No | 85 | Image compression quality (1-100) |
GenerateWebP | No | true | Generate WebP variants of images |
| Parameter | Required | Default | Description |
|---|---|---|---|
Port | No | 3000 | Port for the Watch development server |
| Parameter | Required | Default | Description |
|---|---|---|---|
ImageName | Docker | — | Docker image name |
VersionTag | Docker | — | Docker image version tag |
ContainerName | Docker | — | Docker container name |
HostPort | Docker | — | Host port for Docker container |
ContainerPort | Docker | — | Container port (typically 80) |
| Parameter | Required | Default | Description |
|---|---|---|---|
Title | No | "" | Title for the new content |
Tags | No | "" | Comma-separated tags for the new content |
Author | No | "" | Author name for the new content |
Example .nuke/parameters.json:
{ "$schema": "build.schema.json", "SiteTitle": "My Static Website", "SiteBaseUrl": "http://localhost:8080", "ImageName": "my-static-website", "VersionTag": "latest", "ContainerName": "my-static-website", "HostPort": 8080, "ContainerPort": 80 }Add Markdown files to the input/ directory. Each .md file becomes an HTML page.
# Create a new blog post (dated) ./build.sh NewPost --title "My First Post" --tags "blog, intro" --author "Jane Doe" # Create a new page ./build.sh NewPage --title "About Us" # Initialize a fresh project structure ./build.sh InitAdd metadata at the top of your Markdown files:
--- title: "My Page Title" description: "A brief description for SEO and social sharing" author: "Your Name" date: "2024-09-13" keywords: "tag1, tag2, tag3" image: "./assets/my-image.webp" --- # Your Content Here| Field | Purpose | Used in |
|---|---|---|
title | Page title | <title>, Open Graph, Twitter Card |
description | Page description | <meta>, Open Graph, Twitter Card, feed |
author | Author name | <meta>, feed entries |
date | Publication date | Feed entries, tag pages, blog index |
keywords | Comma-separated tags | Tag pages, <meta> keywords |
image | Social sharing image URL | Open Graph, Twitter Card |
draft | Mark as draft (true/false) | Excluded from build unless --include-drafts |
publishDate | Scheduled publication date | Excluded until date is reached |
lang | Language code (e.g., fr) | <html lang>, hreflang links |
translationOf | Base page name for translations | Hreflang link generation |
menu | Show in navigation (true/false) | Navigation menu |
noindex | Prevent search indexing (true/false) | <meta name="robots"> |
toc | Table of contents (true/false/auto) | TOC generation |
toc_depth | TOC heading depth (1-4) | Number of heading levels in TOC |
sitemap_changefreq | Sitemap change frequency | sitemap.xml |
sitemap_priority | Sitemap priority (0.0-1.0) | sitemap.xml |
- Published — Default state; included in all builds
- Draft — Set
draft: true; excluded unless--include-draftsis passed - Scheduled — Set
publishDateto a future date; excluded until that date - Archived — Older content; built but excluded from navigation
Create translation files with a language suffix (e.g., about.fr.md):
--- title: "À propos" lang: "fr" translationOf: "about" --- Contenu en français...Translations are output to output/{lang}/ subdirectories with automatic hreflang link generation.
Emoji — Use shortcodes like :smile:, :rocket:, :wave:
Mathematics — Inline: \( E = mc^2 \), Display: \[ x = \frac{-b \pm \sqrt{b^2-4ac}}{2a} \]
Code blocks — Use fenced code blocks with a language identifier:
```csharp Console.WriteLine("Hello!"); ```Mermaid diagrams — Use mermaid as the language:
```mermaid graph TD; A-->B; A-->C; ```Place images and other assets in input/assets/. They are copied to output/assets/ during build.
| Target | Depends On | Description |
|---|---|---|
ValidateConfig | — | Validates configuration parameters and loads .env |
Clean | — | Deletes the output directory |
GenerateHtml | Clean | Converts Markdown files to HTML using the template |
GenerateBlogIndex | GenerateHtml | Generates paginated blog index at /blog.html |
GenerateTagPages | GenerateHtml | Generates tag index and individual tag pages |
GenerateSitemap | GenerateHtml | Generates sitemap.xml from all HTML files |
GenerateRobotsTxt | GenerateHtml | Generates robots.txt |
GenerateFeed | GenerateHtml | Generates Atom feed.xml from dated content |
GenerateSearchIndex | GenerateHtml | Generates search-index.json for client-side search |
CopyAssets | Clean | Copies input/assets/ to output/assets/ |
CopyJsScripts | Clean | Copies template/js/ to output/js/ |
CopyCss | Clean | Copies template/css/ to output/css/ |
BuildWebsite | All above | Orchestrates the full site generation |
OptimizeImages | CopyAssets | Resizes images, generates WebP, adds lazy loading |
OptimizeOutput | BuildWebsite | Minifies HTML and fingerprints CSS/JS assets |
Validate | OptimizeOutput | Validates HTML, accessibility, SEO, and links |
GenerateBuildReport | BuildWebsite | Generates build-report.json with build statistics |
CompressOutput | OptimizeOutput | Creates site.zip from the output directory |
BuildDockerImage | BuildWebsite | Builds an nginx Docker image with the site |
DeployDockerImage | BuildDockerImage | Runs the Docker container locally |
Watch | BuildWebsite | Starts dev server with live reload on port 3000 |
NewPost | — | Scaffolds a new dated blog post |
NewPage | — | Scaffolds a new page |
Init | — | Initializes project structure |
Run any target with:
./build.sh <TargetName> --site-base-url "..." --site-title "..."The HTML template uses Scriban syntax. The active template is resolved from themes/{Theme}/template.html if a theme is set, otherwise template/template.html.
| Variable | Type | Description |
|---|---|---|
{{ site_title }} | string | Site title from configuration |
{{ page_title }} | string | Page title from front-matter or filename |
{{ description }} | string | Page description |
{{ keywords }} | string | Page keywords |
{{ author }} | string | Page author |
{{ date }} | string | Publication date |
{{ iso_date }} | string | ISO 8601 formatted date |
{{ og_type }} | string | Open Graph type (article or website) |
{{ schema_type }} | string | JSON-LD schema type (BlogPosting or WebPage) |
{{ noindex }} | bool | Whether to add noindex meta tag |
{{ content }} | string | Rendered HTML content |
{{ toc }} | string | Table of contents HTML |
{{ page_url }} | string | Absolute URL of the page |
{{ canonical_url }} | string | Canonical URL of the page |
{{ image_url }} | string | Social sharing image URL |
{{ analytics_snippet }} | string | Analytics script HTML |
{{ lang }} | string | Page language code |
{{ hreflang_links }} | list | Alternate language links (.lang, .href) |
{{ menu }} | list | Navigation menu items (.title, .url) |
{{ tags }} | list | Page tags (.name, .url) |
Themes are stored in themes/ with this structure:
themes/ ├── default/ # Default theme │ ├── template.html │ ├── theme.yaml │ ├── css/ │ └── js/ ├── docs/ # Documentation theme │ ├── template.html │ └── theme.yaml └── minimal/ # Minimal theme ├── template.html └── theme.yaml Select a theme with --theme <name>. If the theme directory doesn't exist, it falls back to template/.
template/template.html— Main HTML templatetemplate/js/— JavaScript files (theme switcher, Prism.js, modal)template/css/— CSS files (Prism.js theme)
StaticWGen uses a lightweight nginx Alpine image to serve the generated static files.
# Build and deploy in one step ./build.sh DeployDockerImage \ --site-base-url "https://example.com" \ --site-title "My Site" \ --image-name my-site \ --version-tag 1.0.0 \ --container-name my-site \ --host-port 8080 \ --container-port 80Or build the Docker image manually:
# Generate the site first ./build.sh BuildWebsite --site-base-url "https://example.com" --site-title "My Site" # Build and run docker build -t my-site . docker run -d -p 8080:80 --name my-site my-site- Fork the repository
- Create a feature branch from
dev - Make your changes
- Ensure the build passes:
./build.sh BuildWebsite --site-base-url "http://localhost:8080" --site-title "Test" - Submit a pull request to
dev
StaticWGen/ ├── build/ # NUKE build targets (C# interfaces) │ ├── Build.cs # Main build class │ ├── IHasWebsitePaths.cs # Shared paths and core parameters │ ├── IClean.cs # Clean target │ ├── IGenerateWebsite.cs # HTML generation │ ├── IGenerateBlogIndex.cs # Paginated blog index │ ├── IGenerateFeed.cs # Atom feed generation │ ├── IGenerateTagPages.cs # Tag page generation │ ├── IGenerateSearchIndex.cs # Search index generation │ ├── IGenerateBuildReport.cs # Build report generation │ ├── ISitemap.cs # Sitemap generation │ ├── IRobotsTxt.cs # robots.txt generation │ ├── IOptimizeImages.cs # Image optimization │ ├── IOptimizeOutput.cs # HTML minification & asset fingerprinting │ ├── IValidateConfig.cs # Configuration validation │ ├── IValidateOutput.cs # Output validation (HTML, a11y, SEO) │ ├── IWatch.cs # Development server with live reload │ ├── IScaffold.cs # Content scaffolding (NewPost, NewPage, Init) │ ├── ICompressOutput.cs # Output compression │ ├── IDockerOperations.cs # Docker build & deploy │ └── Helpers/ # Shared utilities ├── input/ # Markdown source files │ ├── assets/ # Static assets (images, etc.) │ └── *.md # Content pages ├── template/ # Legacy HTML template and assets │ ├── template.html # Scriban template │ ├── js/ # JavaScript files │ └── css/ # CSS files ├── themes/ # Theme variants │ ├── default/ # Default theme │ ├── docs/ # Documentation theme │ └── minimal/ # Minimal theme ├── src/ # Core library source code ├── tests/ # Test files ├── output/ # Generated site (git-ignored) ├── .github/workflows/ # CI/CD pipelines ├── .env.example # Environment variables template ├── Dockerfile # nginx deployment ├── nginx.conf # nginx configuration └── build.sh # Build entry point This project is licensed under the MIT License — see the LICENSE file for details.