Zenith Efficient Static Toolkit
License · Quick Start · Docs
Zest is a hybrid F# + C# static site generator where templates are real code — not strings. Built on the philosophy that your templating language and your host language should be one and the same.
- Template as Code —
.zpage.fsxare real F# scripts executed at build time viadotnet fsi. Full F#: list comprehensions, pattern matching, string interpolation, arbitrary computation. .zhtmlLightweight Pages — Pure HTML pages with optional Nunjucks template syntax. No FSI overhead.- HTML DSL — Compose HTML declaratively:
render [ h1 []; p [] ]. - Markdown — Standard
.mdfiles with frontmatter support. - ZCSS — A CSS superset with nesting, F#-style
letbindings, math expressions, color functions, and mixins — compiled to standard CSS. - ZestNjk Templates — Nunjucks-compatible template engine for layouts: filters, expressions, macros,
{% if %},{% for %}, template inheritance, Zest API integration. Files use.znjkextension. _init.fsx— Optional initialization script (runs before build) to inject dynamic data, load JSON/TOML, read env vars.- TOML Config — Zero-config defaults; customize via
_config.tomland_data/*.toml. No YAML. - Live Reload —
zest servewatches for changes and auto-rebuilds. - Batch Evaluation — Multiple F# page scripts evaluated in a single FSI process for fast builds.
- Incremental Builds — File change detection skips unchanged pages and assets.
- Cross-Platform — Builds for Windows x64, Linux x64/ARM64, macOS ARM64.
# Scaffold a new project
zest init my-site
# Develop with live reload
cd my-site && zest serve --port 8080
# Build for production
zest build
# Preview the built site
zest preview// @title Hello World
// @layout default
// @description My first Zest page
let pageTitle = "Hello from F#"
let items = ["F#"; "Zest"; "SSG"]
render [
h1 [ text pageTitle ]
p [ text "This page is generated by real F# code at build time." ]
ul [ for i in items -> li [ text i ] ]
]// F#-style let bindings with math expressions
let primary = #3b82f6
let space1 = 0.25r
let space4 = space1 * 4 // 1rem
let primary-light = primary |> lighten(45%)
// Two-letter property shorthands
.tag
c: $primary
bgc: $primary-light
py: $space4
bdr: 9999px
Compiles to:
.tag {
color: #3b82f6;
background-color: #adf4ff;
padding-block: 1rem;
border-radius: 9999px;
}// _init.fsx — runs before every build
addGlobal "api_url" "https://api.example.com"
let team = loadJson "data/team.json"
addGlobal "team" team
let env = loadEnv "ZEST_ENV"
if env = "production" then
addGlobal "analytics_id" "UA-XXXXX-Y"my-site/
├── _config.toml # Site configuration (TOML)
├── _init.zpage.fsx # Optional init script (runs before build)
├── _data/
│ └── site.toml # Global data (accessible from scripts/templates)
├── content/
│ ├── index.zpage.fsx # Home page (F# script template)
│ ├── about.md # About page (Markdown)
│ └── posts/
│ ├── hello-world.zpage.fsx
│ └── contact.zhtml # Pure HTML (no FSI overhead)
├── _layouts/
│ ├── default.html # Layouts (Nunjucks or native replace)
│ └── post.html
├── assets/
│ └── css/
│ └── style.zcss # ZCSS → auto-compiled to style.css
└── _site/ # Build output (auto-generated)
| Project | Language | Responsibility |
|---|---|---|
| Zest.App | C# | CLI entry point, command routing |
| Zest.Engine | F# | Core engine: builds, HTML DSL, ScriptRunner, Markdown, ZCSS compiler, ZestNjk template engine |
| Zest.Dsl | F# | Precompiled DSL helpers for FSI script evaluation |
| Zest.Infra | C# | Configuration loading, file watching, dev server |
git clone https://github.com/YOUR_USER/zest
cd zest
dotnet build Zest.sln
# Publish for your platform
dotnet publish src/Zest.App/Zest.App.csproj -c Release -r win-x64 --self-contained false
# Linux: -r linux-x64
# macOS: -r osx-arm64| Extension | Purpose | Processing |
|---|---|---|
.zpage.fsx |
F# script templates (F# + Markdown + HTML DSL) | Compiled via dotnet fsi |
.zhtml |
Pure HTML pages | Copied as-is (optional ZestNjk) |
.znjk |
Zest Nunjucks templates (Nunjucks-compatible syntax with Zest API integration) | Rendered via ZestNjkEngine — supports filters, expressions, {% if %}, {% for %}, macros, template inheritance |
.zcss |
ZCSS stylesheets (CSS superset) | Compiled to .css |
.md |
Standard Markdown | Rendered to HTML |
.toml |
Configuration and data (no YAML) | Parsed at build time |
| Command | Description |
|---|---|
zest build |
Build the site to _site/ |
zest serve |
Start dev server with live reload |
zest preview |
Preview the built site |
zest init <name> |
Scaffold a new project |
zest clean |
Clean build output |
| Feature | Syntax |
|---|---|
| Variables (SCSS) | $name: value; |
| Variables (F#) | let name = value |
| Math | let x = 0.25r * 4 |
| Color functions | lighten(#hex, %), darken(#hex, %), mix(a, b, %) |
| Pipe operator | value |> fn(args) → fn(value, args) |
| Unit shorthands | r→rem, p→% |
| Property shorthands | py→padding-block, mx→margin-inline, bgc→background-color |
| Nesting | Indent or brace mode |
| Mixins | @mixin, @include |
| Loops | @each, @for |
| Conditionals | @if, @else |
| Built-in modules | @use "zest:utilities", @use "zest:palette", etc. |
| Engine | Config Value | Features |
|---|---|---|
| ZestNjk (default) | template_engine = "znjk" |
Filters, expressions, {% if %}, {% for %}, macros, template inheritance, Zest API filters (pages_by_tag, recent, by_collection, search, where) |
| Native Replace | template_engine = "replace" |
Simple {{ variable }} substitution |
// Elements
h1 [ text "Title" ]
p [ text "Paragraph" ]
a [ href "https://example.com"; text "Link" ]
// Attributes
div [ class' "container"; id "main" ] [ ... ]
// CSS class shortcuts
divC "card" [ p [ text "Content" ] ] // <div class="card">
spanC "badge" [ text "New" ] // <span class="badge">
// List comprehensions
ul [ for item in items -> li [ text item ] ]
// Conditionals
if condition then
p [ text "Yes" ]
else
p [ text "No" ]| Function | Purpose |
|---|---|
addGlobal key value |
Inject key-value into global data |
loadJson path |
Parse JSON file |
loadToml path |
Parse TOML file |
loadEnv key |
Read environment variable |
console_log msg |
Debug output to stderr |
exec cmd args |
Run shell command |
Zest is not a general-purpose static site generator. It is a specific answer to specific constraints.
- F# as the Template — The template is the program.
.zpage.fsxfiles are real F# code, not strings. - ZCSS as the Layout Engine — Not a CSS pre-processor, but a layout engine that emits CSS.
- TOML as the Contract — No YAML. Ever.
- JavaScript as Order — No Node.js, no npm, no bundlers. JavaScript exists only for client-side interactivity.
- The Zealous Few — Built for those who love F#, hate YAML, and prefer simple tools.
Apache 2.0 — see LICENSE.
