Files
sharper.ly/apps/web/app/components/ui/Card/CardHorizontal.tsx
2026-09-11 02:46:56 +02:00

105 lines
3.0 KiB
TypeScript

/* ============================================================================
Card.Horizontal - Figma 25:781 (720 x 200)
----------------------------------------------------------------------------
The listing row: a 240px image beside the text, image at the inline start so
it moves to the right in Arabic on its own. Stacks to a single column below
the sm breakpoint. Like Card.Content, the whole row is one link.
USAGE
-----
<ul className="flex flex-col gap-lg">
<Card.Horizontal
as="li"
href="/en/resources/governance-systems"
eyebrow="Dialogue report"
title="Strengthening Governance Systems"
excerpt="Our annual deep-dive reports focus on structural coordination,
bringing diverse multi-stakeholder views into policy discussions."
actionLabel="Read full report"
image={{ src: "/media/roundtable.jpg", alt: "" }}
/>
</ul>
// External resource - rendered as a plain anchor, not a client-side route.
<Card.Horizontal
href="https://example.org/report.pdf"
title="Annual review 2026"
actionLabel="Download"
/>
========================================================================= */
import {
CardActionHint,
CardEyebrow,
CardLink,
CardMedia,
CardShell,
cx,
} from "./CardShell";
import type { CardElement, CardImage, CardTitleElement } from "./types";
export interface CardHorizontalProps {
href: string;
title: string;
/** Label of the "Read full report" affordance - required, so Arabic cannot inherit English. */
actionLabel: string;
eyebrow?: string;
excerpt?: string;
image?: CardImage;
as?: CardElement;
titleAs?: CardTitleElement;
className?: string;
}
export function CardHorizontal({
href,
title,
actionLabel,
eyebrow,
excerpt,
image,
as = "article",
titleAs = "h3",
className,
}: CardHorizontalProps) {
const Title = titleAs;
return (
<CardShell
as={as}
linked
className={cx(
"flex-col overflow-hidden sm:flex-row sm:items-stretch",
className,
)}
>
{/* Stacked: the ratio reserves the height. Side by side: a fixed 240px
column that stretches to whatever the text column needs. */}
{image ? (
<CardMedia
image={image}
className="aspect-[6/5] w-full sm:aspect-auto sm:w-60 sm:shrink-0"
/>
) : null}
<div className="flex flex-1 flex-col justify-between gap-md p-lg">
<div className="flex flex-col gap-xs">
{eyebrow ? <CardEyebrow>{eyebrow}</CardEyebrow> : null}
<Title className="sh-card-title body-lg-bold text-text-primary">
<CardLink href={href} className="sh-card-link">
{title}
</CardLink>
</Title>
{excerpt ? (
<p className="body-sm text-text-secondary line-clamp-2">{excerpt}</p>
) : null}
</div>
<CardActionHint label={actionLabel} />
</div>
</CardShell>
);
}