The checkout you already know, moving
I stopped trying to invent a new object for paying. Stripe already won the shape. The remainder is Motion: a card that fills as you type, a flip for CVC, a button that becomes a check.
The object at the top is a checkout. It looks like Stripe on purpose. Navy summary, white form, email, card, a purple Pay button. I had already tried to replace that object with a letter and a wax seal. It was a fun afternoon. It was also the wrong fight.
People already know this surface. Inventing a new one asks them to learn paying again. The opportunity was never the fields. It was whether those fields feel like a form you survive, or a moment that notices you are there.
So I kept the shape and spent the craft on Motion. Type a number. The card fills. Focus CVC. It turns around. Add a courtesy. The total springs. Hit Pay. The button becomes processing, then a check. Nothing is charged. Fill test card uses 4242 and stays in this browser.
What it is
A Stripe-shaped checkout with the interactions the hosted page never bothers with.
- A summary: merchant, good, amount. The total is a spring, not a string swap.
- A courtesy: chips with a shared-layout pill, the way a selected tab should feel.
- Express checkout: Apple Pay as a sheet, plus a test-card fill so you are not typing sixteen digits to see the point.
- A live card: brand mark, number, name, expiry. CVC lives on the reverse, so focus flips the card.
- A grouped card field: number, expiry, CVC in one box. Stripe's Payment Element, more or less.
- A Pay button that has a duration. Processing, then Paid, with a check that springs in.
Three goods underneath so you can feel the amount move without leaving the page. The Lab is the wider stage. The note is the argument with the object still in it.
Why I stopped inventing a new object
Checkout is a solved artefact. Email. Card. Country. A primary button with the amount on it. Stripe, Shop Pay, and the Apple sheet are wearing the same coat because customers already own that mental model. Fighting it is how you get a portfolio piece that nobody would ship.
The hosted page is also a ceiling. It is excellent at PCI, at tokens, at not going to jail. It is indifferent to whether the card in your hand and the card on screen agree. It does not flip for CVC. It does not let the total move like a physical quantity. Pay is a click that may or may not be done. Those are the bits a designer still owns, even when the processor is a pipe underneath.
I wanted the remainder: the familiar object, with the motion it deserved.
I still started in Figma
I did not open a code editor and hope Stripe would appear with nicer springs.
I started in Figma because I wanted somewhere cheap to lock the grammar of a checkout people already trust, before I spent any time making it move. Not seven experimental objects. The real questions were smaller.
- Left panel or stacked? (Stripe's two-column Session: navy summary, white form.)
- Where does the card live as a preview, not only as a field?
- Does CVC deserve a flip, or is that theatre that gets in the way?
- How does a courtesy appear in the summary without looking like a coupon?
- What does Pay look like while it is working?
- What does done look like if it is not a thank-you URL?
Those questions have visual answers. Stripe navy #0a2540. Blurple #635bff on the button, because that is the cultural colour of this object now. Grouped card field with a hairline split. 8px radii. Inter on the form. A metallic card with a chip, because a preview that does not look like a card is just a receipt of keystrokes.
Figma is where I locked that language. Density, the split between summary and form, what must still scan as Stripe at a glance. I was not trying to prototype the flip in a file. Smart animate will lie to you about preserve-3d. I was trying to make the familiar object tight enough that code would have something to be loyal to.
Then I stopped designing the picture.
The picture needed a system underneath it
Same as the access row. Before much of the interface existed in a browser, I wrote down the things the UI would have to represent.
Good
└── Amount (cents)
└── Courtesy (0 / 15 / 18 / 20)
Card
├── Number → brand
├── Expiry
├── CVC (flip)
└── Name
Pay
├── Idle
├── Processing
└── Paid
Wallet
└── This-device sheetOnce CVC can flip the preview, the reverse of the card is a real surface, not a third input with a tooltip. Once Pay has Processing, the button is allowed to take time. Once courtesy is a stop, not a text field, the total has a reason to spring. Amounts live in cents so the figure cannot drift. The processor, in a real build, sits under Card as a pipe. In this prototype the pipe is imaginary on purpose.
Then the file had to move
A static checkout is polite. It will show you empty fields, filled fields, a loading button, a success screen. It will not tell you whether a 180-degree card flip is delightful or seasick. It will not tell you that a total which pops to a new string feels like a bug, and a spring feels like money moving. It will not tell you that a Pay button with no processing state feels broken even when it worked.
So the Figma file became a spec I could argue with, and the Lab became the argument.
Figma (Stripe grammar)
↓
Product model (good, card, pay states)
↓
Next.js + Motion
↓
Type into the card
↓
"Oh. That's wrong."
↓
Design againWhat changed once it was real: the flip had to spring, not ease, or it felt like a CSS tutorial. The brand mark needed a wait-mode crossfade or Visa and Mastercard stacked. The courtesy pill uses layoutId so the selection slides instead of blinking. The pay label swaps with AnimatePresence so Processing does not crop. Reduced motion zeros the flip and the springs and keeps the states. Keyboard users get the same form; CVC still flips on focus.
None of that is visible in a still. All of it is the reason I do not treat Figma and code as a handoff. Figma decided it still had to look like checkout. The browser decided whether it felt like one.
The stack was ordinary. The movement wasn't.
- Figma for the familiar grammar: summary, form, card, pay
- Next.js + React + TypeScript for the checkout
- CSS for the Stripe-shaped surface
- Motion for the preview flip, shared-layout chips, pay swap, success check, reduced motion
- The same fast loop between file and browser I wrote about yesterday
Springs on this surface stay quiet. Money is not a wedding site. Overshoot on a price looks like a slot machine. The card flip can bounce a little, because a card is physical. The total should not. Different verbs, different physics.
A processor still has to live underneath
I am not pretending you ship this without Stripe, Adyen, or someone who will take the PCI scope. You should not. The processor is a pipe. The hosted page is a default, not a ceiling. We have spent years letting that default become the cultural shape of money on the internet, and then wondering why every startup checkout feels the same down to the padding on the CVC field.
This is a Lab prototype. Demo card, no network, no charge. A real build would still tokenize, still 3-D Secure, still fail some cards on a Tuesday. The argument is that none of that entitles the vendor to the motion.
Figma made it still look like checkout. Code made the card something you can watch. If you want the wider stage, it lives in the Lab. If you just wanted to flip the card, you already did.
Tender is a Lab prototype. Full two-column checkout, three goods, Apple Pay sheet.
Open Tender in the Lab →