OrderTracker
Ordered → shipped → out for delivery → delivered, with when each happened, the expected date and the tracking link.
Demo
Arriving Thursday, 5 November 2026
- Ordered (done)
- Shipped (done)Left the warehouse in Leeds
- Out for delivery
- Delivered
DHL · JD014600003828
Delivered
- Ordered (done)
- Shipped (done)
- Out for delivery (done)
- Delivered (done)Signed for by R. Cole
Cancelled
- Ordered (done)
- Cancelled
- Delivered
Royal Mail
Get it
It needs theme.qwe, as every component does; 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/order_tracker.qwemain.qwe
import ui/theme.qwe use theme_cssuse ui/order_tracker.qweroute GET / = <html><head><style>{theme_css}</style></head><body class="ui-root">...</body></html>Example
The code of the preview above.
demos/order_tracker.qwe
view OrderTrackerDemo() = <div.demo-stack style="gap: 32px"> <OrderTracker current={2} eta="2026-11-05" carrier="DHL" tracking="JD014600003828" tracking_href="#order_tracker" steps={[{ label: "Ordered", at: "2026-11-02T09:12:00Z" }, { label: "Shipped", at: "2026-11-03T16:40:00Z", hint: "Left the warehouse in Leeds" }, { label: "Out for delivery", at: "2026-11-05T07:30:00Z" }, { label: "Delivered" }]}/> <div.demo-row style="gap: 48px; align-items: flex-start"> <OrderTracker vertical={true} current={4} locale="en-US" steps={[{ label: "Ordered", at: "2026-10-20" }, { label: "Shipped", at: "2026-10-21" }, { label: "Out for delivery", at: "2026-10-23" }, { label: "Delivered", at: "2026-10-23T14:05:00Z", hint: "Signed for by R. Cole" }]}/> <OrderTracker vertical={true} current={1} cancelled={true} carrier="Royal Mail" steps={[{ label: "Ordered", at: "2026-10-25" }, { label: "Shipped" }, { label: "Delivered" }]}/> </div> </div>Source
ui/order_tracker.qwe
// OrderTracker: where a delivery is: ordered, shipped, out for delivery,// delivered, each with when it happened, the ones done ticked and the current// one marked, with the carrier's tracking link and the expected date.//// <OrderTracker current={1} steps={[{ label: "Ordered", at: "2026-11-02T09:12:00Z" },// { label: "Shipped", at: "2026-11-03T16:40:00Z" }, { label: "Out for delivery" }, { label: "Delivered" }]}// eta="2026-11-05" carrier="DHL" tracking="JD014600003828" tracking_href="https://..."/>//// steps: a list of { label, at?, hint? }: at when it happened (an ISO date or// date-time, written for locale; or any text as it is), hint a line of detail// ("Left the warehouse in Leeds"). The default steps are the four above.// current: the index of the step being done, from 0; the ones before are// done; len(steps) when all are. cancelled: the current step is crossed in// red instead ("Cancelled"). vertical: one under another (false). eta: the// expected date, said above ("Arriving Thursday 5 November"). carrier,// tracking, tracking_href: the line under ("DHL · JD0146…", a link). locale// for the dates (en-GB). label: what screen readers call it ("Order progress").import ui/theme.qwe use ui_clslet order_tracker_css = css`.ui-order-tracker { display: grid; gap: 14px; min-width: 0; font: 14px/1.4 var(--ui-font); color: var(--ui-fg); }.ui-order-tracker-eta { margin: 0; font-size: 15px; font-weight: 600; }.ui-order-tracker-eta span { font-weight: 400; color: var(--ui-muted); }.ui-order-tracker-steps { display: flex; margin: 0; padding: 0; list-style: none; }.ui-order-tracker-step { position: relative; flex: 1 1 0; min-width: 0; display: grid; gap: 8px; justify-items: start; padding-right: 12px; }.ui-order-tracker-step:last-child { flex: none; padding-right: 0; }.ui-order-tracker-step:not(:last-child)::after { content: ""; position: absolute; top: 13px; left: 34px; right: 8px; height: 2px; border-radius: 2px; background: var(--ui-border); }.ui-order-tracker-done:not(:last-child)::after { background: var(--ui-success); }.ui-order-tracker-mark { display: inline-grid; place-items: center; width: 28px; height: 28px; border-radius: 50%; border: 2px solid var(--ui-border); background: var(--ui-surface); color: var(--ui-muted); }.ui-order-tracker-mark svg { width: 14px; height: 14px; }.ui-order-tracker-done .ui-order-tracker-mark { border-color: var(--ui-success); background: var(--ui-success); color: #fff; }.ui-order-tracker-current .ui-order-tracker-mark { border-color: var(--ui-accent); color: var(--ui-accent); box-shadow: var(--ui-focus); }.ui-order-tracker-current .ui-order-tracker-mark::before { content: ""; width: 10px; height: 10px; border-radius: 50%; background: var(--ui-accent); animation: ui-order-tracker-pulse 1.6s ease-in-out infinite; }.ui-order-tracker-cancelled .ui-order-tracker-mark { border-color: var(--ui-danger); background: var(--ui-danger); color: #fff; box-shadow: none; }.ui-order-tracker-cancelled .ui-order-tracker-mark::before { display: none; }@keyframes ui-order-tracker-pulse { 50% { opacity: .35; } }.ui-order-tracker-text { display: grid; gap: 1px; }.ui-order-tracker-label { font-weight: 500; }.ui-order-tracker-coming .ui-order-tracker-label { color: var(--ui-muted); font-weight: 400; }.ui-order-tracker-cancelled .ui-order-tracker-label { color: var(--ui-danger); }.ui-order-tracker-at, .ui-order-tracker-hint { font-size: 12px; color: var(--ui-muted); }.ui-order-tracker-vertical .ui-order-tracker-steps { flex-direction: column; }.ui-order-tracker-vertical .ui-order-tracker-step { grid-template-columns: 28px 1fr; align-items: start; gap: 12px; padding: 0 0 20px; }.ui-order-tracker-vertical .ui-order-tracker-step:last-child { padding-bottom: 0; }.ui-order-tracker-vertical .ui-order-tracker-step:not(:last-child)::after { top: 32px; bottom: 4px; left: 13px; right: auto; width: 2px; height: auto; }.ui-order-tracker-vertical .ui-order-tracker-text { padding-top: 4px; }.ui-order-tracker-ship { margin: 0; font-size: 13px; color: var(--ui-muted); }.ui-order-tracker-ship a { color: var(--ui-accent); font-variant-numeric: tabular-nums; }.ui-order-tracker-ship code { font: 13px var(--ui-mono); }@media (max-width: 600px) { .ui-order-tracker:not(.ui-order-tracker-vertical) .ui-order-tracker-at, .ui-order-tracker:not(.ui-order-tracker-vertical) .ui-order-tracker-hint { display: none; } }@media (prefers-reduced-motion: reduce) { .ui-order-tracker-current .ui-order-tracker-mark::before { animation: none; } }`let order_tracker_default = [{ label: "Ordered" }, { label: "Shipped" }, { label: "Out for delivery" }, { label: "Delivered" }]// When a step happened, as it reads: a date-time "3 Nov, 16:40", a date "3 Nov", other text as it is.fn order_tracker_when(at: str, locale: str) -> str { if !date.valid(at) { return at } let day = date.format(at, { locale, style: "medium" }) contains(at, "T") ? `${day}, ${date.format(at, "HH:mm")}` : day}view OrderTracker(steps: list = order_tracker_default, current: int = 0, cancelled: bool = false, vertical: bool = false, eta: str? = null, carrier: str? = null, tracking: str? = null, tracking_href: str? = null, locale: str = "en-GB", label: str = "Order progress", class: str? = null) { style global order_tracker_css let n = len(steps) let done_all = current >= n <div class={ui_cls(["ui-order-tracker", vertical && "ui-order-tracker-vertical", class])}> {eta != null && !cancelled && !done_all && <p.ui-order-tracker-eta> <span>{"Arriving "}</span>{date.valid(eta) ? date.format(eta, { locale, style: "full" }) : eta} </p>} {done_all && !cancelled && <p.ui-order-tracker-eta>Delivered</p>} {cancelled && <p.ui-order-tracker-eta>Cancelled</p>} <ol.ui-order-tracker-steps aria-label={label}> {for [i, s] in enumerate(steps) => { let phase = i < current ? "done" : i == current ? (cancelled ? "cancelled" : "current") : "coming" <li class={`ui-order-tracker-step ui-order-tracker-${phase}`} key={s.label} aria-current={phase == "current" ? "step" : null}> <span.ui-order-tracker-mark aria-hidden="true"> {phase == "done" && <svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><path d="M3.5 8.5l3 3 6-7"/></svg>} {phase == "cancelled" && <svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round"><path d="M4 4l8 8M12 4l-8 8"/></svg>} </span> <span.ui-order-tracker-text> <span.ui-order-tracker-label>{phase == "cancelled" ? "Cancelled" : s.label}{phase == "done" && <span.ui-sr> (done)</span>}</span> {get(s, "at") != null && <span.ui-order-tracker-at>{date.valid(s.at) ? <time datetime={s.at}>{order_tracker_when(s.at, locale)}</time> : s.at}</span>} {get(s, "hint") != null && <span.ui-order-tracker-hint>{s.hint}</span>} </span> </li> }} </ol> {(carrier != null || tracking != null) && <p.ui-order-tracker-ship> {carrier}{carrier != null && tracking != null && " · "} {tracking != null && (tracking_href != null ? <a href={tracking_href} rel="noopener">{tracking}</a> : <code>{tracking}</code>)} </p>} </div>}Related
CheckoutStepsCart → Details → Payment → Done: the steps of a checkout, those done as links back, the current one marked.5 views · 7 downloadsDeliveryEstimate"Arrives Tue 4 – Thu 6 Nov", worked out from how many days delivery takes, working days if asked.6 views · 8 downloadsDiscountBadge"−20%" or "Save $10" as a small label, in tones, solid, soft or outline, or pinned to an image's corner.7 views · 8 downloadsOrderHistoryPast orders with number, date, status and total, each opening on <details> to show its items and links.6 views · 5 downloadsStockStatusIn stock, only a few left, out of stock, pre-order or back-order, with a coloured dot and the count.7 views · 5 downloadsSaveStatusSaving…, Saved, Unsaved changes or Could not save, with an icon, the time last saved and an optional action.5 views · 6 downloads
Thread
Thread 0 posts
On OrderTracker: questions, tips, corrections, and code that uses it.
No posts yet. The first one opens the thread.