Commerce & auth
Money Card
Interactive financial wallet card with quick-send contacts and animated transfer sheets
detent-money-card
Port status
- Catalog status
- implemented
- Origin
- upstream
- Phase
- 3
- Wave
- phase-3-data
- Group
- Commerce & auth
14.8 KB gzip for the class module
Variants
Sample data; nothing is fetched or sent. The card above the Variants heading shows the component's documented
defaults (five quick-send contacts, the last transaction and the send/receive sheets). This one is filled through
the property-only `user`, `contacts` and `transactions` surface in euros. Open the settings drawer to mask every
amount, and the receive sheet to copy the payment link; both emit
detent-send/detent-request with { req: { contact, amount, note } }
and the demo only logs them.
Recorded states
pressedrecorded state; no settererrorrecorded state; no setterclosedrecorded state; no setterdraggingrecorded state; no setterreduced motionrecorded state; no setter
Usage
Import
import '/components/money-card/define.js';
// or register it yourself
import { DetentMoneyCard } from '/components/money-card/money-card.js';
Attributes
balancecurrencylocalepayment-link
Properties
usercontactstransactionsonSendonRequestonViewAll
Slots
defaultsrc
Events
detent-send— {}detent-request— {}detent-view-all— {}detent-close— {}detent-exited— {}detent-submit— {}
Example
Runnable example: site/examples/money-card.ts. The snippet below is generated from the catalogued attributes.
<detent-money-card
balance="52002.5"
currency="USD"
locale="en-US"
payment-link="pay.example/ana"
></detent-money-card>
Property-only values
const element = document.querySelector('detent-money-card');
if (element !== null) {
// user: { name: string; avatar: string }
element.user = undefined;
// contacts: Contact[]
element.contacts = [];
// transactions: Transaction[]
element.transactions = [];
// onSend: (req: TransferRequest) => Promise<void>
element.onSend = () => {};
// onRequest: (req: TransferRequest) => Promise<void>
element.onRequest = () => {};
// onViewAll: () => void
element.onViewAll = () => {};
}
Source
The pinned upstream source for this row:
src/components/ui/MoneyCard.tsxsha256 3e7030d1f578…
Native files
The native module and its artifacts:
src/components/money-card/money-card.tssrc/components/money-card/styles.csssrc/components/money-card/define.tsdocs/components/money-card.mdsite/examples/money-card.tstests/browser/money-card.spec.tsevidence/components/money-card.md
API
Boolean attributes follow HTML presence semantics: present means true, absent means false, and disabled="false" still means disabled.
Attributes
| Attribute | Property | Type | Default | Notes |
|---|---|---|---|---|
balance | balance | number | 52002.5 | Kebab-case attribute; camelCase property. |
currency | currency | string | 'USD' | Kebab-case attribute; camelCase property. |
locale | locale | string | 'en-US' | Kebab-case attribute; camelCase property. |
payment-link | paymentLink | string | 'pay.example/ana' | Kebab-case attribute; camelCase property. |
Properties
| Property | Type | Default | Notes |
|---|---|---|---|
user | { name: string; avatar: string } | DEFAULT_USER | Property-only: assign from script; not accepted as an attribute. |
contacts | Contact[] | DEFAULT_CONTACTS | Property-only: assign from script; not accepted as an attribute. |
transactions | Transaction[] | DEFAULT_TRANSACTIONS | Property-only: assign from script; not accepted as an attribute. |
onSend | (req: TransferRequest) => Promise<void> | undefined | Property-only: assign from script; not accepted as an attribute. |
onRequest | (req: TransferRequest) => Promise<void> | undefined | Property-only: assign from script; not accepted as an attribute. |
onViewAll | () => void | undefined | Property-only: assign from script; not accepted as an attribute. |
Events
| Event | Upstream prop | Payload type | Notes |
|---|---|---|---|
detent-send | onSend | (req: TransferRequest) => Promise<void> | Composed, bubbles; fires only for a user action. |
detent-request | onRequest | (req: TransferRequest) => Promise<void> | Composed, bubbles; fires only for a user action. |
detent-view-all | onViewAll | () => void | Composed, bubbles; fires only for a user action. |
detent-close | onClose | () => void | Composed, bubbles; fires only for a user action. |
detent-exited | onExited | () => void | Composed, bubbles; fires only for a user action. |
detent-submit | onSubmit | (req: TransferRequest) => Promise<void> | Composed, bubbles; fires only for a user action. |
Slots
| Slot | Notes |
|---|---|
default | Consumer content; rendered through a slot, never re-created. |
src | Consumer content; rendered through a slot, never re-created. |
CSS customisation
- `className` prop is replaced by documented CSS parts/custom properties; no Tailwind in consumers.
- React callback props become typed composed CustomEvents; programmatic state assignment emits none.
- `lucide-react` icon components replaced with local SVG assets (ISC notice retained).
- React portals replaced with native dialog/top-layer and DOM insertion.
- detent-submit is recorded but never dispatched: upstream onSubmit is TransferSheet.params.onSubmit, the same user action as detent-send/detent-request, and emitting both would be two events for one action (PC-6.6).
- detent-send and detent-request are fire-and-forget: the upstream promise success screen and rejection message are consumer-owned, and the sheet stays open until the consumer updates it.
- React nodes become strings (user.avatar, contact.avatar, Transaction.icon); BallIcon() returns a local SVG and the icon value "ball" renders it.
- Image values are checked against the R-3 policy: data: and javascript: values never become an img src.
- Debit amounts keep the minus sign (-0.00) where upstream printed 0.00 (PC-12.6).
- The balance panel uses ink #101828 on a lightened blue gradient because the upstream white at 85% failed 4.5:1.
- Masking also covers the transfer hint and the live region, so no hidden balance survives anywhere in the shadow tree.
- mode/open/title/labelId/initialContactId are internal Sheet/TransferSheet params, not host API: observedAttributes is exactly balance, currency, locale and payment-link.
- Framer springs become a cubic-bezier(0.23,1,0.32,1) transition plus one rAF counter (PC-10.4 substitution; the motion gate stays open).
- className is replaced by documented CSS parts and custom properties; BallIcon and the sheet icons are local SVGs.
What it does
A wallet card with a balance panel, quick-send contacts, a transfer sheet with keypad and contact picker, an activity list and a settings drawer.
Upstream behaviour
- Animated balance counter utilizing MotionValue and custom formatters for fluid number interpolation
- Interactive send and receive sheets with built-in keypad, contact selector, note input, and error handling
- Quick-send contact avatars straddling the card boundary with spring-press tactile response
- Animated transaction feed displaying directional amount badges and expandable activity list
- Settings drawer with toggleable balance masking and copyable personal payment link
- Container query architecture ensuring flawless responsiveness across all screen sizes
Differences from upstream
- `className` prop is replaced by documented CSS parts/custom properties; no Tailwind in consumers.
- React callback props become typed composed CustomEvents; programmatic state assignment emits none.
- `lucide-react` icon components replaced with local SVG assets (ISC notice retained).
- React portals replaced with native dialog/top-layer and DOM insertion.
- detent-submit is recorded but never dispatched: upstream onSubmit is TransferSheet.params.onSubmit, the same user action as detent-send/detent-request, and emitting both would be two events for one action (PC-6.6).
- detent-send and detent-request are fire-and-forget: the upstream promise success screen and rejection message are consumer-owned, and the sheet stays open until the consumer updates it.
- React nodes become strings (user.avatar, contact.avatar, Transaction.icon); BallIcon() returns a local SVG and the icon value "ball" renders it.
- Image values are checked against the R-3 policy: data: and javascript: values never become an img src.
- Debit amounts keep the minus sign (-0.00) where upstream printed 0.00 (PC-12.6).
- The balance panel uses ink #101828 on a lightened blue gradient because the upstream white at 85% failed 4.5:1.
- Masking also covers the transfer hint and the live region, so no hidden balance survives anywhere in the shadow tree.
- mode/open/title/labelId/initialContactId are internal Sheet/TransferSheet params, not host API: observedAttributes is exactly balance, currency, locale and payment-link.
- Framer springs become a cubic-bezier(0.23,1,0.32,1) transition plus one rAF counter (PC-10.4 substitution; the motion gate stays open).
- className is replaced by documented CSS parts and custom properties; BallIcon and the sheet icons are local SVGs.