Skip to content

Order tracking timeline

Where’s my package, answered on one screen. A vertical md-stepper plays the shipment timeline — completed scans above, the current leg expanded, future stages greyed out and unreachable. The header pairs an md-chip state token with a pulsing md-status-dot for the live carrier feed, an md-meter reads out kilometres covered on the route, and an md-side-sheet keeps contact options one tap away without leaving the page.

Live preview — press 'Simulate carrier scan' to advance the shipment, or open Contact support
Order #48213 AWB 3805 5521 0021 · Curier Express
Live feed

Payment of 249,90 lei confirmed. Two items reserved at the Bucharest warehouse.

Sealed in one parcel (1.8 kg) and handed to Curier Express at gate D3.

Departed the Sibiu sorting hub on line-haul RO-114. Next scan expected at the Cluj-Napoca depot around 13:30.

Courier Andrei V. is on the route — your stop is number 8 of 22.

Parcel handed over at the door. A signed proof of delivery is attached to this order.

Simulate carrier scan Contact support

Order #48213 · AWB 3805 5521 0021

Done
Show code for each technology
<!-- index.html <head> — the icon font the components draw from -->
<link rel="stylesheet"
  href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200&display=swap">

<!-- index.html — register the AWC UI elements once -->
<script type="module">
  import '@awc-ui/core/define';
</script>

<div class="order-status">
  <header>
    <h2>Order #48213</h2>
    <md-chip label="In transit" color="info" appearance="filled"></md-chip>
    <span style="position: relative; display: inline-flex;">
      <md-status-dot state="online" live label="Carrier feed live"></md-status-dot>
    </span>
  </header>

  <md-meter label="Route progress — Bucharest to Cluj-Napoca"
    max="449" value="302" value-text="302 km of 449 km"
    show-label show-value></md-meter>

    <md-stepper orientation="vertical" nav="false" auto-complete="false"
      active="2" label="Shipment timeline">
      <md-step label="Order placed" description="Mon 18 Aug, 09:14" completed editable>
        Payment confirmed. Two items reserved at the Bucharest warehouse.
      </md-step>
      <md-step label="Packed" description="Mon 18 Aug, 16:40" completed editable>
        Sealed in one parcel and handed to Curier Express.
      </md-step>
      <md-step label="In transit" description="Last scan: Sibiu hub, 07:52">
        Departed the Sibiu sorting hub on line-haul RO-114.
      </md-step>
      <md-step label="Out for delivery" description="Estimated Wed 20 Aug"></md-step>
      <md-step label="Delivered" description="Signature required"></md-step>
    </md-stepper>

    <md-button variant="outlined">Contact support</md-button>
  </div>

  <md-side-sheet variant="modal" headline="Delivery support" detached top-divider bottom-divider>
    <md-list>
      <md-list-item type="link" href="tel:+40723118402" leading-icon="call"
        headline="Call the courier" supporting-text="Andrei V. — +40 723 118 402"></md-list-item>
        <md-list-item type="link" href="mailto:support@curierexpress.ro" leading-icon="mail"
          headline="Email support" supporting-text="Replies within one business day"></md-list-item>
          <md-list-item type="button" leading-icon="report"
            headline="Report a problem" supporting-text="Damaged, delayed, or missing parcel"></md-list-item>
          </md-list>
          <md-button slot="actions" variant="text">Done</md-button>
        </md-side-sheet>

<script type="module">
  const chip = document.querySelector('[data-state-chip]');
  const dot = document.querySelector('[data-feed-dot]');
  const feedText = document.querySelector('[data-feed-text]');
  const meter = document.querySelector('[data-route-meter]');
  const timeline = document.querySelector('[data-timeline]');
  const steps = timeline.querySelectorAll('md-step');
  const advance = document.querySelector('[data-advance]');
  const contact = document.querySelector('[data-contact]');
  const sheet = document.querySelector('[data-support-sheet]');
  const done = document.querySelector('[data-sheet-done]');

  // Each simulated scan: complete the current stage, activate the next,
  // and keep the header chip + route meter in sync with the timeline.
  const scans = [
    { chip: 'Out for delivery', color: 'tertiary', km: 437 },
    { chip: 'Delivered', color: 'success', km: 449 }
  ];
  let scan = 0;

  advance.addEventListener('mdClick', () => {
    const next = scans[scan];
    steps[timeline.active].completed = true;
    timeline.active = timeline.active + 1;
    chip.label = next.chip;
    chip.color = next.color;
    meter.value = next.km;
    meter.valueText = next.km + ' km of 449 km';
    scan = scan + 1;

    if (scan === scans.length) {
      steps[timeline.active].completed = true;
      meter.color = 'success';
      // Setting state and label together: the live dot announces the change.
      dot.state = 'neutral';
      dot.live = false;
      dot.label = 'Carrier feed closed';
      feedText.textContent = 'Feed closed';
      advance.disabled = true;
      advance.textContent = 'Shipment delivered';
    }
  });

  // Slotted side-sheet controls are never auto-wired — open and close it here.
  contact.addEventListener('mdClick', () => sheet.show());
  done.addEventListener('mdClick', () => sheet.close());
  document.querySelector('md-list-item[type="button"]').addEventListener('mdClick', () => sheet.close());
</script>
ComponentRole in this screen
md-stepperThe timeline. orientation="vertical" gives each stage an inline expanding panel; nav="false" removes Back / Continue because a shipment is not user-driven; auto-complete="false" hands completed to the carrier feed instead of to clicks.
md-stepOne scan event each — label for the stage, description for the timestamp, panel content for the detail. Past stages are completed editable so revisiting them never rewrites downstream state.
md-chipThe headline state token. color takes theme role names, so the same chip walks infotertiarysuccess as the parcel moves.
md-status-dotThe carrier-feed pip. label + live make it a role="status" live region, so “Carrier feed closed” is announced when tracking ends.
md-meterRoute progress as a state, not an activity. max="449" with value-text="302 km of 449 km" shows kilometres while aria-valuetext says exactly the same string.
md-side-sheetContact options in a variant="modal" overlay — scrim, focus trap, and Escape handling included, with an md-list of call / email / report rows.
  • The timeline can’t lie about the future. In the stepper’s default mode="linear", stages after the active one are unreachable — their headers render aria-disabled and leave the tab order — so a customer can review past scans but never “open” a delivery that hasn’t happened.
  • Completion belongs to the carrier, not the click. With auto-complete="false" the checkmarks only change when the feed says so; revisiting “Order placed” never un-completes “Packed” the way the default auto-complete would on a backward move.
  • The status dot is a real live region. Because both label and live are set it exposes role="status", so the switch to “Carrier feed closed” is announced politely — and its pulse is already gated behind prefers-reduced-motion, with no extra wiring.
  • One string drives what is seen and what is heard. The meter’s value-text overrides both the visible header value and aria-valuetext, so “437 km of 449 km” can never drift from what a screen reader announces.
  • The support sheet has a guaranteed exit. variant="modal" traps focus and locks body scroll, but closeable (on by default), the scrim, and Escape all dismiss it — and focus returns to the “Contact support” button automatically on close.
  • Compact tracking widget: swap the meter to variant="circular" size="72" show-value and place it beside the chip for a dashboard-tile version of the same reading.
  • Exception states: a failed delivery attempt is error + error-text="Delivery attempted — nobody home" on the affected step; the stepper announces it assertively and the glyph switches to the error mark.
  • Denser timeline for order history: set density="-2" on the stepper to compact indicators and connectors when the page lists several shipments.
  • Mobile: replace the side sheet with an md-bottom-sheet below ~600px — side sheets are a large-screen surface by design.