Card

A bounded surface for one thing: a title, a body, an optional cover and a row of actions along the bottom.

Accessibility

What Card does for you, and what it leaves to the page around it.

What a card is in the accessibility tree

Nothing. A card renders a plain <div> with no role and no ARIA, so the content inside is announced and the box around it is not. A card groups things visually and is not a control.
  • That is usually right. A border is visual, and a role on it adds announcements without meaning. A page with twelve landmarks is as hard to use as one with none.
  • When the grouping matters, put the meaning on the content. Give the card's title a real heading level, and the card can be found by heading navigation with no role at all.
  • When a set of cards is a list, make it one: render them in a <ul>, one per <li>, so the count is announced and the reader knows where the set ends.
  • Use role="region" only for a card a reader should be able to jump to, and only with a name (see below). Most screen readers skip an unnamed region.
  • The parts have no roles either, except actions, which renders a <ul> of <li> elements, so that row is announced as a list. footer is a plain <div>.

Naming the group

A role with no name is worse than no role. When you give the card one, its title already has the words.
  • Point aria-labelledby at the title's id rather than repeating the words in an aria-label, so the visible and announced names cannot drift apart when the copy changes.
  • title takes any node, so a badge, a count or a Tag in it becomes part of the name. A title of “Invoices 12” is read as that.
  • A card with no title has no name to offer, which is another reason a titleless card is usually better as a plain surface.
  • In a grid, keep the titles distinct. Six cards called “Details” give a reader six regions with the same name.

A clickable card needs a real control inside it

An onClick on the card's root gives you a mouse handler and nothing else. The div is not in the tab order, Enter and Space do nothing, no role is announced, and there is no context menu, no middle-click to open a new tab and no link preview.
  • Put a real <a> or <button> inside the card and let it cover the surface: the link goes on the title, and a positioned pseudo-element stretched to the card's edges makes the whole card the target. The tab stop, the role and the URL stay on the link.
  • The overlay has costs. Text under it can no longer be selected with the mouse, and any other control in the card has to be raised above it, which makes a second tab stop inside the first one's area.
  • If the card needs a whole-card link and an actions row, do not stretch the link. Link the title only. Controls inside a control work for neither the mouse nor the keyboard.
  • hoverable is the visual half of the same promise, and only the mouse sees it. Keyboard focus needs a visible focus ring on the same surface.
  • disabled blocks the keyboard too. It dims the card and sets inert, so buttons and links inside cannot be clicked, focused or read by a screen reader. Opacity and pointer-events: none alone leave them in the tab order.

The actions row

Each entry in actions is wrapped in its own <li>, so the row is a list. The entries are not made into controls or given names: the reader gets whatever you pass.
  • Every action must be a real button or link. An icon with a click handler cannot be reached from the keyboard.
  • An icon-only action still needs a word. Keep the word inside the control with sr-only, or give the control an aria-label. An icon is not a name, and neither is the position of a cell.
  • A Tooltip alone is not a name. It helps mouse users who can see it, and not touch or keyboard users. Use both: the tooltip for hover and the label for screen readers.
  • Name the action, not the icon: “Edit invoice”, not “pencil”. In a grid the same names are read many times, so include the object when it is short.

Contrast

  • The tinted levels pass AA everywhere. emphasis="default" and emphasis="subtle" use the palette's dark shade for text, and every palette passes 4.5:1 on both grounds in both modes: at worst 6.42 in light and 6.33 in dark.
  • Check strong cards for contrast. White text on the solid fill falls below WCAG AA for 8 of the 20 hues in light mode and 16 in dark mode, with yellow the weakest (2.72 in light, 1.90 in dark). Keep strong for short or large text, and use default for body copy.
  • Treat a strong card as decoration: do not make it the only place something is said.
  • An inset card's tint only groups. It separates from the inner surface at about 1.09:1 in light and 1.23:1 in dark, far below any text threshold, so nothing may depend on seeing it. On a dark page background the tint disappears, and only the inner surface's edge shows.
  • All other colors come from your theme, so measure again after changing the seed colors. The Tokens tab lists the tokens Card reads.

Motion

  • The hoverable lift is a shadow change and carries no information, so nothing is lost for a reader who never sees it. Put anything you add on top, like a translate, a scale or a cover zoom, behind motion-safe:.
  • Turn off every transition with the theme's motion token, and give a reader who asked for reduced motion that setting.
  • Do not animate the card's height as content arrives. loading keeps the header, cover and actions in place so the frame does not move.
  • In a grid, a hover effect on one card must not move its neighbors. A shadow does not cause reflow, and a scale or a margin does.