sigildocs

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 --serve

This 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 location

The with-api-docs operator:

  • Inserts scan-api-docs stage into the pipeline
  • Adds generate-docs-css if not present
  • Adds watch patterns for .sgb and .json files

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 directory

Development Server

sigil publish --serve           # Build + serve on port 8080
sigil publish -s -p 3000        # Serve on port 3000
sigil publish -s --drafts       # Include draft content

The 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

FieldDescription
titlePage title (used in navigation and <title>)
nav_orderSort order in navigation (lower = earlier)
nav_titleShort title for navigation (defaults to title)
nav_groupGroup pages under a section
draftIf true, excluded unless --drafts is set
templateCustom 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 files
  • scan-assets - Scan static assets (images, CSS, JS)
  • scan-api-docs - Scan module documentation from JSON
  • scan-library-docs - Scan package documentation

Processing stages (transform items):

  • filter-drafts - Remove items marked as drafts
  • build-navigation - Build nav structure from frontmatter
  • build-ref-index - Build cross-reference index
  • build-path-index - Build URL-to-item lookup

Output stages (produce files):

  • attach-content-producers - Set up HTML rendering
  • attach-asset-producers - Set up asset copying
  • generate-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.html

Use 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