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
- Improve collapsible source code block implementation
- https://alhassy.github.io/org-special-block-extras/ could be a good way to go…
- 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.
- currently working if
- Implement
target=_blank
for hyperlinks (presently all links open in the current tab)
Useful links
The following resources were extremely useful to me:
- https://orgmode.org/
- https://orgmode.org/worg/org-tutorials/org-publish-html-tutorial.html
- https://opensource.com/article/20/3/blog-emacs