ADR-0015: FilterableFolderContent — custom Quartz component for frontmatter-driven folder filtering

Status: Accepted Date: 2026-05-20

Context

Two vault folders need client-side filtering on the published Quartz site:

  • /questions/ — filter by priority: (critical | interesting | minor)
  • /reference/ — filter by source resource class (BR, DP, CSG, …) extracted from sources: wikilinks

The default FolderContent Quartz component renders a flat list with no filtering capability.

Decision

Create a custom FilterableFolderContent component that replaces FolderContent as the pageBody for folder pages. It renders pill buttons above the file list and uses client-side JS to show/hide list items based on the active filter. URL query params are kept in sync so filters are deep-linkable and shareable.

Configuration lives in quartz.config.ts via Plugin.FolderPage({ pageBody: FilterableFolderContent({...}) }). The FolderPage emitter already spreads userOpts after its default pageBody, so no emitter code needs modification.

Filter values are auto-discovered from allFiles at build time. An optional displayNames map allows short labels (e.g. "Believers-Responsibility" → "BR"). For multi-value array fields (like sources:), an extractKey function pulls the resource class prefix from each wikilink.

Alternatives considered

  • Emitter generating static per-value pages (e.g. /questions/priority/critical/): Rejected. Multiplies page count, breaks single-canonical-URL convention, doesn’t compose with multi-value fields like sources:.
  • Separate beforeBody filter pills + existing FolderContent list: Rejected. No structured data hook on list items without modifying FolderContent’s HTML output — cross-component DOM coordination is fragile.
  • Tag-page mirror: Rejected in prior session (Q4) as too coarse; doesn’t generalize across field types.

Upgrade resilience

Quartz is vendored at _meta/quartz/ and updated via manual re-clone + diff. Our customizations:

  1. 2 new files (FilterableFolderContent.tsx, folderFilter.inline.ts) — not in upstream; copy them back after re-clone
  2. 2 lines in index.ts — one import, one export; trivial to re-apply
  3. quartz.config.ts — user config, never overwritten by upgrades

Custom files to re-apply after Quartz re-clone:

  • quartz/components/pages/FilterableFolderContent.tsx
  • quartz/components/scripts/folderFilter.inline.ts

Risk: if Quartz significantly changes FolderContent’s HTML/CSS structure, our component’s rendering may diverge visually. Acceptable: we own the file and can update it.

Consequences

  • (+) Readers can filter /questions/ by priority and /reference/ by source with no page reload
  • (+) Deep-linkable URLs (/questions?priority=critical)
  • (+) Generalizes to any frontmatter field on any folder with minimal config
  • (+) Upgrade-safe: 2 new files + 2-line change in vendored index
  • (−) Component must replicate FolderContent list HTML structure — small maintenance surface if upstream HTML changes