Skip to content

Built · v1.0 · Figma set

Action Card

An Action Card pairs a short message with one thing to do about it. It is the component that sells: credits, plans, ad-free, features worth discovering. It is not navigation. If the tap just takes you somewhere, that is a List Row.

Default

Get CreditsBuy credits instantly or earn them by completing small actions

Promo

NEWStellar PlanUnlimited US calls & texts from $5/moUpgrade

Upsell

Ad-FreeRemove all ads from the app and enjoy an uninterrupted experiencefrom $1.99/MOChoose plan

Feature

Free CallsCall any US number for free with your textPlus number

Four types, all 358 wide, all radius/lg (12), all padded 16 with a 16 gap. Flip the theme and watch the Feature card turn over: it is the one surface in the system that reverses with the mode, dark on light and light on dark.

Use when

  • Promoting an offer, a plan or a paid feature.
  • Surfacing something worth discovering that the user has not asked for.
  • Pairing one message with exactly one action.

Don’t use when

  • The tap only navigates. That is a List Row.
  • The screen already has its one primary action. A card never competes with it.
  • You need two actions. Cut one, or it is not a card.

A horizontal row, padding 16 on all four sides, gap 16, radius 12.

  1. Illustration. 48 square, instance-swap, off by default on the selling types.
  2. Content. A vertical stack with a 4 gap: Title Inter Medium 16 / 24 on text/primary, Description Inter Regular 14 / 20 on text/secondary.
  3. Trailing. Either a 16 chevron, or a real Button instance at Size=XSmall. The card does not draw its own button.

That last point is worth holding onto: Promo and Upsell embed the actual DS Button component, not a copy of it. When the button ladder changed in August 2026, both cards moved with it for free. That is the whole argument for instance-swap slots over redrawn parts (D-010).

TypeFillHeightTrailingReads as
Defaultscheme/surface100chevrona thing you can go do
Promoscheme/background + 1 px stroke/default116Button XSmalla new offer
Upsellscheme/surface133Button XSmalla paid upgrade, with a price
Featurescheme/inverseSurface94chevron on scheme/inversePrimarya capability you already have

Upsell is the tallest because it carries a third line, the price, on text/tertiary. Promo is the only one that carries a badge.

Feature is the one card that has to stand apart from the whole screen, and the honest way to do that is not a brand colour. No ink works on green across all three modes: Green/400 and Green/500 both sit in the middle of the lightness range, so black fails on one side and white fails on the other. That is the same limit Foundations → Color records: green cannot both read as type and hold light type on top of it.

So Feature fills with scheme/inverseSurface, a surface that reverses with the mode: dark in Light, light in Carbon and Navy. It is a Material 3 role, so it brought its own partners rather than inventing anything.

fillinkratio
LightTeal/800 #1B3033Teal/5012.71 ✅
CarbonNeutral/100 #DEDEDENeutral/100015.61 ✅
NavyTeal/100 #D3DFE1Teal/90013.36 ✅

The Light fill is Teal/800, which is already the Navy surface, so the card is made of a colour the system owns rather than a new one. In the dark modes it lands on Teal/100 rather than a near-white, which matters in an app people read in bed. In Carbon the inverse stays in Carbon’s own neutral family instead of borrowing a teal tint.

This is the part worth understanding, because it generalises. The chevron is one of green’s nine jobs and it is not a button, so it stays green. But the surface under it flipped, so the green has to flip with it.

The neat symmetry does not work. Taking the opposite mode’s scheme/primary would put Green/500 on the light card at 2.41, under the 3:1 a line icon needs. What works is scheme/inversePrimary, which takes the opposite mode’s line green, exactly stroke/brand with its modes swapped:

cardchevronratio
LightTeal/800Green/4005.49 ✅
CarbonNeutral/100Green/6003.59 ✅
NavyTeal/100Green/6003.54 ✅

Green/700 would give more headroom on the light card, but a 1.5 stroke of very dark green reads as black, which is the failure Foundations → Color already documents. Green/600 is the most chromatic stop in the ramp and keeps its hue at that weight.

The rule to take away: an inverted surface inverts everything on it, including the accent. If you put anything else on scheme/inverseSurface, take its colour from the mode the surface came from, not from the mode you are in.

One card, one action. If a card needs two buttons, it is two cards or it is a screen.

The card never holds the screen’s primary action. Yellow is rationed one per screen by position, and a promo card is rarely the position that earns it. A card inside a list of content takes an XSmall button; the screen’s real commit stays at the bottom.

Description stays at two lines. At the 360 dp floor a three-line description pushes the button below the fold of the card and the offer stops reading as one object.

The illustration is decoration, not information. It is off by default on Promo and Upsell. If the card only makes sense with the picture, the copy is not doing its job.

scheme/surface → Default, Upsellscheme/background → Promoscheme/inverseSurface → Featurescheme/onInverseSurfacescheme/inversePrimary → chevrontext/primary → titletext/secondary → descriptiontext/tertiary → pricestroke/default → Promo borderradius/lg · spacing 16 / 4