Quartz 5 has been released and with it a formidable plugin structure. I use Quartz to generate a static HTML website from The Quantum Garden Vault and since I began using it in September 2023 have made many tweaks to align it with my vision. I’m now in a position where I have to make many of those tweaks once again.
Progress below
What did I change?
The most important change I made was to eliminate links to pages that do not exist. Links between pages are created automatically. However, if a page links to another page that is not published, the link is still created and readers do not know any different until they are shown “Page not found.” In my mind, this is inexcusable and lazy. The system is capable of identifying pages that do not exist, so any internal links should be removed. Within Obsidian such links are valid. Clicking them creates the new page, yet the published website does not offer that functionality.
I (and others) modified the code to remove links that don’t exist and I extended it further to remove all sign of the link having ever existed in the first place. This lets me write public notes referring to people and places of importance to me, with no privacy leak. If my friend’s name is Mary Smith, then I write [[Mary Smith|my friend]], and all readers see, even if they look at the source code of the HTML page is my friend. But for me, in Obsidian the link is still relevant and active.
The next major change was to gain greater control over which pages were included and which were filtered out. I discuss that at length in Merging Public and Private Notes: Three Lines of Defence in Quartz 4.
Following that, the remainder of customisations were navigation or formatting in nature. I added previous and next page links, modified tags on the fly, made headings and fonts consistent across modules, and replaced the explorer tree navigation with a custom crafted list of page links.
Choosing to upgrade
Quartz 5 fundamentally breaks everything I’ve built; in a good way. Thankfully, I don’t need to upgrade all at once and hold off on any website changes until I’ve translated over my code so I can take it slowly.2
The first reason for upgrading to Quartz 5 is to keep up with security. Though my website is static and has very little running code, it’s always good to be on the latest version for security reasons. If I stay on the old version, underlying technologies may change and I’ll be forced to make more changes than I care to understand for the sake of currency. Site creation may even break, as occurred recently when an underlying library change broke some of my filtering. Running the current version reduces this risk greatly.
Secondly, Quartz 5 has some very clever plugins that convert Obsidian Canvas and Obsidian Base formats for web display. As of today I only have one canvas and I can’t convert it so the image is not linked, but I have many bases and displaying them natively cuts directly across several of my customisations making them irrelevant and creating a better experience for me and my readers.
Finally, Quartz 5 introduces a very strong plugin architecture. It allows me to make my changes independently from the core code, lets me easily disable functions I don’t want, and makes it easier for me to tweak code from other developers easily. Disabling some features in the past meant commenting out code I didn’t want to run, at the risk of an update putting it back. That risk has gone.
A staged upgrade
I’ve learned a lot in the last couple of days since the Quartz 5 release; about the architecture of the new version and how I need to best manage it for the way I think. So far,
- I’ve configured a local clone rather than a fork. With the plugin architecture there is less value in me sharing my changes. I have no intention of pushing changes upstream to the main codebase.
- I’ve created my first plugin. It replicates what was covered in Merging Public and Private Notes: Three Lines of Defence in Quartz 4. Filtering is critical to my note organisation so this was the obvious first case. The next will be removing links with a transformer plugin. My plugins will be hosted locally meaning I don’t have to worry about any private information leaking out should I need to include it. I’ve yet to decide if changes to public plugins will be a clone or a fork.
- I added an
en-AUlocalisation in preparation for formatting dates and currency to Australian locales. This is the only change to the base code so far.
Today will be removing links and understanding how styles work. I’ll test bases and decided how best to use the Quartz concept of folder notes, and where bases can replace them. One thing I’m looking forward to is removing the need for the Obsidian Dataview Serializer plugin. It’s been great and solved a very particular Obsidian → website display problem but with the ability to display bases directly from the original file, I won’t need it anymore. I will also likely get dates sorted as I have custom date fields that I may no longer need.
What have I learned
The plugin template has a test harness built in. It was the first time I have ever used one and took great comfort building and running tests for my filter plugin. I am confident it’s working as planned and it greatly reduced the code → build → test cycle.
The content folder is symlinked and indirectly caused my a round of 7+ rebuilds of the whole environment until I understood why my files were disappearing. Eventually I realised that when I added content to .gitignore the files went. It happens that Quartz uses fastglob for generating the list of files and it honours .gitignore so when I added it, the folder’s contents were ignored. The trick was to specify !content instead.
Progress
Outstanding items
- Must do before new site rollout
- Folders
- Format folder pages
- Horizontal layout
- Tag formatting
- Tags linking to correct place (from classes)
- Show modified, standardise formatting across site
- Cmdrs-log doubling up list of items
- Format folder pages
- Replace Explorer
- Callout boxes: use Test (styles) for display
- Replace date functions in `recent-notes
- Folders
- Can do after new site rollout
- RSS feeds
- Transcludes May be tricky to do without duplicating a lot of code
- Add Webmentions
- Add tinylytics heart icon
- Test table formatting cssclasses
- Change the return to footnote source icon away from the ugly arrow.
- Site tagline
- Buy me a coffee
- Inter page navigation not filtered by blog
- Randomise front page images from my flickr library?
- TOC needs to be longer when only a couple of items, e.g. Now, 5 July 2026
- Design a better 404 page
- Test Obsidian Base
- Filter future dated notes
- RSS feeds
30 May 2026
Backup, backup, backup
At one point I accidentally replaced 2,000 markdown files with HTML. It took a matter of seconds. Thankfully I had last night’s backup. Well, I say “thankfully” but that’s why I backup every night. The Obsidian Sync was already underway. Had I been relying on that for my “backup” I would have been in very big trouble.
Fixing the date error was easily. I conformed my system to created/modifed instead of datetime/updated. It meant a few template changes were required, yet very easy using the excellent Notebook Navigator plugin.
I am pleased to say I have all filtering and link removal working as required. I’m not pleased that link removal has taken 8 hours. I started with a Transformer, then an Emitter and finally a PageType emitter. I thought plugins ran in the order given, but they are first grouped by transform → filter → emit, so my transformer could not remove links for pages that were filtered out afterwards. My first go at an emitter worked, but no styling, just HTML, and the final PageType version looked great, but also failed with pages that had been filtered. Strangely, when Quartz filters out pages, it does not update the main list of pages it’s generating.
From here on in, the changes are structural and cosmetic.
31 May 2026
Again, what should have been easy wasn’t.
Out of the box the site looked horrible. I changed the fonts as per the previous version, and the current instructions without effect. It transpires there was a theme plugin also added that was overriding my expected changes. I’m not the only one to have suffered this and it may just be those coming from Quartz 4.
I thought I’d start with the footer next. Three hours later… it is up and running. Admittedly I’ve made some copy and layout changes but most of the time was trying to work out what was required. Multiple web pages open comparing code that worked, to my code that didn’t. At least an hour lost because I’d typed “footer” instead of “afterBody”, then a bit more time because defaults were not being picked up.
As a whole the system is not as modular as I’d like. I use tinylytics and there is a way to have it registered automatically. What’s not there is any mechanism to add a couple of other flags. That requires direct modification of the site-generation backend. Although I expect such changes to be fewer now with the new plugin system, I’ve already had to make a couple and so will resurrect tracking Customisations of Quartz for The Quantum Garden.
8 June 2026
I’m learning. Backlinks were easy to get working. Although I only changed a title and added a sort. Recent notes took longer, mostly getting the formatting right. At least I’m not fighting the system now and making each change knowing what I’m after. Recent notes are ordering by modified, and not creation date but that’s for another day. I want to make a change so that † indicator for an updated note is shown, just like it is in the notes listings anyway.
10 June 2026
In the past I the display and ordering date of a note was created, with an indicator if it had been modified. Quartz defaults to modified and I’ve decided to stay with that. My rationale is my most recent thinking will float to the top.
I’ve added a † marker against the date for recent notes to indicate when a note has been changed from the original creation date (like this one has). If modified, hovering over the date will show the original, and if any of the displayed note titles are modified, instructions to do this are added; if none are modified the instructions are withheld.
I spent too much time hunting down two spurious warnings that are don’t halt site creation. One appears to come from using --- for a horizontal rule instead of ***. To fix it I have to find/replace all occurrences, but since it’s also a YAML marker, tricky. The second is from my use of KaTex for math and it mis-interpreting dollar amounts as math. I resolved that warning by changing the rendering engine to mathjax.
RSS, via the context-index plugin is another example of poor design. The one plugin emits both a sitemap and RSS feed. It should be two separate plugins. Having options to enable/disable one or the other is insufficient with this new plugin model.
The RSS header information for the base blog RSS feed is complete. Still need to add filtering, and the feed for Commander’s Log.
12 June 2026
Pages and RSS feeds share the same link sanitising so there are not escaping private links. I created a test.xml feed for testing just pages with #class/test-page just pages with #class/test-page and a better Test page to feed into it.
13 June 2026
I have added a transform-tags transformer that takes Obsidian tags of the form class/* and strips the class/ from the front as I don’t need that on the site. Only class tags are ever used. The class/blog-post and class/now-post tags are rewritten as blog and now respectively. I have the list of tags to rewrite as an option so I don’t need to modify code for any future additions.
options:
remap:
"blog-post": "blog"
"now-post": "now"
Based on what I learned about options for transform-tags, I’ve extended the options for content-index so that whole RSS definitions can be passed through from options as well. I don’t imagine much will change here but it’s a development mode I prefer and I learned some new tricks along the way.
20 June 2026
I’m some way towards the metadata display at the top of each page. I have the date, ratings and series links working properly.
26 June 2026
The RSS is now emitting the Commander’s Log and an invite to email comments at the bottom of each feed. I looked at transcludes but until I get my head around the environment more it will have to wait. I’ll need to rebuild quite a bit to get past replacing the current link with the actual content.
Big progress today on building my own emitter for modifying <head> outside of the baseline code. Tags control which additional scripts or other information to add so that code is only added where needed. It’s become very easy to add information here. So much so, that I’ve moved other code into it for easier future management.
if (pageData.frontmatter?.tags?.includes("slider")) {
head.push(
<script defer src="https://cdn.jsdelivr.net/npm/img-comparison-slider@8/dist/index.js" ></script>,
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/img-comparison-slider@8/dist/styles.css"/>
)
}
28 June 2026
Each page’s meta data is now complete and matches what I had on Quartz 4. I have also built a set of 10 tests that I can run to check nothing is broken inadvertently. That took most of my late Sunday afternoon programming and was worth it. I learned a lot out of it and can add more tests to my other code.
Got myself into a bind somehow where the <head> changes I made on 26 June 2026 were being duplicated and causing all sorts of problems. I had duplicate code that wasn’t running then, but decided to run today.
Still a lot to go but it does feel like I’ve broken the back of it all, especially skill wise.
29 June 2026
I’ve extracted the tinylytics code into my additionalHead plugin so that I have full control and no longer need to modify the upstream source code to add options it doesn’t allow for. I’ve also updated each page’s meta for cmdrs-log entries to show the times in Universal Galactic Time, matching the true date in Elite: Dangerous. That’s each page’s meta done.
30 June 2026
A simple change to add all the site icons for browser bars, etc. Less simple was bypassing scss → css conversion outside of the main code. And I’ve added the media_cover style for covers like this.

Middle Earth - Shadow of Mordor video game cover showing on the left with a border and shadow.
1 July 2026
A big step forward with inter-page navigation added for blog and cmdrs-log posts. They each have their own prev/next structure.
I was planning to upload my code to github so that later on I can reference it on my laptop but a little too tired to try that.
2 July 2026
Cleaning up code. For example date formatting is consistent across everything. Will make things easier going forward. I spent too much of the afternoon setting up a better flow of scss to css conversion. It started with simply wanting to reduce the height of the footer heading on a page. Now complete I have much better control over the code and even expanded some additions to tell me when my documentation is not up to scratch.
5 July 2026
Lists of pages, called folder-pages, are working properly and I have then fully controlled from Obsidian properties instead of code. The formatting is horrible and needs attention. Getting the content right in the first place is the important step.
Final steps
When ready to launch I need to take these steps.
- Remove _virtual_folders
- Create redirects for blog, cmdrs-log, now at the root level to the notes/* equivalent
Footnotes
-
This page was to link
[[#progress|Progress]]and it showed “Progress” instead of “below”. Something to check in the new version via my Test page. ↩ -
There is a strong pull to get it done as fast as possible for no other reason than the migration representing a series of problems to solve that my mind won’t let go of. ↩
