Publishing websites with Emacs and Org Mode: Export Options

This is the second article in a series about publishing a website with Emacs Org mode. The previous article 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.

Export variables scoping

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:

  1. Add a keyword for a specific file (#+OPTIONS: :author:nil)
  2. Use an export parameter for the project (:with-author nil)
  3. Set a global variable for all future exports (setq org-export-with-author nil)

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.

Generic export options

The help file for org-publish-project-alist 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:

:with-toc nil
:with-author nil
:with-date nil
:time-stamp-file nil

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 #+OPTIONS: toc:1, which in this case would result in a table of contents with only level 1 headings.

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.

HTML Head

The head element 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.

Org assembles the head tag sequentially in three parts:

  1. org-html--build-meta-info: Character set encoding, viewport, document title, generator and available file keywords (author, description, and keywords).
  2. org-html--build-head: Default CSS and Javascript, and/or user-generated content.
  3. org-html--build-mathjax-config: MathJax scripts when LaTeX fragments are part of the content.

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 :html-head can be a string to define your preferences, as shown below.

:html-head-include-scripts nil
:html-head-include-default-style nil
:html-head "<link rel=\"stylesheet\" href=\"https://cdn.simplecss.org/simple.min.css\" />"

Alternatively, you can save the head content in a template file and include CSS, a favicon and other static parts of the head and read it into a string.

(setq my-html-head
      (with-temp-buffer
        (insert-file-contents
         (expand-file-name "templates/head.html"))
        (buffer-string)))

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

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

The next article in this series explains how to create dynamic content, such as Open Graph and Structured Data, using a function instead of a static variable.

The third part of the HTML head is an optional part to load MathJax for rendering mathematical formulas, which is further described below.

Layout Elements

Org mode uses an old HTML standard for generating pages. However, modern websites use HTML5 for improved accessibility, SEO, cleaner code, and easier styling.

The diagram below illustrates a typical semantic HTML5 page layout. It frames the central main area between a header and footer, dividing the main body into an aside for supplementary content and one or more sections, each containing one or more articles for the core message.

┌─ header ──────────────────────────────────────┐
│                                               │
└───────────────────────────────────────────────┘
┌─ main ────────────────────────────────────────┐
│ ┌─aside─┐ ┌─ section ───────────────────────┐ │
│ │       │ │ ┌─article─────────────────────┐ │ │
│ │       │ │ │                             │ │ │
│ │       │ │ │                             │ │ │
│ │       │ │ └─────────────────────────────┘ │ │
│ └───────┘ └─────────────────────────────────┘ │
└───────────────────────────────────────────────┘
┌─ footer ──────────────────────────────────────┐
│                                               │
└───────────────────────────────────────────────┘

You can configure Org mode to use HTML5 in the export process. These HTML export options in Org mode control the Document Object Model for your website:

  • org-html-doctype: Document type definition
  • org-html-html5-fancy: Use new HTML5 elements
  • org-html-divs: Defines the elements for HTML export
  • org-html-container-element: Top level sections

The example below configures Org mode to use semantic elements instead of generic <div> tags. In html-divs, the first string is the HTML tag and the second one is the id, e.g. <main id="content">.

:html-doctype "html5"
:html-html5-fancy t
:html-divs ((preamble  "header" "preamble")
            (content   "main"   "content")
            (postamble "footer" "postamble"))
:html-container "section"

The header of a HTML 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 :html-preamble.

When this variable is set to t, the content of org-html-preamble-format 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:

  • %t: Title of the source file (#+title:)
  • %s: Subtitle of the source file (#+subtitle:)
  • %a: Content of the #+author: keyword or user-full-name.
  • %e: Content of the #+email: keyword or user-mail-addres wrapped in a mailto link.
  • %d: Content of the #+date: keyword.
  • %C: Last modification time of the source file
  • %T: The export time of the source file
  • %c: Content of org-html-creator-string (links to Emacs and Org mode websites).
  • %v: Content of org-html-validation-link

The three timestamp format markers produce a date in accordance with org-html-metadata-timestamp-format, which you can configure to your regional preferences.

A simple example of a header string could be:

:html-preamble "<h1>%t</h1>\n<h2>%a</h2>"

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

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.

Org mode can also evaluate a function to define a header dynamically based on the page's metadata, which is discussed in the next article in this series.

Main content

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.

HTML Attributes

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.

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

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

#+begin_export html
  <span style="color:red">red text</span>
#+end_export

HTML5 documents can also have arbitrary #+BEGIN ... #+END blocks. For example:

#+BEGIN_aside
Lorem ipsum
#+END_aside

Which exports to:

<aside>
  <p>Lorem ipsum</p>
</aside>

The fourth method enables providing tables and images with HTML attributes using the #+ATTR_HTML: 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:

#+ATTR_HTML: :align center :width 300 :height 200 :alt This is an image

Org mode macros as shortcodes

Org mode macros 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:

{{{ youtube(5IJVDCpE3vA, "Video caption") }}}

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

(setq org-export-global-macros
      '(("youtube" . "@@html:<figure><iframe width=\"560\" height=\"315\"
   src=\"https://www.youtube.com/embed/$1\" title=\"$2\" frameborder=\"0\"
   allowfullscreen></iframe><figcaption>$2</figcaption></figure>@@")))

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.

Mathematics

Org mode can contain LaTeX math fragments. When exporting to HTML, Org can use either MathJax or transcode the math into images.

When Org mode detects LaTeX mathematics, it automatically inserts JavaScript for MathJax into the third part of the head, discussed above. Two variables are available to configure how mathematics is typeset:

  • org-html-mathjax-template: The JavaScript template
  • org-html-mathjax-options: Options for the template, such as size and font. Read the documentation for options.

To configure Org to insert images instead of calling the MathJax script, use #+OPTIONS: tex:dvipng 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.

Source Code

The default setting is that source blocks in Org mode are exported in code tags without syntax highlights. Install the htmlize package by Hrvoje Niksic and Org will use this functionality without any further configuration.

The footer

A footer typically contains authorship, copyright and contact information and perhaps links to other pages.

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 :html-postamble. The method is the same as with the header.

When this variable is t, the content of org-html-postamble-format 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.

Further Configuration

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.

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

The next article in this series discusses how to create dynamic content such as the HTML head, preable, postamble, sitemaps and RSS feeds using bespoke publishing Elisp functions.