shadcn.io is not affiliated with official shadcn/ui
Promo Code Popover Success Block for React
A checkout summary with its promo chip and the popover shown open: which items the code covers, the minimum it needs, the discount and the new total worked out from the items, and one button that removes or applies the code.
Installation
Overview
Show a shopper exactly what a promo code did, with this promo applied popover for React with Base UI. The host is a checkout: a heading and one sentence, then a frame with a plus mark on each corner that holds the page, with a basket drawn in grey shapes and a real order summary beside it. The summary lists four items, a promo row, the subtotal, the discount and the total. The chip in the promo row, "SAVE20 applied", is an outline Button that opens a Popover: the code's terms, each item marked Eligible with a check or Not eligible with a minus (the gift card is excluded), the discount as a percent of the eligible items, and the new total, with a button to remove the code. For preview the popover is drawn open in place when the page loads; the chip opens the real one, with the same content. Removing the code puts the full total back in the summary, and the chip becomes Apply SAVE20. It's built from Button, Popover and Guides, with CSS only. For stores, checkouts and any basket with a code.
FAQ
Why is the popover already open, and what do I keep?
So you can see the maths without a click. On load the popover is a copy drawn in place under the chip: a native dialog element that isn't modal, so nothing is trapped or focused and the page around it keeps working. The chip opens the real Popover, with its own positioning, Escape and focus return, and the copy stays closed until a reload. Both show the same PromoMaths component and share the block's state. In your app keep the Popover and drop the copy: the "in-place" state, its dialog and the Escape effect.
How are the discount and the total worked out?
From the items, in whole cents. The eligible subtotal adds the items marked promo, the code only applies when that comes to MINIMUM or more, the discount is PERCENT of it rounded to a cent, and the new total is the subtotal minus the discount. The popover, the discount row and the total all read the same numbers, and the label "20% of $104.00" shows the figure the percentage was taken from. Edit ITEMS, PERCENT, MINIMUM and VALID_UNTIL; your server must apply the same rules, since this page only shows them.
What stays in the page, and how do I wire it for real?
Everything: no order exists and nothing is charged. Remove code and Apply code change the page's state, close the popover, return focus to the chip and say in a polite status line what the total is ("SAVE20 removed in this demo"). To wire it for real, validate the code on your server in toggleCode, take the discount and the total from its response, and show its reason in the popover when it refuses a code.
Is it usable with a keyboard and a screen reader?
Yes. The chip is a named button that opens the popover; Escape closes the popover and focus goes back to the chip, as it does after Remove code or Apply code. The table has a caption, column headers and row headers, eligibility is written in words with an icon (never colour alone), and the discount keeps its minus sign inside a left-to-right span with "Minus" in text for screen readers. The chip is passed to PopoverTrigger through render; the popover is controlled with open and onOpenChange.
How are amounts and the end date written?
Amounts go through Intl.NumberFormat in a fixed en-US locale, each in a single left-to-right span so signs and digits keep their order on a right-to-left page. The end date is a fixed ISO string written with Intl.DateTimeFormat in UTC in a time element, never compared with the visitor's clock here: your server decides whether a code is still valid.
Related
- Pro
Payment Receipt Success
Payments and refundsA full-height payment confirmation split in two: what happened and what comes next on one side, an itemised receipt table with the tax worked out in cents, a copyable receipt number and a text download on the other.
- Pro
Credit Balance Success
Payments and refundsA narrow full-height confirmation for added credit: the new balance counting up from the old one with the change beside it, and a ledger of the credit by expiry that marks the batch used first.
- Pro
Upgrade Proration Billing
Upgrade and downgradeChanging plan inside app settings with the bill worked out first: pick a higher plan and the credit for unused days, the new plan's cost for those days, what's due today and the next invoice roll live, then an alert dialog confirms the charge.
- Pro
Updated Card Billing Success
Payments and refundsA billing panel confirming a new card: the card drawn in greyscale with its real facts, and a switch per subscription that picks which of them it pays for, with the next charges on the card adding up as you flip them.
- Pro
Account Restored Invoice Success
Payments and refundsA full-height confirmation for a paid overdue invoice: a framed before and now pair of the same four capabilities, locked while the account was suspended and unlocked in turn now, then what was settled, with what and when the next invoice goes out.
- Pro
Payout Route Success
Payments and refundsA full-height confirmation for a payout that is on its way: three stops from the platform through the processor to the bank, each with its day and an arrival window that skips weekends, and the gross, fee and net behind the amount.