Card
Card Component, a flexible and stylish container for displaying content
The Card component is a flexible container used for surfaces such as:
- cards
- modals
- panels
- sections
- dashboards
It supports themes, font styling, and automatic theme propagation to nested components like Buttons.
TL;DR: The Card component provides a versatile and visually appealing way to present content in a structured format. It is ideal for grouping related information, highlighting key details, and enhancing the overall user interface.
Installation
Using CLI
if you're using twjlabs/ui cli, it should be pretty easy for you
npx @twjlabs/ui add cardpnpm dlx @twjlabs/ui add cardManual Installation
if you're not using the cli, you would need to install it manually,
Now make sure you have twj-lib folder with necessary utility functions and types in your project. If not, you can refer here to set it up.
Finally,
copy and paste the following code in your components/ui folder
"use client"
import React from "react";
import type { Theme, TWJComponentsProps } from "@/twj-lib/types";
import { cn } from "@/twj-lib/tw";
import { fontApplier } from "@/twj-lib/font-applier";
import { useTheme, ThemeProvider } from "@/contexts/ui-theme-context";
// ----------------------------------------------------
// 🔵 Card Component
// ----------------------------------------------------
interface CardProps extends TWJComponentsProps {
children?: React.ReactNode;
className?: string;
}
export const Card = ({ theme, children, className }: CardProps) => {
const { theme: contextTheme } = useTheme();
const [mounted, setMounted] = React.useState(false);
React.useEffect(() => {
setMounted(true);
}, []);
const activeTheme = theme || contextTheme || "modern";
const appliedTheme = mounted ? activeTheme : "modern";
const fontClass = fontApplier(appliedTheme);
const themeClass = `theme-${appliedTheme}`;
const childrenWithTheme = React.Children.map(children, (child) => {
if (React.isValidElement(child)) {
return React.cloneElement(child as React.ReactElement<any>, {
theme: appliedTheme
});
}
return child;
});
return (
<ThemeProvider initialTheme={appliedTheme} key={appliedTheme}>
<div
data-theme={appliedTheme}
className={cn(
themeClass,
fontClass,
// --- BASE STYLES ---
'rounded-theme font-semibold transition-all duration-200',
'p-4 w-full focus:outline-none',
// --- EXPLICIT DARK MODE SWITCHING ---
// Light Mode Class // Dark Mode Class
'bg-card dark:bg-card-dark',
'border border-border dark:border-border-dark',
'text-card-foreground dark:text-card-foreground-dark',
// --- BRUTALIST OVERRIDES ---
appliedTheme === 'brutalist' && [
'uppercase tracking-wider',
// Explicitly swap border color for brutalist (Black -> White)
'border-2 border-black dark:border-white',
// Explicitly swap shadow color (Black Shadow -> White Shadow)
'shadow-[4px_4px_0px_0px_rgba(0,0,0,1)] dark:shadow-[4px_4px_0px_0px_rgba(255,255,255,1)]',
],
className
)}
>
{childrenWithTheme}
</div>
</ThemeProvider>
);
};
// ----------------------------------------------------
// 🟣 Card Header
// ----------------------------------------------------
interface CardHeaderProps {
title?: string;
icon?: React.ReactNode;
theme?: Theme;
description?: string;
className?: string;
children?: React.ReactNode;
}
export const CardHeader = ({ title, icon, description, theme, className, children }: CardHeaderProps) => {
return (
<div className={cn(
'mb-4 flex flex-col items-start gap-2',
// Brutalist: Border Black -> White
theme === 'brutalist' && 'border-b-2 border-black dark:border-white pb-4 mb-4',
className
)}>
{/* Icon Wrapper */}
{icon && (
<div className={cn(
'mb-2 text-2xl',
theme === 'brutalist' && [
'p-2 border-2',
// Border: Black -> White
'border-black dark:border-white',
// Background: Primary -> Primary Dark Mode
'bg-primary dark:bg-primary-dark-mode',
// Text: Foreground -> Foreground Dark
'text-primary-foreground dark:text-primary-foreground-dark',
// Shadow: Black -> White
'shadow-[2px_2px_0px_0px_rgba(0,0,0,1)] dark:shadow-[2px_2px_0px_0px_rgba(255,255,255,1)]'
]
)}>
{icon}
</div>
)}
{/* Title */}
{title && (
<h2 className="text-2xl font-bold leading-none tracking-tight">
{title}
</h2>
)}
{/* Description */}
{description && (
<p className={cn(
"text-sm",
// Muted -> Muted Dark
"text-muted-foreground dark:text-muted-foreground-dark"
)}>
{description}
</p>
)}
{children}
</div>
);
};
CardHeader.displayName = "CardHeader";
// ----------------------------------------------------
// 🟢 Card Body
// ----------------------------------------------------
interface CardBodyProps {
children?: React.ReactNode;
className?: string;
theme?: Theme;
}
export const CardBody = ({ children, className, theme }: CardBodyProps) => {
return (
<div className={cn(
"text-sm",
// Foreground -> Foreground Dark
"text-card-foreground dark:text-card-foreground-dark",
className
)}>
{children}
</div>
);
};
CardBody.displayName = "CardBody";
// ----------------------------------------------------
// 🟠 Card Footer
// ----------------------------------------------------
interface CardFooterProps {
children?: React.ReactNode;
theme?: Theme;
className?: string;
}
export const CardFooter = ({ children, className, theme }: CardFooterProps) => {
return (
<div className={cn(
"flex items-center pt-4 mt-4",
// Brutalist Divider: Black -> White
theme === 'brutalist' && "pt-4 border-black dark:border-white",
className
)}>
{children}
</div>
);
};
CardFooter.displayName = "CardFooter";Usage
Import the Button component and use it in your React application like so:
import { Card, CardHeader, CardBody, CardFooter } from "@/components/ui/card";
<Card className='max-w-[400px]' theme="brutalist">
<CardHeader title='Card #1' description='This is a simple card component' />
<CardBody>
Hello
</CardBody>
<CardFooter>
<Button>Click me</Button>
</CardFooter>
</Card>Card Props
| Prop | Type | Default | Description |
|---|---|---|---|
theme | Theme | modern | Theme override |
children | ReactNode | — | Card content |
className | string | — | Custom Tailwind classes |
Card Header Props
| Prop | Type | Description |
|---|---|---|
title | string | Header title |
description | string | Subtitle or explanation |
icon | ReactNode | Optional icon |
children | ReactNode | Custom header content |
className | string | Extra styles |
theme | Theme | Auto-injected by Card |
Card Body Props
| Prop | Type | Description |
|---|---|---|
children | ReactNode | Body content |
className | string | Extra styles |
theme | Theme | Optional, rarely used |
Card Footer Props
| Prop | Type | Description |
|---|---|---|
children | ReactNode | Footer elements (buttons etc.) |
className | string | Extra styles |
theme | Theme | Auto-injected |