Add list styles: Podium, Spotlight, Grid, List

- Each list picks a style in List settings (new lists default to Podium);
  existing lists keep Grid or List
- Podium: top 3 on medal-colored steps (#1 center and raised), the rest
  behind a native <details> "Show N more"; stacks #1 over #2/#3 on phones
- Spotlight: #1 as a feature card (art, why-note or description, play
  button), the rest as a numbered list
- Visitors can switch styles on a list page (?style=), preserved across
  pages; later pages fall back to a plain list since they don't start at #1
- Entries' detail data now lives on a wrapper per list, so featured and
  collapsed entries all open the detail view

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
cdnutterandClaude Opus 5.5 committed 2026-09-25 11:18:44 -07:00
1 parent b09533cc00
commit ddf2157e63
15 files changed
+286 -65

No files matched your search

+29 -3
View File
@@ -24,7 +24,7 @@ type List struct {
MaxItems int `json:"maxItems"`
Position int `json:"position"`
Visible bool `json:"visible"`
Layout string `json:"layout"` // "grid" or "rows"
Layout string `json:"layout"` // a ListStyles key, e.g. "podium"
Art string `json:"art"` // "", "poster" or "square"; "" = category default
ItemCount int `json:"itemCount"`
UpdatedAt int64 `json:"updatedAt"`
@@ -97,8 +97,8 @@ func (l *List) normalize() error {
if l.MaxItems < 1 || l.MaxItems > MaxListSize {
return invalid("List size must be between 1 and %d", MaxListSize)
}
if l.Layout != "rows" {
l.Layout = "grid"
if _, ok := StyleByKey(l.Layout); !ok {
l.Layout = DefaultStyle
}
if l.Art != "poster" && l.Art != "square" {
l.Art = ""
@@ -245,3 +245,29 @@ func (l List) Aspect() string {
c, _ := CategoryByKey(l.Category)
return c.Aspect
}
// ListStyle is a way of presenting a ranked list.
type ListStyle struct {
Key string `json:"key"`
Label string `json:"label"`
Icon string `json:"icon"`
Description string `json:"description"`
}
var ListStyles = []ListStyle{
{"podium", "Podium", "podium", "Top 3 on a medal podium, the rest behind “Show more”."},
{"spotlight", "Spotlight", "spotlight", "#1 as a big feature, the rest as a ranked list."},
{"grid", "Grid", "grid", "Every entry as artwork in a grid."},
{"rows", "List", "rows", "A compact numbered list with notes."},
}
const DefaultStyle = "podium"
func StyleByKey(key string) (ListStyle, bool) {
for _, s := range ListStyles {
if s.Key == key {
return s, true
}
}
return ListStyle{}, false
}