slice 3: FeatureStoryHero — single cinematic hero card above bento
New component src/components/FeatureStoryHero.tsx renders a wide 21:9 hero of the top-pinned featured article (same query shape as FeaturedBentoFromDb). Wired into home-page/page.tsx above the bento. Teal accent (#5B7C8D) used intentionally — Slice 4 will sweep the rest of the site to match. Bento remains intact and may share its top article with the hero; that's the magazine "lead story emphasized" treatment, not a bug. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -3,6 +3,7 @@ import type { Metadata } from "next";
|
||||
import Header from "@/components/Header";
|
||||
import Footer from "@/components/Footer";
|
||||
import FeaturedBentoFromDb from '@/components/FeaturedBentoFromDb';
|
||||
import FeatureStoryHero from '@/components/FeatureStoryHero';
|
||||
import FeaturedCarouselServer from '@/components/FeaturedCarouselServer';
|
||||
import TrendsSidebar from '@/components/TrendsSidebar';
|
||||
import LiveWireTicker from '@/components/LiveWireTicker';
|
||||
@@ -156,6 +157,13 @@ export default function HomePage() {
|
||||
</Suspense>
|
||||
</div>
|
||||
|
||||
{/* Top feature story hero — single prominent card above the bento */}
|
||||
<ScrollRevealSection className="section-enter section-enter-2">
|
||||
<Suspense fallback={<BentoSkeleton />}>
|
||||
<FeatureStoryHero />
|
||||
</Suspense>
|
||||
</ScrollRevealSection>
|
||||
|
||||
{/* Featured articles bento */}
|
||||
<ScrollRevealSection className="section-enter section-enter-3">
|
||||
<Suspense fallback={<BentoSkeleton />}>
|
||||
|
||||
105
src/components/FeatureStoryHero.tsx
Normal file
105
src/components/FeatureStoryHero.tsx
Normal file
@@ -0,0 +1,105 @@
|
||||
import Link from "next/link";
|
||||
import { createClient } from "@supabase/supabase-js";
|
||||
import { rewriteLegacyImageUrl } from "@/lib/legacy-image";
|
||||
|
||||
export const dynamic = "force-dynamic";
|
||||
|
||||
interface Row {
|
||||
wp_slug: string;
|
||||
title: string;
|
||||
excerpt: string | null;
|
||||
category: string | null;
|
||||
featured_image: string | null;
|
||||
featured_image_alt: string | null;
|
||||
author_name: string | null;
|
||||
wp_published_at: string | null;
|
||||
}
|
||||
|
||||
export default async function FeatureStoryHero() {
|
||||
const sb = createClient(
|
||||
process.env.NEXT_PUBLIC_SUPABASE_URL!,
|
||||
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
|
||||
{
|
||||
db: { schema: (process.env.NEXT_PUBLIC_SUPABASE_SCHEMA || "bb") as "public" },
|
||||
auth: { persistSession: false },
|
||||
}
|
||||
);
|
||||
|
||||
const { data } = await sb
|
||||
.from("wp_imported_posts")
|
||||
.select(
|
||||
"wp_slug, title, excerpt, category, featured_image, featured_image_alt, author_name, wp_published_at",
|
||||
)
|
||||
.eq("status", "published")
|
||||
.ilike("category", "featured")
|
||||
.order("featured", { ascending: false })
|
||||
.order("wp_published_at", { ascending: false, nullsFirst: false })
|
||||
.limit(1);
|
||||
|
||||
const story = data?.[0] as Row | undefined;
|
||||
if (!story) return null;
|
||||
|
||||
const image = story.featured_image
|
||||
? rewriteLegacyImageUrl(story.featured_image)
|
||||
: "/assets/images/article-placeholder.svg";
|
||||
|
||||
return (
|
||||
<section
|
||||
className="max-w-container mx-auto px-4 mt-6 mb-2"
|
||||
aria-label="Top feature story"
|
||||
>
|
||||
<Link
|
||||
href={`/news/${story.wp_slug}`}
|
||||
className="group block relative overflow-hidden rounded border border-[#252525] bg-[#0b0f17] hover:border-[#5B7C8D] transition-colors"
|
||||
>
|
||||
<div className="relative aspect-[21/9] md:aspect-[21/8] bg-[#111]">
|
||||
<img
|
||||
src={image}
|
||||
alt={story.featured_image_alt || story.title}
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
<div className="absolute inset-0 bg-gradient-to-t from-black via-black/65 to-transparent" />
|
||||
|
||||
<span className="absolute top-4 left-4 inline-flex items-center gap-1.5 bg-[#5B7C8D] text-white px-3 py-1 rounded font-mono text-[11px] uppercase tracking-widest font-bold shadow-md">
|
||||
<svg width="11" height="11" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
|
||||
<path d="M12 2l2.6 7.6H22l-6.2 4.5L18.4 22 12 17.3 5.6 22l2.6-7.9L2 9.6h7.4z" />
|
||||
</svg>
|
||||
Feature Story
|
||||
</span>
|
||||
|
||||
<div className="absolute bottom-0 left-0 right-0 p-6 md:p-10">
|
||||
{story.category && (
|
||||
<div className="text-[10px] font-mono uppercase tracking-widest text-[#5B7C8D] mb-2">
|
||||
{story.category}
|
||||
</div>
|
||||
)}
|
||||
<h2 className="font-serif text-2xl md:text-4xl lg:text-5xl text-white leading-tight max-w-3xl group-hover:text-[#C8D9E6] transition-colors">
|
||||
{story.title}
|
||||
</h2>
|
||||
{story.excerpt && (
|
||||
<p className="font-serif text-base md:text-lg text-[#cbd5e1] mt-3 max-w-2xl line-clamp-2">
|
||||
{story.excerpt}
|
||||
</p>
|
||||
)}
|
||||
<div className="mt-4 flex items-center gap-3 text-[11px] font-mono text-[#9ca3af]">
|
||||
<span>{story.author_name || "Staff Reporter"}</span>
|
||||
{story.wp_published_at && (
|
||||
<>
|
||||
<span>·</span>
|
||||
<span>
|
||||
{new Date(story.wp_published_at).toLocaleDateString("en-US", {
|
||||
month: "short",
|
||||
day: "numeric",
|
||||
year: "numeric",
|
||||
})}
|
||||
</span>
|
||||
</>
|
||||
)}
|
||||
<span aria-hidden="true" className="text-[#5B7C8D] ml-2">→ Read full story</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user