Skip to content
All Taste components

Animated screens

Ride tracking

A trip in progress: the car drives the route on the map while the arrival time counts down.

car on route · ETA countdown · driver card slides up

On the way to Indiranagar

Arriving in 4 min

KA 05
MX 2291
RK
Ravi Kumar

4.92 · White Dzire

/* globals.css
@keyframes sheet-up { from { transform: translateY(100%); } }
@keyframes rise { from { opacity: 0; transform: translateY(8px); } }
*/
import { ArrowLeft, MessageCircle, Phone, Share2, ShieldCheck, Star } from "lucide-react";

// The route in map pixels; the car rides it with CSS offset-path.
const ROUTE = "M40 330V228Q40 214 54 214H152Q166 214 166 200V84Q166 70 180 70H236";

interface Trip {
  progress: number; // 0–1 along the route, from your live location feed
  etaMin: number;
  driver: { name: string; initials: string; rating: number; car: string; plate: string };
}

export default function TripTrackingScreen({ trip = { progress: 0.6, etaMin: 4, driver: { name: "Ravi Kumar", initials: "RK", rating: 4.92, car: "White Swift Dzire", plate: "KA 05 MX 2291" } } }: { trip?: Trip }) {
  const pct = Math.round(trip.progress * 1000) / 10 + "%";
  return (
    <div className="relative mx-auto h-dvh max-w-md overflow-hidden bg-bg font-sans text-fg">
      {/* Map: swap the drawing for your map SDK and keep the overlay. */}
      <div aria-hidden className="absolute inset-x-0 top-0 h-[400px] bg-bg">
        <svg viewBox="0 0 284 400" className="absolute inset-0 h-full w-full">
          <rect x="0" y="0" width="284" height="400" className="fill-surface-2" />
          <path d={ROUTE} fill="none" strokeWidth="9" strokeLinecap="round" className="stroke-bg" />
          <path d={ROUTE} fill="none" strokeWidth="5" strokeLinecap="round" strokeLinejoin="round" className="stroke-primary" />
          {/* Travelled part greys out as the car moves. */}
          <path d={ROUTE} pathLength={1} fill="none" strokeWidth="5" strokeLinecap="round" strokeDasharray="1 1"
            className="stroke-fg-subtle transition-[stroke-dashoffset] duration-1000 ease-linear" style={{ strokeDashoffset: 1 - trip.progress }} />
          <circle cx="40" cy="330" r="6" strokeWidth="3" className="fill-bg stroke-fg" />
          <circle cx="236" cy="70" r="10" className="fill-fg" />
        </svg>
        <span className="absolute left-0 top-0 h-3.5 w-[26px] rounded bg-fg shadow-[0_0_0_10px_var(--tst-primary-soft)] transition-[offset-distance] duration-1000 ease-linear motion-reduce:transition-none"
          style={{ offsetPath: "path('" + ROUTE + "')", offsetDistance: pct, offsetRotate: "auto" }} />
      </div>

      <button type="button" aria-label="Back" className="absolute left-3 top-[52px] grid size-11 place-items-center rounded-full bg-bg shadow-md"><ArrowLeft className="size-5" /></button>

      <section aria-label="Your trip" className="absolute inset-x-0 -bottom-6 rounded-t-[28px] bg-bg px-4 pb-[calc(max(env(safe-area-inset-bottom),12px)+24px)] pt-2.5 shadow-[0_-12px_32px_-16px_rgb(0_0_0/.35)] animate-[sheet-up_.8s_cubic-bezier(.34,1.56,.64,1)_.25s_both] motion-reduce:animate-none">
        <span aria-hidden className="mx-auto mb-3 block h-[5px] w-9 rounded-full bg-border-strong" />
        <div className="flex items-start gap-3 animate-[rise_.5s_cubic-bezier(.2,.7,.1,1)_.55s_both] motion-reduce:animate-none">
          <div className="flex-1">
            <p className="text-xs text-fg-muted">On the way to Indiranagar</p>
            <h1 className="text-2xl font-semibold tracking-tight" aria-live="polite">Arriving in <span className="tabular-nums">{trip.etaMin}</span> min</h1>
          </div>
          <span className="rounded border border-border-strong bg-surface px-2 py-1 text-center text-[11px] font-semibold leading-4 tabular-nums">{trip.driver.plate}</span>
        </div>
        <div className="mt-4 flex items-center gap-3 border-t border-border pt-4 animate-[rise_.5s_cubic-bezier(.2,.7,.1,1)_.63s_both] motion-reduce:animate-none">
          <span className="grid size-9 place-items-center rounded-full bg-primary-soft text-[13px] font-medium text-primary-soft-fg">{trip.driver.initials}</span>
          <div className="min-w-0 flex-1">
            <p className="text-sm font-semibold">{trip.driver.name}</p>
            <p className="flex items-center gap-1 text-xs text-fg-muted"><Star className="size-3 fill-warning text-warning" /> {trip.driver.rating} · {trip.driver.car}</p>
          </div>
          <button type="button" aria-label={"Message " + trip.driver.name} className="grid size-11 place-items-center rounded-full bg-surface-2"><MessageCircle className="size-[18px]" /></button>
          <button type="button" aria-label={"Call " + trip.driver.name} className="grid size-11 place-items-center rounded-full bg-surface-2"><Phone className="size-[18px]" /></button>
        </div>
        <div className="mt-4 grid grid-cols-2 gap-2 animate-[rise_.5s_cubic-bezier(.2,.7,.1,1)_.71s_both] motion-reduce:animate-none">
          <button type="button" className="inline-flex h-11 items-center justify-center gap-1.5 rounded border border-border-strong text-sm font-medium"><Share2 className="size-4" />Share trip</button>
          <button type="button" className="inline-flex h-11 items-center justify-center gap-1.5 rounded border border-border-strong text-sm font-medium"><ShieldCheck className="size-4" />Safety</button>
        </div>
      </section>
    </div>
  );
}

The code uses the Taste Tailwind preset. Make yours on the Taste page with Export my system.