diff options
| author | Danilo M. <danix@danix.xyz> | 2026-04-20 13:08:21 +0200 |
|---|---|---|
| committer | Danilo M. <danix@danix.xyz> | 2026-04-20 13:08:21 +0200 |
| commit | 714abafe1cc4fa18cc7835c702a2cc0603044469 (patch) | |
| tree | 35466c8e9a18c2b03c32d7c32ef5df17ff033eca | |
| parent | fc6a430acb80824501861f16dcfd3ebcd7ff92bd (diff) | |
| download | danixxyz-714abafe1cc4fa18cc7835c702a2cc0603044469.tar.gz danixxyz-714abafe1cc4fa18cc7835c702a2cc0603044469.zip | |
docs: add search functionality design spec
Comprehensive design for unified search across desktop header (modal), mobile menu (inline), and 404 page. Includes lazy-loaded JSON index, Alpine.js components, i18n integration, and WCAG 2.1 AA compliance requirements.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
| -rw-r--r-- | docs/superpowers/specs/2026-04-20-search-functionality-design.md | 252 |
1 files changed, 252 insertions, 0 deletions
diff --git a/docs/superpowers/specs/2026-04-20-search-functionality-design.md b/docs/superpowers/specs/2026-04-20-search-functionality-design.md new file mode 100644 index 0000000..a58526a --- /dev/null +++ b/docs/superpowers/specs/2026-04-20-search-functionality-design.md @@ -0,0 +1,252 @@ +# Search Functionality Design +**Date:** 2026-04-20 +**Status:** Design Approved +**Scope:** Header + mobile menu search, unified 404 page integration + +--- + +## Overview + +Implement a two-context search system for danix.xyz: +- **Desktop (≥768px):** Hidden search icon in header → overlay modal with search bar + results +- **Mobile (<768px):** Search bar integrated into hamburger menu overlay (between nav links and language toggle) +- **Unified backend:** Single lazy-loaded JSON index shared across header search, mobile menu search, and 404 page + +--- + +## Goals + +1. Enable users to quickly find articles from any page (especially the 404 page) +2. Scale efficiently with growing article count (lazy-load index on first interaction) +3. Maintain clean desktop header aesthetic +4. Provide seamless mobile experience within existing menu overlay +5. Follow WCAG 2.1 AA accessibility and project theming standards +6. Eliminate code duplication between 404 and new search features + +--- + +## Architecture + +### Data Layer: Search Index + +**Generation:** +- Hugo builds `/search-index.json` at build time (via `layouts/_default/search-index.json`) +- Contains all articles from `where .Site.RegularPages "Section" "articles"` +- Structure per article: `{ title, url, date, summary }` +- Placed in site root for HTTP access + +**Loading:** +- Fetched on first search interaction (not on page load) +- Cached in `window.searchIndex` to avoid refetching +- Handles both languages transparently (index includes all content regardless of lang) + +**Index Schema:** +```json +[ + { + "title": "Article Title", + "url": "/en/articles/slug/", + "date": "Jan 02, 2006", + "summary": "First 160 chars of article content..." + } +] +``` + +--- + +## Component: Desktop Search Modal + +**Trigger:** +- Search icon (Feather magnifying glass) in header's right control area, next to theme toggle +- Hidden on mobile (`hidden md:flex`) +- Click opens full-screen overlay modal + +**Modal UI:** +- Full-screen overlay with semi-transparent backdrop (`bg-black/50`) +- Centered content box: `max-w-2xl`, `border border-accent`, `rounded-lg` +- Header: "Search Articles" label (localized via i18n) +- Search input: `px-4 py-3 border-2 border-border rounded`, focus ring `ring-accent` +- Results container: scrollable list, max height to prevent overflow +- Empty state: "No results found" message when query returns nothing +- Close: Esc key, click outside backdrop, or close button (×) + +**Behavior:** +- Input triggers filtering on keyup (real-time search) +- Displays max 5 results with title, date, excerpt +- Each result is a clickable link to the article +- Result styling: `p-4 border-l-4 border-accent bg-bg/50 hover:bg-bg/70` + +**Focus Management (A11y):** +- Modal receives focus on open (managed by Alpine.js) +- Focus trap: Tab cycles within modal, cannot escape to background +- Close button and input are focusable +- Esc key closes modal gracefully + +--- + +## Component: Mobile Menu Search + +**Location:** Inside hamburger overlay, positioned between nav links and language toggle +**Visibility:** Always visible when menu is open (no extra toggle) + +**UI:** +- Same search input styling as desktop modal +- Results appear below input as user types +- Max 5 results, same card layout as desktop +- Results stack vertically, scrollable if needed + +**Behavior:** +- Shares filtering logic with desktop search +- Lazy-loads index on first interaction +- Links are direct navigation (close menu on click if desired—handled by existing menu behavior) + +--- + +## Shared Search Logic: `search.js` + +**Location:** `themes/danix-xyz-hacker/assets/js/search.js` + +**Responsibilities:** +1. Load `/search-index.json` on first interaction, cache in `window.searchIndex` +2. Export `filterArticles(query, articles)` function + - Input: lowercase query string, articles array + - Output: filtered array (title/summary match), limited to 5 results + - Case-insensitive matching on title and summary + +**Alpine Components:** +1. `searchOverlay` (desktop modal) + - State: `isOpen`, `searchQuery`, `filteredArticles`, `allArticles` + - Methods: `open()`, `close()`, `filterArticles(query)`, `handleEscape()` + - Initializer: lazy-loads index on first open + +2. `mobileSearch` (hamburger menu integration) + - State: `searchQuery`, `filteredArticles`, `allArticles` + - Methods: `filterArticles(query)`, `ensureIndexLoaded()` + - Initializer: lazy-loads index on menu open (reuses shared load function) + +**404 Page Refactor:** +- Update `notFoundPage` Alpine component to use shared filtering logic +- Replace inline `window.articlesData` with lazy-loaded `window.searchIndex` +- Keep existing modal/toggle logic for Easter Egg (unchanged) + +--- + +## i18n Integration + +**New Keys (English):** `en.yaml` +- `searchArticles: "Search Articles"` +- `searchPlaceholder: "Search by title or content..."` +- `noSearchResults: "No articles found matching your search."` + +**New Keys (Italian):** `it.yaml` +- `searchArticles: "Cerca Articoli"` +- `searchPlaceholder: "Cerca per titolo o contenuto..."` +- `noSearchResults: "Nessun articolo trovato per la tua ricerca."` + +--- + +## Styling & Theme Compliance + +**Color Palette:** +- Text: `text-text` (primary), `text-text-dim` (secondary) +- Accent: `text-accent` (purple, highlights) +- Borders: `border-border` +- Backgrounds: `bg-bg`, `bg-bg/50` (hover), `bg-black/50` (overlay) + +**Typography:** +- Font: JetBrains Mono (monospace throughout) +- Input focus: `focus:ring-2 focus:ring-accent focus:border-transparent` +- Results title: `font-bold text-accent` +- Results date: `text-sm text-text-dim` + +**Utilities Only:** +- No new CSS file +- All styling via Tailwind utility classes +- Reuse existing component patterns (buttons, inputs from form-components.html) + +--- + +## Accessibility (WCAG 2.1 AA) + +**Contrast:** +- All text meets ≥4.5:1 contrast ratio +- Accent text on bg verified + +**Keyboard Navigation:** +- Desktop modal: Esc closes, Tab/Shift+Tab navigates focusable elements +- Mobile: Input focusable, results clickable +- Search icon: Keyboard accessible (button element, aria-label) + +**Screen Readers:** +- Modal: `role="dialog"`, `aria-labelledby="search-modal-title"` +- Input: `<label>` with `for` attribute and `aria-label` fallback +- Results: Semantic `<a>` links with descriptive text +- Empty state: `aria-live="polite"` on results container + +**Visual Indicators:** +- Focus states: `ring-2 ring-accent` on inputs +- Hover states: `hover:bg-bg/70` on result cards +- Disabled/loading states: TBD if needed (assume instant for now) + +--- + +## Files to Create/Modify + +### Create: +1. `themes/danix-xyz-hacker/layouts/_default/search-index.json` — Hugo template for search index +2. `themes/danix-xyz-hacker/layouts/partials/search-modal.html` — Desktop modal partial +3. `themes/danix-xyz-hacker/assets/js/search.js` — Shared search logic + +### Modify: +1. `themes/danix-xyz-hacker/layouts/partials/header.html` — Add search icon trigger +2. `themes/danix-xyz-hacker/layouts/partials/hamburger-menu.html` — Add mobile search bar +3. `themes/danix-xyz-hacker/layouts/_default/baseof.html` — Include search-modal partial + search.js bundle +4. `themes/danix-xyz-hacker/assets/js/not-found-page.js` — Refactor to use shared search logic +5. `i18n/en.yaml` — Add search labels +6. `i18n/it.yaml` — Add search labels (Italian) + +### No changes needed: +- `404.en.html` / `404.it.html` — Alpine component behavior unchanged, data source swapped + +--- + +## Testing Strategy + +**Manual Testing:** +1. Desktop: Click search icon → modal opens, input focused, Esc closes +2. Desktop: Type query → results appear in real-time, max 5 shown +3. Desktop: Click result → navigates to article +4. Mobile: Open menu → search bar visible, type query → results appear +5. Mobile: Click result → navigates to article +6. 404 page: Search bar works as before (refactored, same UX) +7. First interaction: Index loads (check Network tab), cached on second interaction +8. Language switch: Results respect current language links + +**A11y Testing:** +1. Keyboard: Tab navigates modal, Esc closes +2. Screen reader: Modal announced, input label read, results as links +3. Focus visible: All interactive elements have clear focus rings + +--- + +## Future Enhancements + +- Search result highlighting/excerpts +- Search analytics (popular searches) +- Advanced filters (by date, category, type) +- Search suggestions as-you-type +- Keyboard shortcut (Cmd+K / Ctrl+K) to open + +--- + +## Success Criteria + +- ✅ Header search icon hidden on mobile, visible on desktop +- ✅ Mobile search integrated into hamburger menu +- ✅ Search index lazy-loaded (single HTTP request per session) +- ✅ 404 page search unified with header/menu search +- ✅ WCAG 2.1 AA compliant +- ✅ All text localized (EN/IT) +- ✅ Tailwind utilities only, no new CSS +- ✅ Keyboard accessible (Esc, Tab, focus management) +- ✅ Real-time filtering, max 5 results displayed |
