Created: February 9, 2019

Last modified: May 29, 2023

Configuration for this Website

This is the entire configuration for this website including the css styling and org-publish configuration. The features this website supports can be seen here. Ideas for features I’d like to implement in the future can be found here.

CSS

Doom One Dark Theme

https://github.com/doomemacs/themes

@media (prefers-color-scheme: dark) {
    :root {
        --bg        : #282c34;
        --fg        : #bbc2cf;
        --bg-alt    : #21242b;
        --fg-alt    : #5B6268;
        --base0     : #1B2229;
        --base1     : #1c1f24;
        --base2     : #202328;
        --base3     : #23272e;
        --base4     : #3f444a;
        --base5     : #5B6268;
        --base6     : #73797e;
        --base7     : #9ca0a4;
        --base8     : #DFDFDF;
        --grey      : #3f444a;
        --red       : #ff6c6b;
        --i-red     : #ce537a;
        --orange    : #da8548;
        --green     : #98be65;
        --i-green   : #2d9574;
        --teal      : #4db5bd;
        --yellow    : #ECBE7B;
        --blue      : #51afef;
        --dark-blue : #2257A0;
        --magenta   : #c678dd;
        --violet    : #a9a1e1;
        --cyan      : #46D9FF;
        --dark-cyan : #5699AF;
    }
}

Doom One Light Theme

@media (prefers-color-scheme: light) {
    :root {
        --bg        : #fafafa;
        --fg        : #383a42;
        --bg-alt    : #f0f0f0;
        --fg-alt    : #c6c7c7;
        --base0     : #f0f0f0;
        --base1     : #e7e7e7;
        --base2     : #dfdfdf;
        --base3     : #c6c7c7;
        --base4     : #9ca0a4;
        --base5     : #383a42;
        --base6     : #202328;
        --base7     : #1c1f24;
        --base8     : #1b2229;
        --grey      : #9ca0a4;
        --red       : #e45649;
        --i-red     : #e45649;
        --orange    : #da8548;
        --green     : #50a14f;
        --i-green   : #50a14f;
        --teal      : #4db5bd;
        --yellow    : #986801;
        --blue      : #4078f2;
        --dark-blue : #a0bcf8;
        --magenta   : #a626a4;
        --violet    : #b751b6;
        --cyan      : #0184bc;
        --dark-cyan : #005478;
    }
}

elements

Much of this is from org-html-style-default

body {
  font-family: "Helvetica Neue", Helvetica, sans-serif;
  color: var(--fg);
  background-color: var(--bg);
  max-width: 42em;
  margin: auto;
}

#preamble {
  text-align: center;
}
#postamble {
  text-align: center;
  margin: 2em;
}
.menu {
  display: inline-block;
  padding: 0.2em;
  position: relative;
  background-color: var(--base0);
  border-radius: 0.2em;
}
button {
  border: none;
  background-color: inherit;
  font-size: inherit;
  font: inherit;
  cursor: pointer;
  color: var(--blue);
}
button:hover {
  text-decoration: underline;
}

.menu-btn {
  display: inline-block;
  width: 6em;
  transition: all 0.2s ease;
  color: var(--fg);
  padding: 0.3em;
  margin: 0.2em;
  border-radius: 0.2em;
  border: 2px solid var(--magenta);
  fill: var(--fg);
}
.menu-btn:hover {
  background-color: var(--magenta);
  color: var(--base0);
  fill: var(--base0);
}

a {
  color: var(--blue);
  background-color: inherit;
  font: inherit;
  text-decoration: inherit;
}
a:hover {
  text-decoration: underline;
}
.title  { text-align: center;
          margin-bottom: .2em; }
.subtitle { text-align: center;
            font-size: medium;
            font-weight: bold;
            margin-top:0; }
.todo   { font-family: monospace; color: red; }
.done   { font-family: monospace; color: green; }
.priority { font-family: monospace; color: orange; }
.tag    { background-color: #eee; font-family: monospace;
          padding: 2px; font-size: 80%; font-weight: normal; }
.timestamp { color: var(--fg-alt); }
.timestamp-kwd { color: var(--dark-cyan); }
.org-right  { margin-left: auto; margin-right: 0px;  text-align: right; }
.org-left   { margin-left: 0px;  margin-right: auto; text-align: left; }
.org-center { margin-left: auto; margin-right: auto; text-align: center; }
.underline { text-decoration: underline; }
#postamble p, #preamble p { font-size: 90%; margin: .2em; }
p.verse { margin-left: 3%; }

table { border-collapse:collapse; }
caption.t-above { caption-side: top; }
caption.t-bottom { caption-side: bottom; }
td, th { vertical-align:top;  }
th.org-right  { text-align: center;  }
th.org-left   { text-align: center;   }
th.org-center { text-align: center; }
td.org-right  { text-align: right;  }
td.org-left   { text-align: left;   }
td.org-center { text-align: center; }
dt { font-weight: bold; }
.footpara { display: inline; }
.footdef  { margin-bottom: 1em; }
.figure { padding: 1em; }
.figure p { text-align: center; }
.equation-container {
  display: table;
  text-align: center;
  width: 100%;
}
.equation {
  vertical-align: middle;
}
.equation-label {
  display: table-cell;
  text-align: right;
  vertical-align: middle;
}
.inlinetask {
  padding: 10px;
  border: 2px solid gray;
  margin: 10px;
  background: #ffffcc;
}
#org-div-home-and-up
{ text-align: right; font-size: 70%; white-space: nowrap; }
textarea { overflow-x: auto; }
.linenr { font-size: smaller }
.code-highlighted { background-color: #ffff00; }
.org-info-js_info-navigation { border-style: none; }
#org-info-js_console-label
{ font-size: 10px; font-weight: bold; white-space: nowrap; }
.org-info-js_search-highlight
{ background-color: #ffff00; color: #000000; font-weight: bold; }
.org-svg { width: 90%; }
.org-src-container { }
.org-ul {}

org classes

.org-builtin {color: var(--magenta);}
.org-comment {color: var(--fg-alt);}
.org-comment-delimiter {color: var(--fg-alt);}
.org-constant {color: var(--violet);}
.org-css-property {color: var(--green);}
.org-css-selector {color: var(--blue);}
.org-doc {color: #83898d;}
.org-font-latex-bold {font-weight: bold;}
.org-font-latex-math {color: var(--green);}
.org-font-latex-script-char {color: var(--dark-blue);}
.org-font-latex-sedate {color: #d3d3d3;}
.org-function-name {color: var(--magenta);}
.org-keyword {color: var(--blue);}
.org-operator {}
.org-whitespace-line {
  color: var(--red);
  background-color: var(--base0);
  font-weight: bold;
}
.org-negation-char {
  color: var(--blue);
  font-weight: bold;
}
.org-org-block {
  background-color: var(--base3);
}
.org-org-block-begin-line {
  color: var(--fg-alt);
  background-color: var(--base3);
}
.org-org-block-end-line {
  color: var(--fg-alt);
  background-color: var(--base3);
}
.org-org-meta-line {color: #83898d;}
.org-org-target {text-decoration: underline;}
.org-rainbow-delimiters-depth-1 {color: var(--blue);}
.org-rainbow-delimiters-depth-2 {color: var(--magenta);}
.org-rainbow-delimiters-depth-3 {color: var(--green);}
.org-rainbow-delimiters-depth-4 {color: var(--violet);}
.org-rainbow-delimiters-depth-5 {color: var(--teal);}
.org-rainbow-delimiters-depth-6 {color: var(--blue);}
.org-rainbow-delimiters-depth-7 {color: var(--magenta);}
.org-rainbow-delimiters-depth-8 {color: var(--green);}
.org-rainbow-delimiters-depth-9 {color: var(--violet);}
.org-regexp-grouping-backslash {
  color: var(--blue);
  font-weight: bold;
}
.org-regexp-grouping-construct {
  color: var(--blue);
  font-weight: bold;
}
.org-sh-heredoc {color: var(--green);}
.org-sh-quoted-exec {
  color: var(--blue);
  font-weight: bold;
}
.org-string {color: var(--green);}
.org-type {color: var(--yellow);}
.org-variable-name {color: #dcaeea;}
.org-warning {color: var(--yellow);}
.org-diff-added {
  color: var(--green);
  background-color: var(--bg-alt);
}
.org-diff-changed {
  color: var(--violet);
}
.org-diff-changed-unspecified {
  color: var(--violet);
  background-color: #333333;
}
.org-diff-context {}
.org-diff-error {
  color: #ff0000;
  background-color: #000000;
  font-weight: bold;
}
.org-diff-file-header {color: var(--blue);}
.org-diff-function {color: var(--cyan);}
.org-diff-header {color: var(--cyan);}
.org-diff-hunk-header {color: var(--violet);}
.org-diff-index {color: var(--blue);}
.org-diff-indicator-added {
  color: #22aa22;
  background-color: var(--bg-alt);
}
.org-diff-indicator-changed {color: #aaaa22;}
.org-diff-indicator-removed {
  color: #aa2222;
  background-color: var(--base3);
}
.org-diff-nonexistent {color: var(--blue);}
.org-diff-refine-added {
  color: var(--green);
  background-color: var(--bg-alt);
}
.org-diff-refine-changed {color: var(--violet);}
.org-diff-refine-removed {
  color: var(--red);
  background-color: var(--base3);
}
.org-diff-removed {
  color: var(--red);
  background-color: var(--base3);
}

Source Code

pre {
  border: 1px solid #e6e6e6;
  border-radius: 3px;
  padding: 8pt;
  font-family: monospace;
  overflow: auto;
  margin: 1.2em;
}
pre.src {
  position: relative;
  overflow: auto;
}
pre.src:before {
  display: none;
  position: absolute;
  top: -8px;
  right: 12px;
  padding: 3px;
  color: #555;
  background-color: #f2f2f299;
}
pre.src:hover:before { display: inline; margin-top: 14px;}
pre.src-C:before { content: 'C'; }
pre.src-css:before { content: 'CSS'; }
pre.src-dot:before { content: 'Graphviz'; }
pre.src-calc:before { content: 'Emacs Calc'; }
pre.src-emacs-lisp:before { content: 'Emacs Lisp'; }
pre.src-elisp:before { content: 'Emacs Lisp'; }
pre.src-fortran:before { content: 'Fortran'; }
pre.src-haskell:before { content: 'Haskell'; }
pre.src-js:before { content: 'Javascript'; }
pre.src-latex:before { content: 'LaTeX'; }
pre.src-lisp:before { content: 'Lisp'; }
pre.src-lua:before { content: 'Lua'; }
pre.src-org:before { content: 'Org mode'; }
pre.src-python:before { content: 'Python'; }
pre.src-jupyter-python:before { content: 'Jupyter Python'; }
pre.src-jupyter-julia:before { content: 'Jupyter Julia'; }
pre.src-ruby:before { content: 'Ruby'; }
pre.src-scheme:before { content: 'Scheme'; }
pre.src-sed:before { content: 'Sed'; }
pre.src-sh:before { content: 'shell'; }
pre.src-makefile:before { content: 'Makefile'; }
pre.src-perl:before { content: 'Perl'; }
pre.src-shell:before { content: 'Shell Script'; }
pre.src-cpp:before  { content: 'C++'; }
pre.src-bash:before  { content: 'bash'; }
pre.src-html:before { content: 'HTML'; }
pre.src-prolog:before { content: 'Prolog'; }
pre.src-tex:before { content: 'TeX'; }
pre.src-plain-tex:before { content: 'Plain TeX'; }
pre.src-conf:before { content: 'Configuration File'; }

JS

/*
@licstart  The following is the entire license notice for the
JavaScript code in this tag.

Copyright (C) 2012-2019 Free Software Foundation, Inc.

The JavaScript code in this tag is free software: you can
redistribute it and/or modify it under the terms of the GNU
General Public License (GNU GPL) as published by the Free Software
Foundation, either version 3 of the License, or (at your option)
any later version.  The code is distributed WITHOUT ANY WARRANTY;
without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE.  See the GNU GPL for more details.

As additional permission under GNU GPL version 3 section 7, you
may distribute non-source (e.g., minimized or compacted) forms of
that code without the copy of the GNU GPL normally required by
section 4, provided you include this license notice and a URL
through which recipients can access the Corresponding Source.


@licend  The above is the entire license notice
for the JavaScript code in this tag.
*/
 function CodeHighlightOn(elem, id)
 {
   var target = document.getElementById(id);
   if(null != target) {
     elem.cacheClassElem = elem.className;
     elem.cacheClassTarget = target.className;
     target.className = "code-highlighted";
     elem.className   = "code-highlighted";
   }
 }
 function CodeHighlightOff(elem, id)
 {
   var target = document.getElementById(id);
   if(elem.cacheClassElem)
     elem.className = elem.cacheClassElem;
   if(elem.cacheClassTarget)
     target.className = elem.cacheClassTarget;
 }

HTML

https://css-tricks.com/svg-favicons-and-all-the-fun-things-we-can-do-with-them/

<script src="/scripts.js" type="text/javascript"></script>
<link href="/styles.css" rel="stylesheet" type="text/css">
<link href="/favicon.svg" rel="icon" type="image/svg+xml">
<div class="menu">
<a class="menu-btn" href="/">Akira Kyle</a>
<span style="color: var(--func);">|</span>
<a class="menu-btn" href="/blog">Blogoroam</a>
</div>
<script>
MathJax = {
  chtml: {
    displayAlign: "center",
    displayIndent: "0em",
    scale: 1
  },
  svg: {
    scale: 1
  },
  tex: {
    tags: "ams",
    multlineWidth: "85%",
    tagSide: "right",
    tagIndent: ".8em",
    autoload: {
      color: [],
      colorV2: ['color']
    },
    packages: {'[+]': ['physics']}
  },
  options: {
    ignoreHtmlClass: 'tex2jax_ignore',
    processHtmlClass: 'tex2jax_process'
  },
  loader: {
    load: ['[tex]/physics']
  }
};
</script>
<script type="text/javascript" id="MathJax-script" async
src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js">
</script>

Emacs Lisp org-publish

See How I publish my wiki with org-publish for another take on exporting org-roam using org-publish.

(setq my-org-dir (file-name-as-directory "~/org"))
(setq my-org-publish-dir (file-name-as-directory "~/www/org"))
(setq my-website-publish-dir (file-name-as-directory "~/www/akirakyle.com"))

List of notes/pages

(setq my-notes-dir (file-name-as-directory (concat my-org-dir "notes")))

(defun my-org-get-env-key (post-filename key)
  "Extract the value of `#+title:`, `#+author:`, `#+date:`, or `#+filetags:`
from post-filename."
  (with-temp-buffer (insert-file-contents post-filename)
                    (cadar (org-collect-keywords (list key)))))

(defun my-notes-filenames ()
  "Returns a list of all files sorted approprately."
  (sort
   (directory-files my-notes-dir t ".*\\.org$" nil)
   (lambda (a b)
     (>
     (org-time-string-to-seconds (my-org-get-env-key a "date"))
     (org-time-string-to-seconds (my-org-get-env-key b "date"))))))

(defun my-filenames-to-org-list (post-filenames)
  (seq-reduce
   (lambda (acc el) (concat acc "\n- " el))
   (seq-map (lambda (fname)
              (concat
               (org-export-get-date
                `(:date ,(org-element-parse-secondary-string
                          (my-org-get-env-key fname "date")
                          (org-element-restriction 'keyword))) "%Y-%m-%d")
               ": "
               (org-link-make-string
                (concat "file:" (file-relative-name fname))
                (my-org-get-env-key fname "title"))))
            post-filenames)
   ""))

org roam export backlinks and reflinks and citations

  • insert-bibliography eliminates the need for #+print_bibliography: at the end of every file
    • my-filter-citations makes org citations link to the corresponding org-roam nodes
      • I didn’t end up trying to use the csl formatter to get the nice, configurable inline style, but rather just make the org-roam title close to what the csl formatter would do (yeah this is hacky).
;; TODO: Still need to fix spacing
(defun my-filter-citations (data backend info)
  (let* ((cite-to-id
          (lambda (el)
            (let* ((key (concat "@" (org-element-property :key el)))
                   (node (org-roam-node-from-ref key))
                   (_ (and (not node) (error "No org roam node for key %s" key)))
                   (id (org-roam-node-id node))
                   (title (org-roam-node-title node)))
              (org-link-make-string (concat "id:" id) title))))
         (cites-to-ids
          (lambda (elts)
            (with-temp-buffer
              (save-excursion
                (insert (concat " (" (mapconcat cite-to-id elts "; ") ") "))
                (car (org-element-map (org-element-parse-buffer) 'paragraph
                       'org-element-contents))))))
         (map-citation
          (lambda (el)
            (unless (org-element-property :style el)
              (mapc (lambda (l) (org-element-insert-before l el))
                    (funcall cites-to-ids (org-element-contents el)))
              (org-element-extract-element el)))))
    (org-element-map data 'citation map-citation info nil nil t)
    data))

(defun insert-bibliography (backend)
  "Insert reflinks into the end of the org file before parsing it."
  (when (eq backend 'html)
    (goto-char (point-min))
    (when (re-search-forward "\\[cite:.*\\]" nil t)
      (goto-char (point-max))
      (insert "\n\n* References:\n")
      (insert "#+print_bibliography: :title \"References\""))))

;; https://org-roam.discourse.group/t/export-backlinks-on-org-export/1756/2?page=2
(defun collect-backlinks-string (backend)
  "Insert backlinks into the end of the org file before parsing it."
  (when (and (or (eq backend 'html) (eq backend 'html-with-cite-filter))
             (org-roam-node-at-point))
    (let* ((backlinks (org-roam-backlinks-get (org-roam-node-at-point))))
      (when backlinks
        (goto-char (point-max))
        (insert "\n\n* Backlinks:\n")
        (dolist (backlink backlinks)
          (let* ((source-node (org-roam-backlink-source-node backlink))
                 (point (org-roam-backlink-point backlink)))
            (insert
             (format "- [[id:%s][%s]]\n"
                     (file-name-nondirectory (org-roam-node-id source-node))
                     (org-roam-node-title source-node)))))))))

(defun collect-reflinks-string (backend)
  "Insert reflinks into the end of the org file before parsing it."
  (when (and (or (eq backend 'html) (eq backend 'html-with-cite-filter))
             (org-roam-node-at-point))
    (let* ((reflinks (org-roam-reflinks-get (org-roam-node-at-point))))
      (when reflinks
        (goto-char (point-max))
        (insert "\n\n* Reflinks:\n")
        (dolist (reflink reflinks)
          (let* ((source-node (org-roam-reflink-source-node reflink))
                 (point (org-roam-reflink-point reflink)))
            (unless (string= (org-roam-node-id source-node)
                             (org-roam-node-id (org-roam-node-at-point)))
              (insert
               (format "- [[id:%s][%s]]\n"
                       (file-name-nondirectory (org-roam-node-id source-node))
                       (org-roam-node-title source-node))))))))))

publish config

(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
;; I use the ~#+modified:~ property rather than org's default of using the
;; file's modification time, since my org files are in git
(defun my-org-html-build-preamble (info)
  (let* ((spec (org-html-format-spec info))
         (date (cdr (assq ?d spec)))
         (timestamp-format (plist-get info :html-metadata-timestamp-format))
         (modified-str (cadar (org-collect-keywords '("modified"))))
         (modified-parsed (org-element-parse-secondary-string
                           modified-str (org-element-restriction 'keyword)))
         (modified (org-export-get-date `(:date ,modified-parsed)
                                        timestamp-format)))
    (concat
     <<blk-to-elisp-str("html-preamble")>>
     (and (plist-get info :with-date)
          (org-string-nw-p date)
          (format "<p class=\"date\">Created: %s</p>\n" date))
     (and (plist-get info :with-date)
          (org-string-nw-p modified)
          (format "<p class=\"date\">Last modified: %s</p>\n" modified)))))

;;(setq org-export-async-init-file (expand-file-name "~/notebook/setup.el")
;;      org-export-in-background t)
(setq org-html-metadata-timestamp-format "%B %e, %Y")
(setq org-html-htmlize-output-type 'css)
(setq org-html-html5-fancy t)
(setq org-html-doctype "html5")
(setq org-export-with-toc nil)
(setq org-export-with-section-numbers nil)
(setq org-export-time-stamp-file nil)
(setq org-html-head-include-default-style nil)
(setq org-html-head-include-scripts nil)
(setq org-html-head
      <<blk-to-elisp-str("html-header")>>
      )
(setq org-html-preamble 'my-org-html-build-preamble)
(setq org-html-postamble nil)
(setq org-html-mathjax-template
      <<blk-to-elisp-str("mathjax-script")>>
      )

;; for org-html-link to omit .html from a href links
(setq org-html-extension "")

(org-export-define-derived-backend 'html-with-cite-filter 'html)

(defun my-org-html-publish-to-html (plist filename pub-dir)
  (org-publish-org-to 'html filename ".html" plist pub-dir))

(defun my-org-html-publish-to-html-with-cite-filter (plist filename pub-dir)
  (let ((org-export-filter-parse-tree-functions '(my-filter-citations)))
    (org-publish-org-to 'html-with-cite-filter filename ".html" plist pub-dir)))

(add-hook 'org-export-before-processing-functions 'insert-bibliography)
(add-hook 'org-export-before-processing-functions 'collect-backlinks-string)
(add-hook 'org-export-before-processing-functions 'collect-reflinks-string)

(setq collab-url "collab/")

(defun my-publish-dir-with-cite-filter (dir)
  `(,dir
    :base-directory ,(concat my-org-dir dir)
    :publishing-directory ,(concat my-website-publish-dir collab-url dir)
    :publishing-function my-org-html-publish-to-html-with-cite-filter))

(defun my-publish-dir (dir)
  `(,dir
    :base-directory ,(concat my-org-dir dir)
    :publishing-directory ,(concat my-website-publish-dir dir)
    :publishing-function my-org-html-publish-to-html))
    ;;:html-postamble "Insert comments here..."))

(setq org-publish-project-alist
      `(
        ,(cons "top" (cdr (my-publish-dir "")))
        ,(my-publish-dir "config")
        ,(my-publish-dir "notes")
        ,(my-publish-dir "present")
        ,(my-publish-dir-with-cite-filter "ref-notes")
        ,(my-publish-dir-with-cite-filter "notebook")
        ,(my-publish-dir-with-cite-filter "rough")
        ;("pub"
        ; :exclude "info/.*\\|rough/.*\\|notebook/.*\\|ref-notes/.*"
        ; :recursive t)
        ("resources"
         :base-directory ,my-org-dir
         :publishing-directory ,my-website-publish-dir
         :base-extension "svg"
         :publishing-function org-publish-attachment)
        ("website" :components ("top" "config" "notes" "present" "resources"))
        ))

(defun my-tangle-website-js-css ()
  (org-babel-tangle-file (concat my-org-dir "config/website.org")
                         (concat my-website-publish-dir "styles.css")
                         "css")
  (org-babel-tangle-file (concat my-org-dir "config/website.org")
                         (concat my-website-publish-dir "scripts.js")
                         "js"))
<<org-dir-vars>>
<<list-org-files>>
<<org-roam-export-helpers>>
<<org-publish-project>>
(org-publish "website" t)

Need to run org-id-update-id-locations and org-roam-update-org-id-locations when exporting and get “org-export-data: Unable to resolve link”

Preview with simple-httpd

(require 'simple-httpd)

(defun httpd-gen-path (path &optional root)
  "Translate GET to secure path in ROOT (`httpd-root')."
  (let ((clean (expand-file-name (httpd-clean-path path) (or root httpd-root))))
    (if (file-directory-p clean)
        (let* ((dir (file-name-as-directory clean))
               (indexes (cl-mapcar (apply-partially 'concat dir) httpd-indexes))
               (existing (cl-remove-if-not 'file-exists-p indexes)))
          (or (car existing) dir))
      (if (file-exists-p clean)
          clean
        (concat clean ".html")))))

(setq httpd-root my-website-publish-dir)
(setq httpd-listings t)
(setq httpd-host "0.0.0.0")
(httpd-start)

References: