One Owner for the URL — and Chips That Actually Do Something
Train-Case is the storage form and Train Case is the display form, and for a few hours the toolkit confused the two badly enough to route people at /tags/Video%20Generators/. Fixing it meant giving the URL a single owner, teaching Pagefind to index the stored value rather than the rendered label, and lifting a decorative span out of a link so it could finally be clicked.
Changelog — 2026-08-23 (#3)
One Owner for the URL — and Chips That Actually Do Something
Why Care?
Selecting a single tag now puts you on /tags/data-utilities/ — the real, prerendered page, with
its own title and share card — instead of a query string that unfurls generically. Select two and it
falls back to parameters, because 255 tags have 32,385 pairs and none of those pages can exist.
Along the way both kinds of tag chip became clickable. One of them never had been.
What's New?
The URL has exactly one owner.
canonicalPath()lives in the shared atom, and Copy shareable link reads the same function the address bar does, so they cannot disagree.Both chip designs work. The facet row and the card tags are allowed to look different; they now behave the same.
Pagefind indexes the stored value, not the rendered label.
tests/chip-drive.mjspins all of it.
Train-Case is storage, Train Case is display
Tags are stored as Video-Generators and shown as Video Generators. Pagefind takes a filter value
from an element's text by default — which is the display form. So the index filled with labels
that nothing else in the system could resolve, and picking one produced:
/tags/Video%20Generators/ ← a page that does not existThe [attribute] syntax indexes a stored value instead:
<a href={`/tags/${tagSlug(t)}/`} data-tag={t} data-pagefind-filter="tag[data-tag]">
{tagLabel(t)}
</a>Display stays a display concern. Every other layer keeps speaking Train-Case.
Two writers, one address bar
The atom wrote /tools/?tags=…; the explorer wrote /tags/<slug>/. Both fired on every change and
the last one won, so the canonical path kept getting clobbered and the copied link disagreed with
the address bar. The explorer's logic was the better one, so it moved into the atom and the
duplicate was deleted.
A span cannot be clicked
The tag row on each card was <span class="chip"> nested inside the card's <a>. It looked
identical to the working chips and did nothing, because an interactive element inside a link is
invalid markup. It now sits outside the anchor as a real TagChip.
What the browser caught, again
Consolidating the URL writers introduced an infinite loop: an effect that called registerSlugs()
and writeUrl(), where the second reads the state the first writes. Svelte spun to
effect_update_depth_exceeded and took the whole component render down — which is why the card
chips vanished entirely for one build.
Writing state that an effect also reads is a loop, not a sync. The build was green throughout.
