Glrk UI

Card

A simplified wrapper for Shadcn Card with flexible option handling and automatic type conversion

Card Title
Card Description

Lorem ipsum dolor sit amet consectetur adipisicing elit. Dolorum, voluptas nisi natus dolores debitis facere a nam? Libero itaque enim ullam ab iste reiciendis, nemo aliquid ratione saepe non dicta.

Installation

npx shadcn@latest add @glrk-ui/card

If you haven't set up the prerequisites yet, check out Prerequest section.

Copy and paste the following code into shadcn card component.

ui/card.tsx
type CardWrapperProps = {
  title?: React.ReactNode
  description?: React.ReactNode
  children?: React.ReactNode
  footer?: React.ReactNode
  actions?: React.ReactNode
  wrapperCls?: string
  headerCls?: string
  titleCls?: string
  descriptionCls?: string
  contentCls?: string
  footerCls?: string
}

function CardWrapper({
  title,
  description,
  children,
  footer,
  actions,
  wrapperCls,
  headerCls,
  titleCls,
  descriptionCls,
  contentCls,
  footerCls,
}: CardWrapperProps) {
  return (
    <Card className={wrapperCls}>
      <CardHeader className={headerCls}>
        {title && <CardTitle className={titleCls}>{title}</CardTitle>}
        {description && <CardDescription className={descriptionCls}>{description}</CardDescription>}

        {actions}
      </CardHeader>

      {
        children &&
        <CardContent className={contentCls}>
          {children}
        </CardContent>
      }

      {footer
        ? typeof footer === "string"
          ? <CardFooter className={footerCls}>{footer}</CardFooter>
          : footer
        : null
      }
    </Card>
  )
}
export {
  Card,
  CardHeader,
  CardFooter,
  CardTitle,
  CardAction,
  CardDescription,
  CardContent,
  CardWrapper,
}

card.tsx

Copy and paste the following code into your project.

ui/card.tsx
import * as React from "react"

import { cn } from "@/lib/utils"

function Card({ className, ...props }: React.ComponentProps<"div">) {
  return (
    <div
      data-slot="card"
      className={cn(
        "bg-card text-card-foreground flex flex-col gap-6 rounded-xl border py-6 shadow-sm",
        className
      )}
      {...props}
    />
  )
}

function CardHeader({ className, ...props }: React.ComponentProps<"div">) {
  return (
    <div
      data-slot="card-header"
      className={cn(
        "@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-2 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6",
        className
      )}
      {...props}
    />
  )
}

function CardTitle({ className, ...props }: React.ComponentProps<"div">) {
  return (
    <div
      data-slot="card-title"
      className={cn("leading-none font-semibold", className)}
      {...props}
    />
  )
}

function CardDescription({ className, ...props }: React.ComponentProps<"div">) {
  return (
    <div
      data-slot="card-description"
      className={cn("text-muted-foreground text-sm", className)}
      {...props}
    />
  )
}

function CardAction({ className, ...props }: React.ComponentProps<"div">) {
  return (
    <div
      data-slot="card-action"
      className={cn(
        "col-start-2 row-span-2 row-start-1 self-start justify-self-end",
        className
      )}
      {...props}
    />
  )
}

function CardContent({ className, ...props }: React.ComponentProps<"div">) {
  return (
    <div
      data-slot="card-content"
      className={cn("px-6", className)}
      {...props}
    />
  )
}

function CardFooter({ className, ...props }: React.ComponentProps<"div">) {
  return (
    <div
      data-slot="card-footer"
      className={cn("flex items-center px-6 [.border-t]:pt-6", className)}
      {...props}
    />
  )
}

type CardWrapperProps = {
  title?: React.ReactNode
  description?: React.ReactNode
  children?: React.ReactNode
  footer?: React.ReactNode
  actions?: React.ReactNode
  wrapperCls?: string
  headerCls?: string
  titleCls?: string
  descriptionCls?: string
  contentCls?: string
  footerCls?: string
}

function CardWrapper({
  title,
  description,
  children,
  footer,
  actions,
  wrapperCls,
  headerCls,
  titleCls,
  descriptionCls,
  contentCls,
  footerCls,
}: CardWrapperProps) {
  return (
    <Card className={wrapperCls}>
      <CardHeader className={headerCls}>
        {title && <CardTitle className={titleCls}>{title}</CardTitle>}
        {description && <CardDescription className={descriptionCls}>{description}</CardDescription>}

        {actions}
      </CardHeader>

      {
        children &&
        <CardContent className={contentCls}>
          {children}
        </CardContent>
      }

      {footer
        ? typeof footer === "string"
          ? <CardFooter className={footerCls}>{footer}</CardFooter>
          : footer
        : null
      }
    </Card>
  )
}

export {
  Card,
  CardHeader,
  CardFooter,
  CardTitle,
  CardAction,
  CardDescription,
  CardContent,
  CardWrapper,
}

Done

You can now use CardWrapper

Usage

Basic

import { CardWrapper } from "@/components/ui/card";

export function Basic() {
  return (
    <CardWrapper
      title="Card Title"
      description="Card Description"
      footer="Card Footer"
    >
      <p>Card Content</p>
    </CardWrapper>
  )
}

Reference

CardWrapper

Prop

Type