sticky stacking cards on scroll + kill border white space

STACKING EFFECT:
- Cards use position: sticky with increasing top offset (72-120px)
- z-index layering (10-40) so later cards stack on top
- pb-36 between cards for scroll breathing room
- will-change-transform for smooth compositing

WHITE SPACE FIX:
- Removed border border-gray-200 (was creating visible white gap)
- Replaced with shadow-[0_2px_8px_rgba(0,0,0,0.08)] for depth
- Switched from CSS Grid to flexbox (flex-row/flex-row-reverse)
- Image fills full card height via flexbox stretch

LAYOUT:
- md:w-7/12 for image, md:w-5/12 for text
- min-h-[400px] on desktop for substantial card presence
- Alternating image left/right preserved for visual rhythm
This commit is contained in:
2026-03-03 22:55:03 +08:00
parent 233e9320b5
commit dc1253af33
3 changed files with 46 additions and 44 deletions

View File

@@ -157,39 +157,39 @@ export default function HomePage() {
We built the missing system between “I'll donate” and the money arriving. We built the missing system between “I'll donate” and the money arriving.
</p> </p>
{/* Alternating image/text rows — gap-px between */} {/* Stacking cards — sticky scroll effect */}
<div className="flex flex-col gap-px bg-gray-200 mt-14"> <div className="mt-14">
{PERSONAS.map((p, i) => { {PERSONAS.map((p, i) => {
const imgFirst = i % 2 === 0 const imgFirst = i % 2 === 0
return ( return (
<Link <div key={p.slug} className={i < PERSONAS.length - 1 ? "pb-36" : ""}>
key={p.slug}
href={`/for/${p.slug}`}
className="group bg-white block"
>
<div className="grid md:grid-cols-12 items-stretch">
{/* Image — 7 cols, cinematic */}
<div <div
className={`md:col-span-7 ${!imgFirst ? "md:order-2" : ""} relative overflow-hidden aspect-[2/1]`} className="sticky will-change-transform"
style={{ top: `${72 + i * 16}px`, zIndex: 10 + i * 10 }}
> >
<Link
href={`/for/${p.slug}`}
className="group block bg-white shadow-[0_2px_8px_rgba(0,0,0,0.08)] overflow-hidden"
>
<div className={`flex flex-col ${imgFirst ? "md:flex-row" : "md:flex-row-reverse"}`}>
{/* Image — ~58%, fills full card height */}
<div className="md:w-7/12 relative overflow-hidden min-h-[220px] md:min-h-[400px]">
<Image <Image
src={p.image} src={p.image}
alt={p.scenario} alt={p.scenario}
fill fill
className="object-cover transition-opacity duration-300 group-hover:opacity-90" className="object-cover"
sizes="(max-width: 768px) 100vw, 58vw" sizes="(max-width: 768px) 100vw, 58vw"
/> />
</div> </div>
{/* Text — 5 cols */} {/* Text — ~42% */}
<div <div className="md:w-5/12 p-8 md:p-12 lg:p-14 flex flex-col justify-center">
className={`md:col-span-5 ${!imgFirst ? "md:order-1" : ""} p-8 md:p-12 lg:p-14 flex flex-col justify-center`}
>
<h3 className="text-lg md:text-xl font-black text-gray-900 group-hover:text-promise-blue transition-colors"> <h3 className="text-lg md:text-xl font-black text-gray-900 group-hover:text-promise-blue transition-colors">
{p.scenario} {p.scenario}
</h3> </h3>
{/* Pain stats — the gap, visible contrast */} {/* Pain stats */}
<div className="mt-5 space-y-1"> <div className="mt-5 space-y-1">
<p className="text-xl md:text-2xl lg:text-3xl font-black text-gray-400 tracking-tight leading-tight"> <p className="text-xl md:text-2xl lg:text-3xl font-black text-gray-400 tracking-tight leading-tight">
{p.stat1} {p.stat1}
@@ -209,6 +209,8 @@ export default function HomePage() {
</div> </div>
</div> </div>
</Link> </Link>
</div>
</div>
) )
})} })}
</div> </div>

BIN
stack1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 468 KiB

BIN
stack2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 655 KiB