<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom"><author><name>Peter Prevos</name></author>
<title>Praxis ❯ Digital Asceticism</title>
<subtitle>Praxis articles on The Horizon of Reason</subtitle>
<generator>Emacs webfeeder.el</generator>
<link href="https://peterprevos.com/praxis/"/>
<link href="https://peterprevos.com/praxis/atom.xml" rel="self"/>
<id>https://peterprevos.com/praxis/atom.xml</id>
<updated>2026-10-18T19:58:27+11:00</updated>
<entry>
  <title>Emacs as a Digital Fortress of Solitude</title>
  <author><name>Peter Prevos</name></author>
  <content type="html"><![CDATA[<main id="content" class="content"> <header> <h1 class="title">Emacs as a Digital Fortress of Solitude</h1>
 <p class="subtitle" role="doc-subtitle"> <span class="subtitle-row"> <span>Peter Prevos</span> <span>63 words</span></span> <span class="subtitle-row"> <span>16 October 2026</span> <span>1 min read</span></span></p>
</header> <p>
Ever since I owned a computer in the mid eighties I have been looking for a single program that would meet all my
</p>

 <p>
Only ten years ago I found out that this program existed all along, so I wasted 30 years of my digital life.
</p>

 <p>
So what is Emacs? f
Emacs is not just a text editor, it is a malleable computer system
</p>
</main>]]></content>
  <link href="https://peterprevos.com/praxis/emacs-as-a-fortress-of-digital-solitude/"/>
  <id>https://peterprevos.com/praxis/emacs-as-a-fortress-of-digital-solitude/</id>
  <updated>2026-10-16T00:00:00+11:00</updated>
</entry>
<entry>
  <title>Reading and Writing Gemini Capsules With Emacs Org Mode</title>
  <author><name>Peter Prevos</name></author>
  <content type="html"><![CDATA[<main id="content" class="content"> <header> <h1 class="title">Reading and Writing Gemini Capsules With Emacs Org Mode</h1>
</header> <p>
Before the internet as we know it today came into existence there was Gopher. This protocol allows users to access plain text menus to view writing, images, and enjoy other digital resources. When Brenners-Lee released the Wold Wide Web in 1991, Gopher slowly faded away in favour of a more graphical web. The world wide web was in these early days an exciting place where people shared ideas and connected with each other. Almost forty years later, AI-generated content and advertising dominates the digital world and we have lost its human dimension.
</p>

 <p>
Gemini is a modern protocol that is similar to Gopher, emphasising simplicity and security. Gemini extends Gopher's original functionality with modern features. The Gemini protocol is a minimalist, text-first alternative to the modern world-wide web. Gemini does not rely on scripts and complex styling and focuses on content. In this ecosystem, you don’t browse websites; you explore &quot;capsules&quot;.
</p>

 <p>
Given Gemini's focus on plain-text, Emacs is the ideal environment for both reading and writing within the Gemini space. This article explains how to use Emacs for reading and writing Gemini capsules.
</p>
 <section id="outline-container-org26f8f3d" class="outline-2"> <h2 id="org26f8f3d">Browsing Gemini capsules</h2>
 <div class="outline-text-2" id="text-org26f8f3d">
 <p>
The  <a href="https://thelambdalab.xyz/elpher/">Elpher Client</a> is an Emacs plugin to view Gopher holes and Gemini capsules. Some configuration are available to change the way Elpher presents information, such as:
</p>

 <div class="org-src-container">
 <pre class="src src-elisp"> <code>( <span style="font-weight: bold;">use-package</span> elpher
   <span style="font-weight: bold;">:custom</span>
  (elpher-gemini-link-string  <span style="font-style: italic;">&quot;🔗 &quot;</span>)
  (elpher-gemini-bullet-string  <span style="font-style: italic;">&quot;- &quot;</span>))
</code></pre>
</div>

 <p>
Start browsing with  <code>M-x elpher-go</code> and enter a Gemini or Gopher address. Some useful keybindings are (press  <code>?</code> for more):
</p>

 <ul class="org-ul"> <li> <code>O</code>: Back to capsule root page</li>
 <li> <code>R</code>: Reload the current page</li>
 <li> <code>^</code>: Back to previous page</li>
 <li> <code>TAB</code> and  <code>S-TAB</code>: Next and previous link</li>
</ul></div>
</section> <section id="outline-container-org53e8bc0" class="outline-2"> <h2 id="org53e8bc0">Writing a Gemlog</h2>
 <div class="outline-text-2" id="text-org53e8bc0">
 <p>
If you want to become an active participant in the world of Gemlogs, then Emacs also has you covered. Two methods are available to create a Gemini Capsule with Emacs:
</p>

 <ol class="org-ol"> <li>Write directly in Gemtext</li>
 <li>Write in Org mode and publish to Gemtext</li>
</ol></div>
 <div id="outline-container-org2eab665" class="outline-3">
 <h3 id="org2eab665">Write directly in Gemtext</h3>
 <div class="outline-text-3" id="text-org2eab665">
 <p>
You can write Gemtext files directly in Emacs with  <a href="https://git.carcosa.net/jmcbray/gemini.el">Gemini Mode</a> by Jason McBrayer. This package provides a major mode for editing Gemini files ( <code>.gmi</code>,  <code>.gemini</code> and  <code>.geminimap</code>) and provides some convenient keyboard shortcuts:
</p>

 <ul class="org-ul"> <li> <code>gemini-insert-link</code>: Insert a Gemini Link ( <code>C-c C-l</code>)</li>
 <li> <code>gemini-insert-list-item</code>: Start a new list item ( <code>C-c RET</code>)</li>
 <li> <code>gemini-insert-time-stamp</code>: Insert a timestamp ( <code>C-c C-t</code>) in accordance with the   <code>gemini-timestamp-format</code> template</li>
 <li> <code>gemini-open-link-at-point</code>: Open a link in Elpher ( <code>C-c C-o</code>)</li>
</ul> <p>
I have remapped the function to insert list items to  <code>C-c -</code>, for Org mode similarity.
</p>

 <div class="org-src-container">
 <pre class="src src-elisp"> <code>( <span style="font-weight: bold;">use-package</span> gemini-mode
   <span style="font-weight: bold;">:ensure</span> t
   <span style="font-weight: bold;">:custom</span>
  (gemini-timestamp-format  <span style="font-style: italic;">&quot;%d %B %Y, %H:%M&quot;</span>)
   <span style="font-weight: bold;">:bind</span> ( <span style="font-weight: bold;">:map</span> gemini-mode-map
              ( <span style="font-style: italic;">&quot;C-c -&quot;</span> . gemini-insert-list-item)))
</code></pre>
</div>

 <p>
A Gemini capsule is sometimes also called a Gemlog. Each Gemlog needs to at least have an  <code>index.gmi</code> page and the rest is up to you.
</p>
</div>
</div>
</section> <section id="outline-container-org2855f9a" class="outline-2"> <h2 id="org2855f9a">Publish a Capsule with Org mode</h2>
 <div class="outline-text-2" id="text-org2855f9a">
 <p>
While writing Gemtext in Emacs is easy, using Org mode adds more flexibility because you can export the same files as a website, ebook, or even a print-ready PDF for a book. The Org publishing functionality also provides some powerful abilities to add dynamic content and automatically generate sitemaps and other embellishments.
</p>

 <p>
The  <a href="https://git.sr.ht/~abrahms/ox-gemini">ox-gemini</a> package by Justin Abrams provides an Org mode export backend to either convert individual Org files, or publish a complete capsule. Most Org mode functionality doesn't translate to Gemtext, such as front matter keywords, and image captions, and the export module as such ignores them.
</p>

 <p>
You can add sections that are only included when exporting to Gemtext by wrapping them in a source code structure template, which appears with  <code>C-c C-, E gemini</code>. Any text written inside the export block only appears in Gemini exports. Alternatively, you can also add text in an  <code>exort_html</code> block so that it only appears in all the web version of your page, and so on.
</p>

 <div class="org-src-container">
 <pre class="src src-gmi"> <code>#+begin_export gemini
```
⠀⠀⠀⠀⡄⠀⢻⡄⢠⡛⠀⢠⠀⠀⠀⠀
⠀⠀⠀⠀⠘⣧⠸⠇⠸⠇⣼⠃⠀⠀⠀⠀
⠀⠘⠶⠶⡄⣠⣶⣿⣿⣶⣄⢠⠶⠶⠃⠀
⠰⠶⣤⡄⣼⣿⣿⣿⣿⣿⣿⣧⢠⣤⠶⠆
```
#+end_export
</code></pre>
</div>

 <p>
Exporting individual pages works through the usual  <code>org-export-dispatch</code> menu ( <code>C-c C-e</code>) and selecting the appropriate menu options.
</p>

 <p>
Exporting each page individually can be tedious, so Org mode's publishing function has you covered. With this functionality you point Org mode to your source files and provide it with a destination, indicate the export format, and Emacs does the rest. 
</p>

 <p>
To publish a complete Gemini capsule written in Org, create a project directory to start writing content, for example  <code>~/projects/gemini/content/</code>. This directory will contain your capsule in Org mode, including any images or other attachments. For each project component, the following minimal options are available:
</p>

 <ul class="org-ul"> <li> <code>:base-directory</code>: Location of the source files</li>
 <li> <code>:publishing-directory</code>: destination of the exported files</li>
 <li> <code>:publishing-function</code>: how the files are exported. Some options are:
 <ul class="org-ul"> <li> <code>org-html-publish-to-gemini</code>: converts org files to Gentext</li>
 <li> <code>org-publish-attachment</code>: copies files verbatim</li>
</ul></li>
 <li> <code>:recursive</code>: Set to non-nil when you have files in subdirectories</li>
</ul> <p>
The example configuration below has two components: the Gemini pages and any related images, each using different source and target directories and publishing functions. For the static files, the targeted files a regular expression indicates the relevant file extensions without the dot. The two components combine to create the &quot;gemini&quot; project. This project will be available for publishing with  <code>(org-publish &quot;gemini&quot;)</code>, or the dispatch menu with  <code>C-c C-e P x</code>.
</p>

 <div class="org-src-container">
 <pre class="src src-elisp"> <code>( <span style="font-weight: bold;">require</span> ' <span style="font-weight: bold; text-decoration: underline;">ox-publish</span>)

( <span style="font-weight: bold;">setq</span> org-publish-project-alist
      (list '( <span style="font-style: italic;">&quot;gemini-pages&quot;</span>
               <span style="font-weight: bold;">:base-directory</span>  <span style="font-style: italic;">&quot;~/projects/gemini/content/&quot;</span>
               <span style="font-weight: bold;">:publishing-directory</span>  <span style="font-style: italic;">&quot;~/projects/gemini/gemini/&quot;</span>
               <span style="font-weight: bold;">:recursive</span> t
               <span style="font-weight: bold;">:publishing-function</span> org-gemini-publish-to-gemini)
            '( <span style="font-style: italic;">&quot;gemini-images&quot;</span>
               <span style="font-weight: bold;">:base-directory</span>  <span style="font-style: italic;">&quot;~/projects/gemini/org/images&quot;</span>
               <span style="font-weight: bold;">:base-extension</span>  <span style="font-style: italic;">&quot;png</span> <span style="font-weight: bold; font-style: italic;">\\</span> <span style="font-weight: bold; font-style: italic;">|</span> <span style="font-style: italic;">jpg</span> <span style="font-weight: bold; font-style: italic;">\\</span> <span style="font-weight: bold; font-style: italic;">|</span> <span style="font-style: italic;">webp&quot;</span>
               <span style="font-weight: bold;">:publishing-directory</span>  <span style="font-style: italic;">&quot;~/projects/gemini/gemini/images&quot;</span>
               <span style="font-weight: bold;">:recursive</span> t
               <span style="font-weight: bold;">:publishing-function</span> org-publish-attachment)
            '( <span style="font-style: italic;">&quot;gemini&quot;</span>
               <span style="font-weight: bold;">:components</span> ( <span style="font-style: italic;">&quot;gemini-pages&quot;</span>  <span style="font-style: italic;">&quot;gemini-images&quot;</span>))))
</code></pre>
</div>
</div>
 <div id="outline-container-org9073bc2" class="outline-3">
 <h3 id="org9073bc2">Advanced publishing options</h3>
 <div class="outline-text-3" id="text-org9073bc2">
 <p>
This article only discusses the basics of using Org mode to publish a Gemini capsule. Read the three-part series on  <a href="/praxis/publishing-websites-with-org-mode-01-principles/">publishing websites with Org mode</a> to learn more about its powers.
</p>

 <p>
The code I used to publish this capsule and associated website are available on  <a href="https:github.com/pprevos/peterprevos.com/">GitHub</a>. It includes enhancements to the  <code>ox-gemini</code> packages for a frictionless experience.
</p>
</div>
</div>
</section></main>]]></content>
  <link href="https://peterprevos.com/praxis/reading-and-writing-gemini-capsules-with-emacs-org-mode/"/>
  <id>https://peterprevos.com/praxis/reading-and-writing-gemini-capsules-with-emacs-org-mode/</id>
  <updated>2026-10-10T00:00:00+11:00</updated>
</entry>
<entry>
  <title>Publishing Websites with Emacs and Org Mode: Publishing Functions</title>
  <author><name>Peter Prevos</name></author>
  <content type="html"><![CDATA[<main id="content" class="content"> <header> <h1 class="title">Publishing Websites with Emacs and Org Mode: Publishing Functions</h1>
</header> <p>
This is the third article in a series about publishing a website with Emacs Org mode. The previous articles discussed the  <a href="/praxis/publishing-websites-with-org-mode-01-principles/">principles of publishing a website with Org mode</a> and how to  <a href="/praxis/publishing-websites-with-org-mode-02-export-options/">configure Emacs to create a contemporary website</a>. This article examines the publishing pipeline and discusses how to use bespoke functions to fine-tune the resulting website.
</p>

 <p>
When you evaluate  <code>org-publish</code> or  <code>org-publish-all</code>, Emacs evaluates files, manages a timestamp cache, and delegates transformation to the export backend. The process has three phases: preparation, export, and finalisation.
</p>
 <section id="outline-container-orgcb94d69" class="outline-2"> <h2 id="orgcb94d69">Preparation</h2>
 <div class="outline-text-2" id="text-orgcb94d69">
 <p>
The first five steps prepare the source files before the actual export loop starts:
</p>
 <ol class="org-ol"> <li>Evaluate the project</li>
 <li>Run the preparation functions</li>
 <li>Initialise the timestamp cache</li>
 <li>Generate sitemaps</li>
 <li>Collect files</li>
</ol></div>
 <div id="outline-container-org9dd36a9" class="outline-3">
 <h3 id="org9dd36a9">Evaluate the project</h3>
 <div class="outline-text-3" id="text-org9dd36a9">
 <p>
This first step gathers all project variables and stores them in a list. If the requested project is a meta-project defined with  <code>:components (“component1” “component2”)</code>, then  <code>org-publish</code> processes them sequentially.
</p>
</div>
</div>
 <div id="outline-container-org2d36071" class="outline-3">
 <h3 id="org2d36071">Preparation functions</h3>
 <div class="outline-text-3" id="text-org2d36071">
 <p>
The  <code>:preparation-function</code> parameter is a list of one or more functions that run before publishing commences. The output of these functions is ignored in the further process, unless they change or create files in the base directory. These functions can, for example, create category pages, check that every file has a title, date, and keywords, or check that all images have alt tags.
</p>

 <p>
Preparation functions take the project properties as their single argument, as defined in  <code>org-publish-project-alist</code>. You can declare custom variables for use in a bespoke publishing function, for example  <code>:base-url https://peterprevos.com</code>.
</p>

 <p>
This demo function prints the name of each file that will be published.
</p>

 <div class="org-src-container">
 <pre class="src src-elisp"> <code>( <span style="font-weight: bold;">defun</span>  <span style="font-weight: bold;">my-prep-loop-source-files</span> (project-plist)
   <span style="font-weight: bold;">“</span>Demo preparation function using PLIST project properties. <span style="font-weight: bold;">”</span>
  ( <span style="font-weight: bold;">let</span> ((files (org-publish-get-base-files
                (cons  <span style="font-weight: bold;">“</span>temp-project <span style="font-weight: bold;">”</span> project-plist)))
        (base-url (plist-get project-plist  <span style="font-weight: bold;">:base-url</span>)))
    ( <span style="font-weight: bold;">dolist</span> (file files)
      (message  <span style="font-weight: bold;">“</span>Will publish source file: %s for %s <span style="font-weight: bold;">”</span> file base-url))))
</code></pre>
</div>
</div>
</div>
 <div id="outline-container-orga7c9bd2" class="outline-3">
 <h3 id="orga7c9bd2">Initialise the timestamp cache</h3>
 <div class="outline-text-3" id="text-orga7c9bd2">
 <p>
Emacs stores a project-specific timestamp cache on disk. The cache prevents redundant compilation by storing the source file modification timestamps, cross-references, and output mappings. Org stores the cache in the directory mentioned in  <code>org-publish-timestamp-directory</code>.
</p>

 <p>
You can force Emacs to publish all files and ignore the cache with the universal argument ( <code>C-u M-x org-publish-all</code>) or ( <code>org-publish-all t</code>), or for a single project use  <code>org-publish-project</code>.
</p>
</div>
</div>
 <div id="outline-container-orgb31c0ce" class="outline-3">
 <h3 id="orgb31c0ce">Generate the sitemap</h3>
 <div class="outline-text-3" id="text-orgb31c0ce">
 <p>
A sitemap lists all essential pages on your site in an organised structure. There are two main types of sitemaps: XML sitemaps to guide search crawlers through your site, and HTML sitemaps for the convenience of your visitors. Org has no built-in functionality to generate the search-engine standard  <code>sitemap.xml</code> protocol, but it can generate Org versions and export these to the desired format out of the box.
</p>

 <p>
When using  <code>:auto-sitemap t</code>, Org scans the project files and generates a sitemap document (defaulting to  <code>sitemap.org</code>). The following configurations are available:
</p>

 <ul class="org-ul"> <li> <code>:sitemap-filename</code>: The name of the sitemap file</li>
 <li> <code>:sitemap-title</code>: Title of the sitemap page</li>
 <li> <code>:sitemap-style</code>: Either a list or a tree</li>
 <li> <code>:sitemap-sort-files</code>: Either alphabetically, chronologically (old to new), or anti-chronologically (new to old) or nil.</li>
 <li> <code>:sitemap-sort-folders</code>: Either first, last, ignore (no folders) or nil.</li>
 <li> <code>:sitemap-ignore-case</code>: Ignore case when sorting the sitemap.</li>
</ul> <p>
When  <code>:auto-sitemap</code> is  <code>t</code>, two functions control entry generation and document structure, both of which you can replace with your own versions:
</p>

 <ul class="org-ul"> <li> <code>:sitemap-format-entry</code>: Formats the links inside the sitemap</li>
 <li> <code>:sitemap-function</code>: Assembles the sitemap document from the list of formatted entries.</li>
</ul></div>
 <div id="outline-container-org299f8b0" class="outline-4">
 <h4 id="org299f8b0">Format Entry Function</h4>
 <div class="outline-text-4" id="text-org299f8b0">
 <p>
This function formats each link inside the sitemap and outputs a string, e.g.  <code>&quot;[[file:path.org][Title]]&quot;</code>. This function takes three inputs:
</p>

 <ol class="org-ol"> <li> <code>entry</code> (string): File or directory path relative to  <code>:base-directory</code>.</li>
 <li> <code>style</code> (symbol): Either  <code>list</code> or  <code>tree</code>.</li>
 <li> <code>project-plist</code>: List of project properties.</li>
</ol> <p>
The example below enhances the sitemap by adding a date before the link for pages with a  <code>#+DATE:</code> keyword, and it capitalises directory names. You could also, for example, add the content of a  <code>#+DESCRIPTION</code> keyword.
</p>

 <div class="org-src-container">
 <pre class="src src-elisp"> <code>( <span style="font-weight: bold;">defun</span>  <span style="font-weight: bold;">my-sitemap-format-entry</span> (entry style project-plist)
   <span style="font-style: italic;">&quot;Format sitemap ENTRY with publication date.
  ENTRY is a file name.  STYLE is the style of the sitemap.
  PROJECT is the current project.&quot;</span>
  ( <span style="font-weight: bold;">if</span> (directory-name-p entry)
      (capitalize (file-name-nondirectory (directory-file-name entry)))
    ( <span style="font-weight: bold;">let</span> ((title (org-publish-find-title entry project))
          (date  (org-publish-find-date entry project)))
      ( <span style="font-weight: bold;">if</span> date
          (format  <span style="font-style: italic;">&quot;%s: [[file:%s][%s]]&quot;</span>
                  (format-time-string  <span style="font-style: italic;">&quot;%Y-%m-%d&quot;</span> date)
                  entry
                  title)
        (format  <span style="font-style: italic;">&quot;[[file:%s][%s]]&quot;</span> entry title)))))
</code></pre>
</div>
</div>
</div>
 <div id="outline-container-org87c1e5f" class="outline-4">
 <h4 id="org87c1e5f">Sitemap Builder Function</h4>
 <div class="outline-text-4" id="text-org87c1e5f">
 <p>
The second optional function assembles the sitemap document from the list of formatted entries and has two inputs.
</p>

 <ol class="org-ol"> <li> <code>title</code> (string): sitemap title ( <code>:sitemap-title</code>).</li>
 <li> <code>list</code> (list): parsed list structure produced by the format entry function.</li>
</ol> <p>
The default sitemap includes only a title and omits the table of contents. Use a bespoke function to  add further content as such:
</p>

 <div class="org-src-container">
 <pre class="src src-elisp"> <code>( <span style="font-weight: bold;">defun</span>  <span style="font-weight: bold;">my-sitemap-builder</span> (title list)
   <span style="font-style: italic;">&quot;Build the full sitemap document content.&quot;</span>
  (concat  <span style="font-style: italic;">&quot;#+title: &quot;</span> title  <span style="font-style: italic;">&quot;\\n&quot;</span>
           <span style="font-style: italic;">&quot;#+options: toc:nil\\n\\n&quot;</span>
           <span style="font-style: italic;">&quot;Any additional content …&quot;</span>
           <span style="font-weight: bold; font-style: italic;">;; </span> <span style="font-weight: bold; font-style: italic;">Generate the list
</span>          (org-list-to-org list)  <span style="font-style: italic;">&quot;\\n&quot;</span>))
</code></pre>
</div>
</div>
</div>
</div>
 <div id="outline-container-org1c67ea5" class="outline-3">
 <h3 id="org1c67ea5">Collect files</h3>
 <div class="outline-text-3" id="text-org1c67ea5">
 <p>
In the last preparation step, Org scans the  <code>:base-directory</code> for files matching  <code>:base-extension</code> (by default “org”, or a regular expression matching file extensions).
</p>

 <p>
Org mode includes the content of subdirectories when using  <code>:recursive t</code>. The resulting matches are pruned using  <code>:exclude</code> and extended using  <code>:include</code>.
</p>
</div>
</div>
</section> <section id="outline-container-org1a3bcd0" class="outline-2"> <h2 id="org1a3bcd0">The Publishing loop</h2>
 <div class="outline-text-2" id="text-org1a3bcd0">
 <p>
Now the actual publication starts. For every file in the project, Org takes these five steps:
</p>
 <ol class="org-ol"> <li>Compute the destination directories, preserving relative directory hierarchies in the publishing-directory.</li>
 <li>Check if the source file or any of its dependencies have changed since the last build using the timestamp cache, or whether the user asks to ignore the cache.</li>
 <li>When the file has changed, or the user forces publishing all files, call the publishing function and the export engine to process the file.
 <ul class="org-ul"> <li>Parsed into an Abstract Syntax Tree (AST)</li>
 <li>Extract project properties, buffer keywords ( <code>#+TITLE:</code>,  <code>#+DATE:</code>, etc.), and export options and merge into a unified property list.</li>
 <li>Evaluate dynamic functions assigned to  <code>:html-head</code>,  <code>:html-head-extra</code>,  <code>:html-preamble</code>, and  <code>:html-postamble</code> using the property list.</li>
 <li>Write the transcoded output string to the destination file.</li>
</ul></li>
 <li>Cache the output path returned by the publishing function.</li>
 <li>Trigger the  <code>org-publish-after-publishing-hook</code>.</li>
</ol> <p>
The actual transformation occurs in the third step with several opportunities for deep configuration by using filters on the source text.
</p>
</div>
 <div id="outline-container-orgb636128" class="outline-3">
 <h3 id="orgb636128">Dynamic HTML head, Preamble and Postamble</h3>
 <div class="outline-text-3" id="text-orgb636128">
 <p>
The previous article described how to read a template file to include a style sheet and favicon. But the head is missing some critical content, such as a canonical link,  <a href="https://ogp.me/">Open Graph</a>, and  <a href="https://schema.org/">Structured Data</a>. Open Graph controls how social media presents links when somebody shares them. Structured Data gives search engines and generative AI crawlers semantic meaning to parse concepts, entities, and relationships. This content is dynamic and varies by file, so we need to use a function instead of a template.
</p>

 <p>
Functions that generate head content take a single argument: a property list containing the publication project properties and the keywords from the exported file. The example function below extracts metadata from the published file and constructs partial Open Graph metadata.
</p>

 <p>
You can go beyond the standard keywords and add special ones such as  <code>#+IMAGE:</code> to provide the URL for a preview image. To do this, you need to instruct Org to include it in the export options by appending the  <code>org-export-options-alist</code>.
</p>

 <div class="org-src-container">
 <pre class="src src-elisp"> <code>( <span style="font-weight: bold;">with-eval-after-load</span> 'ox
  (add-to-list 'org-export-options-alist
               '( <span style="font-weight: bold;">:image</span>  <span style="font-style: italic;">&quot;IMAGE&quot;</span> nil nil t)))

( <span style="font-weight: bold;">defun</span>  <span style="font-weight: bold;">my-org-html-generate-open-graph</span> (info)
   <span style="font-style: italic;">&quot;Generate standard Open Graph tags using the INFO plist.&quot;</span>
  ( <span style="font-weight: bold;">let*</span> ((title (org-export-data (plist-get info  <span style="font-weight: bold;">:title</span>) info))
         (description (plist-get info  <span style="font-weight: bold;">:description</span>))
         (image (plist-get info  <span style="font-weight: bold;">:image</span>))
         (concat
          (format  <span style="font-style: italic;">&quot;&lt;meta property=\&quot;og:title\&quot; content=\&quot;%s\&quot; /&gt;\\n&quot;</span>
                  ( <span style="font-weight: bold;">or</span> title  <span style="font-style: italic;">&quot;&quot;</span>))
          ( <span style="font-weight: bold;">when</span> (org-string-nw-p description)
            (format  <span style="font-style: italic;">&quot;&lt;meta property=\&quot;og:description\&quot; content=\&quot;%s\&quot; /&gt;\\n&quot;</span> description))
          ( <span style="font-weight: bold;">when</span> (org-string-nw-p image)
            (format  <span style="font-style: italic;">&quot;&lt;meta property=\&quot;og:image\&quot; content=\&quot;%s\&quot; /&gt;\\n&quot;</span> image))
           <span style="font-style: italic;">&quot;&lt;meta property=\&quot;og:type\&quot; content=\&quot;article\&quot; /&gt;&quot;</span>))))
</code></pre>
</div>

 <p>
Add the function name to the  <code>:html-head-extra</code> declaration in your project to append it to the previously defined static part.
</p>

 <p>
You can also use this approach to generate a bespoke preamble and postamble for your pages. However, for typical header and footer content, the provided content markers discussed in the  <a href="/praxis/publishing-websites-with-org-mode-02-export-options/#header">previous article</a> suffice.
</p>
</div>
</div>
 <div id="outline-container-orga088566" class="outline-3">
 <h3 id="orga088566">Dynamically modifying the Content</h3>
 <div class="outline-text-3" id="text-orga088566">
 <p>
When Org mode exports a document, it parses the file into an Abstract Syntax Tree (AST) using the  <code>org-element</code> library. In the AST, every heading, paragraph, list item, link and drawer becomes a nested Lisp data structure which can be manipulated during the process.
</p>

 <p>
The functions listed in  <code>:filter-parse-tree</code> modify the AST for each file during the export process. Each filter is called with three arguments: the parse tree, as returned by  <code>org-element-parse-buffer</code>, the export backend ( <code>'html</code> in our case), and the project property list. Each filter returns the modified parse tree to transcode. If the filter returns nil, the process will crash!
</p>

 <p>
Because the filter operates at the AST level before rendering, it allows you to modify or add content based on context dynamically. For this website, I use the AST filter to construct a subtitle based on author, authoring date, word count, and estimated reading time.
</p>

 <p>
Org publish has many more such filtering functions in its arsenal to finetune the output, basically one for each syntax type. For example, this website uses the  <code>:filter-link</code> option to remove the filenames from the URLs so that  <code>article.html</code> becomes  <code>artcile/index.html</code>.
</p>

 <p>
The  <code>:filter-final-output</code> option is a last opportunity to modify the main content. This filter provides the last hook in the Org mode export pipeline before the transcoded document string is committed to disk. While the syntax tree filter operates on the AST of Lisp elements, the final output filter operates on the complete, fully-rendered document string.
</p>
</div>
</div>
 <div id="outline-container-org6947a13" class="outline-3">
 <h3 id="org6947a13">Publishing hook</h3>
 <div class="outline-text-3" id="text-org6947a13">
 <p>
Every function listed in  <code>org-publish-after-publishing-hook</code> in this hook will be called for each published file with two arguments: the name of the original file and the name of the exported file.
</p>
</div>
</div>
 <div id="outline-container-org7a37388" class="outline-3">
 <h3 id="org7a37388">The main publishing function</h3>
 <div class="outline-text-3" id="text-org7a37388">
 <p>
Org export calls a publishing function for each component to export files to the target format, such as  <code>org-html-publish-to-html</code> for a website. Other backends extend the repertoire of output formats. When, for example, using  <code>org-gemini-publish-to-gemini</code> the Org files create a  <a href="/praxis/reading-and-writing-gemini-capsules-with-emacs-org-mode/">Gemini capsule</a>.
</p>

 <p>
Publishing functions take three parameters:
</p>

 <ul class="org-ul"> <li> <code>plist</code>: property list for the given project (the export variables in  <code>org-publish-project-alist</code>)</li>
 <li> <code>filename</code>: name of the source file</li>
 <li> <code>pub-dir</code>: directory where to store the resulting file</li>
</ul></div>
</div>
</section> <section id="outline-container-org5966df5" class="outline-2"> <h2 id="org5966df5">Finalisation</h2>
 <div class="outline-text-2" id="text-org5966df5">
 <ol class="org-ol"> <li>Generate the index</li>
 <li>Evaluate the completion functions</li>
 <li>Commit timestamp cache to disk</li>
</ol></div>
 <div id="outline-container-org8c826e5" class="outline-3">
 <h3 id="org8c826e5">Generate the index</h3>
 <div class="outline-text-3" id="text-org8c826e5">
 <p>
Besides a sitemap, you can also build an index for a website, just like in a book. Org export can generate an index page based on the  <code>#+INDEX:</code> keywords in a file. Org’s indexing engine treats entries as hierarchical trees separated by exclamation marks:  <code>#+INDEX: TopLevel!SubEntry!SpecificItem</code>.
</p>

 <p>
Org creates a hierarchical index using these items. The final part becomes a hyperlink to the source. To make a subtree appear under more than one index, add more index lines in the front matter.
</p>

 <p>
Index items relate to the whole file by placing them in the front matter, or they link directly to headings by adding the index keyword inside a subtree. You can sprinkle index lines throughout your writing, and Org will create an index page linking to the listed topics.
</p>

 <p>
During the publishing process, Org creates the  <code>index.inc</code> file, which contains a nested list of Org links to the relevant pages and headers. After the export process has completed, Org translates these to HTML links by referring to the section IDs in the exported file.
</p>

 <p>
The process uses the  <code>theindex.org</code> file, which is created during the export process if it does not yet exist. This file only contains a title and  <code>#+INCLUDE: “theindex.inc”</code>. You can edit this file once it is generated to change the title or add any other additional content.
</p>

 <p>
The index is a powerful alternative to category pages or sitemaps that adds heuristic value to a website. Unfortunately, Org mode’s indexation function offers no hooks for customisation beyond editing the Org file.
</p>
</div>
</div>
 <div id="outline-container-org607cc4c" class="outline-3">
 <h3 id="org607cc4c">Evaluate the completion functions</h3>
 <div class="outline-text-3" id="text-org607cc4c">
 <p>
The  <code>:completion-function</code> takes a list of one or more functions that run after publishing the project. While the preparation functions typically act on the source files, the completion functions act on the exported files. For this website, I use a completion function to generate the  <a href="https://www.sitemaps.org/">XML sitemap</a> to guide crawlers.
</p>
</div>
</div>
 <div id="outline-container-orgf1dbaf0" class="outline-3">
 <h3 id="orgf1dbaf0">Commit timestamp cache to disk</h3>
 <div class="outline-text-3" id="text-orgf1dbaf0">
 <p>
When Org mode has finished processing all files, the updated timestamp cache is committed to disk for future reference, and the project closes.
</p>
</div>
</div>
</section> <section id="outline-container-org657d66a" class="outline-2"> <h2 id="org657d66a">Conclusion</h2>
 <div class="outline-text-2" id="text-org657d66a">
 <p>
This detailed description of the publishing process should give you everything you need to create the website you deserve. However, the capabilities are not as advanced as major publishing systems such as Hugo or Jekyll, without advanced Elisp hacking. Org mode is as such ideal for websites that focus on sharing text, but perhaps not so much a fancy graphical landing page.
</p>

 <p>
For me, it is satisfying to do everything inside Emacs so I choose to move from Hugo to using Org mode. You can peruse the source code on the  <a href="https://github.com/pprevos/peterprevos.com">GitHub repository</a>.
</p>
</div>
</section></main>]]></content>
  <link href="https://peterprevos.com/praxis/publishing-websites-with-org-mode-03-publishing-functions/"/>
  <id>https://peterprevos.com/praxis/publishing-websites-with-org-mode-03-publishing-functions/</id>
  <updated>2026-10-03T00:00:00+10:00</updated>
</entry>
<entry>
  <title>Publishing websites with Emacs and Org Mode: Export Options</title>
  <author><name>Peter Prevos</name></author>
  <content type="html"><![CDATA[<main id="content" class="content"> <header> <h1 class="title">Publishing websites with Emacs and Org Mode: Export Options</h1>
</header> <p>
This is the  <i>second</i> article in a series about publishing a website with Emacs Org mode. The  <a href="/praxis/publishing-websites-with-org-mode-01-principles/">previous article</a> covers the basic principles of publishing websites with Org mode's publishing functionality. While the output is fully functional, it does not meet the needs of a modern website. This second article shows how to optimise the website's design.
</p>
 <section id="outline-container-orgad7b609" class="outline-2"> <h2 id="orgad7b609">Export variables scoping</h2>
 <div class="outline-text-2" id="text-orgad7b609">
 <p>
You can control the export process at three levels: keywords for individual files, project parameters, or global variables. The examples below show how to exclude the author name from a web page from an individual page, set it for a project, or configure it for all future exports. Note that the file keyword overrides the export parameter, which in turn overrides global settings, so in order of priority:
</p>

 <ol class="org-ol"> <li>Add a keyword for a specific file ( <code>#+OPTIONS: :author:nil</code>)</li>
 <li>Use an export parameter for the project ( <code>:with-author nil</code>)</li>
 <li>Set a global variable for all future exports  <code>(setq org-export-with-author nil)</code></li>
</ol> <p>
The help file for each variable describes the syntax to use in a project, or in an individual file. The examples in this article describe the export parameters for projects.
</p>
</div>
</section> <section id="outline-container-org8bbea88" class="outline-2"> <h2 id="org8bbea88">Generic export options</h2>
 <div class="outline-text-2" id="text-org8bbea88">
 <p>
The help file for  <code>org-publish-project-alist</code> lists generic properties to finetune the output. These variables mainly determine whether to include or exclude certain Org mode syntax elements, such as drawers, tags or TODO keywords. The help file for each of these variables explains their effect on the result and how to define it as a file keyword or project parameter. For example, to remove the table of contents and standard footer, use this in your project:
</p>

 <div class="org-src-container">
 <pre class="src src-elisp"> <code> <span style="font-weight: bold;">:with-toc</span> nil
 <span style="font-weight: bold;">:with-author</span> nil
 <span style="font-weight: bold;">:with-date</span> nil
 <span style="font-weight: bold;">:time-stamp-file</span> nil
</code></pre>
</div>

 <p>
You can also override these for individual files. For example, if you want to include a table of contents for a long article, then override this project setting by adding  <code>#+OPTIONS: toc:1</code>, which in this case would result in a table of contents with only level 1 headings.
</p>

 <p>
These options apply to any export type, either HTML, ASCII, or whatever your publishing project entails. The next sections discuss how to use website-specific variables to optimise your website's design.
</p>
</div>
</section> <section id="outline-container-org16229c2" class="outline-2"> <h2 id="org16229c2">HTML Head</h2>
 <div class="outline-text-2" id="text-org16229c2">
 <p>
The  <a href="https://w3schools.dev/html/html_head.asp">head element</a> of a web page contains metadata for browsers, search engines, and web services. The head is essential for a responsive, well-designed website that search engines and social media recognise.
</p>

 <p>
Org assembles the head tag sequentially in three parts:
</p>

 <ol class="org-ol"> <li> <code>org-html--build-meta-info</code>: Character set encoding, viewport, document title, generator and available file keywords (author, description, and keywords).</li>
 <li> <code>org-html--build-head</code>: Default CSS and Javascript, and/or user-generated content.</li>
 <li> <code>org-html--build-mathjax-config</code>: MathJax scripts when LaTeX fragments are part of the content.</li>
</ol> <p>
The viewport and character set in the first part are configurable, but for most websites the defaults are just fine. The second part of the head tag is fully configurable. Org export adds a default style sheet and JavaScript to each exported file. You can replace these defaults with your personal preference. The  <code>:html-head</code> can be a string to define your preferences, as shown below.
</p>

 <div class="org-src-container">
 <pre class="src src-elisp"> <code> <span style="font-weight: bold;">:html-head-include-scripts</span> nil
 <span style="font-weight: bold;">:html-head-include-default-style</span> nil
 <span style="font-weight: bold;">:html-head</span>  <span style="font-style: italic;">&quot;&lt;link rel=\&quot;stylesheet\&quot; href=\&quot;https://cdn.simplecss.org/simple.min.css\&quot; /&gt;&quot;</span>
</code></pre>
</div>

 <p>
Alternatively, you can save the head content in a template file and include CSS, a  <a href="https://en.wikipedia.org/wiki/Favicon">favicon</a> and other static parts of the head and read it into a string.
</p>

 <div class="org-src-container">
 <pre class="src src-elisp"> <code>( <span style="font-weight: bold;">setq</span> my-html-head
      ( <span style="font-weight: bold;">with-temp-buffer</span>
        (insert-file-contents
         (expand-file-name  <span style="font-style: italic;">&quot;templates/head.html&quot;</span>))
        (buffer-string)))
</code></pre>
</div>

 <p>
Add this variable to your project component with  <code>:html-head ,my-html-head</code>. The comma before the variable name inserts the value into the list instead of the literal string.
</p>

 <p>
You can also override or append the HTML head settings for individual files with the  <code>#+HTML_HEAD:</code> keyword to replace the template, and  <code>#+HTML_HEAD_EXTRA:</code> to append the template.
</p>

 <p>
The next article in this series explains how to  <a href="/praxis/publishing-websites-with-org-mode-03-publishing-functions/">create dynamic content</a>, such as Open Graph and Structured Data, using a function instead of a static variable.
</p>

 <p>
The third part of the HTML head is an optional part to load MathJax for rendering mathematical formulas, which is further described below.
</p>
</div>
</section> <section id="outline-container-orgaab5fde" class="outline-2"> <h2 id="orgaab5fde">Layout Elements</h2>
 <div class="outline-text-2" id="text-orgaab5fde">
 <p>
Org mode uses an old HTML standard for generating pages. However, modern websites use HTML5 for improved accessibility, SEO, cleaner code, and easier styling.
</p>

 <p>
The diagram below illustrates a typical semantic  <a href="https://w3schools.dev/html/html_layout.asp">HTML5 page layout</a>. It frames the central  <i>main</i> area between a  <i>header</i> and  <i>footer</i>, dividing the main body into an  <i>aside</i> for supplementary content and one or more  <i>sections</i>, each containing one or more  <i>articles</i> for the core message.
</p>

 <pre class="example" id="org3438ec8">
┌─ header ──────────────────────────────────────┐
│                                               │
└───────────────────────────────────────────────┘
┌─ main ────────────────────────────────────────┐
│ ┌─aside─┐ ┌─ section ───────────────────────┐ │
│ │       │ │ ┌─article─────────────────────┐ │ │
│ │       │ │ │                             │ │ │
│ │       │ │ │                             │ │ │
│ │       │ │ └─────────────────────────────┘ │ │
│ └───────┘ └─────────────────────────────────┘ │
└───────────────────────────────────────────────┘
┌─ footer ──────────────────────────────────────┐
│                                               │
└───────────────────────────────────────────────┘
</pre>

 <p>
You can configure Org mode to use HTML5 in the export process. These HTML export options in Org mode control the  <a href="https://en.wikipedia.org/wiki/Document_Object_Model">Document Object Model</a> for your website:
</p>

 <ul class="org-ul"> <li> <code>org-html-doctype</code>: Document type definition</li>
 <li> <code>org-html-html5-fancy</code>: Use new HTML5 elements</li>
 <li> <code>org-html-divs</code>: Defines the elements for HTML export</li>
 <li> <code>org-html-container-element</code>: Top level sections</li>
</ul> <p>
The example below configures Org mode to use semantic elements instead of generic  <code>&lt;div&gt;</code> tags. In  <code>html-divs</code>, the first string is the HTML tag and the second one is the id, e.g.  <code>&lt;main id=&quot;content&quot;&gt;</code>.
</p>

 <div class="org-src-container">
 <pre class="src src-elisp"> <code> <span style="font-weight: bold;">:html-doctype</span>  <span style="font-style: italic;">&quot;html5&quot;</span>
 <span style="font-weight: bold;">:html-html5-fancy</span> t
 <span style="font-weight: bold;">:html-divs</span> ((preamble   <span style="font-style: italic;">&quot;header&quot;</span>  <span style="font-style: italic;">&quot;preamble&quot;</span>)
            (content    <span style="font-style: italic;">&quot;main&quot;</span>    <span style="font-style: italic;">&quot;content&quot;</span>)
            (postamble  <span style="font-style: italic;">&quot;footer&quot;</span>  <span style="font-style: italic;">&quot;postamble&quot;</span>))
 <span style="font-weight: bold;">:html-container</span>  <span style="font-style: italic;">&quot;section&quot;</span>
</code></pre>
</div>
</div>
</section> <section id="outline-container-header" class="outline-2"> <h2 id="header">The header</h2>
 <div class="outline-text-2" id="text-header">
 <p>
The  <a href="https://www.w3docs.com/learn-html/html-header-tag">header of a HTML</a> page (or preamble) usually holds content such as navigation, a logo, title, author name and so on. We can define a header of our site using  <code>:html-preamble</code>.
</p>

 <p>
When this variable is set to  <code>t</code>, the content of  <code>org-html-preamble-format</code> defines the header, which is empty by default. You can also configure the preamble as a string. This string can contain the following format markers to include page variables:
</p>

 <ul class="org-ul"> <li> <code>%t</code>: Title of the source file ( <code>#+title:</code>)</li>
 <li> <code>%s</code>: Subtitle of the source file ( <code>#+subtitle:</code>)</li>
 <li> <code>%a</code>: Content of the  <code>#+author:</code> keyword or  <code>user-full-name</code>.</li>
 <li> <code>%e</code>: Content of the  <code>#+email:</code> keyword or  <code>user-mail-addres</code> wrapped in a  <code>mailto</code> link.</li>
 <li> <code>%d</code>: Content of the  <code>#+date:</code> keyword.</li>
 <li> <code>%C</code>: Last modification time of the source file</li>
 <li> <code>%T</code>: The export time of the source file</li>
 <li> <code>%c</code>: Content of  <code>org-html-creator-string</code> (links to Emacs and Org mode websites).</li>
 <li> <code>%v</code>: Content of  <code>org-html-validation-link</code></li>
</ul> <p>
The three timestamp format markers produce a date in accordance with  <code>org-html-metadata-timestamp-format</code>, which you can configure to your regional preferences.
</p>

 <p>
A simple example of a header string could be:
</p>

 <div class="org-src-container">
 <pre class="src src-elisp"> <code> <span style="font-weight: bold;">:html-preamble</span>  <span style="font-style: italic;">&quot;&lt;h1&gt;%t&lt;/h1&gt;\n&lt;h2&gt;%a&lt;/h2&gt;&quot;</span>
</code></pre>
</div>

 <p>
When including the title in the header, you might want to exclude the title from the main content to avoid duplication by setting  <code>:with-title</code> to nil.
</p>

 <p>
Just like the head element, you can read a static template file with a function, including format markers, and use that as a preamble, as explained above.
</p>

 <p>
Org mode can also  <a href="/praxis/publishing-websites-with-org-mode-03-publishing-functions/">evaluate a function to define a header dynamically</a> based on the page's metadata, which is discussed in the next article in this series.
</p>
</div>
</section> <section id="outline-container-org0a955ec" class="outline-2"> <h2 id="org0a955ec">Main content</h2>
 <div class="outline-text-2" id="text-org0a955ec">
 <p>
Org export also provides a range of variables to finetune how the content converts to HTML. The next sections describe some specialised options to configure the main content of your pages. 
</p>
</div>
 <div id="outline-container-org92f6d48" class="outline-3">
 <h3 id="org92f6d48">HTML Attributes</h3>
 <div class="outline-text-3" id="text-org92f6d48">
 <p>
To embed inline HTML code to finetune the look and feel of the page. Four options are available: inline code, custom block elements, source code blocks, and table / image attributes.
</p>

 <p>
Inline HTML code looks like this:  <code>@@html:&lt;span style=&quot;color:red&quot;&gt;@@red text@@html:&lt;/span&gt;@@</code>. Other export types ignore the HTML, so the text is not red. You can concatenate these to provide alternatives for other export types.
</p>

 <p>
For larger snippets of HTML, include an export block with  <code>C-c C-, h</code>. These blocks are only exported when the HTML backend is active and ignored when using other export formats.
</p>

 <pre class="example" id="org77d3ee0">
#+begin_export html
  &lt;span style=&quot;color:red&quot;&gt;red text&lt;/span&gt;
#+end_export
</pre>

 <p>
HTML5 documents can also have arbitrary  <code>#+BEGIN ... #+END</code> blocks. For example:
</p>

 <pre class="example" id="org229ef15">
#+BEGIN_aside
Lorem ipsum
#+END_aside
</pre>

 <p>
Which exports to:
</p>

 <div class="org-src-container">
 <pre class="src src-html"> <code>&lt; <span style="font-weight: bold;">aside</span>&gt;
  &lt; <span style="font-weight: bold;">p</span>&gt;Lorem ipsum&lt;/ <span style="font-weight: bold;">p</span>&gt;
&lt;/ <span style="font-weight: bold;">aside</span>&gt;
</code></pre>
</div>

 <p>
The fourth method enables providing tables and images with HTML attributes using the  <code>#+ATTR_HTML:</code> keyword. Use this keyword above an image or table to specify image size, alt tags, CSS classes or other functionality as described in the Org manual, for example:
</p>

 <p>
 <code>#+ATTR_HTML: :align center :width 300 :height 200 :alt This is an image</code>
</p>
</div>
</div>
 <div id="outline-container-org2ea7328" class="outline-3">
 <h3 id="org2ea7328">Org mode macros as shortcodes</h3>
 <div class="outline-text-3" id="text-org2ea7328">
 <p>
 <a href="https://orgmode.org/manual/Macro-Replacement.html">Org mode macros</a> are a versatile method to include dynamic content. A macro replaces text snippets during export, just like a shortcode in other static website generators. For example, this macro creates HTML code to embed a YouTube video:
</p>

 <p>
 <code>{{{ youtube(5IJVDCpE3vA, &quot;Video caption&quot;) }}}</code>
</p>

 <p>
You define these shortcodes either as a variable or as a page keyword, as shown below. The  <code>@@html:</code> and  <code>@@</code> markers indicate that this macro is exported as HTML code, and not as Org mode text.
</p>

 <div class="org-src-container">
 <pre class="src src-elisp"> <code>( <span style="font-weight: bold;">setq</span> org-export-global-macros
      '(( <span style="font-style: italic;">&quot;youtube&quot;</span> .  <span style="font-style: italic;">&quot;@@html:&lt;figure&gt;&lt;iframe width=\&quot;560\&quot; height=\&quot;315\&quot;
   src=\&quot;https://www.youtube.com/embed/$1\&quot; title=\&quot;$2\&quot; frameborder=\&quot;0\&quot;
   allowfullscreen&gt;&lt;/iframe&gt;&lt;figcaption&gt;$2&lt;/figcaption&gt;&lt;/figure&gt;@@&quot;</span>)))
</code></pre>
</div>

 <p>
Macros are a powerful tool to generate dynamic content. The category pages on this website use a macro that calls a function to list pages in the relevant subdirectory.
</p>
</div>
</div>
 <div id="outline-container-orgaf99cac" class="outline-3">
 <h3 id="orgaf99cac">Mathematics</h3>
 <div class="outline-text-3" id="text-orgaf99cac">
 <p>
Org mode can contain LaTeX math fragments. When exporting to HTML, Org can use either  <a href="https://www.mathjax.org">MathJax</a> or transcode the math into images.
</p>

 <p>
When Org mode detects LaTeX mathematics, it automatically inserts JavaScript for  <a href="https://www.mathjax.org/">MathJax</a> into the third part of the head, discussed above. Two variables are available to configure how mathematics is typeset:
</p>

 <ul class="org-ul"> <li> <code>org-html-mathjax-template</code>: The JavaScript template</li>
 <li> <code>org-html-mathjax-options</code>: Options for the template, such as size and font. Read the documentation for options.</li>
</ul> <p>
To configure Org to insert images instead of calling the MathJax script, use  <code>#+OPTIONS: tex:dvipng</code> in the relevant file. In this case, Org export will also copy the resulting images to  the publishing directory. Not that this process will significantly slow down the publishing process.
</p>
</div>
</div>
 <div id="outline-container-orgb0299a8" class="outline-3">
 <h3 id="orgb0299a8">Source Code</h3>
 <div class="outline-text-3" id="text-orgb0299a8">
 <p>
The default setting is that source blocks in Org mode are exported in  <code>code</code> tags without syntax highlights. Install the  <a href="https://github.com/emacsorphanage/htmlize">htmlize package</a> by Hrvoje Niksic and Org will use this functionality without any further configuration.
</p>
</div>
</div>
</section> <section id="outline-container-orgc357802" class="outline-2"> <h2 id="orgc357802">The footer</h2>
 <div class="outline-text-2" id="text-orgc357802">
 <p>
A footer typically contains authorship, copyright and contact information and perhaps links to other pages.
</p>

 <p>
By default, Org mode uses the author, email, creator, date and a validation link to construct a footer. You can and define a bespoke footer and override these settings by configuring  <code>:html-postamble</code>. The method is the same as with the header.
</p>

 <p>
When this variable is  <code>t</code>, the content of  <code>org-html-postamble-format</code> becomes the footer. You can also use a bespoke string using the same format markers with the header and read the contents from a template, in the same ways as the head and preamble of the page.
</p>
</div>
</section> <section id="outline-container-org0a780f6" class="outline-2"> <h2 id="org0a780f6">Further Configuration</h2>
 <div class="outline-text-2" id="text-org0a780f6">
 <p>
The export module in Org version 9.8 defines over 130 variables plus 77 variables specific to HTML exports. This article only discussed salient variables.
</p>

 <p>
The Emacs help system is your friend if you like to explore deeper. The help file for  <code>org-publish-project-alist</code> contains an extensive list of configuration options. You can also explore the variables starting with  <code>org-html</code> to deeper explore the available options to configure the output.
</p>

 <p>
The next article in this series discusses how to  <a href="/praxis/publishing-websites-with-org-mode-03-publishing-functions/">create dynamic content</a> such as the HTML head, preable, postamble, sitemaps and RSS feeds using bespoke publishing Elisp functions.
</p>
</div>
</section></main>]]></content>
  <link href="https://peterprevos.com/praxis/publishing-websites-with-org-mode-02-export-options/"/>
  <id>https://peterprevos.com/praxis/publishing-websites-with-org-mode-02-export-options/</id>
  <updated>2026-09-26T00:00:00+10:00</updated>
</entry>
<entry>
  <title>Publishing Websites with Emacs and Org Mode: Principles</title>
  <author><name>Peter Prevos</name></author>
  <content type="html"><![CDATA[<main id="content" class="content"> <header> <h1 class="title">Publishing Websites with Emacs and Org Mode: Principles</h1>
 <p class="subtitle" role="doc-subtitle"> <span class="subtitle-row"> <span>Peter Prevos</span> <span>974 words</span></span> <span class="subtitle-row"> <span>19 September 2026</span> <span>5 min read</span></span></p>
</header> <p>
Emacs Org mode is a powerful plain-text system. The same set of Org mode files can become a website, book, journal article, or anything else. Org mode also includes a publishing tool that is ideal for multi-file projects such as websites. This article is the first in a series that explores how to export a collection of Org mode files to create a website. This series of articles builds on the basic functionality of exporting Org files. If you are new to exporting from Org mode, than this video might help:
</p>

 <p>
 <figure> <center> <iframe width="560" height="315" src="https://www.youtube.com/embed/NwDOKkwtJ34" title=" Introducing Org mode exports - Emacs Writing Studio" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin"></iframe></center> <figcaption> Introducing Org mode exports - Emacs Writing Studio</figcaption></figure></p>
 <section id="outline-container-org67775dc" class="outline-2"> <h2 id="org67775dc">Minimum Working Example</h2>
 <div class="outline-text-2" id="text-org67775dc">
 <p>
Let's assume our mini website looks something like this:
</p>

 <pre class="example" id="orga9f80ba">
content
  ├── index.org
  ├── about.org
  ├── blog/
  │    ├── index.org
  │    ├── 2026-09-12-post-01.org
  │    └── ...
  └── images/
       ├── image01.png
       └── ...
</pre>

 <p>
The standard export process through the dispatch menu ( <code>org-export-dispatch</code>) converts one file at a time, which is a tedious process when publishing a website. You can concatenate Org files with the  <code>#+INCLUDE:</code> keyword, but that will result in a single export file unsuitable for a website. The Org mode publishing functionality lets you export a collection of files and associated static files as a project. The configuration below defines a minimum working example:
</p>

 <div class="org-src-container">
 <pre class="src src-elisp"> <code>( <span style="font-weight: bold;">setq</span> org-publish-project-alist
      `(( <span style="font-style: italic;">&quot;html&quot;</span>
          <span style="font-weight: bold;">:base-directory</span>  <span style="font-style: italic;">&quot;~/content&quot;</span>
          <span style="font-weight: bold;">:publishing-directory</span>  <span style="font-style: italic;">&quot;~/public&quot;</span>
          <span style="font-weight: bold;">:recursive</span> t)
        '( <span style="font-style: italic;">&quot;images&quot;</span>
           <span style="font-weight: bold;">:base-directory</span>  <span style="font-style: italic;">&quot;~/content/images&quot;</span>
           <span style="font-weight: bold;">:base-extension</span>  <span style="font-style: italic;">&quot;png</span> <span style="font-weight: bold; font-style: italic;">\\</span> <span style="font-weight: bold; font-style: italic;">|</span> <span style="font-style: italic;">jpg</span> <span style="font-weight: bold; font-style: italic;">\\</span> <span style="font-weight: bold; font-style: italic;">|</span> <span style="font-style: italic;">webp&quot;</span>
           <span style="font-weight: bold;">:publishing-directory</span>  <span style="font-style: italic;">&quot;~/public/images&quot;</span>
           <span style="font-weight: bold;">:recursive</span> t
           <span style="font-weight: bold;">:publishing-function</span> org-publish-attachment)
        '( <span style="font-style: italic;">&quot;website&quot;</span>
           <span style="font-weight: bold;">:components</span> ( <span style="font-style: italic;">&quot;html&quot;</span>  <span style="font-style: italic;">&quot;images&quot;</span>))))
</code></pre>
</div>

 <p>
The  <code>org-publish-project-alist</code> variable can contain one or more publishing projects (websites, Gemini capsule, collections of PDF files and so on). Each project consists of one or more components, in this example the website project contains HTML files and associated images. The backquote at the start of the list allows you to selectively evaluate elements in the list, which will be useful later on.
</p>

 <p>
Each list begins with the name of the component, followed by an list of named properties. The minimum requirement is to define the base and publishing directories. This minimal setup exports all Org mode files in the base directory and saves the HTML versions in the publishing directory:
</p>

 <ul class="org-ul"> <li> <code>:base-directory</code>: Directory containing the source files, in our case  <code>~/content</code>. Defaults to all files with the  <code>.org</code> extension.</li>
 <li> <code>:publishing-directory</code>: The directory for exported files.</li>
</ul> <p>
The example above also sets  <code>:recursive t</code> for both components. When this option is non-nil, the export includes all subdirectories of  <code>:base-directory</code>.
</p>

 <p>
The second component defines the location of images, which are copied without any transformation. We do this by specifying a publishing function. The  <code>org-publish-attachment</code> function copies files from the base to the publishing directory, which is ideal for images, CSS,  <code>robots.txt</code> and other static files. This config uses a regular expression to define the extensions of the included files. We don't need a publishing function for converting Org mode to HTML as this is the default. 
</p>

 <p>
The third component combines the first two for convenience, so we don't have to export components individually to build the website.
</p>
</div>
</section> <section id="outline-container-orgadc1c34" class="outline-2"> <h2 id="orgadc1c34">Selecting Files</h2>
 <div class="outline-text-2" id="text-orgadc1c34">
 <p>
You might not want to publish every Org mode file in your base directory, so Org mode provides tools to include and exclude specific files. For example, you might want to exclude all files with &quot;draft&quot; in their filename.
</p>

 <ul class="org-ul"> <li> <code>:base-extension</code> - Define the included extension(s) without the dot for source files. The default is &quot;org&quot;. When set to &quot;any&quot;, then Org converts all files to HTML as if they were Org mode files.</li>
 <li> <code>:exclude</code> - Exclude files from publishing using a regular expression to match file names.</li>
 <li> <code>:include</code> - Include extra files with a list of filenames relative to the base directory. Note that the exclusion step evaluates first.</li>
</ul> <p>
These configurations require a regular expression as input. In this website I use a function to generate this regex to exclude any articles with a future data or with  <code>#+draft: t</code> as a keyword.
</p>
</div>
</section> <section id="outline-container-org68682bf" class="outline-2"> <h2 id="org68682bf">Publishing the site</h2>
 <div class="outline-text-2" id="text-org68682bf">
 <p>
To publish the project, use  <code>C-c C-e P x</code> ( <code>org-publish</code>) and select the relevant component or meta-project (i.e. &quot;website&quot;). Alternatively, use  <code>M-x org-publish-all</code> to publish all components or  <code>org-publish-project</code> to select a component or project.
</p>

 <p>
Org mode creates the publishing directory if it doesn't exist yet. Note that  <code>org-publish</code> doesn't delete existing files in the output directory. As a result, removed pages will remain on the server unless you explicitly remove them.
</p>

 <p>
You don't have to republish every file each time you generate the website. Org mode maintains a list of timestamps that record when a file was last published. This cache is stored in the location defined by  <code>org-publish-timestamp-directory</code>. This functionality allows us to skip unmodified files to reduce processing time. To force publishing all files in the project, use the universal argument ( <code>C-u M-x org-publish-all</code>).
</p>
</div>
</section> <section id="outline-container-orgf18d94a" class="outline-2"> <h2 id="orgf18d94a">Testing the published website</h2>
 <div class="outline-text-2" id="text-orgf18d94a">
 <p>
Emacs has a web server package to test the freshly published website that can serve the content of a publishing directory at  <code>http://localhost:8080</code>.
</p>

 <div class="org-src-container">
 <pre class="src src-elisp"> <code>( <span style="font-weight: bold;">use-package</span> simple-httpd
   <span style="font-weight: bold;">:init</span>
  (httpd-serve-directory  <span style="font-style: italic;">&quot;~/public&quot;</span>))
</code></pre>
</div>

 <p>
These are the basic principles of publishing a website with Org mode. However, the result is not up to scratch with the expectations for modern web standards. Org mode inserts its own CSS, and the pages are not SEO optimised, so there is still a lot to do.
</p>

 <p>
The  <a href="/praxis/publishing-websites-with-org-mode-02-export-options/">next article</a> discusses how to expand on the basic functionality to create a simple website that meets modern needs.
</p>
</div>
</section></main>]]></content>
  <link href="https://peterprevos.com/praxis/publishing-websites-with-org-mode-01-principles/"/>
  <id>https://peterprevos.com/praxis/publishing-websites-with-org-mode-01-principles/</id>
  <updated>2026-09-19T00:00:00+10:00</updated>
</entry>
<entry>
  <title>Declaring Intellectual Insolvency: A Hard Reset for the Overwhelmed Creator</title>
  <author><name>Peter Prevos</name></author>
  <content type="html"><![CDATA[<main id="content" class="content"> <header> <h1 class="title">Declaring Intellectual Insolvency: A Hard Reset for the Overwhelmed Creator</h1>
</header> <p>
A to-do list is a promise to your future self. Unless there is an external demand for action, most items on these lists are merely ideas of things you might want to do. A financial analogy is that the items on your list are promissory notes, a form of debt. My debt had accumulated far beyond my available time, so it needed a radical action.
</p>

 <p>
Some months ago I therefore self-declared  <i>intellectual insolvency</i>. This is not the same as bankruptcy, which is a state of having no new or valuable ideas. Intellectual insolvency is a state of mind when the available time and resources are insufficient to undertake personal projects. It leads to inaction and to continuing to consume content instead of creating it.
</p>

 <p>
Instead of staying overwhelmed by my to-do list, I decided to restore creative solvency with a radical restart.
</p>

 <p>
This article is not a how-to guide, and I am in no way promoting anyone replicating my approach. Declaring intellectual bankruptcy is not for the faint of heart. It is a radical form of self-improvement that only works when you have the luxury to do so. I certainly don't recommend going to your manager and declaring bankruptcy because you are approaching burnout. As is also the case in financial situation, the debtor needs to agree to wipe the debt.
</p>

 <p>
The first step was backing up my files and reinstalling my laptop with a fresh Linux setup and an empty Documents folder. I then installed Emacs (my digital fortress of solitude), bought a new paper notebook, and started with a  <a href="https://en.wikipedia.org/wiki/Tabula_rasa">tabula rasa</a>, a 'blank slate'. I then reviewed the landscape of my ideas and future projects and distilled it into a single new focus, as described on the  <a href="/about/">about page of this website</a>.
</p>

 <p>
To return to productivity, I first needed to rebuild the content-creation machine, which meant configuring my computer and Emacs to restart writing and publishing. I embraced a beginner's mindset and relearned Linux administration, bash scripting, and more Emacs.
</p>

 <p>
Part of the clean slate also meant leaving my old websites behind and starting with this new one. The timing is auspicious because I built my first website exactly thirty years ago. This first part of the process has resulted in a series of articles on  <a href="/praxis/publishing-websites-with-org-mode-01-principles/">how to create websites with Emacs</a>, which was one of the items lingering on my old list of promises to myself.
</p>

 <p>
Now that the production machine is running full steam again and the website is live, I can start pulling old projects one by one from the backup, and either archive them or focus on them and bring them to completion.
</p>
</main>]]></content>
  <link href="https://peterprevos.com/praxis/declaring-intellectual-insolvency/"/>
  <id>https://peterprevos.com/praxis/declaring-intellectual-insolvency/</id>
  <updated>2026-09-13T00:00:00+10:00</updated>
</entry>
</feed>
