Skip to content

Commit 7cc239d

Browse files
committed
Add Pagefind search runtime core
Replace the old Fuse search runtime with Pagefind-backed browser search. Add a loader module as the single Pagefind API boundary. The loader imports the configured Pagefind bundle, sets basePath, baseUrl, and excerptLength, initializes once, and serves both search surfaces. Split the prior monolithic search.mjs into focused modules for config, constants, DOM helpers, loading, modal search, full-page search, and result rendering. Rebuild the quick search modal against the new loader with the final keyword/phrase copy and modal layout. Wire the full search page runtime shell with query state, the relevance/number/created sort contract, a status region, a results region, and the final share-link icon button. The share button keeps copy success or failure feedback on the icon state instead of rewriting the result status text. Update base.html to expose resolved search state as a JSON config script for client JavaScript, make the global header search trigger a real /search/ link, and load search UI styles from a dedicated stylesheet instead of injecting them from JavaScript after startup. Add check-pagefind-search-ui coverage for the loader boundary, quick search modal, share icon state, and full search runtime shell against rendered output.
1 parent 90097e6 commit 7cc239d

13 files changed

Lines changed: 1344 additions & 498 deletions

File tree

sass/assets/css/search.scss

Lines changed: 321 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,321 @@
1+
#search {
2+
display: inline-block;
3+
border-radius: var(--bs-border-radius);
4+
border: 1px solid currentColor;
5+
text-decoration: none;
6+
color: rgb(var(--bs-secondary-rgb));
7+
}
8+
9+
#search > div {
10+
padding: 0 32px 0 0.5rem;
11+
margin: 0 0.5em;
12+
}
13+
14+
.search-page-form {
15+
margin-bottom: 1rem;
16+
}
17+
18+
.search-page-form label {
19+
display: block;
20+
font-weight: 600;
21+
margin-bottom: 0.35rem;
22+
}
23+
24+
.search-page-controls {
25+
display: grid;
26+
grid-template-columns: minmax(12rem, 1fr) minmax(12rem, 18rem) auto auto;
27+
gap: 0.5rem;
28+
align-items: center;
29+
}
30+
31+
.search-page-controls input,
32+
.search-page-controls select,
33+
.search-page-controls button,
34+
.search-modal-input,
35+
.search-modal-close,
36+
.search-modal-full-link {
37+
border: 1px solid var(--bs-border-color, #ced4da);
38+
border-radius: 4px;
39+
font: inherit;
40+
min-height: 2.35rem;
41+
padding: 0.4rem 0.65rem;
42+
}
43+
44+
.search-page-controls button,
45+
.search-modal-close,
46+
.search-modal-full-link {
47+
background: var(--bs-secondary-bg, #f8f9fa);
48+
color: inherit;
49+
cursor: pointer;
50+
text-decoration: none;
51+
}
52+
53+
.search-page-controls button:disabled {
54+
cursor: not-allowed;
55+
opacity: 0.65;
56+
}
57+
58+
.search-share-button {
59+
align-items: center;
60+
background: rgba(var(--bs-body-bg-rgb, 255, 255, 255), 0.92);
61+
border-radius: 999px;
62+
box-shadow: 0 0.15rem 0.5rem rgba(0, 0, 0, 0.12);
63+
color: var(--bs-secondary-color, #666);
64+
display: inline-flex;
65+
height: 2.35rem;
66+
justify-content: center;
67+
min-width: 2.35rem;
68+
padding: 0;
69+
transition:
70+
background-color 0.18s ease,
71+
border-color 0.18s ease,
72+
color 0.18s ease,
73+
filter 0.18s ease;
74+
width: 2.35rem;
75+
}
76+
77+
.search-share-button:hover,
78+
.search-share-button:focus-visible {
79+
background: rgba(var(--bs-secondary-bg-rgb, 248, 249, 250), 0.82);
80+
border-color: var(--bs-emphasis-color, currentColor);
81+
color: var(--bs-emphasis-color, currentColor);
82+
text-decoration: none;
83+
}
84+
85+
.search-share-button:focus-visible {
86+
outline: 2px solid currentColor;
87+
outline-offset: 2px;
88+
}
89+
90+
.search-share-button__icon {
91+
align-items: center;
92+
display: inline-flex;
93+
justify-content: center;
94+
transition: filter 0.18s ease, color 0.18s ease;
95+
}
96+
97+
.search-share-button:hover .search-share-button__icon,
98+
.search-share-button:focus-visible .search-share-button__icon {
99+
filter: brightness(1.12);
100+
}
101+
102+
.search-share-button__icon svg {
103+
height: 1rem;
104+
width: 1rem;
105+
}
106+
107+
.search-share-button__status {
108+
display: none;
109+
font-size: 1rem;
110+
font-weight: 700;
111+
line-height: 1;
112+
}
113+
114+
.search-share-button[data-share-state="idle"] .search-share-button__icon,
115+
.search-share-button[data-share-state="working"] .search-share-button__icon {
116+
display: inline-flex;
117+
}
118+
119+
.search-share-button[data-share-state="success"] .search-share-button__icon,
120+
.search-share-button[data-share-state="error"] .search-share-button__icon {
121+
display: none;
122+
}
123+
124+
.search-share-button[data-share-state="success"] .search-share-button__status--success,
125+
.search-share-button[data-share-state="error"] .search-share-button__status--error {
126+
display: inline-flex;
127+
}
128+
129+
.search-share-button[data-share-state="success"] {
130+
color: var(--bs-success-text-emphasis, var(--bs-secondary-color, #666));
131+
}
132+
133+
.search-share-button[data-share-state="error"] {
134+
border-color: currentColor;
135+
color: var(--bs-danger-text-emphasis, #842029);
136+
}
137+
138+
.search-page-status,
139+
.search-modal-status {
140+
color: var(--bs-secondary-color, #666);
141+
margin: 0.75rem 0;
142+
}
143+
144+
.search-page-results,
145+
.search-modal-results {
146+
display: grid;
147+
gap: 0.75rem;
148+
}
149+
150+
.search-result-card {
151+
border: 1px solid var(--bs-border-color, #dee2e6);
152+
border-radius: 6px;
153+
background: var(--bs-body-bg, #fff);
154+
}
155+
156+
.search-result-card-compact {
157+
border-width: 0 0 1px 0;
158+
border-radius: 0;
159+
}
160+
161+
.search-result-link {
162+
display: block;
163+
color: inherit;
164+
padding: 0.85rem;
165+
text-decoration: none;
166+
}
167+
168+
.search-result-link:hover .search-result-title,
169+
.search-result-link:focus .search-result-title {
170+
text-decoration: underline;
171+
}
172+
173+
.search-result-header {
174+
display: flex;
175+
gap: 0.65rem;
176+
align-items: baseline;
177+
flex-wrap: wrap;
178+
}
179+
180+
.search-result-proposal {
181+
color: var(--bs-secondary-color, #666);
182+
font-weight: 700;
183+
}
184+
185+
.search-result-title {
186+
font-size: 1.05rem;
187+
margin: 0;
188+
}
189+
190+
.search-result-metadata {
191+
display: flex;
192+
flex-wrap: wrap;
193+
gap: 0.4rem;
194+
margin-top: 0.5rem;
195+
}
196+
197+
.search-result-meta {
198+
border: 1px solid var(--bs-border-color, #dee2e6);
199+
border-radius: 4px;
200+
color: var(--bs-secondary-color, #666);
201+
font-size: 0.85rem;
202+
padding: 0.1rem 0.35rem;
203+
}
204+
205+
.search-result-excerpt {
206+
color: var(--bs-secondary-color, #666);
207+
margin: 0.55rem 0 0;
208+
}
209+
210+
.search-result-excerpt mark {
211+
padding: 0 0.1rem;
212+
}
213+
214+
.search-modal-open {
215+
overflow: hidden;
216+
}
217+
218+
.search-modal[hidden] {
219+
display: none;
220+
}
221+
222+
.search-modal {
223+
align-items: flex-start;
224+
background: rgba(0, 0, 0, 0.55);
225+
bottom: 0;
226+
display: flex;
227+
justify-content: center;
228+
left: 0;
229+
padding: 8vh 1rem 1rem;
230+
position: fixed;
231+
right: 0;
232+
top: 0;
233+
z-index: 1000;
234+
}
235+
236+
.search-modal-panel {
237+
background: var(--bs-body-bg, #fff);
238+
border-radius: 8px;
239+
box-shadow: 0 1rem 3rem rgba(0, 0, 0, 0.25);
240+
display: flex;
241+
flex-direction: column;
242+
max-height: min(84vh, 48rem);
243+
overflow: hidden;
244+
padding: 1rem;
245+
width: min(42rem, 100%);
246+
}
247+
248+
.search-modal-header {
249+
align-items: center;
250+
display: grid;
251+
gap: 1rem;
252+
grid-template-columns: 2.5rem minmax(0, 1fr) 2.5rem;
253+
margin-bottom: 0.75rem;
254+
}
255+
256+
.search-modal-header h2 {
257+
font-size: 1.1rem;
258+
grid-column: 2;
259+
margin: 0;
260+
text-align: center;
261+
}
262+
263+
.search-modal-close {
264+
grid-column: 3;
265+
justify-self: end;
266+
}
267+
268+
.search-modal-input {
269+
box-sizing: border-box;
270+
flex: 0 0 auto;
271+
width: 100%;
272+
}
273+
274+
.search-modal-status {
275+
flex: 0 0 auto;
276+
}
277+
278+
.search-modal-results {
279+
flex: 1 1 auto;
280+
max-height: 52vh;
281+
min-height: 0;
282+
overflow: auto;
283+
padding-right: 0.25rem;
284+
}
285+
286+
.search-modal-full-link {
287+
align-self: center;
288+
display: inline-block;
289+
flex: 0 0 auto;
290+
margin-top: 0.85rem;
291+
text-align: center;
292+
}
293+
294+
@media (max-width: 720px) {
295+
.search-page-controls {
296+
display: flex;
297+
flex-wrap: wrap;
298+
}
299+
300+
.search-page-controls input {
301+
flex: 1 0 100%;
302+
}
303+
304+
.search-page-controls select {
305+
flex: 1 1 12rem;
306+
min-width: 10rem;
307+
}
308+
309+
.search-page-controls [data-search-page-submit],
310+
.search-share-button {
311+
flex: 0 0 auto;
312+
}
313+
314+
.search-modal-panel {
315+
max-height: 88vh;
316+
}
317+
318+
.search-modal-results {
319+
max-height: 48vh;
320+
}
321+
}

0 commit comments

Comments
 (0)