Skip to content
txbrendan/ui

Receipt

beta

What was paid, by whom, when, and how, with the status after the title. A declined payment puts its fixes where it is said.

Preview

paid, by, when, method, reference

October subscriptionPaid

  • Team plan1 to 31 Oct£15

Total£15

Paid by
Kate Clarke
When
Tue 29 Sep, 18:04
Method
Visa ending 4242
Reference
INV-0042

declined, note, onRetry, onChangeCard

October subscriptionDeclined

Your bank turned it down. No money was taken.

  • Team plan1 to 31 Oct£15

Total£15

When
Tue 29 Sep, 18:04
Method
Visa ending 4242

Nothing pressed yet

refunded, a discount with pence, in a Sheet

Install

  1. 1. Add the registry to components.json, once per app. Without it, the shadcn CLI looks up @txbrendan in a public list that txbrendan/ui does not control.

    npx shadcn@latest registry add "@txbrendan=https://txbrendan.dev/r/{name}.json"
  2. 2. Add the theme, once per app. It replaces your color tokens with ones checked for contrast.

    npx shadcn@latest add @txbrendan/theme
  3. 3. Add the component.

    npx shadcn@latest add @txbrendan/receipt

Use it for

  • A payment's record, inside a Card or a Sheet. It draws no box of its own.
  • status follows the title as colored text: Paid in success, Processing in brand, Refunded in neutral, Declined in danger.
  • A declined payment: onRetry puts a retry icon right beside Declined, note says why in plain words, and onChangeCard adds Try another card under it.
  • A refund's note can say where the money went, like "£15 back to Visa ending 4242 on Wed 30 Sep".
  • A discount is a negative amount. It shows a true minus, like −£20, and screen readers hear "minus £20".
  • Amounts show pence only when some amount has them, so the column lines up.

Avoid it for

  • A badge or a box for the status. The status is colored words after the title.
  • A list of payments. Use DataTable or ListRow, and open a Receipt from a row.

Props

  • title

    Required. What was paid for, like "October subscription".

    string

  • status

    Shown as Paid, Processing, Refunded, or Declined.

    "paid" | "pending" | "refunded" | "declined"

  • note

    One line under the title, in ink.

    string

  • lines

    What the total is made of. A negative amount is a discount.

    { label: string; amount: number; meta?: string }[]

  • total

    Default the sum of the lines.

    number

  • currency

    The money sign.

    string · default "£"

  • by

    Who paid, drawn by Person with their photo.

    { person: string; photo?: string }

  • when

    Like "Tue 29 Sep, 18:04".

    string

  • method

    Like "Visa ending 4242".

    string

  • reference

    The payment's reference.

    string

  • onRetry

    Declined: a retry icon beside Declined. Return a promise and it waits, and a failure turns it into Retry.

    () => void | Promise<unknown>

  • retryLabel

    Names the retry icon, in its tooltip.

    string · default "Try again"

  • onChangeCard

    Declined: a quiet text button with a card icon, under the note.

    () => void

  • changeCardLabel

    Its words.

    string · default "Try another card"

  • className

    Extra classes on the root. Later classes win.

    string

Accessibility

  • The status is words, so it never leans on color alone.
  • The retry icon is a real button named by retryLabel, in its tooltip too. Try another card is a real button with words.
  • A discount is read as "minus £20", and the true minus sign shows.
  • The payer, the time, the method, and the reference are a description list, so each is read with its label.

Examples

Paid

<Receipt
  title="October subscription"
  status="paid"
  lines={[{ label: "Team plan", meta: "1 to 31 Oct", amount: 15 }]}
  by={{ person: "Kate Clarke" }}
  when="Tue 29 Sep, 18:04"
  method="Visa ending 4242"
  reference="INV-0042"
/>

Declined

<Receipt
  title="October subscription"
  status="declined"
  note="Your bank turned it down. No money was taken."
  onRetry={() => retry()}
  onChangeCard={() => openCards()}
  lines={[{ label: "Team plan", amount: 15 }]}
/>