(()=>{const status = document.querySelector('[data-order-status]');const format =newIntl.NumberFormat(document.documentElement.lang ||'en',{style:'currency',currency:'EUR',maximumFractionDigits:0});
document.querySelectorAll('[data-order-card]').forEach((card)=>{const input = card.querySelector('input[type="number"]');const total = card.querySelector('[data-order-total]');const title = card.querySelector('.order-card__title').textContent.trim();// The number group dispatches "change" after every step; update the line total.
input.addEventListener('change',()=>{
total.textContent = format.format(Number(card.dataset.unitPrice)*Number(input.value));});
card.querySelector('[data-order-remove]').addEventListener('click',()=>{// Move the focus to the next card (or the previous one) before this one is gone.const next = card.nextElementSibling?.matches('[data-order-card]')? card.nextElementSibling : card.previousElementSibling;
card.classList.add('is-removing');setTimeout(()=>{
card.remove();
next?.querySelector('[data-order-remove]')?.focus();
status.textContent =`${title} removed from the order.`;
document.dispatchEvent(newCustomEvent('order:remove',{detail:{ title }}));}, window.matchMedia('(prefers-reduced-motion: reduce)').matches ?0:200);});});})();
Order Card
On this page
An order line for carts and checkouts: thumbnail, product details, a quantity control, the line total and a remove button.
Anatomy
.order-card is an article with the unit price in data-unit-price.
.order-card__thumb holds a product image or an icon.
.order-card__title, .order-card__meta (a description list for the variant and unit price).
.order-card__actions groups the Number Group, the line total (.order-card__total) and the remove button.
Accessibility
The quantity input is named by "Quantity of" + the product title (aria-labelledby), so every input in the list is distinguishable.
The remove button reads "Remove" and is described by the product title (aria-describedby).
After a removal, the focus moves to the next line's remove button, and a live region announces "… removed from the order.". Without this, the focus would be lost on the page.
The line total is a polite live region, so its change is announced after a quantity change.
Technical Details
Vanilla JavaScript. Load the Number Group script for the quantity controls and card/order.js for the totals and the removal.
The prices are formatted with Intl.NumberFormat, from the page language.
A removal dispatches an order:remove event on the document, so you can update the cart summary or send the request to your back end.
On narrow screens the actions go to their own row; from the sm breakpoint, the card is one row.
Documentation
Learn about Spruce CSS through our extensive documentation.