Cards
CTA Card
A responsive CTA card with an image, text, and an email subscription form.
Installation
CLI
Installation via the CLI is coming soon. For now, please follow the manual installation instructions below.
Manual
No external dependencies are required. Copy and paste the following code into components/ui/cta-card.tsx
.
'use client';
import { cn } from '@/lib/utils';
import Image from 'next/image';
import { Button } from '@/components/ui/button';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import { useState } from 'react';
interface CTACardProps {
/** The main headline or title for the CTA. */
title: string;
/** A short description supporting the title. */
description: string;
/** The source URL for the image. */
imageSrc: string;
/** Alt text for the image, for accessibility. */
imageAlt?: string;
/** Placeholder text for the email input field. */
inputPlaceholder?: string;
/** Text displayed on the submission button. */
buttonText?: string;
/** Callback function executed when the form is submitted. */
onSubmit: (email: string) => void;
/** Optional additional class names to apply to the root card element for custom styling. */
className?: string;
}
/**
* A responsive Call-to-Action (CTA) card component.
* It displays an image alongside a title, description, and an email subscription form.
* Designed to be theme-aware and fully customizable through props.
*/
export function CTACard({
title,
description,
imageSrc,
imageAlt = 'Promotional image',
inputPlaceholder = 'name@email.com',
buttonText = 'Subscribe',
onSubmit,
className,
}: CTACardProps) {
const [email, setEmail] = useState('');
const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();
onSubmit(email);
setEmail('');
};
return (
<Card
className={cn(
'w-full max-w-4xl overflow-hidden p-3 border rounded-3xl',
className
)}
>
{/*
DECISION: A 16-column grid is used on desktop to allow for fine-grained,
asymmetrical layouts (e.g., the 7/9 split used here) that a standard
12-column grid might not accommodate as cleanly.
*/}
<div className='grid grid-cols-1 md:grid-cols-16 gap-3'>
<div className='relative h-64 md:h-auto rounded-2xl md:col-span-7'>
<Image
src={imageSrc}
alt={imageAlt}
fill
className='object-cover rounded-2xl'
/>
</div>
<div className='flex flex-col w-full justify-start items-start rounded-2xl border md:col-span-9 p-6 md:p-8 gap-8 md:gap-16 lg:gap-20 bg-secondary'>
<CardHeader className='w-full p-0'>
<CardTitle className='text-2xl md:text-3xl font-bold'>
{title}
</CardTitle>
</CardHeader>
<CardContent className='flex w-full flex-col gap-2 p-0'>
<Label className='text-muted-foreground'>{description}</Label>
<form
onSubmit={handleSubmit}
className='mt-2 flex w-full flex-col sm:flex-row sm:space-x-2 space-y-2 sm:space-y-0'
>
<Input
type='email'
placeholder={inputPlaceholder}
value={email}
onChange={(e) => setEmail(e.target.value)}
className='flex-1 text-base'
aria-label='Email for newsletter'
required
/>
<Button type='submit'>{buttonText}</Button>
</form>
</CardContent>
</div>
</div>
</Card>
);
}
Usage
Import the component and provide the required props to render the CTA card.
import { CTACard } from '@/components/ui/cta-card';
export default function MyPage() {
const handleSubscription = (email: string) => {
// Replace with your actual subscription logic
alert(`Subscribing with: ${email}`);
};
return (
<div className='w-full p-4 flex flex-col items-center gap-8 bg-background'>
{/* Example: Customized Usage */}
<CTACard
title='Unlock Exclusive Content'
description='Become a premium member to access our full library of resources and tools.'
imageSrc='https://images.unsplash.com/photo-1611974789855-9c2a0a7236a3'
imageAlt='Abstract financial graph'
inputPlaceholder='your-best@email.com'
buttonText='Get Access Now'
onSubmit={handleSubscription}
className='border-primary'
/>
</div>
);
}
Props
Prop | Type | Default | Description |
---|---|---|---|
title | string | Required | The main headline or title for the CTA. |
description | string | Required | A short description supporting the title. |
imageSrc | string | Required | The source URL for the image. |
imageAlt | string | 'Promotional image' | Alt text for the image, for accessibility. |
inputPlaceholder | string | 'name@email.com' | Placeholder text for the email input field. |
buttonText | string | 'Subscribe' | Text displayed on the submission button. |
onSubmit | (email: string) => void | Required | Callback function executed when the form is submitted. |
className | string | — | Optional additional class names to apply to the root card element for custom styling. |