Style Donor/Activist Guide buttons as campaign-style cover cards

The two Help page guide buttons now mirror CampaignCard's structure: a
16:9 cover image on top with a soft hover scale, a rounded icon badge
floating in the top-left corner, and title + description in the body.

Donor Guide reuses the World Liberty Congress hero image already used
on the Donor Guide page; Activist Guide reuses the 'Raised Fists' image
that opens the Activist Guide's hero gallery, so each button visually
previews its destination.
This commit is contained in:
mkfain
2026-05-21 13:49:55 -05:00
parent 4c32b93f5e
commit 4d827e01f4
+56 -18
View File
@@ -1,12 +1,25 @@
import { useSeoMeta } from '@unhead/react';
import { ChevronRight, HandHeart, HelpCircle, Megaphone, Shield } from 'lucide-react';
import { HandHeart, HelpCircle, Megaphone, Shield } from 'lucide-react';
import { Link } from 'react-router-dom';
import { Card } from '@/components/ui/card';
import { useAppContext } from '@/hooks/useAppContext';
import { useLayoutOptions } from '@/contexts/LayoutContext';
import { PageHeader } from '@/components/PageHeader';
import { TeamSoapboxCard } from '@/components/TeamSoapboxCard';
import { HelpFAQSection } from '@/components/HelpFAQSection';
import { DEFAULT_ACTION_COVERS } from '@/lib/defaultActionCovers';
import { cn } from '@/lib/utils';
/** Cover image used on the Donor Guide button. Mirrors the Donor Guide hero. */
const DONOR_GUIDE_COVER = '/hero/wlc-1.webp';
/**
* Cover image used on the Activist Guide button. Picks the same "Raised
* Fists" image that opens the Activist Guide's hero gallery, so the button
* visually previews the destination.
*/
const ACTIVIST_GUIDE_COVER = DEFAULT_ACTION_COVERS[0].url;
export function HelpPage() {
const { config } = useAppContext();
@@ -21,19 +34,21 @@ export function HelpPage() {
<main className="min-h-screen pb-16 sidebar:pb-0">
<PageHeader title="Help" icon={<HelpCircle className="size-5" />} />
{/* Two large guide buttons */}
<div className="px-4 pt-4 grid gap-3 sm:grid-cols-2">
<GuideButton
{/* Two large guide cards */}
<div className="px-4 pt-4 grid gap-4 sm:grid-cols-2">
<GuideCard
to="/help/donors"
icon={<HandHeart className="size-6" />}
icon={<HandHeart className="size-5" />}
title="Donor Guide"
description="How to support activists privately and safely."
cover={DONOR_GUIDE_COVER}
/>
<GuideButton
<GuideCard
to="/help/activists"
icon={<Megaphone className="size-6" />}
icon={<Megaphone className="size-5" />}
title="Activist Guide"
description="Receiving donations and cashing out privately."
cover={ACTIVIST_GUIDE_COVER}
/>
</div>
@@ -65,27 +80,50 @@ export function HelpPage() {
);
}
interface GuideButtonProps {
interface GuideCardProps {
to: string;
icon: React.ReactNode;
title: string;
description: string;
cover: string;
className?: string;
}
function GuideButton({ to, icon, title, description }: GuideButtonProps) {
/**
* Visually echoes `CampaignCard` so the Help page's two long-form guides
* read like content cards rather than utility buttons.
*/
function GuideCard({ to, icon, title, description, cover, className }: GuideCardProps) {
return (
<Link
to={to}
className="group flex items-center gap-4 rounded-xl border bg-card p-4 text-left shadow-sm transition-colors hover:bg-secondary focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
className={cn(
'group block rounded-xl overflow-hidden focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-2 focus-visible:ring-offset-background motion-safe:transition-transform motion-safe:duration-200 motion-safe:hover:-translate-y-0.5',
className,
)}
>
<div className="flex size-12 shrink-0 items-center justify-center rounded-full bg-primary/10 text-primary">
{icon}
</div>
<div className="flex-1 min-w-0">
<p className="font-semibold leading-snug">{title}</p>
<p className="text-sm text-muted-foreground leading-snug">{description}</p>
</div>
<ChevronRight className="size-5 shrink-0 text-muted-foreground transition-transform group-hover:translate-x-0.5" />
<Card className="overflow-hidden border-border/70 shadow-sm motion-safe:transition-shadow motion-safe:duration-200 group-hover:shadow-lg h-full flex flex-col">
{/* Cover image */}
<div className="relative w-full aspect-[16/9] bg-gradient-to-br from-primary/15 via-primary/5 to-secondary">
<img
src={cover}
alt=""
loading="lazy"
className="absolute inset-0 size-full object-cover motion-safe:transition-transform motion-safe:duration-300 group-hover:scale-[1.02]"
/>
{/* Bottom gradient for legibility of the icon badge */}
<div className="absolute inset-0 bg-gradient-to-t from-background/40 via-transparent to-transparent pointer-events-none" />
<div className="absolute top-3 left-3 flex size-10 items-center justify-center rounded-full bg-background/90 backdrop-blur text-primary shadow-sm">
{icon}
</div>
</div>
{/* Body */}
<div className="flex flex-col gap-1.5 p-5 flex-1">
<h3 className="font-bold leading-tight tracking-tight text-lg">{title}</h3>
<p className="text-sm text-muted-foreground leading-snug">{description}</p>
</div>
</Card>
</Link>
);
}