Displays a card with header, content, and footer.
Installation
npx shadcn@latest add @caprice/cardnpm install @caprice-ui/reactInstall the following dependencies:
npm install @base-ui/react lucide-reactAdd a cn helper
import { defineConfig, type VariantProps } from 'cva';import { twMerge } from 'tailwind-merge';export const { compose, cva, cx: cn,} = defineConfig({ hooks: { onComplete: (className: string) => twMerge(className), },});export type { VariantProps };Copy and paste the following code into your project.
import { mergePropsN } from '@base-ui/react';import { useRender } from '@base-ui/react/use-render';import { cn } from '@/lib/utils';/** * A card component that displays content in a styled container. * Renders a `<div>` element. * * Documentation: [Caprice UI Card](https://caprice-ui.com/docs/components/card) */export namespace Root { export type Props = useRender.ComponentProps<'div'>;}export function Root({ className, render, ...props }: Root.Props) { const defaultProps: useRender.ElementProps<'div'> & { 'data-slot'?: string } = { 'data-slot': 'card', className: cn( 'relative flex flex-col gap-6 rounded-xl border bg-card py-6 text-card-foreground shadow-sm', className ), }; return useRender({ defaultTagName: 'div', render, props: mergePropsN<'div'>([defaultProps, props]), });}/** * A header that contains the title and description of the card. * Renders a `<div>` element. * * Documentation: [Caprice UI Card](https://caprice-ui.com/docs/components/card) */export namespace Header { export type Props = useRender.ComponentProps<'div'>;}export function Header({ className, render, ...props }: Header.Props) { const defaultProps: useRender.ElementProps<'div'> & { 'data-slot'?: string } = { '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 ), }; return useRender({ defaultTagName: 'div', render, props: mergePropsN<'div'>([defaultProps, props]), });}/** * A title that displays the main content of the card. * Renders a `<div>` element. * * Documentation: [Caprice UI Card](https://caprice-ui.com/docs/components/card) */export namespace Title { export type Props = useRender.ComponentProps<'div'>;}export function Title({ className, render, ...props }: Title.Props) { const defaultProps: useRender.ElementProps<'div'> & { 'data-slot'?: string } = { 'data-slot': 'card-title', className: cn('font-semibold leading-none', className), }; return useRender({ defaultTagName: 'div', render, props: mergePropsN<'div'>([defaultProps, props]), });}/** * A description that displays additional information about the card. * Renders a `<div>` element. * * Documentation: [Caprice UI Card](https://caprice-ui.com/docs/components/card) */export namespace Description { export type Props = useRender.ComponentProps<'div'>;}export function Description({ className, render, ...props }: Description.Props) { const defaultProps: useRender.ElementProps<'div'> & { 'data-slot'?: string } = { 'data-slot': 'card-description', className: cn('text-muted-foreground text-sm', className), }; return useRender({ defaultTagName: 'div', render, props: mergePropsN<'div'>([defaultProps, props]), });}/** * A footer that contains the actions of the card. * Renders a `<div>` element. * * Documentation: [Caprice UI Card](https://caprice-ui.com/docs/components/card) */export namespace Footer { export type Props = useRender.ComponentProps<'div'>;}export function Footer({ className, render, ...props }: Footer.Props) { const defaultProps: useRender.ElementProps<'div'> & { 'data-slot'?: string } = { 'data-slot': 'card-footer', className: cn('flex items-center px-6 [.border-t]:pt-6', className), }; return useRender({ defaultTagName: 'div', render, props: mergePropsN<'div'>([defaultProps, props]), });}/** * An action that displays a button or link in the card. * Renders a `<div>` element. * * Documentation: [Caprice UI Card](https://caprice-ui.com/docs/components/card) */export namespace Action { export type Props = useRender.ComponentProps<'div'>;}export function Action({ className, render, ...props }: Action.Props) { const defaultProps: useRender.ElementProps<'div'> & { 'data-slot'?: string } = { 'data-slot': 'card-action', className: cn('col-start-2 row-span-2 row-start-1 self-start justify-self-end', className), }; return useRender({ defaultTagName: 'div', render, props: mergePropsN<'div'>([defaultProps, props]), });}/** * A panel that contains the content of the card. * Renders a `<div>` element. * * Documentation: [Caprice UI Card](https://caprice-ui.com/docs/components/card) */export namespace Panel { export type Props = useRender.ComponentProps<'div'>;}export function Panel({ className, render, ...props }: Panel.Props) { const defaultProps: useRender.ElementProps<'div'> & { 'data-slot'?: string } = { 'data-slot': 'card-panel', className: cn('px-6', className), }; return useRender({ defaultTagName: 'div', render, props: mergePropsN<'div'>([defaultProps, props]), });}/** * @deprecated Use {@link Panel} instead * @see {@link https://caprice-ui.com/docs/components/card} */export const Content = Panel;Update the import paths to match your project setup.
Usage
import * as Card from "@/components/caprice-ui/card"import * as Card from "@caprice-ui/react/card"Examples
Inset
Differences with shadcn/ui / Radix
If you're familiar with Radix UI and shadcn/ui, most patterns will carry over. This guide points out the differences so you can start using Caprice UI without surprises.
Key changes
Comparison Example