Skip to content
All Taste components

Onboarding

Food delivery onboarding

A delivery app's second intro screen, with a scooter and food box on a full-width tinted top half.

full-bleed brand tint · rounded bottom edge · 3-step dots · skip · next

Hot food, at your door

Order in a few taps and follow your rider live on the map.

// Mobile-first screen for a React web app; the same markup maps to React Native via NativeWind (View, Text, Pressable, react-native-svg).
import { cn } from "@/lib/utils";

const slides = [
  { title: "Every kitchen in your city", body: "Browse menus from places near you, all in one app." },
  { title: "Hot food, at your door", body: "Order in a few taps and follow your rider live on the map." },
  { title: "Pay your way", body: "UPI, cards or cash on delivery. Tips go straight to your rider." },
];

function Illustration({ className }: { className?: string }) {
  return (
    <svg viewBox="0 0 260 190" aria-hidden="true" className={cn("w-full max-w-[260px]", className)}>
      <rect x="22" y="176" width="216" height="5" rx="2.5" opacity="0.16" className="fill-primary" />
      <path d="M14 100h34M28 120h28M18 140h24" strokeWidth="5" strokeLinecap="round" strokeLinejoin="round" fill="none" className="stroke-brand-400" />
      <path d="M84 42c0-8 8-8 8-16M102 42c0-8 8-8 8-16" strokeWidth="4" strokeLinecap="round" strokeLinejoin="round" fill="none" className="stroke-brand-400" />
      <rect x="62" y="54" width="64" height="52" rx="8" className="fill-neutral-50" />
      <rect x="89" y="54" width="10" height="52" className="fill-brand-200" />
      <rect x="62" y="54" width="64" height="52" rx="8" strokeWidth="3" fill="none" className="stroke-brand-800" />
      <path d="M56 152c0-26 14-42 40-42h44v42z" className="fill-brand-600" />
      <rect x="78" y="102" width="58" height="12" rx="6" className="fill-brand-800" />
      <rect x="100" y="140" width="92" height="14" rx="7" className="fill-brand-600" />
      <path d="M182 148 200 84" strokeWidth="9" strokeLinecap="round" strokeLinejoin="round" fill="none" className="stroke-brand-800" />
      <path d="M190 82h22" strokeWidth="7" strokeLinecap="round" strokeLinejoin="round" fill="none" className="stroke-brand-800" />
      <path d="M198 96c15 6 21 22 19 46h-22z" className="fill-brand-400" />
      <circle cx="208" cy="102" r="6" className="fill-[var(--tst-warning-300)]" />
      <circle cx="84" cy="158" r="20" className="fill-neutral-800" />
      <circle cx="84" cy="158" r="8" className="fill-neutral-50" />
      <circle cx="206" cy="158" r="20" className="fill-neutral-800" />
      <circle cx="206" cy="158" r="8" className="fill-neutral-50" />
    </svg>
  );
}

function Dots({ step, total, className }: { step: number; total: number; className?: string }) {
  return (
    <div className={cn("flex gap-1.5", className)} role="img" aria-label={"Step " + (step + 1) + " of " + total}>
      {Array.from({ length: total }, (_, i) => (
        <span key={i} className={cn("h-1.5 rounded-full transition-all", i === step ? "w-[22px] bg-primary" : "w-1.5 bg-border-strong")} />
      ))}
    </div>
  );
}

export default function DeliveryOnboarding({ step = 1, onNext, onSkip }: { step?: number; onNext?: () => void; onSkip?: () => void }) {
  const slide = slides[step];
  return (
    <main className="mx-auto flex min-h-dvh max-w-md flex-col bg-bg font-sans text-fg">
      <div className="flex h-[52dvh] min-h-72 flex-col rounded-b-[32px] bg-primary-soft px-6">
        <header className="-mr-3.5 flex h-12 items-center justify-end">
          <button type="button" onClick={onSkip} className="h-11 rounded px-3.5 text-sm font-medium text-fg-muted hover:bg-surface-2">
          Skip
        </button>
        </header>
        <div className="flex flex-1 items-end justify-center pb-6">
          <Illustration />
        </div>
      </div>
      <div className="flex flex-1 flex-col px-6 pb-8 pt-7">
        <h1 className="text-[26px] font-semibold leading-8 tracking-tight [text-wrap:balance]">{slide.title}</h1>
        <p className="mt-2.5 text-[15px] leading-[22px] text-fg-muted">{slide.body}</p>
        <Dots step={step} total={slides.length} className="mb-6 mt-auto pt-6" />
        <button type="button" onClick={onNext} className="h-[52px] w-full rounded-lg bg-primary text-[15px] font-semibold text-primary-fg hover:bg-primary-hover">
        {step === slides.length - 1 ? "Get started" : "Next"}
      </button>
      </div>
    </main>
  );
}

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