feat: 3-column layout with centered results and right column

- results-layout: 3-column grid (1fr | min(768px,100%) | 300px) max-width 1400px, centered
- Widen center results column to 768px max
- Right column (formerly sidebar): sticky, contains knowledge panel + related searches
- Knowledge panel: Wikipedia/infobox summary with optional thumbnail
- Related searches: clickable links to refine the query
- Empty left buffer creates balanced whitespace on large screens
- Responsive: 2-col at 1000px, 1-col at 700px
This commit is contained in:
Franz Kafka 2026-03-22 15:24:40 +00:00
parent 2d22a8cdbb
commit 4b0cde91ed
4 changed files with 105 additions and 92 deletions

View file

@ -1,94 +1,53 @@
{{define "title"}}{{if .Query}}{{.Query}} — {{end}}{{end}}
{{define "content"}}
<div class="results-layout">
<!-- Left Sidebar -->
<aside class="left-sidebar" id="left-sidebar">
<nav class="sidebar-nav">
<div class="sidebar-nav-title">Categories</div>
{{range .Categories}}
<a href="/search?q={{$.Query | urlquery}}&category={{.}}" class="sidebar-nav-item {{if eq $.ActiveCategory .}}active{{end}}">
<span class="sidebar-nav-item-icon">{{index $.CategoryIcons .}}</span>
<span>{{.}}</span>
</a>
{{end}}
<!-- Disabled categories with no links -->
{{range .DisabledCategories}}
<span class="sidebar-nav-item disabled">
<span class="sidebar-nav-item-icon">{{index $.CategoryIcons .}}</span>
<span>{{.}}</span>
</span>
{{end}}
</nav>
<form method="GET" action="/search" class="sidebar-filters"
hx-get="/search" hx-target="#results" hx-trigger="change" hx-swap="innerHTML">
<input type="hidden" name="q" value="{{.Query | urlquery}}">
<input type="hidden" name="category" value="{{.ActiveCategory}}">
<div class="sidebar-filter-group">
<div class="sidebar-filter-label">Time</div>
{{range .TimeFilters}}
<label class="sidebar-filter-option">
<input type="radio" name="time" value="{{.Value}}" {{if eq $.ActiveTime .Value}}checked{{end}}>
<span>{{.Label}}</span>
</label>
{{end}}
</div>
<div class="sidebar-filter-group">
<div class="sidebar-filter-label">Type</div>
{{range .TypeFilters}}
<label class="sidebar-filter-option">
<input type="radio" name="type" value="{{.Value}}" {{if eq $.ActiveType .Value}}checked{{end}}>
<span>{{.Label}}</span>
</label>
{{end}}
</div>
</form>
</aside>
<!-- Center Column -->
<div class="results-column">
<!-- Compact search bar -->
<div class="search-compact">
<div class="search-box">
<form method="GET" action="/search" role="search" id="search-form">
<input type="text" name="q" id="q" value="{{.Query}}" autocomplete="off" autofocus
hx-get="/search" hx-target="#results" hx-trigger="keyup changed delay:500ms" hx-include="this">
<button type="submit" class="search-box-submit" aria-label="Search">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
<circle cx="11" cy="11" r="8"/>
<path d="m21 21-4.35-4.35"/>
</svg>
</button>
</form>
</div>
<!-- Compact search bar spans all columns -->
<div class="search-compact">
<div class="search-box">
<form method="GET" action="/search" role="search" id="search-form">
<input type="text" name="q" id="q" value="{{.Query}}" autocomplete="off" autofocus
hx-get="/search" hx-target="#results" hx-trigger="keyup changed delay:500ms" hx-include="this">
<button type="submit" class="search-box-submit" aria-label="Search">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
<circle cx="11" cy="11" r="8"/>
<path d="m21 21-4.35-4.35"/>
</svg>
</button>
</form>
</div>
</div>
<!-- Mobile filter chips -->
<div class="mobile-filter-chips">
<a href="/search?q={{.Query | urlquery}}" class="mobile-filter-chip {{if not .ActiveCategory}}active{{end}}">All</a>
{{range .Categories}}
<a href="/search?q={{$.Query | urlquery}}&category={{.}}" class="mobile-filter-chip {{if eq $.ActiveCategory .}}active{{end}}">{{.}}</a>
{{end}}
</div>
<!-- Results inner -->
<!-- Center: Results -->
<div class="results-column" id="results">
{{template "results_inner" .}}
</div>
<!-- Right Sidebar -->
<aside class="right-sidebar" id="sidebar">
<!-- Right: Knowledge Panel + Related Searches -->
<aside class="right-column" id="right-column">
{{if and .Infoboxes (gt (len .Infoboxes) 0)}}
{{with index .Infoboxes 0}}
<div class="knowledge-panel">
{{if .img_src}}<img class="knowledge-panel-thumb" src="{{.img_src}}" alt="{{.title}}" loading="lazy">{{end}}
{{if .title}}<div class="knowledge-panel-title">{{.title}}</div>{{end}}
{{if .content}}<div class="knowledge-panel-content">{{.content}}</div>{{end}}
</div>
{{end}}
{{end}}
{{if .Suggestions}}
<div class="sidebar-card">
<div class="sidebar-title">Related Searches</div>
<div class="suggestion-list">
{{range .Suggestions}}<span class="suggestion"><a href="/search?q={{. | urlquery}}">{{.}}</a></span>{{end}}
<div class="sidebar-card-title">Related Searches</div>
<div class="related-searches">
{{range .Suggestions}}
<a class="related-search-link" href="/search?q={{. | urlquery}}">{{.}}</a>
{{end}}
</div>
</div>
{{end}}
{{if .UnresponsiveEngines}}
<div class="sidebar-card">
<div class="sidebar-title">Engines with issues</div>
<div class="sidebar-card-title">Engine Status</div>
<ul class="unresponsive-engines">
{{range .UnresponsiveEngines}}<li>{{index . 0}}: {{index . 1}}</li>{{end}}
</ul>
@ -96,4 +55,4 @@
{{end}}
</aside>
</div>
{{end}}
{{end}}