Card
The composed chassis of every card: a media with overlays, a body with a title, a description, stats and a footer.
Web et mobile — le même composant pour React et React Native. Stable : son API ne change qu’à une version majeure.
Quand l’utiliser
- To present one item of a collection: a video, a creator, a guide, a plan.
- As a link to the item (
asChild+interactive): the whole card is one target. - To group a few figures under a title (
Card.Stat).
Quand ne pas l’utiliser
Installation
Installer
npm install @krizaka/ui@beta @krizaka/tailwind@beta @krizaka/tokens@beta tailwindcssStyles
@import "tailwindcss";
@import "@krizaka/tailwind";
@import "@krizaka/ui/tailwind.css";Importer
import { Card } from "@krizaka/ui/card";Exemples
Media card
An image, badges on the media, a title, a description and a footer.
Night ride across the city
Ten minutes of neon, rain and empty avenues, filmed in one take.
import { Badge } from "@krizaka/ui/badge";
import { Card } from "@krizaka/ui/card";
// A self-contained cover (no network).
const COVER =
"data:image/svg+xml;utf8," +
encodeURIComponent(
'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 160 90"><defs><linearGradient id="g" x1="0" y1="0" x2="1" y2="1">' +
'<stop offset="0" stop-color="#1e3a8a"/><stop offset="1" stop-color="#7c3aed"/></linearGradient></defs>' +
'<rect width="160" height="90" fill="url(#g)"/><circle cx="118" cy="30" r="14" fill="#fde68a"/>' +
'<path d="M0 90 50 44l30 28 22-18 58 36z" fill="#0f172a" opacity=".55"/></svg>',
);
export default function CardMediaCard() {
return (
<Card.Root>
<Card.Media>
<Card.Image src={COVER} />
<Card.Overlay>
<Badge tone="scrim">4K</Badge>
</Card.Overlay>
<Card.Overlay corner="bottom-right">
<Badge tone="scrim">12:04</Badge>
</Card.Overlay>
</Card.Media>
<Card.Body>
<Card.Title>Night ride across the city</Card.Title>
<Card.Description>Ten minutes of neon, rain and empty avenues, filmed in one take.</Card.Description>
<Card.Footer>
<span>1.2k views</span>
<span aria-hidden>·</span>
<span>2 days ago</span>
</Card.Footer>
</Card.Body>
</Card.Root>
);
}Media fallback
No image: the fallback on the accent gradient.
Untitled draft
Processing — the cover arrives with the first frame.
import { Card } from "@krizaka/ui/card";
const PlayIcon = () => (
<svg viewBox="0 0 24 24" className="h-10 w-10" fill="currentColor" aria-hidden>
<path d="M8 5v14l11-7z" />
</svg>
);
// No image: the `fallback` (an icon on the accent gradient), hidden from assistive technology.
export default function CardMediaFallback() {
return (
<Card.Root>
<Card.Media aspect="square">
<Card.Image src={null} fallback={<PlayIcon />} />
</Card.Media>
<Card.Body padding="sm">
<Card.Title>Untitled draft</Card.Title>
<Card.Description>Processing — the cover arrives with the first frame.</Card.Description>
</Card.Body>
</Card.Root>
);
}Interactive link
asChild + <a>: the whole card is a link, with the spotlight and the lift.
import { Card } from "@krizaka/ui/card";
// `asChild` + `<a>`: the whole card is one link; `interactive` adds the spotlight and the lift.
export default function CardInteractiveLink() {
return (
<Card.Root asChild interactive>
<a href="#guide">
<Card.Body>
<Card.Title as="h2">Getting started</Card.Title>
<Card.Description>Install the preset, import the primitives, ship both themes.</Card.Description>
<Card.Footer>Read the guide →</Card.Footer>
</Card.Body>
</a>
</Card.Root>
);
}Stat card
Figures put forward with Card.Stat, tone="elevated".
This month
Revenue
€4,812
Supporters
318
import { Card } from "@krizaka/ui/card";
// `Card.Stat`: the label in capitals, the value in tabular digits.
export default function CardStatCard() {
return (
<Card.Root tone="elevated">
<Card.Body>
<Card.Title>This month</Card.Title>
<div className="grid grid-cols-2 gap-4">
<Card.Stat label="Revenue">€4,812</Card.Stat>
<Card.Stat label="Supporters">318</Card.Stat>
</div>
<Card.Footer>Updated 5 minutes ago</Card.Footer>
</Card.Body>
</Card.Root>
);
}Reveal grid
Cards entering in cascade with reveal and MotionObserver.
Enter
Step 1 of the signature.
Reveal
Step 2 of the signature.
Open
Step 3 of the signature.
Lift
Step 4 of the signature.
Glow
Step 5 of the signature.
Roll
Step 6 of the signature.
import { MotionObserver } from "@krizaka/ui";
import { Card } from "@krizaka/ui/card";
const COVER =
"data:image/svg+xml;utf8," +
encodeURIComponent(
'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 160 90"><rect width="160" height="90" fill="#4338ca"/>' +
'<circle cx="118" cy="30" r="14" fill="#fde68a"/></svg>',
);
// `reveal={index}` + one `MotionObserver` per page: the cards enter in cascade (at once under reduced motion).
export default function CardRevealGrid() {
return (
<>
<MotionObserver />
<div className="grid grid-cols-2 gap-4 sm:grid-cols-3">
{["Enter", "Reveal", "Open", "Lift", "Glow", "Roll"].map((word, index) => (
<Card.Root key={word} reveal={index} interactive radius="lg">
<Card.Media aspect="video">
<Card.Image src={index % 2 ? null : COVER} />
</Card.Media>
<Card.Body padding="sm">
<Card.Title>{word}</Card.Title>
<Card.Description>Step {index + 1} of the signature.</Card.Description>
</Card.Body>
</Card.Root>
))}
</div>
</>
);
}Props
CardRoot
| Prop | Type | Défaut | Description |
|---|---|---|---|
asChild | boolean | — | Renders the child (a link, an article) instead of the <div>, with props, classes and ref merged. |
interactive | boolean | — | The spotlight that follows the pointer and the lift on hover: for a card that is a link. |
radius | "md" | "lg" | "xl" | xl | md · lg · xl. |
reveal | number | — | Enters on scroll (MotionObserver): the index in the grid becomes the delay (50 ms a step, 8 steps at most). |
tone | "default" | "elevated" | "glass" | default | default (the base surface) · elevated (raised, a shadow) · glass (translucent, blurred). |
CardMedia
| Prop | Type | Défaut | Description |
|---|---|---|---|
aspect | "video" | "auto" | "square" | "portrait" | video | The ratio of the media: video (16:9) · square · portrait (3:4) · auto (the content's). |
CardImage
The image of a media, or its fallback when there is none. alt is empty by default: the title names the card.
| Prop | Type | Défaut | Description |
|---|---|---|---|
fallback | ReactNode | — | Shown when there is no image: an icon, an illustration (decorative). |
src | string | null | — | The image's URL; null or nothing shows the fallback. |
CardOverlay
What sits on the media (badges, a caption). scrim and fg-on-media are invariant: nothing to do for light.
| Prop | Type | Défaut | Description |
|---|---|---|---|
corner | "top-left" | "top-right" | "bottom-left" | "bottom-right" | top-left | The corner of the media it sits in. |
CardBody
| Prop | Type | Défaut | Description |
|---|---|---|---|
padding | "none" | "sm" | "md" | "lg" | md | none · sm · md · lg. |
CardTitle
| Prop | Type | Défaut | Description |
|---|---|---|---|
as | "h2" | "h3" | "h4" | h3 | The heading level, to fit the page's outline. |
CardStat
A figure put forward: the label in capitals, the value in tabular digits.
| Prop | Type | Défaut | Description |
|---|---|---|---|
labelrequis | ReactNode | — | What the figure is, in small capitals above it (the figure is the children). |
Accessibilité
Card.Titleis a heading (h3by default,asto change the level) so cards are reachable by heading.- A card that is a link is named by its content: keep the title first.
- The image fallback is decorative and hidden from assistive technology.
Clavier
| Touches | Action |
|---|---|
| Tab / Enter | An asChild link card is one focusable link that Enter follows. |
Bonnes pratiques
- Build product cards from these parts and override any of them with
className— never fork the card. - Keep one primary target per card: either the whole card is a link, or it holds buttons — not both.
- Give
Card.Mediaa fixedaspectso a grid stays aligned while images load. reveal={index}with oneMotionObserverper page for the entrance; it is shown at once under reduced motion.
Notes
Anatomy
Card.Root · Card.Media · Card.Image · Card.Overlay · Card.Body · Card.Title · Card.Description ·
Card.Stat · Card.Footer
Card.Rootis the frame:tone(default, elevated, glass),radius, andinteractivefor a card that is a link — the spotlight follows the pointer, the card lifts on hover. WithasChildit renders your<a>or<article>.Card.Mediaholds the image at a fixedaspect(video, square, portrait, auto);Card.Imagefalls back tofallbackwhen there is nosrc.Card.Overlaysits on the media in acorner— badges and durations usetone="scrim", identical in both themes.Card.Bodycarries the title, the description, the stats and the footer.
Products build their own cards from these parts and override any of them through className — never a fork.
Composants liés
Générée depuis le code de @krizaka/ui 2.0.0-beta.6 : son meta.ts, ses exemples et ses types.Modifier cette documentation sur GitHub