diff --git a/docs/superpowers/plans/2026-03-22-brave-search-frontend-redesign.md b/docs/superpowers/plans/2026-03-22-brave-search-frontend-redesign.md new file mode 100644 index 0000000..486cee4 --- /dev/null +++ b/docs/superpowers/plans/2026-03-22-brave-search-frontend-redesign.md @@ -0,0 +1,1204 @@ +# Brave Search Frontend Redesign โ Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Redesign the kafka frontend to match Brave Search's layout: three-column results page, category tiles on homepage, and a hybrid preferences system with full-page `/preferences` route. + +**Architecture:** CSS Grid for page-level layouts (three-column results, two-column preferences). JavaScript popover for quick settings (theme + engines only). Server-rendered full preferences page with localStorage persistence. Category tiles are static links with category query params. + +**Tech Stack:** Go (Go templates), CSS Grid/Flexbox, Vanilla JavaScript (HTMX for search), localStorage for preferences + +--- + +## File Map + +| File | Responsibility | +|------|----------------| +| `internal/views/static/css/kafka.css` | Add layout grids, category tiles, sidebar styles, mobile breakpoints | +| `internal/views/templates/index.html` | Add category tiles below search box | +| `internal/views/templates/results.html` | Add left sidebar, restructure for three-column grid | +| `internal/views/templates/preferences.html` | **New** โ full preferences page with nav | +| `internal/views/templates/base.html` | No structural changes needed | +| `internal/views/static/js/settings.js` | Reduce popover to theme + engines; add preferences page JS | +| `internal/httpapi/handlers.go` | Add `GET /preferences` and `POST /preferences` handlers | +| `internal/views/views.go` | Add `RenderPreferences` and `tmplPreferences` template | + +--- + +## PHASE 1: CSS Layout Framework + +### Task 1: Add CSS Grid Layouts and Breakpoints + +**Files:** +- Modify: `internal/views/static/css/kafka.css` + +- [ ] **Step 1: Add three-column results layout CSS** + +Append to end of `kafka.css`, before the `@media print` block: + +```css +/* ============================================================ + Three-Column Results Layout + ============================================================ */ + +.results-layout { + display: grid; + grid-template-columns: 200px 1fr 240px; + gap: 2rem; + align-items: start; +} + +.results-layout .left-sidebar { + position: sticky; + top: calc(var(--header-height) + 1.5rem); + max-height: calc(100vh - var(--header-height) - 3rem); + overflow-y: auto; +} + +.results-layout .right-sidebar { + position: sticky; + top: calc(var(--header-height) + 1.5rem); + max-height: calc(100vh - var(--header-height) - 3rem); + overflow-y: auto; +} + +.results-layout .results-column { + min-width: 0; +} +``` + +- [ ] **Step 2: Add mobile breakpoints** + +```css +/* Tablet: hide left sidebar, two columns */ +@media (min-width: 769px) and (max-width: 1024px) { + .results-layout { + grid-template-columns: 1fr 220px; + } + .results-layout .left-sidebar { + display: none; + } +} + +/* Mobile: single column, no sidebars */ +@media (max-width: 768px) { + .results-layout { + grid-template-columns: 1fr; + } + .results-layout .left-sidebar, + .results-layout .right-sidebar { + display: none; + } +} +``` + +- [ ] **Step 3: Add preferences page layout CSS** + +```css +/* ============================================================ + Preferences Page Layout + ============================================================ */ + +.preferences-layout { + display: grid; + grid-template-columns: 200px 1fr; + gap: 2rem; + align-items: start; + padding: 2rem 0; +} + +.preferences-nav { + position: sticky; + top: calc(var(--header-height) + 1.5rem); +} + +.preferences-nav-item { + display: flex; + align-items: center; + gap: 0.6rem; + padding: 0.6rem 0.75rem; + border-radius: var(--radius-sm); + color: var(--text-secondary); + text-decoration: none; + font-size: 0.9rem; + transition: background 0.15s, color 0.15s; + cursor: pointer; +} + +.preferences-nav-item:hover { + background: var(--bg-tertiary); + color: var(--text-primary); +} + +.preferences-nav-item.active { + background: var(--accent-soft); + color: var(--accent); + font-weight: 500; +} + +.preferences-content { + background: var(--bg); + border: 1px solid var(--border); + border-radius: var(--radius-lg); + padding: 1.5rem; +} + +@media (max-width: 768px) { + .preferences-layout { + grid-template-columns: 1fr; + } + .preferences-nav { + position: static; + display: flex; + overflow-x: auto; + gap: 0.5rem; + padding-bottom: 0.5rem; + } + .preferences-nav-item { + white-space: nowrap; + } +} +``` + +- [ ] **Step 4: Add category tiles CSS** + +```css +/* ============================================================ + Category Tiles + ============================================================ */ + +.category-tiles { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(100px, 1fr)); + gap: 1rem; + margin-top: 2rem; +} + +.category-tile { + display: flex; + flex-direction: column; + align-items: center; + gap: 0.5rem; + padding: 1rem 0.5rem; + border-radius: var(--radius-md); + text-decoration: none; + color: var(--text-secondary); + font-size: 0.85rem; + transition: background 0.15s, color 0.15s, transform 0.15s, box-shadow 0.15s; +} + +.category-tile:hover { + background: var(--bg-tertiary); + color: var(--text-primary); + transform: translateY(-2px); + box-shadow: var(--shadow-sm); +} + +.category-tile-icon { + font-size: 1.5rem; + line-height: 1; +} + +.category-tile.disabled { + opacity: 0.5; + cursor: not-allowed; + pointer-events: none; +} + +@media (max-width: 768px) { + .category-tiles { + grid-template-columns: repeat(auto-fill, minmax(80px, 1fr)); + gap: 0.75rem; + } + .category-tile { + padding: 0.75rem 0.25rem; + font-size: 0.75rem; + } + .category-tile-icon { + font-size: 1.25rem; + } +} +``` + +- [ ] **Step 5: Add left sidebar navigation styles** + +```css +/* ============================================================ + Left Sidebar (Results Page) + ============================================================ */ + +.left-sidebar { + padding: 0; +} + +.sidebar-nav { + display: flex; + flex-direction: column; + gap: 0.25rem; +} + +.sidebar-nav-title { + font-size: 0.7rem; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.06em; + color: var(--text-muted); + padding: 0.5rem 0.75rem; + margin-top: 0.5rem; +} + +.sidebar-nav-item { + display: flex; + align-items: center; + gap: 0.5rem; + padding: 0.5rem 0.75rem; + border-radius: var(--radius-sm); + color: var(--text-secondary); + text-decoration: none; + font-size: 0.875rem; + transition: background 0.15s, color 0.15s; +} + +.sidebar-nav-item:hover { + background: var(--bg-tertiary); + color: var(--text-primary); +} + +.sidebar-nav-item.active { + background: var(--accent-soft); + color: var(--accent); + font-weight: 500; +} + +.sidebar-nav-item-icon { + font-size: 1rem; + width: 20px; + text-align: center; +} + +.sidebar-filters { + margin-top: 1rem; + padding-top: 1rem; + border-top: 1px solid var(--border); +} + +.sidebar-filter-group { + margin-bottom: 0.75rem; +} + +.sidebar-filter-label { + font-size: 0.75rem; + font-weight: 500; + color: var(--text-muted); + padding: 0 0.75rem; + margin-bottom: 0.25rem; +} + +.sidebar-filter-option { + display: flex; + align-items: center; + gap: 0.5rem; + padding: 0.35rem 0.75rem; + font-size: 0.8rem; + color: var(--text-secondary); + cursor: pointer; + border-radius: var(--radius-sm); + transition: background 0.15s; +} + +.sidebar-filter-option:hover { + background: var(--bg-tertiary); +} + +.sidebar-filter-option input[type="radio"] { + accent-color: var(--accent); +} + +/* Mobile filter chips */ +.mobile-filter-chips { + display: none; + overflow-x: auto; + gap: 0.5rem; + padding: 0.75rem 0; + -webkit-overflow-scrolling: touch; +} + +.mobile-filter-chips::-webkit-scrollbar { + display: none; +} + +.mobile-filter-chip { + display: inline-flex; + align-items: center; + padding: 0.4rem 0.75rem; + border: 1px solid var(--border); + border-radius: var(--radius-full); + font-size: 0.8rem; + color: var(--text-secondary); + white-space: nowrap; + text-decoration: none; + transition: background 0.15s, border-color 0.15s; +} + +.mobile-filter-chip:hover, +.mobile-filter-chip.active { + background: var(--accent-soft); + border-color: var(--accent); + color: var(--accent); +} + +@media (max-width: 768px) { + .mobile-filter-chips { + display: flex; + } +} +``` + +- [ ] **Step 6: Verify CSS changes compile** + +Run: `go build ./...` +Expected: No errors + +- [ ] **Step 7: Commit** + +```bash +git add internal/views/static/css/kafka.css +git commit -m "feat(frontend): add CSS layout framework for three-column results and preferences page" +``` + +--- + +## PHASE 2: Results Page Three-Column Layout + +### Task 2: Restructure Results Template + +**Files:** +- Modify: `internal/views/templates/results.html` +- Modify: `internal/views/views.go` + +- [ ] **Step 1: Read current results.html to understand exact content** + +Current structure has `.results-layout` grid with `.search-compact` spanning full width, `.results-column`, and `.sidebar`. Need to add left sidebar and restructure grid. + +- [ ] **Step 2: Replace results.html content** + +Replace the entire file content: + +```html +{{define "title"}}{{if .Query}}{{.Query}} โ {{end}}{{end}} +{{define "content"}} +
Search the web privately, without tracking or censorship.
+