All Taste components
Mobile screens
Book a time slot
The date-and-time step of a salon booking, with the chosen service, a day strip and open slots.
step progress · service summary · day strip with closed day · 3×3 slots with taken · sticky summary + continue
Haircut & beard trim
₹65045 min · with Imran
Date
October 2026Time
7 slots freeFri 16 Oct · 1:00 pm
₹650 · pay at the salon
// Mobile-first page; the same markup maps to React Native via NativeWind (View/ScrollView/Pressable).
"use client";
import { useState } from "react";
import { ChevronLeft, Scissors } from "lucide-react";
import { cn } from "@/lib/utils";
const service = { name: "Haircut & beard trim", minutes: 45, stylist: "Imran", price: "₹650" };
const days = [
{ id: "2026-10-15", day: "Thu", date: 15 },
{ id: "2026-10-16", day: "Fri", date: 16 },
{ id: "2026-10-17", day: "Sat", date: 17 },
{ id: "2026-10-18", day: "Sun", date: 18 },
{ id: "2026-10-19", day: "Mon", date: 19, closed: true },
{ id: "2026-10-20", day: "Tue", date: 20 },
{ id: "2026-10-21", day: "Wed", date: 21 },
];
const slots = [
{ time: "10:00 am" }, { time: "10:45 am", taken: true }, { time: "11:30 am" },
{ time: "12:15 pm" }, { time: "1:00 pm" }, { time: "1:45 pm" },
{ time: "2:30 pm", taken: true }, { time: "3:15 pm" }, { time: "4:00 pm" },
];
export default function BookTimeScreen() {
const [day, setDay] = useState(days[1]);
const [slot, setSlot] = useState<string | null>("1:00 pm");
const free = slots.filter((s) => !s.taken).length;
return (
<div className="mx-auto flex min-h-dvh max-w-md flex-col bg-bg font-sans text-fg">
<header className="flex h-12 items-center gap-1 px-2">
<button type="button" aria-label="Back" className="grid size-11 place-items-center rounded-full hover:bg-surface-2"><ChevronLeft className="size-[22px]" /></button>
<div className="flex-1 text-center leading-tight">
<h1 className="text-[15px] font-semibold">Book appointment</h1>
<p className="text-xs text-fg-muted">Step 2 of 3 · Date & time</p>
</div>
<span className="size-11" />
</header>
<div className="grid grid-cols-3 gap-1 px-4 pb-2" aria-hidden>
{[1, 2, 3].map((s) => <i key={s} className={cn("h-1 rounded-full", s <= 2 ? "bg-primary" : "bg-surface-2")} />)}
</div>
<main className="flex-1 px-4 pt-2">
<div className="flex items-center gap-3 rounded-lg bg-surface p-3">
<span className="grid size-10 place-items-center rounded-full bg-primary-soft text-primary-soft-fg"><Scissors className="size-[18px]" /></span>
<div className="min-w-0 flex-1">
<p className="font-semibold">{service.name}</p>
<p className="text-xs text-fg-muted">{service.minutes} min · with {service.stylist}</p>
</div>
<span className="font-semibold tabular-nums">{service.price}</span>
</div>
<div className="mt-5 flex items-baseline justify-between"><h2 className="text-base font-semibold">Date</h2><span className="text-xs text-fg-muted">October 2026</span></div>
<div className="-mx-4 mt-2 flex gap-1.5 overflow-x-auto px-4 [scrollbar-width:none]">
{days.map((d) => (
<button
key={d.id}
type="button"
disabled={d.closed}
aria-pressed={day.id === d.id}
aria-label={d.day + " " + d.date + " October" + (d.closed ? ", closed" : "")}
onClick={() => setDay(d)}
className={cn(
"grid h-16 w-[46px] shrink-0 content-center justify-items-center gap-0.5 rounded-lg border border-border text-[17px] font-semibold tabular-nums transition",
"disabled:cursor-not-allowed disabled:bg-surface disabled:text-fg-subtle",
day.id === d.id && "border-primary bg-primary text-primary-fg",
)}
>
<span className={cn("text-[11px] font-medium", day.id === d.id ? "text-primary-fg" : "text-fg-muted")}>{d.day}</span>
{d.date}
</button>
))}
</div>
<div className="mt-5 flex items-baseline justify-between"><h2 className="text-base font-semibold">Time</h2><span className="text-xs tabular-nums text-fg-muted">{free} slots free</span></div>
<div className="mt-2 grid grid-cols-3 gap-2">
{slots.map((s) => (
<button
key={s.time}
type="button"
disabled={s.taken}
aria-pressed={slot === s.time}
onClick={() => setSlot(s.time)}
className={cn(
"h-11 rounded border border-border text-sm font-medium tabular-nums transition",
"disabled:cursor-not-allowed disabled:bg-surface disabled:text-fg-subtle disabled:line-through",
slot === s.time && "border-primary bg-primary-soft font-semibold text-primary-soft-fg ring-1 ring-inset ring-primary",
)}
>
{s.time}
</button>
))}
</div>
</main>
<footer className="sticky bottom-0 flex items-center gap-3 border-t border-border bg-bg px-4 pb-[max(env(safe-area-inset-bottom),16px)] pt-3">
<div className="min-w-0 flex-1">
<p className="font-semibold tabular-nums">{day.day} {day.date} Oct{slot ? " · " + slot : ""}</p>
<p className="text-xs tabular-nums text-fg-muted">{service.price} · pay at the salon</p>
</div>
<button type="button" disabled={!slot} className="h-12 rounded bg-primary px-5 text-[15px] font-semibold text-primary-fg hover:bg-primary-hover disabled:opacity-50">
Continue
</button>
</footer>
</div>
);
}
The code uses the Taste Tailwind preset. Make yours on the Taste page with Export my system.