Commerce

OrderTracker

Ordered → shipped → out for delivery → delivered, with when each happened, the expected date and the tracking link.

#commerce#status#progress#time#no-js

Demo

Preview

Arriving Thursday, 5 November 2026

  1. Ordered (done)
  2. Shipped (done)Left the warehouse in Leeds
  3. Out for delivery
  4. Delivered

DHL · JD014600003828

Delivered

  1. Ordered (done)
  2. Shipped (done)
  3. Out for delivery (done)
  4. Delivered (done)Signed for by R. Cole

Cancelled

  1. Ordered (done)
  2. Cancelled
  3. 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.qwe
main.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>}
Thread
56Useful? Like it, or add to its thread below.

Thread 0 posts

On OrderTracker: questions, tips, corrections, and code that uses it.

No posts yet. The first one opens the thread.