All Taste components
Animated screens
Splash to home
A launch screen whose logo flies into the app bar while the home feed slides in under it.
logo dock · splash fade · staggered feed · tab bar rise
PantryIndiranagar · in 12 min
Search milk, atta, eggs…
This weekendFree delivery
over ₹199
over ₹199
Fresh today
See allAlphonso mangoes1 kg · ₹349
Baby spinach200 g · ₹45
Cherry tomatoes250 g · ₹60
Buy again
Toned milk500 ml · ₹28
AddBrown eggs6 pcs · ₹72
Add// Splash → home: the logo docks into the app bar, then the feed slides in.
// Mobile-first page (React + Tailwind on the Taste preset); maps to React Native via NativeWind.
"use client";
import { useEffect, useRef, useState } from "react";
import { Bell, Home, LayoutGrid, Leaf, MapPin, Plus, Search, ShoppingBag, User } from "lucide-react";
import { cn } from "@/lib/utils";
const products = [
{ name: "Alphonso mangoes", meta: "1 kg · ₹349", tone: "from-[var(--tst-warning-300)] to-[var(--tst-warning-600)]" },
{ name: "Baby spinach", meta: "200 g · ₹45", tone: "from-[var(--tst-success-300)] to-[var(--tst-success-700)]" },
{ name: "Cherry tomatoes", meta: "250 g · ₹60", tone: "from-[var(--tst-danger-300)] to-[var(--tst-danger-600)]" },
];
const again = [
{ name: "Toned milk", meta: "500 ml · ₹28", tone: "from-[var(--tst-info-200)] to-[var(--tst-info-500)]" },
{ name: "Brown eggs", meta: "6 pcs · ₹72", tone: "from-[var(--tst-warning-200)] to-[var(--tst-warning-500)]" },
];
export default function SplashToHome() {
const mark = useRef<HTMLSpanElement>(null);
const [ready, setReady] = useState(false);
useEffect(() => {
const el = mark.current;
if (!el || window.matchMedia("(prefers-reduced-motion: reduce)").matches) {
setReady(true);
return;
}
// FLIP: start the logo big in the middle of the screen, land it in the app bar.
const r = el.getBoundingClientRect();
const dx = window.innerWidth / 2 - (r.left + r.width / 2);
const dy = window.innerHeight * 0.45 - (r.top + r.height / 2);
const from = "translate(" + dx + "px, " + dy + "px) scale(2.5)";
const anim = el.animate(
[{ transform: from }, { transform: from, offset: 0.5 }, { transform: "none" }],
{ duration: 1500, easing: "cubic-bezier(.34,1.56,.64,1)", fill: "both" },
);
const t = setTimeout(() => setReady(true), 800);
return () => {
anim.cancel();
clearTimeout(t);
};
}, []);
const enter = (i: number) => ({
className: ready ? "animate-slide-up motion-reduce:animate-none" : "opacity-0",
style: { animationDelay: 250 + i * 80 + "ms" },
});
return (
<main className="relative mx-auto flex min-h-dvh max-w-md flex-col overflow-hidden bg-bg font-sans text-fg">
<header className="flex h-14 items-center gap-2.5 pl-4 pr-1.5">
<span
ref={mark}
aria-hidden
className={cn(
"relative z-20 grid size-8 shrink-0 place-items-center rounded-lg transition-colors duration-500 motion-reduce:transition-none",
ready ? "bg-primary text-primary-fg" : "bg-primary-fg text-primary",
)}
>
<Leaf className="size-[18px]" />
</span>
<div className={cn("min-w-0 flex-1 transition duration-500 ease-taste motion-reduce:transition-none", ready ? "opacity-100" : "-translate-x-2.5 opacity-0")}>
<p className="text-base font-semibold leading-5 tracking-tight">Pantry</p>
<p className="flex items-center gap-1 text-[11.5px] text-fg-muted"><MapPin className="size-3 text-primary" />Indiranagar · in 12 min</p>
</div>
<button type="button" aria-label="Notifications" className="relative grid size-11 place-items-center rounded-full hover:bg-surface-2">
<Bell className="size-5" />
<span className="absolute right-3 top-3 size-2 rounded-full bg-danger ring-2 ring-bg" />
</button>
</header>
<div className="flex flex-1 flex-col gap-3.5 px-4 pt-1">
<label {...enter(0)}>
<span className="flex h-11 items-center gap-2.5 rounded-lg border border-border bg-surface px-3 text-fg-subtle">
<Search className="size-[18px]" />
<input className="min-w-0 flex-1 bg-transparent text-sm text-fg outline-none placeholder:text-fg-subtle" placeholder="Search milk, atta, eggs…" />
</span>
</label>
<div {...enter(1)}>
<div className="relative grid h-24 content-center overflow-hidden rounded-lg bg-primary-soft px-4 text-primary-soft-fg">
<p className="text-xs">This weekend</p>
<p className="text-base font-semibold leading-5">Free delivery<br />over ₹199</p>
<span aria-hidden className="absolute -bottom-6 -right-3.5 size-24 rounded-full bg-gradient-to-br from-[var(--tst-warning-300)] to-[var(--tst-warning-500)]" />
</div>
</div>
<section {...enter(2)}>
<div className="flex items-baseline justify-between">
<h2 className="text-base font-semibold">Fresh today</h2>
<a href="/fresh" className="text-[13px] font-medium text-primary">See all</a>
</div>
<ul className="-mx-4 mt-2.5 flex gap-2.5 overflow-x-auto px-4 pb-1">
{products.map((p) => (
<li key={p.name} className="w-28 shrink-0">
<div className="relative grid h-24 place-items-center rounded-lg bg-surface-2">
<span className={cn("size-12 rounded-full bg-gradient-to-br", p.tone)} />
<button type="button" aria-label={"Add " + p.name} className="absolute bottom-1.5 right-1.5 grid size-7 place-items-center rounded-full bg-bg text-primary ring-1 ring-border">
<Plus className="size-4" />
</button>
</div>
<p className="mt-1.5 truncate text-[12.5px] font-medium">{p.name}</p>
<p className="text-[11.5px] tabular-nums text-fg-muted">{p.meta}</p>
</li>
))}
</ul>
</section>
<section {...enter(3)}>
<h2 className="text-base font-semibold">Buy again</h2>
{again.map((r) => (
<div key={r.name} className="mt-2.5 flex items-center gap-2.5">
<span className="grid size-11 shrink-0 place-items-center rounded bg-surface-2"><span className={cn("size-[26px] rounded-full bg-gradient-to-br", r.tone)} /></span>
<div className="min-w-0 flex-1">
<p className="text-[13px] font-medium">{r.name}</p>
<p className="text-[11.5px] tabular-nums text-fg-muted">{r.meta}</p>
</div>
<button type="button" className="h-[30px] rounded border border-primary px-3 text-[12.5px] font-semibold text-primary hover:bg-primary-soft">Add</button>
</div>
))}
</section>
</div>
<nav aria-label="Main" className={cn("grid grid-cols-4 border-t border-border bg-bg px-1.5 pb-6 pt-1 transition-transform duration-500 ease-taste motion-reduce:transition-none", !ready && "translate-y-full")}>
{[["Home", Home], ["Categories", LayoutGrid], ["Orders", ShoppingBag], ["Account", User]].map(([label, Icon], i) => {
const I = Icon as typeof Home;
return (
<a key={label as string} href="#" aria-current={i === 0 ? "page" : undefined} className={cn("flex min-h-11 flex-col items-center justify-center gap-0.5 text-[10.5px] font-medium", i === 0 ? "text-primary" : "text-fg-subtle")}>
<I className="size-[22px]" />
{label as string}
</a>
);
})}
</nav>
{/* Splash: fades away while the logo docks */}
<div aria-hidden className={cn("pointer-events-none absolute inset-0 z-10 grid place-items-center bg-primary transition-opacity duration-500 motion-reduce:transition-none", ready && "opacity-0")}>
<p className="mt-[110px] text-sm font-medium text-primary-fg">Groceries in 12 minutes</p>
</div>
</main>
);
}
The code uses the Taste Tailwind preset. Make yours on the Taste page with Export my system.