Card
Agrupar un tema con su acción. No anides cards dentro de cards.
Agrupa un dato o una tarea con título, contenido y acciones.
Instalación
shadcn
npx shadcn@latest add https://design.moodinary.com/r/card.jsonimport
import { Card, CardHeader, CardTitle, CardDescription, CardAction, CardContent, CardFooter } from "@moodinary/ui/components/card";Ejemplos
Card de ajustes
El patrón más común del panel: CardHeader con título y descripción, y el contenido en CardContent.
Alertas automáticas
Guardá una idea (“enojo”, “renuncias”) y te avisamos acá cuando aparezcan pulsos nuevos sobre ese tema. También podés pedírselo a Nubi con @Nubi en cualquier seguimiento.
Código
settings.tsx
import { Button } from "@moodinary/ui/components/button";
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "@moodinary/ui/components/card";
import { Input } from "@moodinary/ui/components/input";
import { Label } from "@moodinary/ui/components/label";
import { Select } from "@moodinary/ui/components/select";
export default function CardSettings() {
return (
<Card className="w-full">
<CardHeader>
<CardTitle>Alertas automáticas</CardTitle>
<CardDescription>
Guardá una idea (“enojo”, “renuncias”) y te avisamos acá cuando
aparezcan pulsos nuevos sobre ese tema. También podés pedírselo a Nubi
con @Nubi en cualquier seguimiento.
</CardDescription>
</CardHeader>
<CardContent>
<form
className="flex flex-col gap-2 sm:flex-row"
onSubmit={(event) => event.preventDefault()}
>
<div className="min-w-0 flex-1 space-y-2">
<Label className="sr-only" htmlFor="card-alert-query">
Idea a vigilar
</Label>
<Input
id="card-alert-query"
maxLength={200}
placeholder="p. ej. enojo con los horarios"
/>
</div>
<div className="space-y-2">
<Label className="sr-only" htmlFor="card-alert-org">
Organización
</Label>
<Select id="card-alert-org">
<option>Organización de ejemplo</option>
</Select>
</div>
<Button type="submit">Crear alerta</Button>
</form>
</CardContent>
</Card>
);
}Resumen
Métrica principal con CardAction para el cambio.
Participación
+6 %
82 %312 de 380
Datos ilustrativos.
Código
summary.tsx
import { Badge } from "@moodinary/ui/components/badge";
import {
Card,
CardAction,
CardContent,
CardHeader,
CardTitle,
} from "@moodinary/ui/components/card";
export default function CardSummary() {
return (
<Card className="w-full max-w-sm">
<CardHeader>
<CardTitle>Participación</CardTitle>
<CardAction>
<Badge variant="ok">+6 %</Badge>
</CardAction>
</CardHeader>
<CardContent>
<div className="flex items-baseline gap-2">
<b className="font-semibold text-3xl tabular-nums">82 %</b>
<span className="text-ink-3">312 de 380</span>
</div>
<p className="mt-2 text-ink-3 text-xs">Datos ilustrativos.</p>
</CardContent>
</Card>
);
}Con acciones
CardFooter con borde lleva las acciones a la derecha.
Informe mensual
Septiembre listo para revisar. Informe ilustrativo.
Código
report.tsx
import { Button } from "@moodinary/ui/components/button";
import {
Card,
CardContent,
CardFooter,
CardHeader,
CardTitle,
} from "@moodinary/ui/components/card";
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@moodinary/ui/components/dialog";
export default function CardReport() {
return (
<Card className="w-full max-w-sm">
<CardHeader>
<CardTitle>Informe mensual</CardTitle>
</CardHeader>
<CardContent className="text-ink-2">
Septiembre listo para revisar. Informe ilustrativo.
</CardContent>
<CardFooter className="justify-end border-t">
<Dialog>
<DialogTrigger
render={
<Button size="sm" variant="ghost">
Vista previa
</Button>
}
/>
<DialogContent>
<DialogHeader>
<DialogTitle>Informe de ejemplo</DialogTitle>
<DialogDescription>
Acá aparecería el resumen del período elegido.
</DialogDescription>
</DialogHeader>
</DialogContent>
</Dialog>
<Dialog>
<DialogTrigger render={<Button size="sm">Enviar</Button>} />
<DialogContent>
<DialogHeader>
<DialogTitle>Envío de ejemplo</DialogTitle>
<DialogDescription>
Este ejemplo no envía correos ni comparte datos.
</DialogDescription>
</DialogHeader>
</DialogContent>
</Dialog>
</CardFooter>
</Card>
);
}Cargando
Skeletons con la forma del contenido final.
Cargando resumen…
Código
loading.tsx
import { Card, CardContent, CardHeader } from "@moodinary/ui/components/card";
import { Skeleton } from "@moodinary/ui/components/skeleton";
export default function CardLoading() {
return (
<Card
aria-busy="true"
aria-label="Cargando resumen"
className="w-full max-w-sm"
>
<CardHeader>
<Skeleton className="h-3.5 w-2/5" />
</CardHeader>
<CardContent className="grid gap-3">
<Skeleton className="h-[26px] w-[55%]" />
<Skeleton className="h-2.5 w-4/5" />
<span className="sr-only" role="status">
Cargando resumen…
</span>
</CardContent>
</Card>
);
}Props
Card
| Prop | Tipo | Default | Descripción |
|---|---|---|---|
| size | "default" | "sm" | "default" | Padding compacto en sm. |
| noise | boolean | - | Textura sutil de fondo. |
| className | string | - | Clases extra. |
CardHeader, CardTitle, CardDescription, CardAction, CardContent, CardFooter
| Prop | Tipo | Default | Descripción |
|---|---|---|---|
| ...props | ComponentProps<"div"> | - | Partes de la card. |
| className | string | - | Clases extra. |
Cuándo sí y cuándo no
Hacé
- Un tema por card.
- Poné la acción principal en el footer.
- Usá skeletons con la forma real.
Evitá
- No anides cards.
- No pongas filas de tres cards iguales como relleno.
- No uses spinners para cargar una card.
Accesibilidad
- El título visible identifica la card.
- Mientras carga, usá
aria-busy.