Created: February 5, 2019

Last modified: February 5, 2023

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 all this, 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.

Noweb

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):

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

#+begin_src emacs-lisp :noweb no-export
(setq org-publish-project-alist
      `(
        ("pages"
         :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 "
<<html-header>>
"
        ))
#+end_src

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:

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

It would have expanded to

: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:

#+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)
                            element))
                        nil t))
       (result (org-element-property :value named-element)))
  (format "\"%s\"" (replace-regexp-in-string "\\\"" "\\\\\"" result))) ;; escape quote
#+end_src

Tangling

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

#+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%;
  margin-left:auto;
  margin-right:auto;
}
#+end_src

Backlinks: