Building Static Sites
This guide covers building static websites with sigil publish. You'll learn to create documentation sites, blogs, and custom static sites using Sigil's composable pipeline architecture.
Overview
The sigil publish command builds static sites from content files (Markdown, SXML) and assets. It provides:
- Pipeline architecture: Composable stages for scanning, transforming, and rendering content
- Dev server: Live reload for rapid development
- Site operators: Composable functions to add API docs, themes, and more
- Themes: Customizable HTML rendering with CSS generation
Quick Start
Create a site.sgl configuration file in your project root:
(import (sigil publish))
(site title: "My Blog"
content: "posts/**/*.md"
assets: "static/**/*")Build and serve:
sigil publish --serveThis creates a site at http://localhost:8080 with:
- Markdown files rendered as HTML
- Navigation built from frontmatter
- Live reload on file changes
The site Function
The site function creates a site specification with sensible defaults:
(site
title: "My Site" ; Site title
content: "docs/**/*.md" ; Content glob pattern(s)
assets: "static/**/*" ; Asset glob pattern
output-dir: "out" ; Output directory
base-url: "/" ; Base URL for links
theme: default-theme ; Theme function
transforms: (list rewrite-links))Multiple Content Sources
Pass a list to content: for multiple content directories:
(site title: "My Site"
content: (list "docs/**/*.md"
"blog/**/*.md"
"pages/**/*.md"))Site Operators
Site operators modify a site spec using the threading macro ->. This enables composable configuration:
(import (sigil publish)
(sigil publish docs))
(-> (site title: "My Project"
content: "docs/**/*.md")
(with-docs-theme)
(with-api-docs "build/dev/lib"
output-prefix: "/api/"
filter: (lambda (path)
(string-starts-with? path "myproject/"))))Available Operators
with-docs-theme - Apply the documentation theme:
(-> (site ...)
(with-docs-theme))with-api-docs - Add API documentation from compiled modules:
(-> (site ...)
(with-api-docs "build/dev/lib"
output-prefix: "/api/" ; URL prefix for API pages
filter: (lambda (path) ...) ; Filter which modules to include
packages-dir: "packages")) ; Package metadata locationThe with-api-docs operator:
- Inserts
scan-api-docsstage into the pipeline - Adds
generate-docs-cssif not present - Adds watch patterns for
.sgband.jsonfiles
The docs-site Helper
For documentation-focused sites, docs-site provides an all-in-one configuration:
(import (sigil publish docs))
(docs-site
title: "My Project"
lib-dir: "build/dev/lib"
lib-filter: (lambda (path)
(string-starts-with? path "myproject/")))This is equivalent to:
(-> (site title: "My Project"
content: "docs/**/*.md"
assets: "docs/static/**/*")
(with-docs-theme)
(with-api-docs "build/dev/lib" ...))Command Options
sigil publish [options] Options: -c, --config FILE Configuration file (default: site.sgl or publish.sgl) -o, --output DIR Override output directory -s, --serve Build and serve with live reload -p, --port PORT Server port (default: 8080) -d, --drafts Include draft content
Build Only
sigil publish # Build using site.sgl
sigil publish -o dist # Build to custom directoryDevelopment Server
sigil publish --serve # Build + serve on port 8080
sigil publish -s -p 3000 # Serve on port 3000
sigil publish -s --drafts # Include draft contentThe dev server watches source files and rebuilds automatically when changes are detected.
Content
Markdown Files
Markdown files in your content directory are converted to HTML. Use YAML frontmatter for metadata:
---
title: Getting Started
nav_order: 1
draft: false
---
# Getting Started
Your content here...Frontmatter Fields
| Field | Description |
|---|---|
title | Page title (used in navigation and <title>) |
nav_order | Sort order in navigation (lower = earlier) |
nav_title | Short title for navigation (defaults to title) |
nav_group | Group pages under a section |
draft | If true, excluded unless --drafts is set |
template | Custom template for this page |
SXML Files
For complex pages, use SXML (S-expression XML):
;; site/index.sxml
(html
(head (title "Welcome"))
(body
(h1 "My Project")
(p "A brief description.")
(a (@ (href "/docs/")) "Read the docs")))Custom Pipelines
For full control, pass a custom pipeline: to the site function:
(import (sigil publish)
(sigil publish scan)
(sigil publish stages)
(sigil publish docs))
(site
title: "My Site"
output-dir: "public"
theme: docs-theme
transforms: (list rewrite-links syntax-highlight)
pipeline: (list
;; Scan content
(scan-files "docs/**/*.md" output-prefix: "/docs/")
(scan-files "blog/**/*.md" output-prefix: "/blog/")
(scan-assets "static/**/*")
;; Process
filter-drafts
build-navigation
build-ref-index
build-path-index
;; Render
attach-content-producers
attach-asset-producers
generate-docs-css)
watch-patterns: (list "docs/**/*.md"
"blog/**/*.md"
"static/**/*"))Modifying the Default Pipeline
Pass a procedure to pipeline: to modify the auto-generated pipeline:
(site
title: "My Docs"
content: "docs/**/*.md"
assets: "static/**/*"
;; Add CSS generation to the default pipeline
pipeline: (lambda (stages)
(append stages (list generate-docs-css))))Pipeline Stages
Stages are functions that transform site state. Built-in stages include:
Scanning stages (create items from files):
scan-files- Scan Markdown/SXML filesscan-assets- Scan static assets (images, CSS, JS)scan-api-docs- Scan module documentation from JSONscan-library-docs- Scan package documentation
Processing stages (transform items):
filter-drafts- Remove items marked as draftsbuild-navigation- Build nav structure from frontmatterbuild-ref-index- Build cross-reference indexbuild-path-index- Build URL-to-item lookup
Output stages (produce files):
attach-content-producers- Set up HTML renderingattach-asset-producers- Set up asset copyinggenerate-docs-css- Generate theme CSS file
Custom Stages
Create custom stages as functions taking (config state) and returning updated state:
(define (add-build-timestamp config state)
(let ((timestamp (current-date-string)))
(state-update-items state
(lambda (items)
(map (lambda (item)
(item-set-metadata item 'build-time timestamp))
items)))))Themes
Themes are functions that wrap content in HTML structure:
(define (my-theme context content)
(let ((title (dict-ref context title:))
(nav (dict-ref context navigation:)))
`(html
(head
(title ,title)
(link (@ (rel "stylesheet") (href "/style.css"))))
(body
(nav ,@(render-nav nav))
(main ,@content)
(footer "Built with Sigil")))))Use your theme in the config:
(site title: "My Site"
theme: my-theme
...)Built-in Themes
default-theme- Minimal theme from(sigil publish)docs-theme- Documentation theme from(sigil publish docs)
API Documentation
Generate API docs from compiled Sigil modules using the with-api-docs operator:
(import (sigil publish)
(sigil publish docs))
(-> (site title: "My Library"
content: "docs/**/*.md")
(with-docs-theme)
(with-api-docs "build/dev/lib"
filter: (lambda (path)
(string-starts-with? path "mylib/"))))This requires modules to be compiled with sigil build first. The build process extracts docstrings and exports them as JSON files alongside the compiled bytecode.
Docstring Format
Document your exports with ;;; comments:
;;; Parse a JSON string into Sigil data structures.
;;;
;;; Returns nested dicts, lists, strings, numbers, booleans, and 'null.
;;; Raises an error if the input is not valid JSON.
;;;
;;; Examples:
;;; ```scheme
;;; (json-parse "{\"name\": \"Alice\"}") ; => #{"name": "Alice"}
;;; (json-parse "[1, 2, 3]") ; => (1 2 3)
;;; ```
(define (json-parse str) ...)Example: Full Documentation Site
Here's a complete example for a project with guides and API docs:
;;; site.sgl - Project documentation site
(import (sigil publish)
(sigil publish docs)
(sigil fs)
(sigil io))
;; Warn if API docs won't be generated
(when (not (directory-exists? "build/dev/lib"))
(println "")
(println "WARNING: build/dev/lib not found - API docs will not be generated.")
(println " Run 'sigil build' first to compile modules.")
(println ""))
(-> (site title: "My Project"
content: "docs/**/*.md"
assets: "static/**/*"
output-dir: "public")
(with-docs-theme)
(with-api-docs "build/dev/lib"
output-prefix: "/api/"
filter: (lambda (path)
(string-starts-with? path "myproject/"))))Tips
Organize Content with Directories
docs/
index.md # /docs/
getting-started.md # /docs/getting-started.html
guides/
index.md # /docs/guides/
basics.md # /docs/guides/basics.html
advanced.md # /docs/guides/advanced.htmlUse nav_group for Sections
---
title: Installation
nav_group: Getting Started
nav_order: 1
---Cross-Reference with ref::
Link to other pages or API items:
See [ref::getting-started] for setup instructions.
The [ref::json-parse] function handles JSON decoding.Debug with --drafts
Mark work-in-progress content as drafts and use --drafts during development:
---
title: New Feature (WIP)
draft: true
---sigil publish -s --drafts