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 (unordered)
- RSS feeds
- Check for guid, if missing abort
- Create Commander’s Log RSS feed
- Add comment invitation links
- Add all RSS header information
- Filter to
class/blog-post - Confirm date is creation date, not modified date here
- Test blog filtering of private pages is in place
- Test RSS pages do not include links to missing or filtered pages
- Transcludes
- Transform all
class/*tags to removeclass/prefix - Format folder pages
- Create folder pages for blog, commanders log
- Check dates are Australia/Melbourne time
- Image swipe as seen in Danish ships cross-stitch
- Replace Explorer
- Improve link output of missing articles as Recent Notes is using the filtered set so I may be checking in the wrong place.
- Add Webmentions
- Add tinylytics heart icon
- Styling for self-reference page links
- Test table formatting cssclasses
- Callout boxes: use Test (styles) for display
- Test footnote height display
- Site tagline
- Buy me a coffee
- page meta
- Add modified and created dates
- Add link to ratings
- Add permalink
- Add prefix for cmdrs-log pages
- Add series links
- Add class tag
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 and ratings working properly. Now I need to reintegrate the code that links to a series. Too much for me right now as I once again have to deal with potential links to files that don’t exist. Because the content meta is only created for pages that have been filtered, I should have the list available to check. My original code ran early in the process. This is the only place I’ll need it so I can take some shortcuts.
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. ↩
