# 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
