Commerce & auth
Payment Receipt Printer
Animated thermal receipt printer with smooth paper extrusion motion
detent-payment-receipt-printer
Port status
- Catalog status
- implemented
- Origin
- upstream
- Phase
- 3
- Wave
- phase-3-data
- Group
- Commerce & auth
11.0 KB gzip for the class module
Variants
Sample data; nothing is fetched, printed or sent anywhere. The printer in the main preview runs its real extrusion
timeline (1.2 s after the pinned 400 ms mount delay) and the chassis vibrates while it prints;
Replay Print, Copy Order and Print are keyboard reachable. The
cards here are pinned to their completed state and show the light, cream and dark paper themes with the serrated cut
edges. The barcode is generated locally from the order number — no image, no network — and the readable code line
under it is real text. With prefers-reduced-motion: reduce every card is complete immediately and no
motion runs.
Recorded states
focus-visiblerecorded state; no setterreduced motionrecorded state; no setteractiverecorded state; no settercompletedrecorded state; no setteridlerecorded state; no setterprintingrecorded state; no settersuccessrecorded state; no setter
Usage
Import
import '/components/payment-receipt-printer/define.js';
// or register it yourself
import { DetentPaymentReceiptPrinter } from '/components/payment-receipt-printer/payment-receipt-printer.js';
Attributes
auto-printcurrencydatediscountmerchantmerchant-subtextmessageorder-numberpaper-themepayment-methodprint-durationprinter-class-namereceipt-class-nameshow-actionsshow-barcodeshow-cut-effectshow-status-cardstatusstatus-subtitlestatus-titlesubtotaltaxtotal
Properties
itemitemsonPrintCompleteonPrintStartonReplay
Slots
merchant-logo
Events
detent-print-start— {}detent-print-complete— {}detent-replay— {}
Example
Runnable example: site/examples/payment-receipt-printer.ts. The snippet below is generated from the catalogued attributes.
<detent-payment-receipt-printer
auto-print
currency="{{MAIN}}quot;
date
discount
merchant="Detent Store"
merchant-subtext="Official Component Registry"
message="Thank you for your order!"
order-number="#4821"
paper-theme="light"
payment-method="Apple Pay •••• 4242"
print-duration="2.4"
printer-class-name
receipt-class-name
show-actions
show-barcode
show-cut-effect
show-status-card
status="idle"
status-subtitle="Receipt has been issued"
status-title="Payment Complete"
subtotal
tax
total="$200.00"
></detent-payment-receipt-printer>
Property-only values
const element = document.querySelector('detent-payment-receipt-printer');
if (element !== null) {
// items: ReceiptItem[]
element.items = [];
// item: ReceiptItem
element.item = undefined;
// onPrintStart: () => void
element.onPrintStart = () => {};
// onPrintComplete: () => void
element.onPrintComplete = () => {};
// onReplay: () => void
element.onReplay = () => {};
}
Source
The pinned upstream source for this row:
src/components/ui/PaymentReceiptPrinter.tsxsha256 c355a8ea5f8b…
Native files
The native module and its artifacts:
src/components/payment-receipt-printer/payment-receipt-printer.tssrc/components/payment-receipt-printer/styles.csssrc/components/payment-receipt-printer/define.tsdocs/components/payment-receipt-printer.mdsite/examples/payment-receipt-printer.tstests/browser/payment-receipt-printer.spec.tsevidence/components/payment-receipt-printer.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 |
|---|---|---|---|---|
auto-print | autoPrint | boolean | true | Presence semantics: present means true, absent means false. |
currency | currency | string | '{{MAIN}}#39; | Kebab-case attribute; camelCase property. |
date | date | string | Date | Current date | Kebab-case attribute; camelCase property. |
discount | discount | string | number | undefined | Kebab-case attribute; camelCase property. |
merchant | merchant | string | 'Detent Store' | Kebab-case attribute; camelCase property. |
merchant-subtext | merchantSubtext | string | 'Official Component Registry' | Kebab-case attribute; camelCase property. |
message | message | string | 'Thank you for your order!' | Kebab-case attribute; camelCase property. |
order-number | orderNumber | string | '#4821' | Kebab-case attribute; camelCase property. |
paper-theme | paperTheme | 'light' | 'dark' | 'cream' | 'light' | Kebab-case attribute; camelCase property. |
payment-method | paymentMethod | string | 'Apple Pay •••• 4242' | Kebab-case attribute; camelCase property. |
print-duration | printDuration | number | 2.4 | Kebab-case attribute; camelCase property. |
printer-class-name | — | — | — | Recorded in the catalog as an attribute. |
receipt-class-name | — | — | — | Recorded in the catalog as an attribute. |
show-actions | showActions | boolean | true | Presence semantics: present means true, absent means false. |
show-barcode | showBarcode | boolean | true | Presence semantics: present means true, absent means false. |
show-cut-effect | showCutEffect | boolean | true | Presence semantics: present means true, absent means false. |
show-status-card | showStatusCard | boolean | true | Presence semantics: present means true, absent means false. |
status | status | 'idle' | 'printing' | 'completed' | 'success' | 'idle' | Kebab-case attribute; camelCase property. |
status-subtitle | statusSubtitle | string | 'Receipt has been issued' | Kebab-case attribute; camelCase property. |
status-title | statusTitle | string | 'Payment Complete' | Kebab-case attribute; camelCase property. |
subtotal | subtotal | string | number | undefined | Kebab-case attribute; camelCase property. |
tax | tax | string | number | undefined | Kebab-case attribute; camelCase property. |
total | total | string | number | '$200.00' | Kebab-case attribute; camelCase property. |
Properties
| Property | Type | Default | Notes |
|---|---|---|---|
item | ReceiptItem | undefined | Property-only: assign from script; not accepted as an attribute. |
items | ReceiptItem[] | [] | Property-only: assign from script; not accepted as an attribute. |
onPrintComplete | () => void | undefined | Property-only: assign from script; not accepted as an attribute. |
onPrintStart | () => void | undefined | Property-only: assign from script; not accepted as an attribute. |
onReplay | () => void | undefined | Property-only: assign from script; not accepted as an attribute. |
Events
| Event | Upstream prop | Payload type | Notes |
|---|---|---|---|
detent-print-start | onPrintStart | () => void | Composed, bubbles; fires only for a user action. |
detent-print-complete | onPrintComplete | () => void | Composed, bubbles; fires only for a user action. |
detent-replay | onReplay | () => void | Composed, bubbles; fires only for a user action. |
Slots
| Slot | Notes |
|---|---|
merchant-logo | Consumer content; rendered through a slot, never re-created. |
CSS customisation
- React `children`/`ReactNode` props become DOM slots; caller content stays light DOM.
- `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.
- Framer Motion springs/AnimatePresence ported to CSS transitions, WAAPI or the shared rAF spring helper.
- `lucide-react` icon components replaced with local SVG assets (ISC notice retained).
- React state/hooks replaced by element-owned state with pre-upgrade property preservation.
- The mount timeline starts idle and enters printing with detent-print-start after the pinned 400 ms delay; upstream mounts in printing and fires its callback 400 ms later, so the paper moves before the event.
- A Print action is rendered because the row feature list records it (the pinned JSX has only Replay and Copy); it restarts the extrusion and emits the same two events, with no new event name.
- The date default is the pinned source literal "Aug 20, 2026 · 10:42 AM"; the upstream meta says "Current date", and the source wins (PC-3.6).
- className is replaced by documented parts and custom properties; printer-class-name and receipt-class-name remain and add class tokens only, never markup.
- Small copy was lifted for contrast (#A1A1A1 state text and LED text, boundaries #6B6B6B for 3:1) as a recorded WCAG correction (PC-12.6).
- The barcode is generated from the order number as local SVG geometry instead of a hard-coded pattern: it stays aria-hidden, keeps a readable code line beneath, and requests no asset or network resource.
- Framer Motion springs become CSS transitions; the vibration, LED and dot are CSS keyframes, all removed under reduced motion, which still reaches the completed state.
- ReceiptItem.tag is accepted and not rendered, exactly as upstream.
- Default demo copy and labels use Detent branding instead of EasyUI (pre-release rebrand).
What it does
A thermal receipt printer: a payment status banner, a printer chassis with its feed slot and LED, the receipt extruding out of the slot with serrated cut edges, and the Replay / Copy Order / Print action bar.
Upstream behaviour
- Authentic thermal paper extrusion animation emerging downward from printer slot
- Subtle chassis vibration physics during active printing phase
- Multiple receipt items support with automatic dynamic height calculation
- Customizable merchant brand, order number, payment method, taxes, barcode, and message
- Interactive Replay, Copy Order Number, and Print action controls
- Light, dark, and cream paper themes with serrated paper perforation cuts
- Full accessibility support with aria-live status and prefers-reduced-motion detection
Differences from upstream
- React `children`/`ReactNode` props become DOM slots; caller content stays light DOM.
- `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.
- Framer Motion springs/AnimatePresence ported to CSS transitions, WAAPI or the shared rAF spring helper.
- `lucide-react` icon components replaced with local SVG assets (ISC notice retained).
- React state/hooks replaced by element-owned state with pre-upgrade property preservation.
- The mount timeline starts idle and enters printing with detent-print-start after the pinned 400 ms delay; upstream mounts in printing and fires its callback 400 ms later, so the paper moves before the event.
- A Print action is rendered because the row feature list records it (the pinned JSX has only Replay and Copy); it restarts the extrusion and emits the same two events, with no new event name.
- The date default is the pinned source literal "Aug 20, 2026 · 10:42 AM"; the upstream meta says "Current date", and the source wins (PC-3.6).
- className is replaced by documented parts and custom properties; printer-class-name and receipt-class-name remain and add class tokens only, never markup.
- Small copy was lifted for contrast (#A1A1A1 state text and LED text, boundaries #6B6B6B for 3:1) as a recorded WCAG correction (PC-12.6).
- The barcode is generated from the order number as local SVG geometry instead of a hard-coded pattern: it stays aria-hidden, keeps a readable code line beneath, and requests no asset or network resource.
- Framer Motion springs become CSS transitions; the vibration, LED and dot are CSS keyframes, all removed under reduced motion, which still reaches the completed state.
- ReceiptItem.tag is accepted and not rendered, exactly as upstream.
- Default demo copy and labels use Detent branding instead of EasyUI (pre-release rebrand).