Skip to content
All Taste components

Onboarding

Fitness onboarding

A workout app's welcome, with a dumbbell and heartbeat line centred on a large soft circle.

centred illustration on circle · centred text · 4-step dots · skip · next

Small workouts, big changes

Ten-minute plans that fit around your day. No gym needed.

// 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: "Small workouts, big changes", body: "Ten-minute plans that fit around your day. No gym needed." },
  { title: "Track every rep", body: "Log sets in a tap and see your strength climb week by week." },
  { title: "Rest counts too", body: "Recovery days are part of the plan, not a break from it." },
  { title: "Ready when you are", body: "Tell us your goal and we will build your first week." },
];

function Illustration({ className }: { className?: string }) {
  return (
    <svg viewBox="0 0 240 220" aria-hidden="true" className={cn("h-full w-full", className)}>
      <circle cx="120" cy="110" r="100" className="fill-primary-soft" />
      <rect x="56" y="104" width="128" height="12" rx="6" transform="rotate(-24 120 110)" className="fill-neutral-300" />
      <rect x="44" y="84" width="14" height="52" rx="6" transform="rotate(-24 120 110)" className="fill-brand-800" />
      <rect x="182" y="84" width="14" height="52" rx="6" transform="rotate(-24 120 110)" className="fill-brand-800" />
      <rect x="60" y="72" width="20" height="76" rx="8" transform="rotate(-24 120 110)" className="fill-brand-600" />
      <rect x="160" y="72" width="20" height="76" rx="8" transform="rotate(-24 120 110)" className="fill-brand-600" />
      <rect x="65" y="80" width="4" height="26" rx="2" transform="rotate(-24 120 110)" className="fill-brand-400" />
      <rect x="165" y="80" width="4" height="26" rx="2" transform="rotate(-24 120 110)" className="fill-brand-400" />
      <path d="M36 178h44l10-18 14 32 12-24 8 10h80" strokeWidth="5" strokeLinecap="round" strokeLinejoin="round" fill="none" className="stroke-primary" />
      <path d="M196 43l2.7 6.3 6.3 2.7-6.3 2.7-2.7 6.3-2.7-6.3-6.3-2.7 6.3-2.7z" className="fill-brand-400" />
      <path d="M44 64l1.8 4.2 4.2 1.8-4.2 1.8-1.8 4.2-1.8-4.2-4.2-1.8 4.2-1.8z" className="fill-brand-600" />
    </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 FitnessOnboarding({ step = 0, 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 px-6 pb-8 font-sans text-fg">
      <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="min-h-0 flex-1 py-2">
        <Illustration />
      </div>
      <div className="mt-5">
        <h1 className="text-center text-[26px] font-semibold leading-8 tracking-tight [text-wrap:balance]">{slide.title}</h1>
        <p className="mt-2.5 text-center text-[15px] leading-[22px] text-fg-muted">{slide.body}</p>
      </div>
      <Dots step={step} total={slides.length} className="my-6 justify-center" />
      <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>
    </main>
  );
}

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