February 5, 2019

Using Literate Programming to make CSS+HTML+JS+ELisp play nice together

Among the features of Emacs’ Org-Mode that I love is its support for literate programming. For an good intro to how this all checkout this page. I wanted to take advantage of this to hopefully make it easier to work with the CSS, HTML, JavaScript, and Emacs Lisp that powers this site.


The first place I used the noweb support of org-mode was in including my HTML headers the setup of the org-publish-project-alist for this site which looks something like (note that this is just a snippet of org-publish-project-alist):

org code
#+name: html-header
#+begin_src html
<link href=\"/styles.css\" rel=\"stylesheet\" type=\"text/css\">
<script src=\"/scripts.js\" type=\"text/javascript\"></script>

#+begin_src emacs-lisp :noweb no-export
(setq org-publish-project-alist
         :base-directory "~/Documents/website/"
         :publishing-directory "~/Documents/www/"
         :recursive t
         :base-extension "org"
         :publishing-function org-html-publish-to-html

         :html-doctype "html5"
         :html-head "

Notice that <<html-header>> is on it’s own line completely left justified. This is because by no web references will include whatever is before the reference before every line in the referenced block. For example if I had done the natural thing and written:

org code
:html-head "<<html-header>>"

It would have expanded to

html code
:html-head "<link href="/styles.css" rel="stylesheet" type="text/css">"
:html-head "<script src="/scripts.js" type="text/javascript"></script>"

Which of course in invalid html.

Notice also that we must escape all the double quotes in the html block because that block is actually going into a elisp string. This is annoying as it’s not actually valid html. To fix this we can borrow an idea from here and create a sort of function reference which will take care of escaping the quotes:

org code
#+NAME: blk-to-elisp-str
#+begin_src elisp :var name=""
(let* ((named-element (org-element-map (org-element-parse-buffer) org-element-all-elements
                        (lambda (element)
                          (when (string= (org-element-property :name element) name)
                        nil t))
       (result (org-element-property :value named-element)))
  (format "\"%s\"" (replace-regexp-in-string "\\\"" "\\\\\"" result))) ;; escape quote


I often find it annoying that I have to switch between multiple files to work on HTML, CSS, and JS. Of course I could just put them in <style> and <script> tags in the HTML header, but that’s kind of inefficient and tangling is the perfect solution! All I have to do is have a css block in the org file with all my configuration for the website and tell it where to tangle out the CSS when I execute org-babel-tangle

org code
#+begin_src css :tangle ~/Documents/www/styles.css
body {
  font-family: "Helvetica Neue", Helvetica, sans-serif;
  color: var(--base);
  background-color: var(--bg1);
  color: white;
  background-color: rgb(29, 31, 33);
  width: 50%;
Email: akira@akirakyle.com
GPG Public Key: 963C 2413 0BD3 BF1B 624C EF4C 8850 284C 20B8 078D