Episodes page: highlights as one tab per series with show-all preview; v2.9.3
Publish Container / build (push) Successful in 14m56s

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
nmemmertandClaude Sonnet 5.5 committed 2026-10-08 11:24:19 -04:00
1 parent 1eb0dc1296
commit 810097bb8e
3 files changed
+57 -21

No files matched your search

+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "siteforge",
"private": true,
"version": "2.9.2",
"version": "2.9.3",
"type": "module",
"scripts": {
"dev": "vite",
+21 -8
View File
@@ -13126,15 +13126,28 @@
.study-status-badge.planned { background: rgba(255, 255, 255, 0.05); color: #9a9486; border: 1px solid rgba(255, 255, 255, 0.15); }
.study-status-badge.complete { background: rgba(120, 170, 130, 0.16); color: #8fc79b; border: 1px solid rgba(120, 170, 130, 0.45); }
.highlight-group + .highlight-group { margin-top: 2.5rem; }
.highlight-group-title {
margin: 0 0 1rem;
font-size: 1.05rem;
letter-spacing: 0.04em;
color: #c9a84c;
border-bottom: 1px solid rgba(201, 168, 76, 0.22);
padding-bottom: 0.5rem;
.highlight-tabs {
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: 0.6rem;
margin: 0 0 1.75rem;
}
.highlight-tab {
background: transparent;
color: #b9b09b;
border: 1px solid rgba(201, 168, 76, 0.3);
border-radius: 999px;
padding: 0.5rem 1.1rem;
font: inherit;
font-size: 0.9rem;
cursor: pointer;
transition: border-color 200ms, color 200ms, background 200ms;
}
.highlight-tab:hover { color: #f0ead8; border-color: rgba(201, 168, 76, 0.6); }
.highlight-tab--active { background: #c9a84c; border-color: #c9a84c; color: #0a0a08; }
.highlight-tab-count { opacity: 0.7; font-size: 0.8em; margin-left: 0.25rem; }
.highlight-more { display: flex; justify-content: center; margin-top: 1.5rem; }
.admin-tabs--page { margin: 0 0 1.5rem; padding-bottom: 1rem; border-bottom: 1px solid #2a2518; }
.admin-tabs--page .admin-tab { font-size: 0.8rem; padding: 0.45rem 0.9rem; }
+35 -12
View File
@@ -743,20 +743,25 @@ function HighlightCard({ link }: { link: PodcastFeaturedLink }) {
return <Link to={`/episodes/${link.id}`} className="episode-card">{inner}</Link>
}
// Highlights are grouped by the series they belong to, so each book being studied gets its own row.
const HIGHLIGHTS_PREVIEW_COUNT = 6
// Highlights are grouped by the series they belong to, one tab per series, so a long series
// never pushes the others down the page. Each tab previews the newest few with a "show all" option.
function PodcastHighlightsSection({ content }: { content: SiteContent }) {
const [activeKey, setActiveKey] = useState('')
const [expanded, setExpanded] = useState<Record<string, boolean>>({})
const links = [...(content.podcastFeaturedLinks ?? [])].reverse()
if (links.length === 0) return null
const laneOf = (link: PodcastFeaturedLink) => (link.lane === 'second' ? 'second' : link.lane === 'general' ? 'general' : 'primary')
const secondTitle = content.secondSeries?.title?.trim()
const groups = [
{ key: 'primary', title: content.seriesTitle, links: links.filter(link => laneOf(link) === 'primary') },
{ key: 'second', title: secondTitle, links: links.filter(link => laneOf(link) === 'second') },
{ key: 'second', title: content.secondSeries?.title?.trim() || 'Second Series', links: links.filter(link => laneOf(link) === 'second') },
{ key: 'general', title: 'More Highlights', links: links.filter(link => laneOf(link) === 'general') },
].filter(group => group.links.length > 0)
// Only label the rows when there is more than one to tell apart
const showGroupTitles = groups.length > 1
const active = groups.find(group => group.key === activeKey) ?? groups[0]
const isExpanded = expanded[active.key] === true
const shown = isExpanded ? active.links : active.links.slice(0, HIGHLIGHTS_PREVIEW_COUNT)
return (
<section className="section-resources" aria-label="Podcast highlights">
@@ -765,14 +770,32 @@ function PodcastHighlightsSection({ content }: { content: SiteContent }) {
<span className="ornament">✦</span> Podcast Highlights{' '}
<span className="ornament">✦</span>
</h2>
{groups.map(group => (
<div key={group.key} className="highlight-group">
{showGroupTitles && <h3 className="highlight-group-title">{group.title}</h3>}
<div className="episode-list">
{group.links.map(link => <HighlightCard key={link.id} link={link} />)}
</div>
{groups.length > 1 && (
<div className="highlight-tabs" role="tablist" aria-label="Highlights by series">
{groups.map(group => (
<button
key={group.key}
type="button"
role="tab"
aria-selected={group.key === active.key}
className={`highlight-tab${group.key === active.key ? ' highlight-tab--active' : ''}`}
onClick={() => setActiveKey(group.key)}
>
{group.title} <span className="highlight-tab-count">{group.links.length}</span>
</button>
))}
</div>
))}
)}
<div className="episode-list">
{shown.map(link => <HighlightCard key={link.id} link={link} />)}
</div>
{active.links.length > HIGHLIGHTS_PREVIEW_COUNT && (
<div className="highlight-more">
<button type="button" className="btn-secondary" onClick={() => setExpanded(prev => ({ ...prev, [active.key]: !isExpanded }))}>
{isExpanded ? 'Show fewer' : `Show all ${active.links.length} highlights`}
</button>
</div>
)}
</div>
</section>
)