STATIC SITE GENERATOR

Citadel

citadel — construct_page.rs

Data in. HTML out. Nothing else.

Four stages. One direction.

01

Define

Pages are enum variants. Routes are types. If a page doesn't exist in your enum, it doesn't compile.

02

Construct

Plain functions receive site + page, build HTML. No lifecycle hooks. No inheritance. Just functions.

03

Decree

Global find-and-replace across every page. CSS variables, year stamps, site-wide tokens. Decreed once, applied everywhere.

04

Write

Static HTML files. Sitemap. Robots. RSS. Optimised images. Written to disk. Deployed anywhere.

Every SSG is the same problem wearing a different hat

Template Archaeology

Your page is a template that extends a layout that includes a partial that references a config that pulls from a data file. You need five files open to understand one page.

Plugin Roulette

Need sitemap generation? Install a plugin. Need image optimisation? Another plugin. Need SEO meta tags? Another. Each with its own config format, its own bugs, its own abandonment timeline.

Framework Theatre

You learn the framework's opinions; its routing conventions, its build hooks, its special directories. Then the framework updates. Your site breaks. You archaeology again.

AI-Hostile

An AI working with your site needs to reconstruct your mental model from fragments scattered across dozens of files. Templates here, config there, logic somewhere else. No single file tells the whole story.

What if a page was just a function?

Citadel is a Rust library. Your pages are data. Your constructors are plain functions. CSS is declared alongside HTML in the same function scope. There are no templates, no themes, no plugins, no config files, no magic directories.

One file. One function. One page. The entire mental model fits in a single scroll.

An AI reads one function and understands the entire page: the HTML, the CSS, the routing, the SEO. That's not a feature. That's the architecture.

Built for the context window

Traditional SSG
  • Template file
  • Layout file
  • Partial files (3-5)
  • Config file
  • CSS file(s)
  • Plugin configs
  • Data files

7-12 files to understand one page

Citadel
  • Constructor function

1 file. Everything.

When an AI builds with Citadel, it sees the whole page in a single function. No context chasing. No framework archaeology. No guessing where the CSS lives. We built it because AI needed an SSG it could actually use.

Real code from this site

site_data.rs — Type-safe page routing
pub enum WIPages {
    Homepage,
    Services,
    Contact,
    Product(ProductId),
    BlogHub,
    InsightsList,
    InsightsPost(PostData<WIFrontmatter>),
    EmergenceList,
    EmergencePost(PostData<WIFrontmatter>),
    CaseStudies,
}
construct_homepage.rs — One function, one page
pub fn construct_homepage(
    site: &mut Site<WIPages, ()>,
    page: &mut Page<WIPages>,
) {
    let head = site.construct_head(page);
    css(site);

    page.foundation.content = Some(format!(r##"
        <!DOCTYPE html>
        <html lang="en">
        {head}
        <body>
            <main class="homepage">
                ...
            </main>
        </body>
        </html>
    "##));
}
main.rs — The pipeline
site = site
    .add_constructor(Homepage, construct_homepage)
    .add_constructor(Services, construct_services)
    .add_constructor(Product(Citadel), construct_citadel)
    .add_head_constructor()
    .add_pages(pages);

site.commence();
declare_css — Scoped, idempotent, ordered
site.declare_css("homepage", r##"
    main.homepage {
        .hero {
            min-height: 100vh;
            display: flex;
            align-items: center;
        }
        .features {
            display: grid;
            grid-template-columns: repeat(3, 1fr);
        }
    }
"##);

Declare everything. Configure nothing.

declare_css(key, css)

CSS declared with a semantic key. Idempotent. Declare the same key from anywhere, it's only included once. Automatic priority ordering: foundation → typography → components.

declare_decree(key, value)

Global find-and-replace across every page. 2026 becomes 2026. Wilderness Interactive becomes your title. Works in HTML and CSS. Decreed once, applied everywhere.

declare_placement(pos, html)

Inject content at eight strategic positions: Fonts, Schemas, HeadTop, HeadBottom, BodyTop, BodyBottom, Analytics, Scripts. Site-wide, automatic, ordered.

construct_head(page)

Generates the entire <head> tag from page data. Title, meta description, Open Graph, Twitter Cards, canonical URL, CSS bundle, fonts, schemas. All from your PageFoundation.

Everything you need. Nothing you don't.

Type-Safe Routing

Pages are enum variants. Routes derived from types. If a page variant doesn't exist, your code doesn't compile. Discriminant matching handles dynamic content types: blog posts, products, categories. All through the same type system.

Automatic SEO

Sitemap generation, robots.txt, Open Graph tags, Twitter Cards, canonical URLs, meta descriptions, JSON-LD schemas. All generated from your page data. No plugins. No config files. Just data in, SEO out.

CSS Priority System

Foundation styles load first. Typography second. Page-specific CSS alphabetically after. Declare CSS from any function in any file. Citadel orders it correctly. No specificity wars. No !important hacks.

Content System

Generic frontmatter parsing with full markdown rendering. Define your own frontmatter struct, point at a directory, and Citadel parses, sorts, and hands you typed data. Posts, products, docs. Any content model you need.

Eight Placements

Fonts, schemas, head-top, head-bottom, body-top, body-bottom, analytics, scripts. Inject content at exactly the right position in the document. Site-wide. Automatic. No guessing.

Zero Runtime

Compiles to a native binary. Generates pure static HTML and CSS. No JavaScript framework shipped. No hydration step. No client-side routing. Pages load as fast as the network can deliver them.

Intelligent Assets

Images are automatically optimised, converted to modern formats, and served with correct dimensions. Responsive srcsets generated from a single source image. No manual resizing.

No Config Files

No YAML. No TOML. No JSON config. No special directories. Your site is a Rust program. Configuration is code. Type-checked, refactorable, version-controlled.

You're looking at it.

This entire site, every page, every blog post, every product page you're reading, is built on Citadel. So are the client sites serving over 30,000 monthly visitors. The CSS was declared with declare_css. The head was built with construct_head. The sitemap was generated automatically. Content drops in as markdown, compiles, and deploys.

22 Pages
0 JS Frameworks
100 Lighthouse Score

We don't sell a vision. We ship on it.

What zero runtime actually means

~0ms

Time to Interactive. There's no JavaScript to parse, no framework to boot, no hydration step. The page is interactive the moment HTML arrives.

<50KB

Total page weight for a typical page. HTML + CSS. No framework bundle. No polyfills. No tree-shaking because there's nothing to shake.

~10ms

Full site build time. Native Rust binary. 22 pages, multiple content types, image optimisation, sitemap generation. Faster than your framework's dev server starts.

Sovereign software.

Citadel is available under the Citadel License. Build with it. Sell what you build. The tool itself remains sovereign. Because the things that matter shouldn't be owned by committees.

Get in Touch