Skip to content
EspañolEnglish
All components (204)

Card

Surfaces · Card · .amset-card

What it is and when to use it

A surface with a header (title, subtitle and a slot on the right), body, full-bleed media and a footer separated by a divider. It can be static, pressable (onPress) or a link (href).

When to use it

  • To group what belongs together: an order, an invoice, a block of settings, a dashboard figure.
  • onPress when the whole card opens something on the same screen; href when it leads to another page. In both cases title is required: it is the name of the button or the link.
  • tone paints a color strip on the start edge: it accompanies a text that already says it ("Overdue by 12 days"), it does not replace it.
  • size: sm in a grid of options or figures, lg for a card that heads a section. withoutPadding leaves the content full-bleed (a table, a list), with the header and the footer in their place.
  • loading paints a bar that runs along the top edge and marks the body as busy.
  • selected marks the chosen one in a grid of pressable options, and it is announced: the pressable card is a button that stays pressed.

When not to

  • The product in a shop grid: ProductCard, which already brings the photo, the price and the actions.
  • A single option from a group that is chosen with the arrow keys and travels in a form: RadioGroup, or ShippingPicker if they are shipping methods. The selected card is a pressed button, not a radio.
  • Pairs of label and value (a customer's details): DataList.
  • Controls inside the body of a pressable card: the stretched link covers them. Whatever is pressed separately goes in footer or in extra, which stay on top.

Keyboard: the static card has no Tab stops. On the pressable one, a single Tab reaches the title button; Enter and Space press it. With href, Enter follows the link. disabled (only on the pressable one) leaves the button disabled and does not fire onPress. Whatever goes in footer and in extra has its own stop.

Accessibility: the title is a real heading, h3 by default: headingLevel adjusts it to the place where it is mounted (from 2 to 6: the h1 belongs to the page). On the pressable card, the button or the link goes INSIDE the heading and is stretched over the card with a pseudo-element: a single destination is announced with the name of the title, the HTML stays valid (a button cannot contain an h3) and the footer buttons keep working. selected is announced according to the form: on the pressable card, aria-pressed on the button (also false, so people know it can be chosen; without the prop, it is a normal button); on the link card, aria-current; on the static card, which has no control, "(selected)" written after the title. loading sets aria-busy on the body.

Fine-tuning: CSS variables --amset-card-*: -padding (12, 20 or 24 px depending on the size), -title, -radius, -background, -border, -shadow, -slot (the thickness of the tone strip, 3 px) and -media-width (10rem in horizontal; in windows narrower than 30rem the media goes back to the top). The focus of the pressable card is the 2 px outline shared by all controls.

What it looks like

Order 4821

Delivered on October 3

Three items, €148.20. Collected at pickup point.

<Card title="Order 4821" subtitle="Delivered on October 3" variant="raised">
  <Text>Three items, 148.20 EUR. Collected at pickup point.</Text>
</Card>

Props

Import it with import { Card } from '@amset/ui'. Required props are marked.

PropTypeWhat it does
childrenReactNode—
classNamestringMerged with the library classes; it does not replace them.
disabledboolean—
extraReactNodeTo the right of the title: a status tag, an actions menu.
footerReactNodeBottom strip, separated by a divider: buttons or metadata.
headingLevelCardHeadingLevelHeading level. It is set by the place where the card is mounted, not by the card: inside a section with `h2` this is an `h3`.
horizontalbooleanThe media beside the text instead of above it.
hrefstring—
idstring—
loadingbooleanMarks the content as busy while it arrives.
mediaReactNodeFull-bleed, before the content: image, chart, map.
onPress() => void—
relstring—
selectedbooleanChosen: accent border and background, for grids of options. It is also announced, each shape in its own way: the pressable one is a button that stays pressed (`aria-pressed`, also with `false`: that way you know it can be chosen), the link one is the current one (`aria-current`) and the static one, which has no control, says it in writing after the title: "(selected)".
size"sm" | "md" | "lg"The padding and the font size of the title: 12, 20 or 24 px around.
subtitleReactNode—
targetstring—
titleReactNodeRequired: it is the accessible name of the pressable card. Required: it is the text of the card's link.
tone"neutral" | "accent" | "secondary" | "tertiary" | "info" | "success" | "warning" | "error"Paints a color strip on the start edge. The neutral one does not paint it.
variant"outline" | "raised" | "flat"`outline` (default), `raised` (with shadow) or `flat` (borderless, on an alternate background).
withoutPaddingbooleanNo padding in the content, for what runs edge to edge: a table, a list, a map. The header and the footer keep theirs.

It also accepts the attributes of the element it renders: className, style, data-*, aria-* and its events.

CSS variables

These CSS variables are how you adapt it. Declare them on the component's selector, not on :root: the stylesheet redeclares them in the rule itself, and a declaration on the same element always wins.

VariableDefault
--amset-card-paddingvar(--amset-space-5)
--amset-card-titlevar(--amset-text-lg)
--amset-card-radiusvar(--amset-radius-lg)
--amset-card-backgroundvar(--amset-surface)
--amset-card-bordervar(--amset-border)
--amset-card-shadownone
--amset-card-slot3px
--amset-card-media-width10rem

How to change it

.amset-card {
  --amset-card-padding: ...;
  --amset-card-title: ...;
}

In the test bench

Every state and variant, to try one at a time, on its page at ui.amset.tech: