Order summary
Category: Tables & lists · Tags: checkout, order, cart, totals, discount
Order summary
| Item | Qty | Amount |
|---|---|---|
| Ceramic pour-over set | 1 | $42.00 |
| Paper filters, pack of 100 | 2 | $13.00 |
| Medium roast beans, 500 g | 1 | $18.50 |
| Subtotal | $73.50 | |
| Discount | -$0.00 | |
| Shipping | $5.00 | |
| Total | $78.50 | |
How to use it
Use beside a checkout form to confirm what is being bought and what it will cost. For an editable cart with quantity controls and remove buttons, use a full cart table instead. Fill the rows and data-cents values from your order, check discount codes on the server (the in-page SAVE10 code is only a demo), and change the currency formatting to match your store.
Accessibility
The items and totals are one native <table> with a caption, <th scope="col"> column headings and <th scope="row"> row headings, so every amount is read with its item or total name. The discount code has its own <label for>, Apply is a native submit <button>, and the result (or an error, with aria-invalid on the field) is announced through a polite live region linked by aria-describedby. Text is #0f172a on white (17.85:1), muted text #475569 on white (7.58:1), the discount row #166534 on white (7.13:1), errors #b91c1c on white (6.47:1) and Apply white on #1d4ed8 (6.70:1, 8.72:1 hovered), with a 2px #1d4ed8 focus outline.
Source (copy-ready)
<section class="dcl-order" aria-labelledby="dcl-order-title">
<h2 class="dcl-order__title" id="dcl-order-title">Order summary</h2>
<div class="dcl-order__scroll">
<table class="dcl-order__table">
<caption class="dcl-order__caption">3 items</caption>
<thead>
<tr><th scope="col" class="dcl-order__head">Item</th><th scope="col" class="dcl-order__head dcl-order__num">Qty</th><th scope="col" class="dcl-order__head dcl-order__num">Amount</th></tr>
</thead>
<tbody>
<tr><th scope="row" class="dcl-order__item">Ceramic pour-over set</th><td class="dcl-order__num">1</td><td class="dcl-order__num dcl-order__line" data-cents="4200">$42.00</td></tr>
<tr><th scope="row" class="dcl-order__item">Paper filters, pack of 100</th><td class="dcl-order__num">2</td><td class="dcl-order__num dcl-order__line" data-cents="1300">$13.00</td></tr>
<tr><th scope="row" class="dcl-order__item">Medium roast beans, 500 g</th><td class="dcl-order__num">1</td><td class="dcl-order__num dcl-order__line" data-cents="1850">$18.50</td></tr>
</tbody>
<tfoot>
<tr><th scope="row" colspan="2" class="dcl-order__label">Subtotal</th><td class="dcl-order__num dcl-order__subtotal">$73.50</td></tr>
<tr class="dcl-order__discount-row" hidden><th scope="row" colspan="2" class="dcl-order__label dcl-order__saving">Discount</th><td class="dcl-order__num dcl-order__saving dcl-order__discount">-$0.00</td></tr>
<tr><th scope="row" colspan="2" class="dcl-order__label">Shipping</th><td class="dcl-order__num dcl-order__shipping" data-cents="500">$5.00</td></tr>
<tr><th scope="row" colspan="2" class="dcl-order__label dcl-order__grand">Total</th><td class="dcl-order__num dcl-order__grand dcl-order__total">$78.50</td></tr>
</tfoot>
</table>
</div>
<form class="dcl-order__form">
<label class="dcl-order__code-label" for="dcl-order-code">Discount code</label>
<div class="dcl-order__code-row">
<input class="dcl-order__input" type="text" id="dcl-order-code" name="discount-code" autocomplete="off" spellcheck="false" aria-describedby="dcl-order-message">
<button type="submit" class="dcl-order__apply">Apply</button>
</div>
<p class="dcl-order__message" id="dcl-order-message" aria-live="polite">Try SAVE10 for 10% off.</p>
</form>
</section>
<style>
.dcl-order { max-width: 26rem; padding: 1rem; border: 1px solid #cbd5e1; border-radius: 0.5rem; font: inherit; color: #0f172a; background-color: #ffffff; }
.dcl-order__title { margin: 0 0 0.5rem; font-size: 1.15rem; color: #0f172a; background-color: #ffffff; }
.dcl-order__scroll { max-width: 100%; overflow-x: auto; }
.dcl-order__table { width: 100%; border-collapse: collapse; }
.dcl-order__caption { padding-bottom: 0.4rem; text-align: left; font-size: 0.9rem; color: #475569; background-color: #ffffff; }
.dcl-order__head { padding: 0.4rem 0.25rem; border-bottom: 1px solid #cbd5e1; font-size: 0.85rem; text-align: left; color: #475569; background-color: #ffffff; }
.dcl-order__item, .dcl-order__label { padding: 0.45rem 0.25rem; font-weight: 400; text-align: left; color: #0f172a; background-color: #ffffff; }
.dcl-order__table td { padding: 0.45rem 0.25rem; color: #0f172a; background-color: #ffffff; }
.dcl-order__num { text-align: right; white-space: nowrap; font-variant-numeric: tabular-nums; }
.dcl-order__table tfoot tr:first-child th, .dcl-order__table tfoot tr:first-child td { border-top: 1px solid #cbd5e1; }
.dcl-order__table .dcl-order__saving { color: #166534; background-color: #ffffff; }
.dcl-order__table .dcl-order__grand { border-top: 2px solid #0f172a; font-weight: 700; font-size: 1.05rem; color: #0f172a; background-color: #ffffff; }
.dcl-order__form { margin-top: 1rem; }
.dcl-order__code-label { display: block; margin-bottom: 0.3rem; font-weight: 600; color: #0f172a; background-color: #ffffff; }
.dcl-order__code-row { display: flex; gap: 0.5rem; }
.dcl-order__input { box-sizing: border-box; flex: 1 1 auto; min-width: 0; padding: 0.45rem 0.6rem; border: 1px solid #64748b; border-radius: 6px; font: inherit; text-transform: uppercase; color: #0f172a; background-color: #ffffff; }
.dcl-order__input[aria-invalid="true"] { border: 2px solid #b91c1c; }
.dcl-order__input:focus-visible { outline: 2px solid #1d4ed8; outline-offset: 1px; }
.dcl-order__apply { padding: 0.45rem 1rem; border: none; border-radius: 6px; font: inherit; font-weight: 600; cursor: pointer; color: #ffffff; background-color: #1d4ed8; }
.dcl-order__apply:hover { color: #ffffff; background-color: #1e40af; }
.dcl-order__apply:focus-visible { outline: 2px solid #1d4ed8; outline-offset: 2px; }
.dcl-order__message { margin: 0.4rem 0 0; font-size: 0.9rem; color: #475569; background-color: #ffffff; }
.dcl-order__message.dcl-order__message--error { color: #b91c1c; background-color: #ffffff; }
</style>
<script>
(function () {
var codes = { SAVE10: 10 };
function money(cents) { return "$" + (cents / 100).toFixed(2); }
document.querySelectorAll(".dcl-order").forEach(function (root) {
if (root.dataset.dclBound) { return; }
root.dataset.dclBound = "true";
var form = root.querySelector(".dcl-order__form");
var input = root.querySelector(".dcl-order__input");
var message = root.querySelector(".dcl-order__message");
if (!form || !input) { return; }
function cents(node) { return node ? Number(node.dataset.cents) || 0 : 0; }
function set(selector, text) { var node = root.querySelector(selector); if (node) { node.textContent = text; } }
function render(percent, code) {
var subtotal = 0;
root.querySelectorAll(".dcl-order__line").forEach(function (cell) { subtotal += cents(cell); });
var discount = Math.round(subtotal * percent / 100);
var total = subtotal - discount + cents(root.querySelector(".dcl-order__shipping"));
var row = root.querySelector(".dcl-order__discount-row");
if (row) {
row.hidden = discount === 0;
var label = row.querySelector("th");
if (label) { label.textContent = "Discount (" + code + ")"; }
}
set(".dcl-order__subtotal", money(subtotal));
set(".dcl-order__discount", "-" + money(discount));
set(".dcl-order__total", money(total));
return total;
}
form.addEventListener("submit", function (event) {
event.preventDefault();
var code = input.value.trim().toUpperCase();
var percent = codes[code];
if (!percent) {
input.setAttribute("aria-invalid", "true");
if (message) {
message.classList.add("dcl-order__message--error");
message.textContent = code ? "The code " + code + " is not valid. Check it and try again." : "Enter a discount code.";
}
return;
}
input.removeAttribute("aria-invalid");
var total = render(percent, code);
if (message) {
message.classList.remove("dcl-order__message--error");
message.textContent = code + " applied: " + percent + "% off. New total " + money(total) + ".";
}
});
});
})();
</script>
Was this useful? Send feedback.