Card

Updated: Sep 24, 2026
Use Card for image-forward or custom content that forms one destination. It builds on Container with the card material, a medium rounded shape, and optional top and bottom gradient scrims that keep overlaid text legible across varied imagery.
Treat the whole card as one focus and press target. Descendants must not expose separate actions because nested targets create ambiguous focus and activation. For a standard row, prefer ListItem.
CardAboveScrim and CardBelowScrim identify whether content paints above or below the scrim.

Usage

import {
  Card,
  CardAboveScrim,
  CardBelowScrim,
  ScrimType,
  TextStyle,
  TextView,
} from '@wearables-ui-toolkit/mrbd';

export function StoryCard({ story, onOpen }: StoryCardProps) {
  return (
    <Card
      ariaLabel={story.title}
      bottomScrim={ScrimType.TALL}
      clickable
      onActivate={onOpen}
    >
      <CardBelowScrim>
        <img className="story-image" src={story.image} alt="" aria-hidden="true" />
      </CardBelowScrim>
      <CardAboveScrim>
        <div className="story-caption">
          <TextView textStyle={TextStyle.BODY2_EMPHASIZED}>{story.title}</TextView>
        </div>
      </CardAboveScrim>
    </Card>
  );
}
.story-image {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
Let the card clip full-bleed media to its shape. Inset content that should not reach the edge with a spacing token:
.story-caption {
  padding: var(--uit-spacing-large);
}
For a presentational card with no action, disable the interaction capabilities together:
import { Card, ScrimType } from '@wearables-ui-toolkit/mrbd';

<Card interactive={false} topScrim={ScrimType.MEDIUM}>
  <StatusArtwork />
</Card>;
Card, both scrim layer components, and ScrimType are also available from the component subpath:
import {
  Card,
  CardAboveScrim,
  CardBelowScrim,
  ScrimType,
} from '@wearables-ui-toolkit/foundation/components/Card';

Props

The complete prop type is CardProps. It extends Container props, which extend InteractableBase props. The material, shape, interaction, and tooltip props documented on those pages are available to Card. The component forwards remaining props and a ref to the card root.

Own props

PropTypeDefaultDescription
children
ReactNode
—
Card content. Children not wrapped in CardAboveScrim paint below the scrim.
topScrim
ScrimType
ScrimType.NONE
Gradient scrim size measured from the top edge.
bottomScrim
ScrimType
ScrimType.NONE
Gradient scrim size measured from the bottom edge.

Notable inherited props

PropTypeDefaultDescription
material
ContainerMaterial
the card material
Material painted behind and in front of the content.
shapeProvider
ShapeProvider
rounded rectangle at the medium corner radius
Geometry used for content clipping and every material path.
width
number \| string
'auto'
CSS width. A number represents pixels.
height
number \| string
'auto'
CSS height. A number also seeds the FULL scrim before measurement.
clipContent
boolean
true
Clips the content subtree to the card shape.
interactive
boolean
true
Default value for focusable, pressable, and clickable.
focusable / pressable / clickable
boolean
value of interactive
Individual interaction capability switches.
disabled
boolean
false
Blocks activation while keeping the card focusable.
onActivate
(event: InteractableActivationEvent) => void
—
Card action. Fires once for each pointer or keyboard activation.
onClick
MouseEventHandler
—
Raw DOM click handler for pointer and compatibility activation.
onStateChange
(prev: InteractionState, next: InteractionState) => void
—
Called after the card applies an interaction state change.
ariaLabel
string
—
Accessible name for the card.
tabIndex
number
0
Tab index applied while the card is focusable.
contentScaleForStateFn
(state: State, containerSize: {width: number; height: number}) => number
—
Replaces content scaling for each state.
visualStateOverride
VisualState
—
Fixes the material’s rendered visual state instead of deriving it from interaction.
className
string
''
Class added to the card root.
style
CSSProperties
—
Inline styles applied to the card root.

Scrim layer props

CardAboveScrim and CardBelowScrim use the exported CardScrimLayerProps type. Both forward a ref to their wrapper div.
PropTypeDefaultDescription
children
ReactNode
—
Content placed in the selected layer.
className
string
''
Class added to the layer wrapper.
style
CSSProperties
—
Inline styles applied to the layer wrapper.

Types and enums

ScrimType
  • NONE — no scrim
  • SMALL — the shortest fixed gradient
  • MEDIUM — the middle fixed gradient
  • TALL — the tallest fixed gradient
  • FULL — a gradient spanning the card’s measured height

Behavior

Layering.Card first paints children that are not wrapped in CardAboveScrim, then paints the scrim, and finally paints CardAboveScrim children. DOM order controls stacking within each group; the component adds no per-child offset. CardBelowScrim is optional and makes the default layer explicit.
The above-scrim check requires an exact element-type match. Place CardAboveScrim directly under Card; wrapping it in another element, putting it in a fragment, or importing it from a duplicate package copy moves it into the below-scrim group. This direct composition is required for correct text layering.
Scrims.SMALL, MEDIUM, and TALL use fixed gradient heights. FULL uses the measured card height, then a numeric height prop, then a built-in fallback height before the first measurement. Measurement stays live, so a FULL scrim follows responsive card resizing. Each gradient holds a three-quarter-opacity black band across the first fifth of its height from its edge, then fades to transparent at the opposite end. Top and bottom scrims can appear together in one decorative layer. When both values are NONE, Card renders no scrim layer.
The gradient is black. Black emits no light on the additive-light display, so the scrim reduces emitted light instead of adding a tint. Its behavior does not change with the theme.
Choose a scrim size for the area occupied by foreground content, not for the darkness of one image. Check legibility against the lightest and darkest real assets the application displays.
Material and focus. The default card material keeps focus and press feedback visible over opaque content such as a photo. Let the component own this feedback rather than drawing another focus ring, which could create inconsistent focus cues. When another surface is needed, pass a material from the material library and retain it with useMemo. Each mounted card must own a distinct material instance; mounting one material on two cards at the same time throws.
Interaction.Card is focusable, pressable, and clickable by default because interactive defaults to true. The root is always a div. A clickable card receives a button role unless the caller provides another role. Use interactive={false} for a presentational card. Consider changing one of focusable, pressable, or clickable only when the interaction model needs a specific capability.
Shape. The default shape is a rounded rectangle with the medium corner radius. A different ShapeProvider updates the content clip and every material fill and stroke path together. This keeps media aligned with custom geometry, such as a tail shape.
Sizing.Card stacks content in a layer that fills the card, so content does not create an intrinsic card size. Set width and height, or let a parent grid cell or stretched flex item size the card. The component writes its size inline, so class-based width and height do not take effect; use the props or style. Prefer values derived from available space instead of a fixed Meta Ray-Ban Display size. Provide a numeric height when a FULL scrim must be correct on the first paint.

Accessibility

  • An interactive card is one target. It must have an accessible name through ariaLabel or aria-label that describes the activation result.
  • Use onActivate for the card action so pointer and keyboard activation each invoke it exactly once.
  • When the card’s name already describes the destination, mark decorative imagery with an empty alt and aria-hidden="true" so assistive technology does not announce redundant content.
  • The component marks its decorative scrim layer aria-hidden.
  • Use interactive={false} when the card has no action so it does not expose a button role or enter the tab order.
  • Place media and background content in CardBelowScrim, and place text or glyphs that depend on the scrim in CardAboveScrim.
  • Use a scrim whenever text overlays variable imagery so the text remains legible across assets.
  • Inset authored content with a spacing token and let the card clip full-bleed media.
  • Prefer real imagery whose subject matches the destination represented by the card.

Avoid

  • Avoid nesting another rounded material surface, such as a container, static container, button, surface, chip, tag, or header, inside a card. Competing surfaces weaken hierarchy and can create unsupported interaction nesting.
  • Do not put separately focusable descendants inside an interactive card. The card must remain one unambiguous focus and activation target.
  • Place CardAboveScrim directly under Card; wrappers prevent the component from recognizing the above-scrim layer.
  • Avoid imitating photography with gradients or recreating a scrim with an overlay class. Use real media and the component-owned scrim for consistent additive-light display behavior.