Files
blog/org/source.org
2025-11-20 16:32:06 +01:00

33 KiB
Raw Blame History

Webbieweb.org Sources

Webbieweb.org Literate Sources   h4left h5left h3underline

Introduction

These are the literate sources of webbieweb.org. The code blocks here constitute the actual sources; therefore, this document is guaranteed to be up to date.

This page is self-contained; the only dependency is ox-publish. The following directory structure is expected:

.
|-- html                ;; Export destination; you can copy this dir to any static file host and host the website that way.
|-- org                 ;; Org-mode sources; these will get compiled and the output copied to html/.
    `-- source.org      ;; This file.
|-- static              ;; Images, videos, CSS files, fonts etc will be copied unchanged to html/static.
`-- util
    |-- head.html       ;; Contains HTML <head>; will be prepended to every output file.
    |-- postamble.html  ;; Navbar; will be prepended to <body> of every output file.
    `-- preamble.html   ;; Footer; as above.

(Apologies for the misalignment, I don't know why that happens.)

Export Commands

Load this file up in Emacs and hit C-c C-c on the code block below to export changes to HTML.

<<blog-scaffolding>>
(blog/publish)
(org-babel-tangle)

The one below will ignore caching; necessary in some situations, for instance: changes under util/.

<<blog-scaffolding>>
(blog/force-publish)
(org-babel-tangle)

Elisp

Basic Settings/Scaffolding   autocollapse

(require 'ox-publish)

<<no-sitemap>>

(defun blog/get-util (x)
  (with-temp-buffer
    (insert-file-contents (concat "../util/" x))
    (buffer-string)))

(setq org-html-metadata-timestamp-format "%Y-%m-%d")

(defun blog/spec ()
  "Return project settings for use with `org-publish-project-alist'."
  (let* ((html-head (blog/get-util "head.html"))
         (html-preamble (blog/get-util "preamble.html"))
         (html-postamble (blog/get-util "postamble.html")))


    `(("pages"
       :base-directory "."
       :base-extension "org"
       :recursive t
       :publishing-directory "../html"
       :publishing-function org-html-publish-to-html

       :html-doctype "html5"
       :html-html5-fancy t

       :html-viewport ((width "100%")
                       (initial-scale "0.7")
                       (minimum-scale "")
                       (maximum-scale "")
                       (user-scalable ""))

       :language "en"
       :section-numbers nil

       :with-toc nil
       :with-date t
       :with-title nil
       :with-author nil

       :auto-sitemap t

       :sitemap-sort-files anti-chronologically
       :sitemap-format-entry blog/sitemap-format-entry

       :sitemap-function
       blog/sitemap-function


       :headline-levels 4
       :html-head ,html-head

       :html-preamble ,html-preamble
       :html-postamble
       (lambda (p)
         (let* ((timestamp-format  (plist-get p :html-metadata-timestamp-format))
                (date (org-export-data (org-export-get-date p timestamp-format)
                                       p))
                (file (plist-get p :input-file))
                (modified (format-time-string
                           timestamp-format
                           (and file (file-attribute-modification-time
                                      (file-attributes file))))))
           (concat
            "<div id='footer'>"
            (when (not (string-blank-p (format "%s" date)))
              (format "<div id='publish-date'>Published: %s</div>" date))
            (format "<div id='modified-date'>Last modified: %s</div>" modified)
            ,html-postamble
            "</div>"))))

      ("static"
       :base-directory "../static"
       :base-extension "css\\|txt\\|jpg\\|gif\\|png\\|ttf\\|js\\|mp4\\|webm"
       :recursive t
       :publishing-directory  "../html/static"
       :publishing-function org-publish-attachment)

      ("RSS"
       :base-directory "../RSS"
       :base-extension "xml"
       :publishing-directory "../html"
       :publishing-function org-publish-attachment)

      ("blog" :components ("pages" "static" "RSS")))))

(defun blog/publish ()
  (setq org-publish-project-alist (blog/spec))
  (org-publish-all))

(defun blog/force-publish ()
  (setq org-publish-project-alist (blog/spec))
  (org-publish-remove-all-timestamps)
  (org-publish-all))

Features

Sitemap   autocollapse

Files with #+FILETAGS: :no-sitemap: at the top will be excluded from the sitemap. Every directory dir/ will link to dir/index.org, with index.org being removed as a child entry.

(defun blog/sitemap-format-entry
    (entry style project)
  ;; Blank out entries with :no_sitemap:
  (let* ((tags (org-publish-find-property entry :filetags project))
         (skip (member "no_sitemap" tags)))

    (cond
     (skip "")

     ((not (directory-name-p entry))
      (format
       "[[file:%s][%s]]\n"
       entry
       (org-publish-find-title entry project)))

     ;; Link index to top level
     ((eq style 'tree)
      (format
       "[[file:%sindex.org][%s]]\n"
       entry
       (capitalize (file-name-nondirectory (directory-file-name entry)))))

     (t entry))))


;; Remove blank entries from sitemap
(defun blog/sitemap-function
    (title lst)
  (let* ((sitemap (org-publish-sitemap-default title lst)))
    (replace-regexp-in-string "^[ \t]*-[ \t]*\n" "" sitemap)))

Tag-based Section Control   autocollapse

Org-mode headers support tags:

* Header :foo:
    Sample text

Which will compile into something like:

<div id="outline-container-orgf43334f" class="outline-5">
  <h5 id="orgf43334f" style="cursor: pointer;">Header
    <span class="tag">
      <span class="foo">foo</span>
    </span>
  </h5>
  <div class="outline-text-5" id="text-orgf43334f">
    <p> Sample text </p>
  </div>
</div>

We can use these tags to granularly control styling and JS features per header. We will hide the tags themselves:

.tag { display: none; }

Header Styling Tags   autocollapse

Tags for granular styling. This page has :h4left:h5left:h3underline: at the top level.

div:has(* .tag .h4left) h4 {
    text-align: left;
}

div:has(* .tag .h5left) h5 {
    text-align: left;
}

div:has(* .tag .h3underline) h3 {
    text-decoration: underline;
}

Journal   autocollapse

Here's a template for the journal files, as reference for what follows:

Template   autocollapse
Styling
div:has(h3 .tag .journal) {
    border: none;
    box-shadow: none;
}

div:has(> h3 .tag .journal)::after {
    content: '~ ❦ ~';
    display: block;
    text-align: center;
    padding-top: 0.2rem;
    padding-bottom: 0.2rem;
    color: var(--grey4);
    font-size: 2rem;
}

h3:has(.tag .journal) {
    text-align: left;
}

h4:has(.tag .jentry) {
    display: none;
}

h4:has(.tag .habits) {
    text-align: left;
}

Habit Tracker   autocollapse

Implements the habit tracking table on the front page. Habits are extracted from journal entries and rendered into a table, including a "streak" heatmap and the ability to click cells to see the corresponding entry.

Elisp   autocollapse
(defun akk0/org-to-html (org-string)
  "Convert ORG-STRING to HTML."
  (with-temp-buffer
    (insert org-string)
    (org-mode)
    (org-export-as 'html nil nil t nil)))

(defun akk0/sort-habits (habit-alist)
  "Sort habit-alist by a predefined order of custom-ids."
  (let ((order '("dailies-blogging" "dailies-meditation" "dailies-french"  "dailies-engineering"
                 "dailies-exercise" "dailies-drawing" "dailies-reading" "dailies-social")))
    (sort (copy-sequence habit-alist)
          (lambda (a b)
            (let ((pos-a (or (cl-position (car a) order :test #'equal) 999))
                  (pos-b (or (cl-position (car b) order :test #'equal) 999)))
              (< pos-a pos-b))))))


(defun akk0/extract-habits (file)
  "Extract habits with date context from FILE.
Returns a list of plists with :custom-id, :todo-state, :date, :day-of-year."
  (with-temp-buffer
    (insert-file-contents file)
    (org-mode)
    (let (results)
      (org-element-map (org-element-parse-buffer) 'headline
        (lambda (hl)
          (let ((custom-id (org-element-property :CUSTOM_ID hl))
                (todo-state (org-element-property :todo-keyword hl)))
            (when (and custom-id todo-state)
              ;; Get parent properties for context
              (let* ((parent (org-element-property :parent hl))
                     (day-of-year (or (org-element-property :DAILIES-DAY parent)
                                     (let ((grandparent (org-element-property :parent parent)))
                                       (when grandparent
                                         (org-element-property :DAILIES-DAY grandparent)))))
                     (body (org-element-interpret-data
                            (org-element-contents hl))))
                (push (list :custom-id custom-id
                            :todo-state todo-state
                            :day-of-year day-of-year
                            :file file
                            :body body)
                      results))))))
      (nreverse results))))

(defun akk0/extract-all-habits (files)
  (mapcan #'akk0/extract-habits files))

(setq akk0/journal-files
      (directory-files "./journal/" t "^w.*\\.org$"))

(defun akk0/habits-alist (habits)
  "Transform HABITS list into nested alists: custom-id → day-of-year → habit-data."
  (let (result)
    (dolist (habit habits)
      (let* ((custom-id (plist-get habit :custom-id))
             (day-of-year (plist-get habit :day-of-year))
             (todo-state (plist-get habit :todo-state))
             (body (plist-get habit :body))
             ;; Get the alist for this custom-id
             (inner-alist (alist-get custom-id result nil nil #'equal))
             ;; Store full data instead of just todo-state
             (habit-data (list :todo-state todo-state :body body :day-of-year day-of-year))
             ;; Update the inner alist
             (updated-inner (cons (cons day-of-year habit-data) inner-alist)))
        ;; Update result
        (setf (alist-get custom-id result nil nil #'equal) updated-inner)))
    result))


(setq habit-alist (akk0/habits-alist (akk0/extract-all-habits akk0/journal-files)))

(defun akk0/get-habit-history (habit-alist custom-id day-number days-back window-size)
  (let* ((inner-alist (alist-get custom-id habit-alist nil nil #'equal))
         (result nil)
         (all-states nil)
         (score-for-state (lambda (state)
                            (cond ((equal state "NO") -1)
                                  ((equal state "YES") 1)
                                  ((equal state "EXCELLENT") 2)
                                  (t 0)))))
    (dotimes (i days-back)
      (let* ((current-day (+ (- day-number days-back) i 1))
             (current-day-str (number-to-string current-day))
             (habit-data (alist-get current-day-str inner-alist nil nil #'equal))
             (todo-state (if habit-data
                            (plist-get habit-data :todo-state)
                          "NODATA"))
             (body (if habit-data
                      (plist-get habit-data :body)
                    ""))

             (doy (if habit-data
                      (plist-get habit-data :day-of-year)
                    ""))
             )
        (push todo-state all-states)
        (let* ((window-states (seq-take all-states window-size))
               (rolling-score (apply #'+ (mapcar score-for-state window-states))))
          (push (list :todo-state todo-state
                     :score (max 1 (min 5 (/ (+ rolling-score 5) 2)))
                     :body body
                     :doy doy)
                result))))
    (nreverse result)))

(defun akk0/habits-to-html-table (habit-alist day-number days-back window-size)
  "Generate HTML table of habits with rolling scores.
Rows are custom-ids, columns are days."
  (let ((color-map '(("NODATA" . "grey")
                     ("YES" . "green")
                     ("NO" . "red")
                     ("FREED" . "purple")
                     ("EXCELLENT" . "blue")))
        (symbol-map '(("NODATA" . "")
                     ("YES" . "●")
                     ("NO" . "×")
                     ("FREED" . "♣")
                     ("EXCELLENT" . "♦")))

        (sorted-habits (akk0/sort-habits habit-alist))
        (html ""))
    ;; Start table
    (setq html (concat html "<table style='margin-left: auto; margin-right:auto; margin-bottom: 0.8rem;'>\n"))

    ;; Header row with day numbers
    (setq html (concat html "  <tr>\n    <th></th>\n"))
    (dotimes (i days-back)
      (let ((day (+ (- day-number days-back) i 1)))
        (setq html (concat
                    html
                    (cond ((= day day-number) "<th>●</th>")
                          ((= 0 (% (- day day-number) 7)) "<th>○</th>")
                          (t "<th />"))))))
    (setq html (concat html "  </tr>\n"))


    ;; Data rows - one per habit
    (dolist (entry sorted-habits)
      (let* ((custom-id (car entry))
             (history (akk0/get-habit-history habit-alist custom-id day-number days-back window-size)))
        (setq html (concat html (format "  <tr>\n    <td style='padding-right: 20px; padding-top: 5px; padding-bottom: 5px;'><i>%s</i></td>\n" (capitalize (string-remove-prefix "dailies-" custom-id)))))

        ;; Cell for each day
        (dolist (day-data history)
          (let* ((todo-state (plist-get day-data :todo-state))
                 (score (plist-get day-data :score))
                 (body (plist-get day-data :body))
                 (doy (plist-get day-data :doy))
                 (body-html (if (and body (not (string-empty-p body)))
                               (akk0/org-to-html body)
                             ""))
                 (color (alist-get todo-state color-map nil nil #'equal))
                 (symbol (alist-get todo-state symbol-map nil nil #'equal))
                 (class (format "habit-brightness-%d" score))
                 (style-var (format "--%s%d" color score))
                 (escaped-body (replace-regexp-in-string "\"" "&quot;"
                                 (replace-regexp-in-string "\n" "&#10;" body-html)))
                 )
            (setq html (concat html (format "    <td class=\"%s habit-cell\" style=\"background-color:var(%s)\" data-body=\"%s\" onclick=\"showHabitPopup(this)\" data-doy=\"%s\" data-activity=\"%s\"
            data-status=\"%s\">%s</td>\n"
                                            class style-var
                                            escaped-body
                                            doy
                                            custom-id
                                            todo-state
                                            symbol)))))

        (setq html (concat html "  </tr>\n"))))

    (setq html (concat html "</table>\n"))

    ;; Legend and expansion section
    (setq html (concat html "<span class='center'><b>Key:</b>
<span style='color: var(--grey3);'>Unknown</span> |
<span style='color: var(--red3);'>× No</span> |
<span style='color: var(--green3);'>● Yes</span> |
<span style='color: var(--blue3);'>♦ Excellent</span> |
<span style='color: var(--purple3);'>♣ Freed Up</span>
</span>
<hr />
<div class='habit-popup' id='habitPopup'>
  <div class='habit-popup-content' id='habitPopupContent'>
    <span class='center'><i>This section intentionally left blank.</i></span>
  </div>
</div>
"))

    html))

(akk0/habits-to-html-table habit-alist (string-to-number (format-time-string "%j")) 30 5)
Expansion Section   autocollapse

A bit of JS for enabling the expanding of entries.

function formatDayOfYear(dayOfYear, year) {
  const date = new Date(year, 0, dayOfYear);

  return date.toLocaleDateString('en-GB', {
    day: 'numeric',
    month: 'short',
    year: 'numeric'
  });
}

let selectedCell = null;

function showHabitPopup(cell) {
  var bodyHtml = cell.getAttribute('data-body');
  var doy = cell.getAttribute('data-doy');
  var activity = cell.getAttribute('data-activity');
  var status = cell.getAttribute('data-status');
  activity = activity.replace(/^dailies-/, '').replace(/^./, c => c.toUpperCase())

  if (!bodyHtml) bodyHtml = "<span class='center'><i>This section intentionally left blank.</i></span>"

  bodyHtml = `<h3><span class="grid ${status}">${activity}</span> — ${formatDayOfYear(parseInt(doy), 2025)}</h3> ${bodyHtml}`

  document.getElementById('habitPopupContent').innerHTML = bodyHtml;

  if (selectedCell) {
    selectedCell.classList.remove('habitgrid-selected');
  }

  cell.classList.add('habitgrid-selected');
  selectedCell = cell;
  enableFootnotes();
}
CSS   autocollapse

Color habit entries based on their completion status:

.done.YES, .todo.NO, .done.EXCELLENT, .done.FREED {
    display: none;
}

h2:has(.done.YES), h3:has(.done.YES), h4:has(.done.YES), h5:has(.done.YES), .grid.YES {
    color: var(--green3);
}

h2:has(.todo.NO), h3:has(.todo.NO), h4:has(.todo.NO), h5:has(.todo.NO), .grid.NO {
    color: var(--red3);
}

h2:has(.done.EXCELLENT), h3:has(.done.EXCELLENT), h4:has(.done.EXCELLENT), h5:has(.done.EXCELLENT), .grid.EXCELLENT {
    color: var(--blue3);
}

h2:has(.done.FREED), h3:has(.done.FREED), h4:has(.done.FREED), h5:has(.done.FREED), .grid.FREED {
    color: var(--purple3);
}

Style the cells, set legible text color, outline clickable:

.habit-cell {
    text-align: center;
    vertical-align: middle;
    width: 20px;
    height: 20px;
    cursor: pointer;
    user-select: none;
}

.habit-cell.habit-brightness-1 { color: var(--grey5); outline-color: var(--grey5) !important; }
.habit-cell.habit-brightness-2 { color: var(--grey5); outline-color: var(--grey5) !important; }
.habit-cell.habit-brightness-3 { color: var(--grey5); outline-color: var(--grey5) !important; }
.habit-cell.habit-brightness-4 { color: var(--grey2); outline-color: var(--grey5) !important; }
.habit-cell.habit-brightness-5 { color: var(--grey3); outline-color: var(--grey5) !important; }

.habit-cell:not([data-body=""]) {
    outline: dotted 2px;
    outline-offset: -2px;
}
.habitgrid-selected {
    outline: solid 2px var(--purple5) !important;
    outline-offset: -2px;
}

Header Collapsing   autocollapse

Sections can be expanded and collapsed by clicking on their headers; this will assign .orgjq-expanded and .orgjq-contracted CSS classes as appropriate. Headers with the :autocollapse: tag will be collapsed by default (like this section).

.orgjq-expanded p {
    margin-top: 0;
    padding-bottom: 1.5rem;
    margin-bottom: 0;
}

.orgjq-contracted > div {
    display: none;
}

.orgjq-contracted h2, .orgjq-contracted h3, .orgjq-contracted h4, orgjq-contracted h5 {
    padding-top: 0.3rem !important;
    padding-bottom: 0.3rem !important;
}

.orgjq-contracted > :first-child::before {
    content: '⮞ ';
}

.orgjq-expanded > h4:has(.tag .autocollapse)::before {
    content: '⮟ ';
}
function isHideable(div_obj) {
  // Ignore for TOC since it is handled differently
  if (div_obj.id === "text-table-of-contents") return false;
  if (div_obj.id === "table-of-contents") return false;
  // No point in hiding top level
  if (div_obj.classList.contains("outline-2")) return false;
  if (div_obj.classList.contains("outline-text-2")) return false;
  return true;
}

function orgjqHide(div_obj) {
  if (!isHideable(div_obj)) return;
  const parent = div_obj.parentElement;
  parent.classList.remove("orgjq-expanded");
  parent.classList.add("orgjq-contracted");
}

function orgjqShow(div_obj) {
  const parent = div_obj.parentElement;
  parent.classList.remove("orgjq-contracted");
  parent.classList.add("orgjq-expanded");
}

function orgjqToggle(div_obj) {
  const parent = div_obj.parentElement;
  if (parent.classList.contains("orgjq-expanded")) {
    orgjqHide(div_obj);
  } else {
    orgjqShow(div_obj);
  }
}

function orgjqEnable() {
  // Called once e.g. the first time the page is loaded
  // handle the click event for each header
  for (let i = 2; i <= 7; ++i) {
    const headers = document.querySelectorAll(`h${i}`);
    headers.forEach(header => {
      header.style.cursor = "pointer";
      header.addEventListener('click', function() {
        // Get the first div sibling after the header
        const parent = this.parentElement;
        const divs = parent.querySelectorAll(':scope > div');
        if (divs.length > 0) {
          orgjqToggle(divs[0]);
        }
      });
    });
  }

  // Mark everything as open...
  for (let i = 2; i <= 7; ++i) {
    const headers = document.querySelectorAll(`h${i}`);
    headers.forEach(header => {
      const parent = header.parentElement;
      parent.classList.remove("orgjq-contracted");
      parent.classList.add("orgjq-expanded");
    });
  }

  // ... except TOC ...
  const toc = document.querySelector("div#table-of-contents");
  if (toc) {
    toc.classList.remove("orgjq-expanded");
    toc.classList.add("orgjq-contracted");
  }
  // ... and autocollapse.
  const autocollapse = document.querySelectorAll(".autocollapse");
  autocollapse.forEach(element => {
    const grandparent = element.parentElement?.parentElement;
    if (grandparent) {
      orgjqHide(grandparent);
    }
  });
}

// Run when DOM is loaded
if (document.readyState === 'loading') {
  document.addEventListener('DOMContentLoaded', orgjqEnable);
} else {
  orgjqEnable();
}

Hover Notes   autocollapse

Turns org-mode footnotes ([fn::Like this]) into hover-notes1.

function enableFootnotes() {
  const footnoteRefs = document.querySelectorAll('.footref')

  footnoteRefs.forEach(ref => {
    const footnoteId = ref.href.split('#')[1]
    ref.setAttribute('data-footnote-id', footnoteId)
    ref.href = 'javascript:void(0)';

    ref.addEventListener('mouseenter', function() {
      // Find footnote element
      const footnoteId = this.getAttribute('data-footnote-id')

      const footnoteElement = document.getElementById(footnoteId).parentElement.nextElementSibling.children[0]

      if (footnoteElement) {
        // Create tooltip container
        const tooltip = document.createElement('div');
        tooltip.className = 'footnote-tooltip'
        tooltip.innerHTML = footnoteElement.innerHTML

        // Position tooltip
        const rect = this.getBoundingClientRect();
        tooltip.style.position = 'absolute'
        tooltip.style.top = (rect.bottom + window.scrollY + 5) + 'px'
        tooltip.style.left = rect.left + 'px'

        // Add to page
        document.body.appendChild(tooltip)

        // Store reference for cleanup
        this._tooltip = tooltip
      }
    });

    ref.addEventListener('mouseleave', function() {
      // Remove tooltip
      if (this._tooltip) {
        this._tooltip.remove()
        this._tooltip = null
      }
    });
  });

  // Hide footnotes section
  document.querySelector("#footnotes").style.display='none'
}

document.addEventListener('DOMContentLoaded', enableFootnotes);

Appearance

Global   autocollapse

Variables
:root {
    --site-width: 1000px;
    --vert-content-margin: 0.3rem;

    --font-size: 1.2rem;
    --line-height: 1.6rem;
    --font-family: "Source Serif Pro";

    --scale: 2;
}
Color Variables
:root {
    --green1: #123218;
    --green2: #254f1b;
    --green3: #356c22;
    --green4: #5e8e40;
    --green5: #85aa5f;

    --blue1: #1b2459;
    --blue2: #153a79;
    --blue3: #2f5394;
    --blue4: #417eaf;
    --blue5: #69a8c6;

    --purple1: #2e1e58;
    --purple2: #493281;
    --purple3: #6a45a6;
    --purple4: #855aa3;
    --purple5: #9b77b5;

    --red1: #4b1313;
    --red2: #682017;
    --red3: #84301c;
    --red4: #9c4830;
    --red5: #c46849;

    --grey1: #202124;
    --grey2: #33333b;
    --grey3: #605b66;
    --grey4: #a79fa7;
    --grey5: #c5bcbc;

    --accent1: var(--purple4);
    --link: var(--blue3);
    --black: var(--grey1);
    --grey: var(--grey3);
    --background: var(--grey5);
    --background-tooltip: var(--grey5);
}
Background image
body::before,
body::after {
    content: '';
    position: fixed;
    top: 0;
    height: 100vh;
    width: 50%;
    background-image: url('/static/images/background.png');
    background-repeat: no-repeat;
    z-index: -1;

    image-rendering: pixelated; /* For Chrome/Safari */
    image-rendering: -moz-crisp-edges; /* For Firefox */
    image-rendering: crisp-edges; /* Fallback */

    background-size: calc(960px * var(--scale)) calc(1080px * var(--scale));
}

body::before {
    left: 0;
    background-position: top right;
}

body::after {
    right: 0;
    background-position: top right;
    /* Flip the right side horizontally */
    transform: scaleX(-1);
}
TODO This is a weird approach and has some issues
  • White line in center in some setups
  • Crunchy lines depending on DPI
Fonts
body {
    font-family: var(--font-family);
    font-size: var(--font-size);
    line-height: var(--line-height);
    word-spacing: 0.25ch;
    font-weight: 400;
}
Header/Content/Footer blocks
#content, #header, #footer {
    min-width: 500px;
    max-width: min(var(--site-width), 90vw);
    margin: 0 auto;
    padding: 5px 25px;

    border: 2px double var(--grey1);
    margin-bottom: 1.2rem;
    background-color: var(--background);
}

#header {
    display: flex;
}

#content, #footer {
    box-shadow: 7px 7px 7px var(--grey1);
}

#footer {
    padding-top: 1rem;
    padding-bottom: 1rem;
}

#header a, #header a:visited{
    color: var(--purple2);
}

#header hr {
    margin-bottom: var(--vert-content-margin);
}

#footer hr {
    margin-top: var(--vert-content-margin);
}

#content h2, #content h3, #content h4, #content h5 {
    margin-bottom: 1rem;
    margin-top: 0.5rem;
    margin-left: 0.5rem;
}

#content h2 {
    margin-top: 1rem;
}

#content h3, #content h4 {
    padding-top: 0.5rem;
    padding-bottom: 0.5rem;
}

#content h5 {
    padding-top: 0.3rem;
    padding-bottom: 0.3rem;
}

Colors   autocollapse

Color Assignments
body {
    color: var(--black);
    background-color: var(--background);
}
Syntax Highlighting
pre                                      {background-color:var(--grey1); color:var(--grey5);}
pre span.org-builtin                     {color:var(--blue4);font-weight:bold;}
pre span.org-string                      {color:var(--green4);}
pre span.org-keyword                     {color:var(--purple5);font-weight:bold;}
pre span.org-variable-name               {color:var(--green5);font-style:italic;}
pre span.org-function-name               {color:var(--blue5);}
pre span.org-type                        {color:var(--purple4);}
pre span.org-preprocessor                {color:var(--grey5);font-weight:bold;}
pre span.org-constant                    {color:var(--red5);}
pre span.org-comment-delimiter           {color:var(--grey3);}
pre span.org-comment                     {color:var(--grey3);font-style:italic}
pre span.org-outshine-level-1            {color:var(--grey5);font-style:italic}
pre span.org-outshine-level-2            {color:var(--grey5);font-style:italic}
pre span.org-outshine-level-3            {color:var(--grey5);font-style:italic}
pre span.org-outshine-level-4            {color:var(--grey5);font-style:italic}
pre span.org-outshine-level-5            {color:var(--grey5);font-style:italic}
pre span.org-outshine-level-6            {color:var(--grey5);font-style:italic}
pre span.org-outshine-level-7            {color:var(--grey5);font-style:italic}
pre span.org-outshine-level-8            {color:var(--grey5);font-style:italic}
pre span.org-outshine-level-9            {color:var(--grey5);font-style:italic}
pre span.org-rainbow-delimiters-depth-1  {color:var(--grey4);}
pre span.org-rainbow-delimiters-depth-2  {color:var(--blue4);}
pre span.org-rainbow-delimiters-depth-3  {color:var(--green4);}
pre span.org-rainbow-delimiters-depth-4  {color:var(--red4);}
pre span.org-rainbow-delimiters-depth-5  {color:var(--purple4);}
pre span.org-rainbow-delimiters-depth-6  {color:var(--blue4);}
pre span.org-rainbow-delimiters-depth-7  {color:var(--green4);}
pre span.org-rainbow-delimiters-depth-8  {color:var(--red4);}
pre span.org-rainbow-delimiters-depth-9  {color:var(--purple4);}
pre span.org-sh-quoted-exec              {color:var(--purple3);}
pre span.org-doc                         {color:var(--green5);font-style:italic;}
pre span.org-css-selector                {color:var(--blue5);font-weight:bold;}
pre span.org-css-property                {color:var(--purple4); font-weight: bold;}

General CSS   autocollapse

Et Cetera
hr {
    border: 0;
    border-top: 2px dotted var(--black);
}

a, a:visited {
    color: var(--link);
    text-decoration: none;
}

.figure-number {
    display: none;
}

.caption {
    padding-top: 0.5rem;
    font-style: italic;
}

.outline-3, .outline-4, .outline-5 {
    padding-left: 0.6rem;
    border-radius: 0.2em;
    margin: 0.7rem;
}

.outline-text-3, .outline-text-4, .outline-text-5 {
    margin: 0.3rem 1rem 0.5rem 0.5rem;
}

h2 {
    line-height: 1.5rem;
    text-align: center;
}

h3, h4, h5 {
    text-align: center;
}

.section-number-1, .section-number-2, .section-number-3, .section-number-4 { display: none; }
Utility Classes
.center {
    display: block;
    margin-left: auto;
    margin-right: auto;
    text-align: center;
}

.navbar-link {
    margin-right: 5px;
    margin-left: 5px;
}

.center, div.center {
    text-align: center;
    margin-left: auto;
    margin-right: auto;
}

.center figure,
.center figcaption,
div.center figure,
div.center figcaption {
    text-align: center;
    margin-left: auto;
    margin-right: auto;
}

.multi-img {
    display: flex;
    justify-content: center;
    text-align: center;
    margin: 20px;
    gap: 20px;
}

.multi-img figure { margin: 0px; }

.poetry {
    padding-top: 4rem;
    padding-bottom: 4rem;
    text-align: center;
}

Elements   autocollapse

Looking for Work
#looking-for-work {
    background-color: var(--red2);
    color: var(--grey5);
    padding: 5px 25px;
    margin-left: auto;
    margin-top: -0.32rem;
    margin-bottom: -0.32rem;
    margin-right: -1.55rem;
    border: 2px double var(--purple5);
}

#looking-for-work a, #looking-for-work a:visited {
    color: var(--blue5) !important;
}
Code Blocks
.src, .example {
    font-family: "monospace";
    font-size: 1rem;
}
Publish/modified Date
#publish-date, #modified-date {
    font-style: italic;
}
Table of Contents
#table-of-contents {
    z-index: 1;
    margin-top: 105px;
    margin-right: 5%;
    font-size: calc(var(--font-size) * 0.8);
    position: fixed;
    right: 0em;
    top: 0em;
    background: var(--background-toc);
    text-align: right;
    min-height: 3rem;

    box-shadow: 0 0 0.5em var(--shadow-toc);
    -webkit-box-shadow: 0 0 0.5em var(--shadow-toc);
    -moz-box-shadow: 0 0 0.5em var(--shadow-toc);
    -webkit-border-bottom-left-radius: 5px;
    -moz-border-radius-bottomleft: 5px;
    /* ensure doesn't flow off the screen when expanded */
    max-height: 80%;
    overflow: auto;
}
#table-of-contents h2 {
    font-size: 13pt;
    max-width: 9em;
    border: 0;
    font-weight: normal;
    margin-top: 0.75em;
    margin-bottom: 0.75em;
    padding-left: 0.5em;
    padding-right: 0.5em;
    padding-top: 0.05em;
    padding-bottom: 0.05em; }
#table-of-contents #text-table-of-contents {
    display: none;
    text-align: left; }
#table-of-contents:hover #text-table-of-contents {
    display: block;
    padding: 0.5em;
    margin-top: -1.5em;
    padding-right: 20px;
}

#table-of-contents {
    display: none;
}
To-Dos
.TODO { color: var(--red3); }
.DONE { color: var(--green3); }

1

Like this