How I built this website

I built this site using (Doom) Emacs and Org-Mode. The literate programming capabilities built into Org-Mode with Org-Babel were particularly useful for evaluating and rendering the Python source code blocks. The source code for the web site is also available from my GitHub repo. I have laid out some of the important details for putting the page together below and all source files were rendered using Org-mode’s #+INCLUDE: tag (see docs) ensuring that they are up-to-date.

Org-Mode

To avoid rendering large blocks of source code and cluttering the pages I implemented a “hacky” solution for collapsible source code blocks using a combination of inline HTML, CSS and JavaScript. This is not ideal but is the best that I could manage without expending too much time.

Above each source block I added the following inline HTML:

#+HTML: <button type="button" class="collapsible">Toggle code</button

For the case where I wanted all code blocks to be hidden by default I added the following HTML and to the bottom of the org-file.

#+HTML_HEAD: <style> .org-src-container {padding: 0 18px; display: none; overflow: hidden; background-color: #f1f1f1;} </style>
#+begin_export html
<script>
    var coll = document.getElementsByClassName("collapsible");
    var i;

    for (i = 0; i < coll.length; i++) {
        coll[i].addEventListener("click", function() {
            this.classList.toggle("active");
            var content = this.nextElementSibling;
            if (content.style.display === "block") {
                content.style.display = "none";
                } else {
                content.style.display = "block";
            }
        });
    }
</script>
#+end_export

In the case where I want the source blocks to be visible by default (this page) I used the following:

#+begin_export html
<script>
    var coll = document.getElementsByClassName("collapsible");
    var i;

    for (i = 0; i < coll.length; i++) {
        coll[i].addEventListener("click", function() {
            this.classList.toggle("active");
            var content = this.nextElementSibling;
            if (content.style.display === "none") {
                content.style.display = "block";
                } else {
                content.style.display = "none";
            }
        });
    }
</script>
#+end_export

Emacs-lisp

The Emacs-lisp code used to publish the web page via the make file.

;;; publish.el ---- Jacob P. Brady
;;; Commentary:
;;;     config for webpage
(require 'ox-publish)
;;; Code:
;;;
(setq org-link-file-path-type 'relative)
(setq org-publish-project-alist
      '(
        ("j-brady.github.io-content"
        :base-directory "./"
        :base-extension "org"
        :publishing-directory "./html/"
        :htmlized-source t
        :recursive t
        :publishing-function org-html-publish-to-html
        :headline-levels 4
        ;:auto-preamble t
        :auto-sitemap t                ; Generate sitemap.org automagically...
        :sitemap-filename "sitemap.org"  ; ... call it sitemap.org (it's the default)...
        :sitemap-title "Sitemap"         ; ... with title 'Sitemap'.
        :email "jacob.brady@protonmail.com"
        :author "Jacob P Brady"
        ; HTML5
        :html-doctype "html5"
        :html-html5-fancy t
        ; Disable some Org's HTML defaults
        :html-head-include-scripts nil
        :html-head-include-default-style nil
        ; HTML preamble
        ;:html-preamble
        :html-postamble "<p class=\"author\">Author: %a (%e)</p><p class=\"creator\">%c</p><div id=\"updated\">Updated: %C</div>")
       ("j-brady.github.io-static"
        :base-directory "./"
        :base-extension "css\\|js\\|png\\|jpg\\|gif\\|pdf\\|mp3\\|ogg\\|swf"
        :publishing-directory "./html/"
        :recursive t
        :publishing-function org-publish-attachment
        )
       ("j-brady.github.io"
        :components ("j-brady.github.io-content" "j-brady.github.io-static"))))

(provide 'publish)
;;; publish.el

Unfortunately, I had trouble getting the source code syntax highlighting to work with this method so I also added this code (slightly modified) to my literate emacs configuration file, config.org, and publish the website directly from within Emacs by running org-publish-all. The relevant section of my config file is shown below:

(require 'ox-publish)

;;; Code:
;;;

(setq org-link-file-path-type 'relative)
(setq org-publish-project-alist
      '(
        ("j-brady.github.io-content"
        :base-directory "~/src/j-brady.github.io/"
        :base-extension "org"
        :publishing-directory "~/src/j-brady.github.io/html/"
        :recursive t
        :publishing-function org-html-publish-to-html
        :headline-levels 4
        ;:auto-preamble t
        :auto-sitemap t                ; Generate sitemap.org automagically...
        :sitemap-filename "sitemap.org"  ; ... call it sitemap.org (it's the default)...
        :sitemap-title "Sitemap"         ; ... with title 'Sitemap'.
        :email "jacob.brady@protonmail.com"
        :author "Jacob P Brady"
        ; HTML5
        :html-doctype "html5"
        :html-html5-fancy t
        ; Disable some Org's HTML defaults
        :html-head-include-scripts nil
        :html-head-include-default-style nil
        ; HTML preamble
        ;:html-preamble
        :html-postamble "<p class=\"author\">Author: %a (%e)</p><p class=\"creator\">%c</p><div id=\"updated\">Updated: %C</div>")
       ("j-brady.github.io-static"
        :base-directory "~/src/j-brady.github.io/"
        :base-extension "css\\|js\\|png\\|jpg\\|gif\\|pdf\\|mp3\\|ogg\\|swf"
        :publishing-directory "~/src/j-brady.github.io/html/"
        :recursive t
        :publishing-function org-publish-attachment
        )
       ("j-brady.github.io"
        :components ("j-brady.github.io-content" "j-brady.github.io-static"))))

;;(provide 'publish)
;;; publish.el

Setting relative paths

In order for links to work properly on a server they should be relative. By default Org-mode’s org-link-file-path-type is set to 'adaptive which will convert paths like ../blog/filename.org to absolute paths (not what we want). Therefore, I set this variable to 'relative. I also added set this in publish.el… not sure if that is necessary though.

(setq org-link-file-path-type 'relative)

Makefile

Make is a very useful tool! Thanks to John Gosset of introducing it to me!

# Makefile for mywebpage

.PHONY: all publish publish_no_init clean-touch

all: clean publish
clean-touch: clean touch

publish: publish.el
    @echo "Publishing... with current Emacs configurations."
    emacs --batch --load publish.el --funcall org-publish-all

publish_no_init: publish.el
    @echo "Publishing... with --no-init."
    emacs --batch --no-init --load publish.el --funcall org-publish-all

clean:
    @echo "Cleaning up.."
    @rm -rvf *.elc
    @rm -rvf html
    @rm -rvf ~/.org-timestamps/*

touch:
    @echo "Touching files..."
    @touch *.org
    @touch **/*.org
    @touch **/*.png
    @touch static/css/*.css

CSS

The custom CSS code used to style the page is included below. For the collapsible code blocks I used this example from the W3 Schools website.

body {
    font-family: 'Alegreya', serif;
    margin: 8px;
}
.title {
  text-align: center;
}

/* Larger than mobile screen */
@media (min-width: 40.0rem) {
  body {
    margin-left: 12rem;
    margin-right: 12rem;
    margin-top: 2rem;
  }
}

/* Larger than tablet screen */
@media (min-width: 80.0rem) {
  body {
    margin-left: 24rem;
    margin-right: 24rem;
    margin-top: 4rem;
  }
}

/* Larger than desktop screen */
@media (min-width: 120.0rem) {
  body {
    margin-left: 32rem;
    margin-right: 32rem;
    margin-top: 6rem;
  }
}

/* Add a background color to the button if it is clicked on (add the .active class with JS), and when you move the mouse over it (hover) */
.active, .collapsible:hover {
  background-color: #ccc;
}

/*.src-python {
  background-color: #ccc;
}*/

Todo

  1. Improve collapsible source code block implementation
  2. Enable syntax highlighting (I think this has something to do with using emacs --batch see Makefile)
    • currently working if org-publish-all is executed from within emacs.
    • this is sub-optimal and requires extra steps
    • I changed to a light Doom theme in order that Python variables were not colored white.
  3. Implement target=_blank for hyperlinks (presently all links open in the current tab)

Useful links

The following resources were extremely useful to me:


Back to main page


Author: Jacob P Brady (jacob.brady@protonmail.com)

Emacs 27.1 (Org mode 9.6)

Updated: 2022-12-28 Wed 12:40