This Digital Garden is built with Obsidian and Quartz which is a static site generator built specifically for converting Obsidian vaults into websites.

I have a private repository notes.hamatti.org that starts with a clone of the Quartz repository and then adds my own configuration on top.

Building

I don’t want to store my Obsidian vault inside the tooling, so I have set up a build & development server scripts inside package.json:

"scripts" {
  "build": "npx quartz build --directory=/path/to/my/Obsidian/vault",
  "serve": "npx quartz build --serve --port 8888 --directory=/path/to/my/Obsidian/vault"
}

When I want to update my digital garden, I run a build, add changes to git and push it to GitHub from where it gets automatically picked up by my hosting and built into a new version and published.

This requires manual building and publishing but like with my main website, I like that manual step and in case of publishing a public subset of private notes, I want to be able to manually check that I haven’t accidentally put a note into a wrong folder from where it would be picked up by automation to publish on the web.

For digital gardens, I think it’s nice to visually indicate which links are internal links and which one lead to elsewhere. Quartz provides this as a configuration option for its CrawlLinks plugin but I turn that off and instead, I use CSS ::after pseudo-element. My main reason for this is to avoid the SVGs from showing up wonky in different RSS readers.

/*
The first and last fill values should be the same as the --secondary color
and prefixed with %23 (which stands for #).
 
The middle fill should be 'none' because it's the inside of the box
*/
a[href^="http"]::after {
    content: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewbox='0 0 12.2 12.2' width='14' height='14'%3E%3Cpath fill='%23284b63' d='M5.7 0v1.5h4L4.5 6.7l1 1.1 5.3-5.2v3.9h1.4V0z'/%3E%3Cpath fill='none'  d='M3.4 6.7l3-2.9H1.5v7h7V5.9l-3 2.9z'/%3E%3Cpath fill='%23284b63' d='M8.5 5.9v4.9h-7v-7h4.9l1.5-1.6H0v10h10V4.4z'/%3E%3C/svg%3E");
    margin-left: 0.25em;
  }

In Obsidian, I create links to other notes before I make those notes. Sometimes I make those notes right away, sometimes they are there to let me know through autocomplete that I’ve written about it before.

On a published site, those would all lead to 404 for missing note and that’s not a great UX.

There’s currently no way to configure this in Quartz but thanks to it being open source and people contributing, I found help in the GitHub issues. In contentPage.tsx, inside the emit method, inside the for (const [tree, file] of content) loop, I added

/** Until the end of visit(), this code snippet is from
 * https://github.com/jackyzha0/quartz/issues/454#issuecomment-2408792538
 * by auctumnus
 * 
 * It removes all the links that would lead to missing pages, ie.
 * 
 * [[Missing link]] when Missing link.md does not exist.
 */
const allSlugs = allFiles.map((f) => (f.slug ? resolveRelative(slug, f.slug) : ""))
 
visit(tree as Root, "element", (elem) => {
  if (elem.tagName === "a" && elem.properties.href) {
	const href = elem.properties.href.toString()
 
	if (href.startsWith("#")) {
	  return
	}
 
	if (!allSlugs.includes(href as RelativeURL)) {
	  if (elem.properties.className === undefined) {
		elem.properties.className = "dead-link"
	  } else if (Array.isArray(elem.properties.className)) {
		if (elem.properties.className.includes("external")) {
		  return
		}
		elem.properties.className.push("dead-link")
	  } else if (typeof elem.properties.className === "string") {
		if (elem.properties.className.includes("external")) {
		  return
		}
		elem.properties.className += " dead-link"
	  } else {
		return
	  }
	  elem.tagName = "span"
	}
  }
})

and added the missing imports. This changes the missing links from a tags to span tags, saving the end user from confusion and unnecessary navigation to 404 page.

Thoughts on Quartz

While in general I’m happy with the ease of use and the ability to configure things by changing the source code of Quartz, one thing that bothers me is the extensive use of client-side Javascript.

There’s a lot of inlined Javascript I haven’t yet been able to clean up and a lot of that causes git to be super slow because there each content change introduces changes into the inlined Javascript so calculating diffs becomes a chore to the computer.

On each individual page, there are thousands of incredibly long lines of inlined Javascript and CSS and I can’t figure out why or how I can get rid of them. A static site does not need this much extra weight.

As an experiment, in renderPage.tsx, I commented out contentIndexScript and staticResources.js. This seems to have removed a good few thousand lines of content and so far the only non-working part I could find was search which I removed gladly.

What if I can’t or don’t want to code?

Maggie Appleton has a great guide at Digital Gardening for Non-Technical Folks