shadcn.io is not affiliated with official shadcn/ui
Receipt Details Billing Block for Svelte
A payment's receipt in a side sheet shown open: the lines and totals with a refund taken off, the payment trail from authorised to settled with each id and a copy button, and a text download of exactly what it shows.
Installation
Overview
Show what happened to a payment, not only what it cost, with this receipt details sheet for Svelte with Ark UI. It opens from the Payments page of Bramblemoor, invoicing for freelancers: the page's header holds an outline Open receipt Button, and the payments list, three rows with a tile, a receipt number and a status chip, is drawn under a soft scrim inside a hairline frame with a plus mark on each corner. The Sheet opens at the end of the page. It lists the two lines of receipt BM-20418 with the subtotal, the sales tax, the total, the $10.00 refund and the net paid, all worked out from typed cents, then the payment trail as an ordered list: Authorised, Captured, Partly refunded and Settled to the merchant, each with its day and time in UTC and its ids in monospace, every one with a Copy Button. The settlement day is counted in business days from the capture. Download receipt (text), the only Shine Button, saves a text file built from those lines and that trail through a Download Trigger. For preview the sheet is drawn open in place when the page loads; the header button opens the real Sheet, with the same content. For invoicing tools, marketplaces and any payment that has more than one step.
FAQ
Why is the sheet already open, and what do I keep?
So you can see it without a click. On load the sheet is a copy drawn in place at the end of the frame: a native dialog element that isn't modal, so nothing is trapped, hidden or focused. The Open receipt button opens the real Sheet, modal, with its focus trap, Escape and focus return, and the copy stays closed until a reload. Both show the same ReceiptBody and ReceiptFoot components and share the block's state. In your app keep the Sheet and drop the copy: the "in-place" state, the scrim and the dialog element inside the frame.
How do I fill it with a real payment?
Replace LINES, STOPS and SETTLEMENT_ID with the payment from your processor: the lines in integer cents, the instants of authorisation, capture and refund as ISO strings in UTC, and the ids it gave you. The subtotal, tax, total, net and the settlement day are derived, so they stay in step with the lines. Tax is a rate in basis points rounded to the cent, and a refund is subtracted from the total, never from a line.
Where does the settlement day come from?
From the capture and a count of business days, Monday to Friday, which is two in the example. It does not know bank holidays or your processor's real schedule, so in your app show the date the processor reports and keep this helper only for an estimate you label as one.
What is real in the demo, and what is example data?
Copying an id and downloading the file are real: each copy button writes its id to the clipboard and says so, and the download saves receipt-bm-20418.txt with the same lines, totals and trail the sheet shows. The receipt itself is example data. Nothing was charged, no card network or bank was asked, and the ids are made up. The page does not print: add your own print stylesheet if you need paper.
Which side does the sheet open from?
From the end of the page's reading direction: the right in a left-to-right page and the left in a right-to-left one. The sheet's own sides are physical, so sideOf reads the page's direction when the sheet opens. The copy shown on load uses logical positions. On a phone the copy docks to the foot of the frame as a bottom sheet, and the real sheet is full width.
Is it accessible from the keyboard?
Yes. The real sheet is named by its title and described by its sentence, focus stays inside until it closes, and Escape or Close returns focus to Open receipt. Each stop of the trail is a list item with its time in a time element, every copy button names the id it copies, and what changed is said in a polite message. The trigger is the button wrapped in SheetTrigger with asChild, and the sheet is controlled with open and onOpenChange.