Skip to content

Commit d648f93

Browse files
jsanzclaude
andcommitted
Fix Pagefind search results sort and excerpt rendering
Replace <pagefind-results sort=...> (sort attribute is unsupported on that component) with a custom JS implementation using the pagefind JS API. Results are now sorted by date descending, matched terms are highlighted and visible within the 3-line excerpt clamp, and destination pages receive the highlight query parameter. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 2345f48 commit d648f93

2 files changed

Lines changed: 138 additions & 25 deletions

File tree

_includes/search.html

Lines changed: 62 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,27 +2,73 @@
22
<pagefind-config bundle-path="{{ site.baseurl }}/pagefind/" highlight-param="highlight"></pagefind-config>
33
<pagefind-modal>
44
<pagefind-modal-header>
5-
<pagefind-input></pagefind-input>
5+
<div class="pf-input-wrapper">
6+
<input type="search" id="pf-input" class="pf-input"
7+
placeholder="Search" autocomplete="off" spellcheck="false" aria-label="Search this site">
8+
</div>
69
</pagefind-modal-header>
710
<pagefind-modal-body>
8-
<pagefind-summary></pagefind-summary>
9-
<pagefind-results sort='{"date": "desc"}'>
10-
<script type="text/pagefind-template">
11-
{% raw %}<li>
12-
<div class="pf-result-card">
13-
<div class="pf-result-content">
14-
<p class="pf-result-title">
15-
<a class="pf-result-link" href="{{ url }}">{{#if meta.date}}{{ meta.date }} - {{/if}}{{#if meta.rid}}#{{ meta.rid }}{{/if}}</a>
16-
</p>
17-
<p class="pf-result-excerpt">{{+ excerpt +}}</p>
18-
</div>
19-
</div>
20-
</li>{% endraw %}
21-
</script>
22-
</pagefind-results>
11+
<p id="pf-summary" role="status"></p>
12+
<ul id="pf-results"></ul>
2313
</pagefind-modal-body>
2414
<pagefind-modal-footer>
2515
<pagefind-keyboard-hints></pagefind-keyboard-hints>
2616
</pagefind-modal-footer>
2717
</pagefind-modal>
18+
<script type="module">
19+
(async () => {
20+
const pfModule = await import('{{ site.baseurl }}/pagefind/pagefind.js');
21+
const pf = await pfModule.createInstance({ highlightParam: 'highlight', excerptLength: 30 });
22+
await pf.init();
23+
24+
const input = document.getElementById('pf-input');
25+
const list = document.getElementById('pf-results');
26+
const summary = document.getElementById('pf-summary');
27+
const modal = document.querySelector('pagefind-modal');
28+
let timer;
29+
30+
// Focus the input whenever the modal opens
31+
if (modal) {
32+
new MutationObserver(() => {
33+
if (modal.hasAttribute('open')) input.focus();
34+
}).observe(modal, { attributes: true, attributeFilter: ['open'] });
35+
}
36+
37+
// Clear results when the input is emptied (e.g. pressing × in type=search)
38+
input.addEventListener('search', e => {
39+
if (!e.target.value) { list.innerHTML = ''; summary.textContent = ''; }
40+
});
41+
42+
input.addEventListener('input', e => {
43+
clearTimeout(timer);
44+
timer = setTimeout(() => search(e.target.value), 300);
45+
});
46+
47+
function render(data, query) {
48+
list.innerHTML = '';
49+
summary.textContent = data.length
50+
? `${data.length} results for "${query}"`
51+
: `No results for "${query}"`;
52+
for (const d of data) {
53+
const date = d.meta?.date ?? '';
54+
const rid = d.meta?.rid ? '#' + d.meta.rid : '';
55+
const label = [date, rid].filter(Boolean).join(' - ');
56+
const li = document.createElement('li');
57+
li.innerHTML =
58+
`<a class="pf-result-link" href="${d.url}">${label}</a>` +
59+
`<p class="pf-result-excerpt">${d.excerpt}</p>`;
60+
list.appendChild(li);
61+
}
62+
}
63+
64+
async function search(query) {
65+
list.innerHTML = '';
66+
summary.textContent = '';
67+
if (!query.trim()) return;
68+
const res = await pf.search(query, { sort: { date: 'desc' } });
69+
const data = await Promise.all(res.results.map(r => r.data()));
70+
render(data, query);
71+
}
72+
})();
73+
</script>
2874
{% endif %}

_sass/_search.scss

Lines changed: 76 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -24,15 +24,82 @@
2424
--pf-border-radius: 2px;
2525
}
2626

27-
// Excerpt: default is single-line ellipsis (white-space:nowrap + overflow:hidden).
28-
// Allow up to 4 lines so the matched term is actually visible in results.
29-
pagefind-modal .pf-result-excerpt {
30-
white-space: normal !important;
31-
overflow: hidden !important;
32-
text-overflow: unset !important;
33-
display: -webkit-box !important;
34-
-webkit-line-clamp: 4 !important;
35-
-webkit-box-orient: vertical !important;
27+
// Custom search input — replaces <pagefind-input> component (closed shadow DOM).
28+
.pf-input-wrapper {
29+
display: flex;
30+
align-items: center;
31+
width: 100%;
32+
padding: 0.5rem 1rem;
33+
}
34+
35+
.pf-input {
36+
width: 100%;
37+
border: 1px solid var(--pf-border);
38+
border-radius: var(--pf-border-radius);
39+
padding: 0.5rem 0.75rem;
40+
font: inherit;
41+
font-size: 1rem;
42+
color: var(--pf-text);
43+
background: transparent;
44+
outline: none;
45+
46+
&:focus {
47+
border-color: var(--pf-border-focus);
48+
box-shadow: 0 0 0 2px var(--pf-outline-focus);
49+
}
50+
}
51+
52+
// Custom results list — replaces <pagefind-results> component.
53+
// Styles .pf-result-* classes that were previously inside the component shadow DOM.
54+
#pf-summary {
55+
font-size: 0.75rem;
56+
color: var(--pf-text-secondary);
57+
padding: 0.25rem 1rem;
58+
margin: 0;
59+
}
60+
61+
#pf-results {
62+
list-style: none;
63+
margin: 0;
64+
padding: 0.5rem 1rem;
65+
display: flex;
66+
flex-direction: column;
67+
gap: 0.5rem;
68+
69+
li {
70+
border: 1px solid var(--pf-border);
71+
border-radius: var(--pf-border-radius);
72+
padding: 0.75rem 1rem;
73+
}
74+
75+
.pf-result-link {
76+
font-weight: 600;
77+
color: var(--pf-text);
78+
text-decoration: none;
79+
display: block;
80+
margin-bottom: 0.4rem;
81+
82+
&:hover { color: var(--pf-outline-focus); }
83+
}
84+
85+
.pf-result-excerpt {
86+
font-size: 0.85rem;
87+
color: var(--pf-text-secondary);
88+
margin: 0;
89+
// pagefind-component-ui.css uses :is(*, #\#)×3 specificity — !important needed.
90+
white-space: normal !important;
91+
overflow: hidden !important;
92+
text-overflow: unset !important;
93+
display: -webkit-box !important;
94+
-webkit-line-clamp: 3 !important;
95+
-webkit-box-orient: vertical !important;
96+
97+
mark {
98+
background: transparent;
99+
font-weight: bold !important;
100+
color: var(--pf-text);
101+
}
102+
}
36103
}
37104

38105
// Nav trigger — set CSS variables scoped to this element so they cascade into

0 commit comments

Comments
 (0)