All Taste components
Onboarding
Meditation onboarding
A wellness app's first screen: a sun over calm waves on a soft sky, then one quiet line and a start button.
full-bleed sky gradient · layered waves · centred text · 3-step dots · skip · begin
Breathe in. Slow down.
Short guided sessions to help you rest, focus and sleep better.
// 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: "Breathe in. Slow down.", body: "Short guided sessions to help you rest, focus and sleep better." },
{ title: "Five minutes is enough", body: "Pick a length that fits. Even one quiet minute helps." },
{ title: "Sleep a little deeper", body: "Wind-down sounds and stories that switch off on their own." },
];
function Illustration({ className }: { className?: string }) {
return (
<svg viewBox="0 0 284 236" aria-hidden="true" className={cn("w-full", className)}>
<circle cx="142" cy="104" r="60" opacity="0.28" className="fill-[var(--tst-warning-300)]" />
<circle cx="142" cy="104" r="40" className="fill-[var(--tst-warning-300)]" />
<path d="M68 64q6-6 12 0q6-6 12 0M194 46q5-5 10 0q5-5 10 0" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round" fill="none" className="stroke-brand-800" />
<path d="M0 150c24-12 47-12 71 0s47 12 71 0 47-12 71 0 47 12 71 0V236H0z" className="fill-brand-200" />
<path d="M0 180c24 12 47 12 71 0s47-12 71 0 47 12 71 0 47-12 71 0V236H0z" className="fill-brand-400" />
<path d="M0 208c24-10 47-10 71 0s47 10 71 0 47-10 71 0 47 10 71 0V236H0z" 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 CalmOnboarding({ 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 font-sans text-fg">
<div className="flex h-[54dvh] min-h-72 flex-col overflow-hidden rounded-b-[32px] bg-gradient-to-b from-primary-soft to-bg">
<header className="flex h-12 items-center justify-end px-2.5">
<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="mt-auto">
<Illustration />
</div>
</div>
<div className="flex flex-1 flex-col px-6 pb-8 pt-7">
<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>
<Dots step={step} total={slides.length} className="mb-6 mt-auto justify-center 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 ? "Begin" : "Next"}
</button>
</div>
</main>
);
}
The code uses the Taste Tailwind preset. Make yours on the Taste page with Export my system.