Detent web components
By Shane Logsdon

Commerce & auth

Payment Receipt Printer

Implemented

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-visible recorded state; no setter
  • reduced motion recorded state; no setter
  • active recorded state; no setter
  • completed recorded state; no setter
  • idle recorded state; no setter
  • printing recorded state; no setter
  • success recorded 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-print
  • currency
  • date
  • discount
  • merchant
  • merchant-subtext
  • message
  • order-number
  • paper-theme
  • payment-method
  • print-duration
  • printer-class-name
  • receipt-class-name
  • show-actions
  • show-barcode
  • show-cut-effect
  • show-status-card
  • status
  • status-subtitle
  • status-title
  • subtotal
  • tax
  • total

Properties

  • item
  • items
  • onPrintComplete
  • onPrintStart
  • onReplay

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.tsx sha256 c355a8ea5f8b…

Native files

The native module and its artifacts:

  • src/components/payment-receipt-printer/payment-receipt-printer.ts
  • src/components/payment-receipt-printer/styles.css
  • src/components/payment-receipt-printer/define.ts
  • docs/components/payment-receipt-printer.md
  • site/examples/payment-receipt-printer.ts
  • tests/browser/payment-receipt-printer.spec.ts
  • evidence/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

Attributes
AttributePropertyTypeDefaultNotes
auto-printautoPrintbooleantruePresence semantics: present means true, absent means false.
currencycurrencystring'{{MAIN}}#39;Kebab-case attribute; camelCase property.
datedatestring | DateCurrent dateKebab-case attribute; camelCase property.
discountdiscountstring | numberundefinedKebab-case attribute; camelCase property.
merchantmerchantstring'Detent Store'Kebab-case attribute; camelCase property.
merchant-subtextmerchantSubtextstring'Official Component Registry'Kebab-case attribute; camelCase property.
messagemessagestring'Thank you for your order!'Kebab-case attribute; camelCase property.
order-numberorderNumberstring'#4821'Kebab-case attribute; camelCase property.
paper-themepaperTheme'light' | 'dark' | 'cream''light'Kebab-case attribute; camelCase property.
payment-methodpaymentMethodstring'Apple Pay •••• 4242'Kebab-case attribute; camelCase property.
print-durationprintDurationnumber2.4Kebab-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-actionsshowActionsbooleantruePresence semantics: present means true, absent means false.
show-barcodeshowBarcodebooleantruePresence semantics: present means true, absent means false.
show-cut-effectshowCutEffectbooleantruePresence semantics: present means true, absent means false.
show-status-cardshowStatusCardbooleantruePresence semantics: present means true, absent means false.
statusstatus'idle' | 'printing' | 'completed' | 'success''idle'Kebab-case attribute; camelCase property.
status-subtitlestatusSubtitlestring'Receipt has been issued'Kebab-case attribute; camelCase property.
status-titlestatusTitlestring'Payment Complete'Kebab-case attribute; camelCase property.
subtotalsubtotalstring | numberundefinedKebab-case attribute; camelCase property.
taxtaxstring | numberundefinedKebab-case attribute; camelCase property.
totaltotalstring | number'$200.00'Kebab-case attribute; camelCase property.

Properties

Properties
PropertyTypeDefaultNotes
itemReceiptItemundefinedProperty-only: assign from script; not accepted as an attribute.
itemsReceiptItem[][]Property-only: assign from script; not accepted as an attribute.
onPrintComplete() => voidundefinedProperty-only: assign from script; not accepted as an attribute.
onPrintStart() => voidundefinedProperty-only: assign from script; not accepted as an attribute.
onReplay() => voidundefinedProperty-only: assign from script; not accepted as an attribute.

Events

Events
EventUpstream propPayload typeNotes
detent-print-startonPrintStart() => voidComposed, bubbles; fires only for a user action.
detent-print-completeonPrintComplete() => voidComposed, bubbles; fires only for a user action.
detent-replayonReplay() => voidComposed, bubbles; fires only for a user action.

Slots

Slots
SlotNotes
merchant-logoConsumer 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).
Previous: Particle Delete Next: Payment Status

Docs

Components

Project