From: Danilo M. Date: Mon, 20 Apr 2026 11:08:21 +0000 (+0200) Subject: docs: add search functionality design spec X-Git-Tag: release_22042026-1342~76 X-Git-Url: https://git.danix.xyz/?a=commitdiff_plain;h=714abafe1cc4fa18cc7835c702a2cc0603044469;p=danix.xyz-2.git 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 --- 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: `