Skip to content
/ orgy Public

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

58 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Orgy

https://img.shields.io/badge/versioning-intver.org-blue.svg?style=for-the-badge

A static blog generator that turns Org mode files into a website. Powered by organ and Selmer, runs on Babashka.

Install

Assuming bbin is installed:

bbin install io.github.bzg/orgy

Features

  • Org files parsed to AST and exported to HTML (via organ)
  • Content sections: subdirectories become categories
  • Monolingual/multilingual detection: files go to public/slug/ by default, or public/en/slug/ when multiple languages are detected post.fr.org and post.en.org produce separate indexes and RSS feeds
  • Tag pages and tag index
  • Client-side quick search (opt-in via :quick-search true)
  • Light/dark theme toggle (opt-in via :theme-toggle true)
  • Navigation menu with support for direct links
  • Draft support (#+draft: true excludes a file from export)
  • SEO: meta description, Open Graph tags, sitemap.xml
  • CSS theme (pico-themes name, URL or local file)
  • Syntax highlighting enabled via highlight.js
  • LaTeX math supported natively.

Orgy will not support:

  • Markdown
  • Org babel execution

Quick start

# Build the current directory as a website in public/
orgy

# Build myblog as a website in ./public
orgy -i myblog

# Bootstrap a site: writes myblog/config.edn and myblog/templates/
orgy -i myblog init

# Build and serve locally, watching for changes
orgy -i myblog serve

# Show all options
orgy help

You can also check this tutorial: Get ready for Orgy in 15 minutes

Once orgy serve launches the server, open http://localhost:1888 in to preview the site. The server rebuilds automatically when you edit your org files — just refresh the page to see the changes. Use serve PORT to pick a different port (e.g. orgy -i myblog serve 3000).

Example directory layout

myblog/
  config.edn            # site configuration (created by `init`)
  index.en.org           # homepage (optional, else 10 latest posts)
  index.fr.org           # French homepage
  about.en.org           # root-level page → /en/about/
  notes/                 # section → /en/notes/, /fr/notes/
    my-post.en.org       # post → /en/notes/my-post/
    my-post.fr.org       # post → /fr/notes/my-post/
    diagram.png          # copied to /notes/diagram.png
  static/                # copied verbatim to output root
    img/photo.jpg        # → /img/photo.jpg
    favicon.ico          # → /favicon.ico
  templates/             # custom Selmer templates (created by `init`)
  • Sections: any subdirectory (except static/, templates/, public/, .git/) is a section with its own index page.
  • Static assets: static/ contents are copied to the output root. Non-org files elsewhere in the tree are also copied, preserving paths.
  • Language detection: from filename suffix (post.fr.org → French) or #+language: header. No suffix and no header defaults to "en".
  • Index pages: index.{lang}.org (or index.org) at the root renders as the homepage for that language. When absent, orgy generates a listing of the 10 most recent posts instead.

Org file headers

Each .org file supports these headers:

#+title: My Post Title
#+date: 2026-03-28
#+author: Author Name
#+language: fr
#+tags: emacs org-mode
#+draft: true

All headers are optional. Files with #+draft: true are excluded from the build. Files without #+title still appear in the nav (using the filename as label).

CLI

Usage: orgy [options] [command]

Options:
  -i, --input-dir DIR   Input directory containing org files (default: .)
  -o, --output-dir DIR  Output directory (default: ./public)
  -c, --config FILE     Path to config.edn (default: input-dir or working dir)
  -C, --config-write    Write a default config.edn in the input directory
  -s, --skip-dirs DIRS  Comma-separated directories to skip (e.g. drafts,old)
  -t, --theme VALUE     CSS theme (see examples below)
  -h, --help            Show this help

Commands:
  (none)           Build the static site
  serve [port]     Build and serve locally (default port: 1888)
  init             Export config.edn and templates for customization
  clean            Remove the output directory
  help             Show this help

The --theme option accepts four forms:

# pico-themes name → loaded from CDN as <link>
orgy -i myblog -t teletype

# https:// URL → loaded as <link>
orgy -i myblog -t https://example.com/my-theme.css

# file:/// URL → inlined as <style>
orgy -i myblog -t file:///home/user/mytheme.css

# local .css path (relative or absolute) → inlined as <style>
orgy -i myblog -t ./themes/custom.css

Configuration

All keys in config.edn are optional:

KeyDefaultDescription
:titledirectory nameSite title
:base-url""Base URL for absolute links (RSS, sitemap, etc.)
:copyright""Footer copyright notice
:languagesinferredLanguages to render; inferred from posts when omitted
:menuallOrdered list of nav entries (see below)
:themenoneCSS theme (name, URL, or path — see CLI section)
:quick-searchfalseEnable client-side quick search
:theme-togglefalseShow a light/dark theme toggle in the nav bar

When :languages is omitted, orgy infers the language set from post filenames (post.fr.orgfr) and #+language: headers, falling back to ["en"] only when nothing is detected — so an all-French blog never gets a phantom English subtree.

Menu configuration

When :menu is omitted, all root-level pages and sections appear in the nav. To control what appears and in what order:

:menu ["notes"                                                    ;; section by slug
       "about"                                                    ;; root page by slug
       {:url "https://github.com/me" :description "GitHub"}]      ;; direct link

A “Tags” link is always appended after the menu entries. In multilingual mode, language switcher links are also appended.

Templates

Run orgy -i DIR init to export a default config.edn and the full set of Selmer templates into DIR/templates/. You can then edit them freely. To only write the config without the templates, use orgy -i DIR --config-write.

Available templates:

  • base.html – page layout (head, nav, footer, search, highlight.js)
  • post.html – single post / page
  • list.html – post listing (section index, fallback homepage)
  • tag.html – posts for a given tag
  • tags-index.html – all tags
  • feed.xml – RSS feed
  • sitemap.xml – XML sitemap
  • redirect.html – root-to-language redirect used in multilingual mode

Templates use Selmer syntax. Template variables include title, date, tags, content, lang, menu, site.title, site.copyright, description, canonical, has-code, hl-langs, lang-prefix, theme-link, and theme-inline.

Output structure

Monolingual (default)

When no multilingual indicator is detected (no :languages with multiple entries, no :menu map, no .xx.org file suffixes, no distinct #+language: headers), pages are generated directly under public/:

public/
  index.html              # homepage
  feed.xml                # RSS feed
  search.json             # search index (when :quick-search is true)
  sitemap.xml             # generated when base-url is set
  notes/
    index.html            # section listing
    my-post/
      index.html          # post
  tags/
    index.html            # all tags
    emacs/
      index.html          # posts tagged "emacs"
  img/                    # from static/
  favicon.ico             # from static/

Multilingual

When multiple languages are detected, each language gets its own subtree and a root index.html redirects to the first language:

public/
  index.html              # redirect to first language
  sitemap.xml             # generated when base-url is set
  en/
    index.html            # homepage
    feed.xml              # RSS feed
    search.json           # search index (when :quick-search is true)
    notes/
      index.html          # section listing
      my-post/
        index.html        # post
    tags/
      index.html          # all tags
      emacs/
        index.html        # posts tagged "emacs"
  fr/
    ...
  img/                    # from static/
  favicon.ico             # from static/

Contributing

You can also send me an email and support my work on liberapay.

Intentional Versioning

This project uses Intentional Versioning, here are the three audiences:

  • Users : end users who publish static website with orgy
  • Integrators : external packagers
  • Maintainers : maintainers of the codebase

Support the Clojure(script) ecosystem

If you like Clojure(script), please consider supporting maintainers by donating to clojuriststogether.org.

License

Copyright © 2026 Bastien Guerry

The Clojure code is distributed under the Eclipse Public License 2.0 and the Javascript code is distributed under the Mozilla Public License 2.0.

About

Clojure static website engine for Org files

Resources

Stars

Watchers

Forks

Releases

Sponsor this project

Packages

Used by

Contributors

Languages