Bridge view

Card

A bounded container that groups a coherent unit of content — typically a title, supporting body, optional media, and optional actions — and lifts it as a single perceivable object on the page.

When to use

Use

When grouping a coherent unit of content — title plus body plus optional media plus optional actions — that should perceive as a single object on the page. Typical hosts: dashboards, marketing grids, search results, content collections.

Avoid

For dense list rows where every row is structurally identical and visual separation is purely a horizontal rule — that is `ListItem`. For decorative content tiles in a grid where each tile is primarily an image with a label — `Tile`. For announcing transient information — `Banner` or `Toast`.

Versus related

  • tile

    `Tile` is image-led with minimal supporting text; `Card` is content-led with hierarchical title plus body plus media. A wall of Tiles reads as a gallery; a wall of Cards reads as a feed.

  • list-item

    `ListItem` lives inside an explicit `<ul>` or `<ol>` and cooperates with sibling rows for keyboard navigation and selection semantics. `Card` is a standalone object with no implicit sibling relationship.

Highlight
Fig 1.1 · Card · Bridge view
Both

Figma↔Code mismatches

Where designer and developer worlds typically misalign on this component.

  1. 01
    Figma

    Variants for hover / focus / active / disabled

    Code

    CSS pseudo-classes (:hover, :focus-visible, :active) and aria/disabled attributes

    Consequence

    Variant explosion in Figma (3 variants × 4 states × 2 orientations = 24+ component variants), and the developer cannot map a Figma "hover variant" to a CSS pseudo-class without manual translation.

    Correct

    Treat interactive states as a separate spec (a "states" sheet or component property documentation) — not as Figma variants. Variants are reserved for structurally different versions (elevated / outlined / flat).

  2. 02
    Figma

    Media-on-top vs. media-on-leading-edge expressed as separate variants

    Code

    A single component with an `orientation` prop ('vertical' | 'horizontal')

    Consequence

    Designers and developers count card variants differently. Designers see 6+ variants; developers see 3 variants × 2 orientations. Implementation drift: designer adds a third orientation in Figma but the prop type is a binary union.

    Correct

    Model orientation as a *property*, not a variant. Document the property in Figma using a component property of type 'variant' that *only* captures orientation, separate from visual variants.

  3. 03
    Figma

    A clickable card built by stacking a "card" component on top of an invisible "button" component

    Code

    An <a>/<button> wrapping the entire card (or a pseudo-element overlay pattern)

    Consequence

    The Figma artifact does not encode the affordance — designers may not realise the whole card must be a single accessible activator, and developers may forget the keyboard story when implementing.

    Correct

    When the card is interactive as a whole, model the activator as an explicit boolean property (`interactive`) on the canonical component. Render it as a single anchor or button with the rest of the card visually inside, using the overlay pattern to keep nested controls reachable.

  4. 04
    Figma

    Selected state expressed via an "outline + filled background" variant

    Code

    A `data-selected` or `aria-selected` attribute toggled by the application

    Consequence

    The selection style is duplicated in two places (variant + CSS) and inevitably drifts. Worse, treating selection as a variant blocks multi-state selection (e.g., selected + disabled).

    Correct

    Document selection as a *data state*, not a variant. The visual treatment is described once and is composable with the interactive states.

Both

Variants, properties, states

Variants

Structurally different versions of the component.

elevatedoutlinedflat

Properties

The same component, parameterised.

PropertyType
interactiveboolean
orientationvertical | horizontal
densitycomfortable | compact

States

Browser/user-driven (interactive) vs. app-driven (data).

KindStates
interactive
hoverfocus-visibleactivedisabled
data
selectedloading
Both

Figma ↔ Code property map

FigmaTypeCodeNotes
VariantVariantvariantMaps elevated / outlined / flat.
OrientationVariantorientationvertical / horizontal — Figma encodes as a separate Variant property, not bundled with the visual variant.
DensityVariantdensity
InteractiveBooleaninteractiveToggles the card-as-link overlay pattern. In Figma it controls hover state visibility; in code it conditionally wraps the card surface with the interactive activator.
Has MediaBooleanmediaSlot-visibility toggle; code conditionally renders the media slot.
MediaInstance SwapmediaSwaps the media component instance (image, video, iframe).
Has EyebrowBooleaneyebrow
EyebrowTexteyebrow
TitleTexttitle
Has SubtitleBooleansubtitle
SubtitleTextsubtitle
BodyTextbodyFor non-prose bodies, Figma uses an Instance Swap on a 'Body Slot' instead.
Has ActionsBooleanactions
Has FooterBooleanfooter
Designer

Figma anatomy

Slot Figma type Hint
media frame Aspect-ratio-locked frame; image fill or instance swap
eyebrow text Text style "Eyebrow" / "Overline"; uppercase or small-caps
title text Heading text style; bound to a component property for content
subtitle text Subtitle text style; lighter weight than title
body text-or-frame Auto-layout text frame, or nested component instance for richer payloads
actions frame Auto-layout horizontal frame containing button instances
footer frame Auto-layout horizontal frame; smaller text style
Dev

Code anatomy

Slot Code slot Semantic
media media img-or-video
eyebrow eyebrow text-with-role-or-tag
title title heading-or-link
subtitle subtitle text
body body prose-or-children
actions actions button-group
footer footer contentinfo-region
Dev

Cross-framework expression

FrameworkStructure mechanismVariant mechanism
Web Components named slots (<slot name="media">, <slot name="title">, …) attributes reflected to properties (variant="elevated", orientation="horizontal")
React compound components (Card.Media, Card.Title, Card.Body, …) or explicit slot props props with class-variance-authority / tailwind-variants for the variant/property axis
Angular (signals) ng-content with select="[card-media]" / [card-title] etc., and signal-based input() for slots input<'elevated' | 'outlined' | 'flat'>() and input<'vertical' | 'horizontal'>()
Vue named slots (<slot name="media" />, <slot name="title" />, …) defineProps with literal-union types
Both

Internationalisation

RTL · mirroring

Horizontal-orientation cards flip their leading-edge media to the right side via logical `inline-start` properties. Eyebrow / title / subtitle / body inherit document direction; numerals and dates should follow the locale's preferred numeral system. Action button order reverses: the primary action moves from inline-end (right in LTR) to inline-end (left in RTL) — the *logical* position is unchanged, the visual position mirrors. Decorative media that carries directionality (left-pointing arrow icons, before/after photo pairs) needs explicit alt-text or composition handling.

Text expansion

Title is clamped to 2 lines (`-webkit-line-clamp: 2`) by canonical convention; subtitle to 3; body free-flow. Eyebrow may wrap to a second line under heavy expansion (DE/RU). Action labels follow Button's expansion rules. Multi-column card grids may need to re-flow at narrower widths for languages with longer-than-average text.

Both

Accessibility

Slot Accessibility hint
media Provide alt text for informative images; alt="" for purely decorative media. Do not duplicate the title in alt text.
eyebrow Avoid heading semantics — the eyebrow is metadata, not a heading. If grouped with the title, treat as part of the same labelling relationship via aria-labelledby.
title Use a heading element of the appropriate level for the surrounding document outline. If the card is interactive as a whole, the title also carries the accessible name of the activator.
subtitle If the subtitle qualifies the title's meaning, associate it via aria-describedby on the interactive element.
body Body content keeps its native semantics (lists stay lists, links stay links). Avoid wrapping body in role="text" — it strips semantics from assistive tech.
actions Keep nested buttons keyboard-reachable. If the whole card is also clickable, see mistake "card-as-link-nested-buttons" for the correct overlay pattern.
footer Footer content is not the card's accessible name. If it carries status, use aria-live on the chip itself, not the card.
Both

Accessibility acceptance

Keyboard walk

KeysExpected
TabFor non-interactive cards, focus skips the card surface and lands on nested interactive elements (buttons, links inside actions or the body) in document order. For `interactive: true` cards using the overlay pattern, the title's `<a>` is the single tab stop for the card; nested actions remain individually reachable on subsequent Tab presses (kept on a higher stacking context).
Enter (on overlay link)Activates the card-as-link target. Spacebar does not activate — anchors are Enter-only by native convention.

Screen-reader announcements

TriggerExpected
Focus enters card-as-linkThe title's accessible name followed by "link" (e.g. "Q3 revenue report, link"). The eyebrow, subtitle, and body are not part of the link's accessible name unless explicitly tied via `aria-labelledby`.
`selected` data state setSelection is announced via `aria-selected="true"` on the card's primary actionable element. SR reads "selected" before the accessible name (varies slightly by SR).

axe-core rules to assert

  • link-name
  • color-contrast
  • heading-order
  • image-alt
Both

Common mistakes

#clickable-card-no-keyboard

Click handler on the card without a focusable activator

Problem

Attaching a click handler to a <div> card with no <a> or <button> inside makes the card unreachable by keyboard and unannounceable to assistive tech.

Fix

Always anchor the activation on a real interactive element (<a href> for navigation, <button> for in-page actions). If the visual treatment requires the whole card to look clickable, use the overlay pattern — never role="button" on the card div.

#variant-explosion-from-states

Modelling interactive states as Figma variants

Problem

Adding hover / focus / active / disabled as variants in Figma causes the variant matrix to explode and forces the developer to hand-translate Figma variants into CSS pseudo-classes.

Fix

Document interactive states once in a separate "states" sheet or component documentation. Reserve Figma variants for structurally different versions (elevated / outlined / flat). Document the same separation in the canonical reference and the production library.

#media-alt-duplicates-title

Image alt text repeats the card title

Problem

Setting `alt` to the title text causes screen readers to announce the title twice when the card is interactive — once as the link name and once as the image alt.

Fix

For decorative / supporting media use `alt=""`. For media that carries information not in the title, write alt that adds the missing information ("Chart showing 12% YoY growth"), not a restatement of the headline.