Shared pieces for the Pathwright site: query result cards and rows, bylines, and
the date formatter. Each result component reads its own Source's metadata. The
$author and $edition bindings follow the page being shown; they are exported
by plugins/pathwright.plugin.ts.
Lays out query result cards in a responsive grid.
<div class="card-grid"><slot /></div>
A blog post or update with its artwork, series, title, summary and date.
<a class="card article-card" href="{{ href }}">
<span class="card-media"><img src="{{ image }}" alt="" loading="lazy" /></span>
<span class="card-body">
<span class="card-eyebrow">{{ series }}</span>
<span class="card-title">{{ title }}</span>
<span class="card-text">{{ description }}</span>
<time class="card-date" datetime="{{ date }}">{{ date }}</time>
</span>
</a>
A Collider edition with its cover image and date.
<a class="card edition-card" href="{{ href }}">
<span class="card-media"><img src="{{ image }}" alt="" loading="lazy" /></span>
<span class="card-body">
<time class="card-date" datetime="{{ date }}">{{ date }}</time>
<span class="card-title">{{ title }}</span>
</span>
</a>
A changelog entry in the timeline: date, title, summary, and a badge while it is a draft.
<a class="change-row" href="{{ href }}">
<time class="change-date" datetime="{{ date }}">{{ date }}</time>
<span class="change-body">
<span class="change-title">{{ title }}<span class="status-badge" data-status="{{ status }}">Draft</span></span>
<span class="change-text">{{ description }}</span>
</span>
</a>
An author's portrait and name, linking to their page.
<a class="person-chip" href="{{ href }}"><img src="{{ image }}" alt="" width="24" height="24" /><span>{{ title }}</span></a>
A team member with their portrait and focus.
<a class="card person-card" href="{{ href }}">
<img src="{{ image }}" alt="" width="56" height="56" />
<span class="card-body"><span class="card-title">{{ title }}</span><span class="card-text">{{ description }}</span></span>
</a>
The Collider edition a link appeared in, read through the edition binding.
<x-source-read name="edition">
<a class="edition-chip" href="{{ $edition.href }}">From Collider · <time datetime="{{ $edition.date }}">{{ $edition.date }}</time></a>
<slot name="fallback"></slot>
</x-source-read>
The author of the page being shown, read through the author binding.
<x-source-read name="author">
<a class="person-chip" href="{{ $author.href }}"><img src="{{ $author.image }}" alt="" width="26" height="26" /><span>{{ $author.title }}</span></a>
<slot name="fallback"></slot>
</x-source-read>
The series, date and author above a post's title.
<div class="page-meta"><span class="page-meta-series">{{ source.series }}</span><time datetime="{{ source.date }}">{{ source.date }}</time><byline /></div>
The date and draft status above a changelog entry.
<p class="page-meta"><a href="/changelog">Changelog</a><time datetime="{{ source.date }}">{{ source.date }}</time><span class="status-badge" data-status="{{ source.status }}">Draft</span></p>
The title bar the Pathwright app shows above the changelog in its side panel.
It appears only inside a frame (html[data-pmx-embedded]), where the site's
header and footer are hidden. Close asks the app to close the panel with the
{ action: "close" } message it listens for. Inside a frame, links that leave
the changelog open in a new tab instead of replacing the panel.
<div class="embed-bar">
<a class="embed-bar-title" href="/changelog"><img src="/site/assets/pathwright.svg" alt="" width="24" height="24" /><span>Changelog</span></a>
<button class="embed-bar-close" type="button" aria-label="Close changelog">
<svg viewBox="0 0 24 24" width="20" height="20" aria-hidden="true"><path d="M6 6l12 12M18 6 6 18" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" /></svg>
</button>
</div>
const embedded = () => document.documentElement.hasAttribute("data-pmx-embedded")
const inChangelog = (url) => url.origin === location.origin && /^\/changelog(?:\/|$)/.test(url.pathname)
on(".embed-bar-close", "click", () => window.parent.postMessage({ action: "close" }, "*"))
on(window, "click", (event) => {
if (!embedded() || event.defaultPrevented || event.button !== 0) return
if (event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return
const link = event.target instanceof Element ? event.target.closest("a[href]") : null
if (!link || link.target || link.hasAttribute("download")) return
const url = new URL(link.href, location.href)
if (!/^https?:$/.test(url.protocol) || inChangelog(url)) return
event.preventDefault()
event.stopPropagation()
window.open(url.href, "_blank", "noopener")
}, { capture: true })
Formats every time[datetime] holding an ISO date as a readable date, such as
"Jun 16, 2026", including content that arrives after navigation.
<span hidden></span>
const format = new Intl.DateTimeFormat(undefined, { month: "short", day: "numeric", year: "numeric", timeZone: "UTC" })
function run() {
for (const time of document.querySelectorAll("time[datetime]")) {
const value = time.getAttribute("datetime")
if (!/^\d{4}-\d{2}-\d{2}/.test(value ?? "") || time.dataset.formatted === value) continue
const date = new Date(`${value.slice(0, 10)}T00:00:00Z`)
if (Number.isNaN(date.getTime())) continue
time.textContent = format.format(date)
time.dataset.formatted = value
}
}
run()
const observer = new MutationObserver(run)
observer.observe(document.body, { childList: true, subtree: true })
cleanup(() => observer.disconnect())
The portrait at the top of a person's page.
<img class="person-avatar" src="{{ source.image }}" alt="" width="96" height="96" />