As my WordPress subscription neared its expiration I decided I would finally take the time to build my own website using a pure Emacs and Org mode setup. WordPress, while a fine CMS, is overkill as I have no need for a built-in editor, themes, or plug-ins. I just need a powerful markup language and static HTML page generation.
While using WordPress I already composed my posts in Org mode and I imported them using the org2blog package. This was an okay solution, but I was never happy with how clunky of an experience it was and felt redundant when Org mode has built in HTML exporting. Add to that a cheap WordPress.com plans offer few customization options.
- Have a lightweight, consistent layout and mobile scaling website
- Be able to easily rebuild my website from source and sync it to any web server
- Do so without adding any new software dependencies (just Emacs, git, make, ssh, rsync)
|Markup Language||Org mode|
|Syncing||ssh & rsync|
Org mode's built-in publishing functionality handles converting the Org files
into HTML pages and linking them all together. We need to specify the
org-publish-project-alist where our files are, how to export them, and where to
(setq org-publish-project-alist `(("home" :base-directory "~/Documents/blog/org/" :base-extension "org" :publishing-directory "~/Documents/blog/html/" :publishing-function org-html-publish-to-html :recursive nil :html-head ,taingram-head :html-preamble "<div id=\"updated\">Updated: %C</div>" :html-postamble ,taingram-footer) ("blog" :base-directory "~/Documents/blog/org/blog/" :base-extension "org" :publishing-directory "~/Documents/blog/html/blog/" :publishing-function org-html-publish-to-html :auto-sitemap t :sitemap-filename "blog.org" :sitemap-sort-files anti-chronologically :sitemap-format-entry org-sitemap-custom-entry-format :html-head ,taingram-head :html-preamble "<div id=\"updated\">Updated: %C</div><nav><a href=\"/\">< Home</a></nav>" :html-postamble ,taingram-footer) ("static" :base-directory "~/Documents/blog/org/" :base-extension "css\\|jpg\\|gif\\|png\\|pdf" :recursive t :publishing-directory "~/Documents/blog/html/" :publishing-function org-publish-attachment) ("taingram.org" :components ("home" "blog" "static"))))
One interesting thing to note is that I've separated "home" from "blog". I did this because while I am interested in having a sitemap generated listing my blog posts, of which I will keep adding many pages to, I see less value having one for the root directory of my website. Where I will rarely add pages and when I do it is easier to just add links manually than have a large and confusing sitemap.
In order to have my blog sitemap shown on the home page I included just part
of generated sitemap. Here's the snippet from
* Blog #+HTML: <div class="blog-entries"> #+INCLUDE: blog/blog.org :lines "2-" #+HTML: </div>
The surrounding div class is used for additional CSS styling.
Another thing of note is the variables
html-(head|preamble|postamble) this is
where I insert my stylesheet, and extra HTML for navigation and footer. The
variables I set them to are shown below.
(defvar taingram-head "<link rel=\"stylesheet\" href=\"/style.css\" type=\"text/css\"/>") (defvar taingram-footer "<hr/> <footer> <div class=\"copyright-container\"> <div class=\"copyright\"> Copyright © 2019 Thomas Ingram some rights reserved<br/> Content is available under <a rel=\"license\" href=\"http://creativecommons.org/licenses/by-sa/4.0/\"> CC-BY-SA 4.0 </a> unless otherwise noted </div> <div class=\"cc-badge\"> <a rel=\"license\" href=\"http://creativecommons.org/licenses/by-sa/4.0/\"> <img alt=\"Creative Commons License\" src=\"https://i.creativecommons.org/l/by-sa/4.0/88x31.png\" /> </a> </div> </div> <div class=\"generated\"> Created with %c; powered by <a href=\"https://httpd.apache.org/\">Apache</a> and <a href=\"https://getfedora.org/\">Fedora</a> <a href=\"https://www.gnu.org\">GNU</a>/<a href=\"https://www.kernel.org/\">Linux</a> </div> </footer>")
I preferred to write my own CSS stylesheet from scratch so I disabled
; Disable some Org publish defaults :html-head-include-scripts nil :html-head-include-default-style nil :with-toc nil :section-numbers nil ; Use HTML5 tags :html-html5-fancy t :html-doctype "html5"
In this config I do not make use of Emacs' tramp features which would allow for
directly exporting these files to the remote server by changing
:publishing-directory to something like
"//ssh:email@example.com:~/public_html//". In my (limited) testing I have
found this to be far to slow in comparison with syncing the files after export.
TODO RSS Feed
There are two ways to build this blog, one within Emacs and one outside of Emacs.
For those unfamiliar with Emacs I have provided a makefile that will generate the HTML files (only GNU Emacs and make are required dependencies).
all: emacs -Q --batch -l publish.el -f org-publish-all publish: all rsync -e ssh -vr html/ firstname.lastname@example.org:/var/www/taingram.org/html/ clean: rm -r html/*
Thanks to Thibault Marin for his help with make a custom sitemap function using an Org mode macro.