Skip to content

Commit 9d54560

Browse files
docs(ramp): add Design principles section to Headless Buy plan
Captures two cross-cutting API rules so future contributors know the constraints before extending `useHeadlessBuy`: 1. Callbacks-only, three terminal events. No intermediate progress callbacks (onAuthStarted / onKycRequired / etc.) — they would couple consumers to ramp internals and force them to update on every flow change. 2. The consumer renders all visible UI. No render-shape props (loadingText / spinnerComponent / etc.). Headless Ramps is a behavior provider, not a UI provider — Phase 9.5 implements this on the Host side; the API side must stay this shape. Both principles were implicit in the API as designed but undocumented; making them explicit makes them defensible in PR review and harder to erode by accretion. Section sits between "Architecture at a glance" and Phase 1 so it is visible to anyone reading PLAN.md top-down.
1 parent a90c1ed commit 9d54560

1 file changed

Lines changed: 22 additions & 0 deletions

File tree

  • app/components/UI/Ramp/headless

app/components/UI/Ramp/headless/PLAN.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,28 @@ Key idea: the hook orchestrates by (a) seeding the controller with the quote's t
4545

4646
---
4747

48+
## Design principles
49+
50+
Cross-cutting rules that shape what `useHeadlessBuy` does and doesn't expose. They keep the consumer surface small so new consumers (MMPay's `TransactionPayController`, future SDKs, future non-MMPay teams) can integrate without coupling to ramp internals or being forced into a specific UI.
51+
52+
### 1. Callbacks-only, three terminal events
53+
54+
The hook exposes exactly three lifecycle callbacks: `onOrderCreated`, `onError`, `onClose`. Each session ends in exactly one of them. **No intermediate progress callbacks** (`onAuthStarted`, `onKycRequired`, `onPaymentMethodChosen`, etc.) — consumers do not get a play-by-play of the ramp flow.
55+
56+
**Why.** Intermediate callbacks couple consumers to ramp internals; changing the order of OTP / KYC / payment-method screens or adding a fraud step would force every consumer to update. The "wait for any of the three terminal callbacks" model lets ramp evolve internally without breaking consumers.
57+
58+
**How to apply.** When a consumer asks for state-aware copy ("Verifying email…", "Awaiting KYC…"), point them at their own context — they know the quote, the user, and the time elapsed. Don't add a callback. If timing-based copy is the real ask, see the Phase 9 timeout open question for a single opaque "past expected latency" signal that doesn't expose flow internals.
59+
60+
### 2. The consumer renders all visible UI
61+
62+
`useHeadlessBuy` returns no render-shape props (no `loadingText`, no `spinnerComponent`, no required JSX). Consumers render whatever loading indicator, error treatment, or copy fits their design system. **Headless Ramps is a behavior provider, not a UI provider.**
63+
64+
**Why.** Render-shape props would force consumers either to accept ramp's design choices or to opt out via overrides — both bad. The [May 6 design thread](https://consensys.slack.com/archives/C0AK3NXRM7W/p1778072992397499) settled on "consumer owns the visible UI" (Pedro reply 11; Lorenzo + Yanrong reply 9/17; Goktug reply 20). Phase 9.5 implements this on the Host side by stripping its visible chrome.
65+
66+
**How to apply.** When a consumer asks "can the hook render the spinner for me?", the answer is no — give them the three callbacks and let them flip their own `isLoading` boolean. Shared UI primitives belong in a design-system library, not in `useHeadlessBuy`.
67+
68+
---
69+
4870
## Phase 1 — Playground scaffolding (read-only)
4971

5072
Goal: land an empty playground screen wired to the existing `useRampsController`. No behavior changes.

0 commit comments

Comments
 (0)