Media Card
A record as a card — an image, the title, a meta line, a badge and an actions menu — where the whole card is one link.
- Status
- Since
0.23.10- Accessibility pattern
- one link named by the title, menu above the link
Last updated
Install
Add Media Card from the VegaStack registry. The CLI verifies the item's integrity hash before writing it.
pnpm dlx shadcn@latest add @vegastack/media-cardThe same command installs the registry items it composes: @vegastack/thumbnail.
Usage
import { MediaCard } from "@/components/ui/media-card";
<MediaCard
href={`/families/${f.id}`}
linkRender={<Link href="" />}
image={f.imageUrl}
fallback={<BrandMark />}
title={f.name}
meta="8 products · 3 sub-families"
badge={
<Badge variant="warning">
<TriangleAlert aria-hidden />3 missing specs
</Badge>
}
timestamp={<RelativeTime date={f.updatedAt} format="suffix" />}
actions={<RowActionsMenu label={f.name} actions={actions} />}
/>;MediaCard is one record in a card grid: a 48px Thumbnail on the
left, the title, one meta line (meta, then the badge, then the timestamp), and the ⋯ menu on
the right, top-aligned with the title's first line (never centred on the text block). With href the whole card is a link — the title is the link, stretched over the card —
and it never underlines. DataList's grid view renders one per row.
Examples
Fallback image
A record with no image shows fallback — the app's brand mark.
Large
size="lg" puts a 16:9 image on top, for records whose picture matters.
Without an image
A card with neither image nor fallback has no image area, and surface={false} drops the
card's own border for a host that owns the surface, such as a Board card.
API Reference
| Prop | Type | Default | Description |
|---|---|---|---|
title* | React.ReactNode | — | The record's name — the card's link text. |
actions | React.ReactNode | — | The ⋯ slot on the right — a RowActionsMenu. It shows on hover and on focus, and always on
a touch screen; it sits above the card's link, so it never follows it. |
badge | React.ReactNode | — | A badge after the meta — a status, or a warning pill (<Badge variant="warning">). |
fallback | React.ReactNode | <ImageIcon /> | What shows when there is no image — the app's brand mark. |
href | string | — | Make the whole card a link. The title is the link, stretched over the card. |
image | string | — | The image URL. Missing or failing to load shows fallback. A card with neither image nor
fallback has no image area at all. |
linkRender | React.ReactElement<unknown, string | React.JSXElementConstructor<any>> | <a /> | The element the link renders — a router link such as <Link href="" />. The card's href
wins over the template's. |
meta | React.ReactNode | — | One muted meta line under the title ("8 products · 3 sub-families"). |
size | "default" | "lg" | "default" | default puts a 48px thumbnail on the left; lg puts a 16:9 image on top. |
surface | boolean | true | Draw the card's own border, padding and hover. Off when a host (a Board card) owns the surface. |
timestamp | React.ReactNode | — | A last meta item after the badge — usually a RelativeTime ("2h ago"). |
Data attributes and CSS variables on MediaCard
| Attribute | Values |
|---|---|
data-size | mirrors a prop or state value |
data-slot | "media-card" | "media-card-actions" | "media-card-meta" | "media-card-title" |
Accessibility
- The card is one link, named by its title; the meta line, badge and time are read after it.
- The ⋯ menu sits above the stretched link, so it is its own tab stop and never follows the link. It shows on hover and on keyboard focus, and always on a touch screen.
- The image is decorative (
alt="") because the title names the record.
| Key | Action |
|---|---|
| Tab | Move to the card link, then its ⋯ |
| Enter | Open the record |
| Enter / Space | On ⋯, open the menu |
| Contract | States tested |
|---|---|
| Behaviour | link, plain, lg, no-media |
| Accessibility | labeled, browser-accessibility-test |
| Visual | default, hover |