Expandable Cards

Cartes qui se deploient au clic pour reveler du contenu supplementaire. Installez Expandable Cards depuis le registre VLLNT UI avec la CLI shadcn.

Signaler un bug

Cartes qui se deploient au clic pour reveler du contenu supplementaire. Fait partie de la famille content dans VLLNT UI, il est publie comme entree de registre lisible par machine — copiez la source directement dans votre application avec la CLI shadcn et possedez-la, sans dependance d'execution a une bibliotheque de composants.

Apercu

Basculez entre clair et sombre pour inspecter l'apercu Storybook integre.

Installation

Ajoutez Expandable Cards a votre projet avec la CLI shadcn. La source arrive dans votre code, prete a etre adaptee :

pnpm dlx shadcn@latest add https://ui.vllnt.ai/r/expandable-cards.json

Source

"use client"; import * as React from "react"; import { cn } from "../../lib/utils"; /** Single expandable card entry. */ export type ExpandableCardItem = { /** Revealed content shown while expanded. */ content: React.ReactNode; /** Supporting text shown under the title. */ description?: string; /** Stable identifier used to track the open card. */ id: string; /** Header label and toggle. */ title: string; }; /** Props for {@link ExpandableCards}. */ export type ExpandableCardsProps = React.ComponentPropsWithoutRef<"div"> & { /** Cards rendered as an accordion-like stack. */ cards: ExpandableCardItem[]; }; /** * Stack of cards that expand to reveal content with a height transition. * * Respects `prefers-reduced-motion`: panels open without animating. * * @example * ```tsx * <ExpandableCards cards={items} /> * ``` */ export const ExpandableCards = ({ cards, className, ref, ...props }: ExpandableCardsProps & { ref?: React.Ref<HTMLDivElement> }) => { const [expandedId, setExpandedId] = React.useState<null | string>(null); const toggle = (id: string): void => { setExpandedId((current) => (current === id ? null : id)); }; return ( <div className={cn("flex flex-col gap-3", className)} ref={ref} {...props}> {cards.map((card) => ( <ExpandableCard card={card} expanded={expandedId === card.id} key={card.id} onToggle={toggle} /> ))} </div> ); }; ExpandableCards.displayName = "ExpandableCards"; function ExpandableCard({ card, expanded, onToggle, }: { card: ExpandableCardItem; expanded: boolean; onToggle: (id: string) => void; }) { return ( <div className="overflow-hidden rounded-xl border bg-card text-card-foreground shadow-sm"> <button aria-expanded={expanded} className="flex w-full flex-col items-start gap-1 p-4 text-left transition-colors hover:bg-accent hover:text-accent-foreground" onClick={() => { onToggle(card.id); }} type="button" > <span className="font-medium">{card.title}</span> {card.description === undefined ? null : ( <span className="text-sm text-muted-foreground"> {card.description} </span> )} </button> <div className={cn( "grid transition-all duration-300 motion-reduce:transition-none", expanded ? "grid-rows-[1fr]" : "grid-rows-[0fr]", )} > <div className="overflow-hidden"> <div className="px-4 pb-4 text-sm text-muted-foreground"> {card.content} </div> </div> </div> </div> ); }

Stories

Explorez chaque variante et etat dans le Storybook interactif :

Apercu

Basculez entre clair et sombre pour inspecter l'apercu Storybook integre.

Dependances

  • @vllnt/ui@^0.3.0