shadimage
kapwa
componentarticlesource: ok · hit

Article

Article card and content components for displaying news, blog posts, and long-form content.

npx shadcn add "https://kapwa-two.vercel.app/r/article.json"
free

Where it goes

blog-list

Tags auto-generated by rules on 9/24/2026 — empty means no evidence, not lack of support.

Dependencies

Bundle weight · light

Registry dependencies

cardbutton

Files1 · source cached 10/14/2026, 7:24:14 AM

src/lib/kapwa/article/index.tsx
import { Card, CardImage, CardContent } from '@kapwa/card';
import { Button } from '@kapwa/button';

type ArticleCardItem = {
  id: number;
  image: string;
  imageAlt: string;
  category: string;
  title: string;
  description: string;
  hasOverlay?: boolean;
};

type ArticleContentSection = {
  id: number;
  type: 'heading' | 'paragraph';
  level?: 1 | 2 | 3;
  content: string;
};

const ArticleCard = ({ article }: { article: ArticleCardItem }) => {
  return (
    <Card>
      {article.hasOverlay ? (
        <div className='relative'>
          <CardImage src={article.image} alt={article.imageAlt} />
          <div className='absolute inset-0 bg-linear-to-t from-black/75 to-transparent flex items-end p-6'>
            <div className='text-white'>
              <span className='inline-block px-2 py-1 text-xs font-medium rounded-sm bg-white/20 mb-2'>
                {article.category}
              </span>
              <h3 className='text-xl font-semibold mb-2'>{article.title}</h3>
              <p className='text-white/80'>{article.description}</p>
            </div>
          </div>
        </div>
      ) : (
        <>
          <CardImage src={article.image} alt={article.imageAlt} />
          <CardContent>
            <span className='inline-block px-2 py-1 text-xs font-medium rounded-sm bg-primary-100 text-primary-800 mb-2'>
              {article.category}
            </span>
            <h3 className='text-xl font-semibold mb-2'>{article.title}</h3>
            <p className='text-gray-800 mb-4'>{article.description}</p>
            <Button variant='link'>Read More</Button>
          </CardContent>
        </>
      )}
    </Card>
  );
};

const ArticleContent = ({
  articleContentTitle,
  articleContent,
}: {
  articleContentTitle: string;
  articleContent: ArticleContentSection[];
}) => {
  return (
    <div>
      <h3 className='font-medium mb-4'>{articleContentTitle}</h3>
      <div className='prose max-w-none'>
        {articleContent.map(section => {
          if (section.type === 'heading') {
            const HeadingTag =
              `h${section.level}` as keyof JSX.IntrinsicElements;
            const headingClass =
              section.level === 1
                ? 'text-3xl font-bold mb-4'
                : section.level === 2
                  ? 'text-2xl font-semibold mb-3'
                  : 'text-xl font-semibold mb-2';
            return (
              <HeadingTag key={section.id} className={headingClass}>
                {section.content}
              </HeadingTag>
            );
          }
          return (
            <p key={section.id} className='text-gray-800 mb-4'>
              {section.content}
            </p>
          );
        })}
      </div>
    </div>
  );
};

export { ArticleCard, ArticleContent };
export type { ArticleCardItem, ArticleContentSection };

More from kapwa

Browse all components in this library →