import type { ComponentType } from 'react'; import { Link } from 'react-router-dom'; import { ArrowRight } from 'lucide-react'; import type { DemoMeta } from '@/lib/demo-kit/types'; import { VERTICAL_LABELS } from '@/lib/demo-kit/registry'; import { Badge } from '@/components/ui/badge'; import { DemoIcon } from '@/components/site/DemoIcon'; import { cn } from '@/lib/utils'; export interface DemoCardProps { meta: DemoMeta; /** Defaults to the canonical demo route. */ href?: string; /** * The demo's OWN board, drawn compact, as the thumbnail. A screenshot would * go stale the first time the board changed and nobody would notice; this * cannot, because it is the same component the demo page renders. */ Surface?: ComponentType<{ state: T; compact?: boolean }>; /** A representative state for the thumbnail — usually a solved board. */ thumbnailState?: T; className?: string; } export function DemoCard({ meta, href, Surface, thumbnailState, className }: DemoCardProps) { const to = href ?? `/demos/${meta.slug}`; const isSpec = meta.status === 'spec'; const showSurface = Surface !== undefined && thumbnailState !== undefined; return (

{/* Stretched link: the whole card is the hit target, but there is still exactly ONE link in the accessibility tree for it. */} {meta.title}

{meta.tagline}

{isSpec ? ( Spec ) : null}
{showSurface ? (
{/* Decorative: the title and tagline already name the demo, and a board with no run behind it is not information. */}
) : null}
For
{meta.persona}
Vertical
{VERTICAL_LABELS[meta.vertical]}

Reward: {meta.rewardLine}

{isSpec ? 'Read the specification' : 'Open the demo'}

); }