feat(ui): rebuild shared chrome and core browse/read pages

- Header: sticky app-wide, active nav highlighting, data-driven theme
  picker with active swatch, unified SVG icons, deduped login fragment
  (was duplicated verbatim for desktop + mobile), removed stray console.log
- LibrarySwitcher: cleaner sticky bar, SVG action icons, theme-aware
- BookCard: the atomic unit now has a real surface (was floating text
  when wood-paneling was off due to an undefined --wood-border), with
  cohesive shadow + hover lift; reused across dashboard/bookshelf/series
- Dashboard: calmer section headers, snap carousels with SVG chevrons
- Bookshelf: recompose the filter wall into a search toolbar + collapsible
  Filters panel (all 9 filters + save/load/clear preserved), redesigned
  empty state and pagination
- Book detail, series, collections, browse: cohesive cards, SVG action
  icons, uppercase muted labels, theme-aware badges/links
This commit is contained in:
2026-08-05 16:01:09 -04:00
parent 6f271d624f
commit 28e40e302c
19 changed files with 3127 additions and 2593 deletions
+17 -16
View File
@@ -16,32 +16,33 @@ templ BrowseDetail(user User, badgeIcon string, badgeLabel string, title string,
</head>
<body class="theme-{ user.Theme }">
@Header(user, backUrl)
<div class="sticky top-0 z-40 bg-opacity-95 backdrop-blur border-b" style="background-color: var(--bg-primary);">
<div class="w-full px-4 py-3 flex items-center gap-4">
<a href={ backUrl } class="text-sm hover:opacity-80 transition-opacity" style="color: var(--text-secondary); text-decoration: none;">
{ backLabel }
<div class="sticky top-16 z-30 border-b border-line bg-surface/95 backdrop-blur">
<div class="w-full px-4 sm:px-6 lg:px-8 py-2.5">
<a href={ backUrl } class="inline-flex items-center gap-1.5 text-sm font-medium text-content-muted transition-colors hover:text-content">
@Icon("arrow-left", "h-4 w-4")
{ backLabel }
</a>
</div>
</div>
<div class="w-full px-4 py-8">
<div class="flex items-center gap-4 mb-6">
<span class="px-3 py-1 rounded-full text-sm font-semibold" style="background-color: var(--accent); color: white;">
{ badgeIcon } { badgeLabel }
</span>
<h1 class="text-3xl font-bold" style="color: var(--text-primary)">{ title }</h1>
<span class="text-sm" style="color: var(--text-secondary)">{ fmt.Sprintf("%d", len(books)) } books</span>
<div class="w-full px-4 sm:px-6 lg:px-8 py-8">
<div class="mb-6 flex flex-wrap items-center gap-3">
<span class="chip">{ badgeIcon } { badgeLabel }</span>
<h1 class="text-2xl font-bold tracking-tight text-content">{ title }</h1>
<span class="text-sm text-content-muted">{ fmt.Sprintf("%d", len(books)) } books</span>
</div>
if len(books) > 0 {
<div class="grid grid-cols-2 md:grid-cols-4 lg:grid-cols-6 xl:grid-cols-8 gap-4">
<div class="grid grid-cols-2 gap-4 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-6">
for _, book := range books {
@BookCard(book)
}
</div>
} else {
<div class="text-center py-16" style="color: var(--text-secondary)">
<div class="text-6xl mb-4">{ emptyIcon }</div>
<h3 class="text-xl font-semibold mb-2" style="color: var(--text-primary)">No Books Found</h3>
<p>{ emptyMessage }</p>
<div class="flex flex-col items-center justify-center rounded-2xl border border-dashed border-line py-20 text-center">
<div class="mb-3 flex h-14 w-14 items-center justify-center rounded-full bg-surface-hover text-content-muted">
@Icon("book", "h-7 w-7")
</div>
<h3 class="text-lg font-semibold text-content">No Books Found</h3>
<p class="mt-1 text-sm text-content-muted">{ emptyMessage }</p>
</div>
}
</div>