Skip to content

Add search capability via astro-pagefind#118

Open
eXpl0it3r wants to merge 2 commits into
Zuehlke:mainfrom
eXpl0it3r:feature/search
Open

Add search capability via astro-pagefind#118
eXpl0it3r wants to merge 2 commits into
Zuehlke:mainfrom
eXpl0it3r:feature/search

Conversation

@eXpl0it3r
Copy link
Copy Markdown
Contributor

This PR adds search functionality to The Dev Exchange blog by using pagefind through astro-pagefind.

A new icon is added to the navigation bar:

image

Which opens a modal with a search input field and blurs the background:

image

This PR really just restyles the <Search /> component provided by astro-pagefind to match the look and feel of the blog.

pagefind works by indexing the blog posts at build time, thus there's no dynamic backend component required. It offers a bunch of options like filters for authors or tags, which could be considered at a later point.

Copy link
Copy Markdown

@kunman93 kunman93 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice addition overall — pagefind is a solid choice for static-site search, and the styling integration looks clean. I have a few findings, two of which are functional bugs.

Comment on lines +177 to +184
function handleKeydown(event: KeyboardEvent) {
if (event.key === 'Escape' && !searchModal?.classList.contains('hidden')) {
closeModal();
}
}

document.removeEventListener('keydown', handleKeydown);
document.addEventListener('keydown', handleKeydown);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: keydown listeners accumulate on every page navigation

handleKeydown is defined inside initSearchModal(), so each call to initSearchModal creates a brand-new function reference. The removeEventListener on line 183 tries to remove that freshly-created reference — but it was never registered. The old handler from the previous call is still alive. After n page navigations there will be n active keydown listeners.

The cleanest fix is to hoist the handler or use an AbortController:

let keydownController: AbortController | null = null;

function initSearchModal() {
  // ...existing cloneNode logic...

  keydownController?.abort();
  keydownController = new AbortController();

  document.addEventListener('keydown', (event: KeyboardEvent) => {
    if (event.key === 'Escape' && !searchModal?.classList.contains('hidden')) {
      closeModal();
    }
  }, { signal: keydownController.signal });
}

padding: 0 10px;
max-height: 66vh;
overflow-y: scroll;
scrollbar-color: var(--color-zag-dark);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: scrollbar-color requires two values

scrollbar-color accepts exactly two values: <thumb-color> <track-color>. Providing a single value is invalid CSS and will be ignored by the browser. The same applies to the dark-mode variant on line 104.

/* suggestion */
scrollbar-color: var(--color-zag-dark) transparent;

And for dark mode:

scrollbar-color: var(--color-zag-light) transparent;

Comment on lines +36 to +38
#search-modal.hidden {
animation: fadeOut 0.2s ease-out;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Visual bug: fadeOut animation never plays

When closeModal() adds the Tailwind hidden class, it immediately sets display: none. The browser removes the element from layout before the animation has a chance to run, so the fade-out is skipped entirely — the modal just disappears instantly.

The fade-in works fine because hidden is removed before the animation starts. For the fade-out you'd need a different approach, e.g. listen for animationend before adding hidden, or switch to an opacity/pointer-events toggle instead of display:

/* instead of toggling hidden, toggle a .visible class */
#search-modal {
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.2s ease-out;
}
#search-modal.visible {
  opacity: 1;
  pointer-events: auto;
}
function openModal() { searchModal?.classList.add('visible'); ... }
function closeModal() { searchModal?.classList.remove('visible'); ... }

And remove the hidden class from the initial markup (<div id="search-modal" class="fixed inset-0 z-50">).

Alternatively, if the instant-close behaviour is acceptable, just remove the #search-modal.hidden animation rule to avoid the false expectation.

Comment on lines +59 to +62
.backdrop-blur-sm {
backdrop-filter: blur(4px);
-webkit-backdrop-filter: blur(4px);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor: Redundant .backdrop-blur-sm vendor-prefix override

Tailwind v4 already emits backdrop-filter with the -webkit- vendor prefix automatically. Redefining .backdrop-blur-sm here is a no-op and adds noise. This block can be removed safely.

@kunman93
Copy link
Copy Markdown

kunman93 commented May 8, 2026

I let Copilot review the code to save some time. :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants