Add client-side search, filter, and sort for items#116
Open
jeroenjanssens wants to merge 21 commits intomainfrom
Open
Add client-side search, filter, and sort for items#116jeroenjanssens wants to merge 21 commits intomainfrom
jeroenjanssens wants to merge 21 commits intomainfrom
Conversation
Replace Alpine.js and server-side pagination with a vanilla JS CardManager that hydrates data-* attributes from Hugo-rendered cards. Supports debounced search (with quoted phrase matching), pill-based OR filters, multiple sort modes, URL state persistence, and sticky filter controls with smart scroll. Applied to blog, software, events, and resource/video pages.
✅ Deploy Preview for posit-open-source canceled.
|
- Hide filter controls by default, reveal via JS (progressive enhancement) - Add bottom border when controls are in sticky mode via IntersectionObserver - Fix bug where cards stayed in wrong order after clearing a URL search query by sorting all cards during reorder, not just visible ones
Card metadata is now generated as separate card-index.json files via a Hugo CardIndex output format, fetched asynchronously by JavaScript. This removes ~300 bytes of data-* attributes per card from the HTML, reducing page size significantly (especially for the videos page with 1,592 cards). No-JS users never download the JSON. Cards are matched by their existing DOM id attribute.
Move frequently repeated Tailwind class strings from item.html into named CSS component classes: .pill, .pill-link, .card-link, .card-badge, .hero-icon, .hero-meta, .hero-meta-lg, .meta-item, .tile-meta. Reduces HTML size by ~19% overall (4.6MB), with the biggest impact on the videos page (-3.8MB / -21%).
- Add important modifier to no-underline in .card-link so it wins over prose typography styles (fixes underlined text on featured blog card) - Use container.querySelector instead of document.getElementById to find cards, so only cards inside the grid are managed by the filter/sort JS
- Add Language filter (Python, R, SQL, Rust, JavaScript, Other) to blog - "JavaScript" also matches TypeScript via aliases config - "Other" matches any language not in the known set (C, C++, Julia, etc.) - Make title sort case-insensitive and ignore punctuation
Pre-compute a _sortTitle key in JS that strips everything except a-z, 0-9, and spaces. This replaces the emoji-stripping regex in the Hugo template and the localeCompare options, giving cleaner alphabetical sorting.
Replace per-section grids with a single filterable grid on category and tag pages. Add a Type filter (blog post, software, event, video, cheatsheet) so users can narrow by item type. Enable CardIndex output for term pages and add card-index JSON templates for both taxonomies.
…sheets - Add type field to card-index-entry.html, resolved from data/items.yaml badge_text (e.g., "blog post", "software", "event") - Add language filter pills (Python, R, Other) to events page - Add language filter pills (Python, R, Other) to cheatsheets page
Add an initially invisible button that JS reveals on load. Clicking it toggles the filter controls panel. Uses invisible instead of hidden to prevent page reflow when the button appears.
- Wire up Filters toggle button with showFilters URL parameter - Auto-show controls when URL has active filters or showFilters=true - Defer sorting on page load: preserve Hugo-rendered order unless a sort param is in the URL, avoiding unnecessary DOM reordering - Show default sort option in dropdown without triggering a sort - Normalize accented characters to ASCII (via NFD decomposition) in search index and sort keys so e.g. "Kalman" matches "Kálmán" - Hydrate type field from card index JSON for type-based filtering
…okup - Add inline script after filter controls that immediately shows the Filters button and opens controls when showFilters=true is in the URL, eliminating the flash before the main JS bundle loads - Switch from previousElementSibling to parentNode.querySelector for finding filter elements, since the inline script tag breaks the sibling chain - Only add showFilters=true to URL when filters are shown; omit when hidden to keep URLs clean
Include raw frontmatter software values in the card index JSON and the JS search index so searching for package names always works, even when the software taxonomy page doesn't exist.
Treat an empty sort key as equivalent to the default sort, so section headings (Current/Upcoming/Past Events) display correctly on initial render without requiring a manual reset. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Events had no `date` frontmatter (only `start_date`), so Hugo's default page ordering didn't match the JS date-desc sort, causing cards to jump position on first interaction. Now all three groups (current, upcoming, past) use consistent `sort ... ".Params.start_date" "desc"`. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Search input takes full width on mobile, shares row on wider screens - Hide "Showing" text and button icons on mobile to save space - Rename sort labels: "Newest First" → "Date", "Title (A-Z)" → "Title" Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Extract duplicated filter/sort config from templates into YAML data files (data/filters/*.yaml) as single source of truth. Templates now load config via site.Data.filters and pass it to both the partial and the data-filter-config attribute. Also refactors search-filter-sort.js to pre-compute filter config map and default sort config.
Access pattern (site.Data.filters.blog etc.) stays the same.
Rename card-index-entry.html to item-index-entry.html, *.cardindex.json to *.itemindex.json, CardIndex output format to ItemIndex, and data-card-container to data-item-container. Rename CardManager class to ItemManager and cards array to items in JS. Keep "card" only where it refers to the specific card display format (vs tile, row, hero).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Add client-side search, filtering, and sorting to the blog, software, events, resources, category, and tag pages so visitors can quickly find what they're looking for.
Features
Performance
card-index.jsonfiles via a Hugo CardIndex output format, fetched asynchronously — removes ~300 bytes ofdata-*attributes per card from the HTMLPages with search/filter/sort
Key files
assets/js/search-filter-sort.js— CardManager class (IIFE, no dependencies)layouts/partials/filter-controls.html— Reusable filter UI with dropdown multiselectslayouts/partials/card-index-entry.html— Generates per-card metadata for JSON indexassets/css/components.css— CSS component classes extracted from item.htmlTest plan
"machine learning")