CartSummary
Subtotal, shipping, tax, a discount and the total added up, with a Checkout button as a link, a form or a callback.
Demo
Order summary
- Subtotal
- $213.50
- Shipping
- Free
- Tax
- $42.70
- Total
- $256.20
Order summary
3 items
- Subtotal
- $213.50
- Shipping
- $9.90
- SAVE10
- −$21.35
- Total
- $202.05
Taxes are included.
Checkout not clicked.
Your order
- Subtotal
- ¥1,250
- Shipping
- Calculated at checkout
- Gift wrap
- ¥300
- Total
- ¥1,550
Get it
It needs theme.qwe, as every component does, and price.qwe; or take the whole kit, quillo-ui.qwe. Each component brings its own styles to the pages that show it.
$ curl -O https://quillolang.org/components/ui/theme.qwe$ curl -O https://quillolang.org/components/ui/price.qwe$ curl -O https://quillolang.org/components/ui/cart_summary.qwemain.qwe
import ui/theme.qwe use theme_cssuse ui/cart_summary.qweroute GET / = <html><head><style>{theme_css}</style></head><body class="ui-root">...</body></html>Example
The code of the preview above.
demos/cart_summary.qwe
component CartSummaryDemoLive() { state clicked = 0 <div.demo-stack> <CartSummary subtotal={213.5} shipping={9.9} discount={21.35} discount_label="SAVE10" count={3} note="Taxes are included." on_checkout={() => { clicked += 1 }}/> <p.demo-note>{clicked == 0 ? "Checkout not clicked." : `on_checkout called ${clicked} ${clicked == 1 ? "time" : "times"}.`}</p> </div>}view CartSummaryDemo() = <div.demo-row style="align-items: flex-start; gap: 24px"> <div style="width: 300px"> <CartSummary subtotal={213.5} shipping={0} tax={42.7} checkout_href="#cart_summary" continue_href="#product_grid"/> </div> <div style="width: 300px"><CartSummaryDemoLive/></div> <div style="width: 300px"> <CartSummary subtotal={1250} shipping="Calculated at checkout" currency="JPY" locale="ja-JP" title="Your order" lines={[{ label: "Gift wrap", amount: 300 }]} action="/checkout" disabled={true}/> </div> </div>Source
ui/cart_summary.qwe
// CartSummary: the sums at the side of a cart: subtotal, shipping, tax, a// discount, the total, and the Checkout button. Numbers are added up and written// as Price writes them (this file needs price.qwe).//// <CartSummary subtotal={213.5} shipping={0} tax={42.7} checkout_href="/checkout"/>// <CartSummary subtotal={213.5} shipping={9.9} discount={21.35} discount_label="SAVE10"// count={3} note="Taxes are included." on_checkout={() => { step = 2 }}/>//// subtotal: the lines added up. shipping: a number (0 reads "Free"), text// ("Free", "Calculated at checkout"), or left out (not shown). tax: a number, or// left out. discount: taken off, shown in green with a minus; discount_label// names it ("Discount", or the code). total: given, or worked out from the// numbers above. lines: more lines, { label, amount, tone? }, before the total.// count: how many items, said under the title ("3 items"). currency, locale,// cents as for Price. title ("Order summary"). note: a quieter line under the// total. checkout_href makes the button a link; action a form that posts to it;// otherwise, inside one of your components, it calls on_checkout().// checkout_label ("Checkout"). continue_href: a "Continue shopping" link under// it. disabled: the button cannot be clicked (an empty cart).import ui/price.qwe use price_textimport ui/theme.qwe use ui_clslet cart_summary_css = css`.ui-cart-sum { display: grid; gap: 10px; min-width: 0; padding: 18px 20px; border: 1px solid var(--ui-border); border-radius: var(--ui-radius); background: var(--ui-surface); font: 14px/1.4 var(--ui-font); color: var(--ui-fg); }.ui-cart-sum-title { margin: 0; font-size: 16px; font-weight: 600; }.ui-cart-sum-count { margin: -6px 0 0; font-size: 13px; color: var(--ui-muted); }.ui-cart-sum-lines { display: grid; gap: 8px; margin: 0; }.ui-cart-sum-line { display: flex; justify-content: space-between; align-items: baseline; gap: 12px; }.ui-cart-sum-line dt { color: var(--ui-muted); }.ui-cart-sum-line dd { margin: 0; font-variant-numeric: tabular-nums; white-space: nowrap; }.ui-cart-sum-success dt, .ui-cart-sum-success dd { color: var(--ui-success); }.ui-cart-sum-muted dd { color: var(--ui-muted); }.ui-cart-sum-total { padding-top: 10px; border-top: 1px solid var(--ui-border); font-size: 16px; font-weight: 700; }.ui-cart-sum-total dt { color: var(--ui-fg); }.ui-cart-sum-note { margin: 0; font-size: 12px; color: var(--ui-muted); }.ui-cart-sum-form { display: contents; }.ui-cart-sum-btn { display: flex; align-items: center; justify-content: center; width: 100%; height: 42px; margin-top: 4px; border: 1px solid transparent; border-radius: var(--ui-radius-sm); background: var(--ui-accent); color: var(--ui-accent-fg); font: 600 15px/1 var(--ui-font); text-decoration: none; cursor: pointer; transition: background .15s, box-shadow .15s; }.ui-cart-sum-btn:hover { background: color-mix(in srgb, var(--ui-accent) 88%, var(--ui-fg)); }.ui-cart-sum-btn:focus-visible { outline: none; box-shadow: var(--ui-focus); }.ui-cart-sum-btn[disabled], .ui-cart-sum-btn[aria-disabled="true"] { opacity: .55; cursor: not-allowed; pointer-events: none; }.ui-cart-sum-more { text-align: center; font-size: 13px; }.ui-cart-sum-more a { color: var(--ui-accent); }@media (prefers-reduced-motion: reduce) { .ui-cart-sum-btn { transition: none; } }`view CartSummary(subtotal, shipping = null, tax = null, discount = null, discount_label: str = "Discount", total = null, lines: list = [], count: int? = null, currency: str = "USD", locale: str? = null, cents: bool = true, title: str? = "Order summary", note: str? = null, checkout_href: str? = null, action: str? = null, checkout_label: str = "Checkout", continue_href: str? = null, disabled: bool = false, on_checkout = null, class: str? = null) { style global cart_summary_css let num = (x) => x != null && type_of(x) != "str" ? dec(x) : dec(0) let sum = num(subtotal) + num(shipping) + num(tax) - num(discount) + sum(lines | map((l) => num(l.amount))) let grand = total ?? sum let money_of = (x) => <span>{price_text(x, currency, locale, cents)}</span> let ship = shipping == null ? null : type_of(shipping) == "str" ? shipping : dec(shipping) == 0 ? "Free" : price_text(shipping, currency, locale, cents) let button = checkout_href != null ? <a.ui-cart-sum-btn href={checkout_href} aria-disabled={disabled ? "true" : null}>{checkout_label}</a> : <button.ui-cart-sum-btn type={action != null ? "submit" : "button"} disabled={disabled} @click={() => { if action == null && on_checkout != null { on_checkout() } }}>{checkout_label}</button> <section class={ui_cls(["ui-cart-sum", class])} aria-label={title}> {title != null && <h3.ui-cart-sum-title>{title}</h3>} {count != null && <p.ui-cart-sum-count>{count} {count == 1 ? "item" : "items"}</p>} <dl.ui-cart-sum-lines> <div.ui-cart-sum-line><dt>Subtotal</dt><dd>{money_of(subtotal)}</dd></div> {ship != null && <div class={ui_cls(["ui-cart-sum-line", ship == "Free" && "ui-cart-sum-success"])}><dt>Shipping</dt><dd>{ship}</dd></div>} {tax != null && <div.ui-cart-sum-line><dt>Tax</dt><dd>{money_of(tax)}</dd></div>} {discount != null && <div.ui-cart-sum-line.ui-cart-sum-success><dt>{discount_label}</dt><dd>−{price_text(discount, currency, locale, cents)}</dd></div>} {for l in lines => <div key={l.label} class={ui_cls(["ui-cart-sum-line", get(l, "tone") != null && `ui-cart-sum-${l.tone}`])}> <dt>{l.label}</dt><dd>{money_of(l.amount)}</dd> </div>} <div.ui-cart-sum-line.ui-cart-sum-total><dt>Total</dt><dd>{money_of(grand)}</dd></div> </dl> {note != null && <p.ui-cart-sum-note>{note}</p>} {action != null ? <form.ui-cart-sum-form method="post" action={action}>{button}</form> : button} {continue_href != null && <p.ui-cart-sum-more><a href={continue_href}>Continue shopping</a></p>} </section>}Related
CartItemOne line of a cart: picture, name and options, a quantity control, the line's total and Remove, as inputs a form sends.6 views · 6 downloadsProductCardA product in a shop's grid: picture, name, price with the discount, stars, a badge and Add to cart, as a plain form or a callback.6 views · 6 downloadsAddressCardA saved address with a Default badge and Edit and Remove, as submit buttons in a plain form or callbacks.6 views · 7 downloadsBestCouponWorks out the lowest total from several coupons, trying each combination and order under their rules, and explains the winner and why each other coupon lost.3 views · 7 downloadsCheckoutStepsCart → Details → Payment → Done: the steps of a checkout, those done as links back, the current one marked.5 views · 7 downloadsCouponInputA promo code box with Apply: on_apply(code) says whether it took, and an applied code shows with Remove.6 views · 5 downloads
Thread
Thread 0 posts
On CartSummary: questions, tips, corrections, and code that uses it.
No posts yet. The first one opens the thread.