Vertical content surface — a self-contained unit combining optional media/icon slots with a text block. The shipped component is the customCard base preset; ready-made presets are assembled from it.
Overview
Three things ship today: the open customCard base and two product variants. Card is a vertical surface — StartSlot to StartText to EndSlot on a configurable background, plus an optional CornerSlot overlay. A horizontal row with start text and end media is Cell, not Card.
-
1
customCard The base — every axis open (slots, layout flow, media pin, surface, radius, paddings, gaps, typography). Assemble anything from it; the locked product contracts live in the spec (§8).
-
2
serviceCard · L / M / S Entry into a vertical: name + transport artwork. Three sizes from the designer tiles — L City Rides (artwork full-width, flush bottom) · M Couriers (crop window in the end corner) · S City to city / Freight. Two S plus the grid gap equal one M.
-
3
storyCard Promo story tile: headline over a full-bleed photo on a pastel or dark surface (--card-surface), 104 x 156.
States
Card has two states. It is static by default — interactive instances add .card--interactive and press with pushItem (see Animation), which is a motion behaviour rather than a separate state.
Standard
-
1
Background --surface-on-white — by default.Can be changed to any Surface / Background / Pastel colour style from the DS.
-
2
StartText Title — Main Body, --text-and-icon-primary (inherited from the root).Subtitle — Compact Body, --text-and-icon-secondary.Both text styles and colours can be changed per instance.
-
3
Slot content --text-and-icon-primary — icons inherit the root colour in StartSlot and EndSlot.A slot accepts any DS element; a non-default colour goes inline on that element.
-
4
CornerSlot Checkbox instance — box border --text-and-icon-secondary, radius var(--sp-s8).The var(--sp-s48) slot is the touch target; the card surface shows through (no background of its own).
Skeleton
-
1
Placeholder tint --surface-overlay — the Skeleton bars are re-tinted for this surface.Skeleton/OnWhite would not read against the beige card.
-
2
Icon placeholder var(--sp-s24) square, radius var(--sp-s6) — stands in for the StartSlot icon. EndSlot has no placeholder.
-
3
Text placeholders Title bar — full width, skeleton-text--main-body.Subtitle bar — var(--sp-s80) wide, skeleton-text--compact-body.Stack gap 0 with space-between; CornerSlot hidden; the shimmer and its reduced-motion stop come from the Skeleton master.
Anatomy
Vertical stack on the card surface — StartSlot, StartText, EndSlot — plus the CornerSlot overlay pinned to the top-right corner. StartText is the only mandatory part.
-
1
StartSlot Free-content slot, fill × hug, left/top. IconContainer by default — any DS element.Optional, visible by default.
-
2
StartText Stack of TextRow instances, gap var(--sp-s4).Title — Main Body, --text-and-icon-primary (always visible).Subtitle — Compact Body, --text-and-icon-secondary (below the Title by default; can be off / above).
-
3
EndSlot Same contract as StartSlot — free content, fill × hug. Optional, visible by default.
-
4
CornerSlot var(--sp-s48) × var(--sp-s48) touch target overlaying the top-right corner — Checkbox instance by default.Optional, visible by default. Mirrors to the top-left in RTL. Checkbox has no web master yet — the page composes it from the Figma geometry.
Layout
All internal spacing uses SP tokens. Width and height are instance-level (--card-width / --card-height): the default width is calc(var(--sp-s80) + var(--sp-s80)) — 160 — and the height hugs the content (136 with all slots visible).
customCard — default
-
General Vertical stack on --surface-on-white.var(--sp-s16) — padding on all sides.var(--sp-s8) — gap between StartSlot / StartText / EndSlot.var(--sp-s20) — roundness.var(--sp-s0) — border; instances may add 1px solid var(--border-default).
-
1
StartSlot / EndSlot Fill × hug rows inside the padding area; the default IconContainer content is var(--sp-s24) tall.Free slots — any DS element fits (see Slot content below); the row hugs whatever height it gets.
-
2
StartText Gap var(--sp-s4) between the rows.Title line-height 20 · Subtitle line-height 16 — 40 total.
-
3
CornerSlot var(--sp-s48) × var(--sp-s48) pinned to the top-right corner with var(--sp-s8) top/right padding — the 24px Checkbox lands var(--sp-s16) from both card edges.Overlay — it does not participate in the stack.
Slot content
default
fill × hug · align start / top
illustration / photo
fill × hug · align end / bottom
Lottie host…
fill × fill · align end / center
-
A slot is a free container StartSlot and EndSlot are Slot instances — fill × hug, no visual style of their own. IconContainer is the default content, not the only option.
-
1
IconContainer var(--sp-s24) DS icon inheriting --text-and-icon-primary — the default in both slots.
-
2
ImgContainer Illustration or photo: .card__artwork (full-width strip) or .card__img-container (crop window), positioned by the mediaPin axis with the media knobs from §4b.Card-owned content classes today — no standalone ImgContainer spec yet.
-
3
Any other DS element Tag, Button, Squircle, Rating, a Lottie animation host — whatever the composition needs.Nested components keep their own default classes and states (no opacity or colour bleed from the card); a non-default colour goes inline on that element.
Animation
The base card is static. Interactive instances add .card--interactive and press with the catalogued pushItem recipe (motion-rules §6.1 — cards are medium/large components: scale, not colour). Under prefers-reduced-motion: reduce both props resolve to 0ms — the scale applies instantly.
Motion
Pulses until you interact.
-
1
Press — pushItem Scale 100% → 95% over var(--card-push-item-press) (200ms, standard-ease-in-out).
-
2
Release Scale 95% → 100% over var(--card-push-item-release) (200ms, standard-ease-in-out).
-
3
Skeleton shimmer Owned by the Skeleton component (Patterns/Shimmer) — the card only re-tints the placeholders.
Usage
Use when a discrete content unit needs its own surface — product tiles, category cards, option cards in a grid or a scrollable row. The CornerSlot makes a card selectable.
-
1
Selectable option card A discrete choice with visual context — tapping the card (or its Checkbox) toggles the selection. One surface per option, options side by side.
-
2
A list row is not a Card A full-width tappable row inside a list is a Cell — it has no own surface and composes into a cell-list. Stretching a Card into a row duplicates Cell with the wrong contract.
Accessibility
A display card reads as one group — "{Title}, {Subtitle}"; an interactive card is a button (or a link when it navigates). The two platforms carry the same structure and word it differently: an interactive card ends in "button" on VoiceOver and in "double-tap to activate" on TalkBack, and the nested Checkbox reads "checkbox, unchecked" against "not checked, checkbox". A Skeleton card is silent on both, with the owning container reporting the loading. The per-platform tables are the reference: specs/components/card/card-a11y.md.
Screen reader
-
1
One announcement per card Non-interactive card: role="group" with the label composed as "{Title}, {Subtitle}" (Title only when the Subtitle is hidden). Interactive card: role="button", same label.
-
2
CornerSlot Checkbox — independent Always focusable separately from the card, with its own label and aria-checked state. Tab order: card root (if interactive) → Checkbox. Space toggles.
-
3
Skeleton — silent The whole skeleton card is aria-hidden="true" and unfocusable; the owning container carries aria-busy="true" while loading.
- Contrast
-
5
Text and icons on OnWhite Title --text-and-icon-primary and Subtitle --text-and-icon-secondary on --surface-on-white — ≥ 4.5:1. Slot icons and the Checkbox border — ≥ 3:1 (non-text UI). Skeleton placeholders are decorative and exempt.