Blocks

Booking

Four booking flows built on one set of steps: a meeting, a hotel stay, a restaurant table and an appointment. Going back keeps every choice, and each step checks itself before moving on.

Examples

Meeting

Choose a length, a day and a time. Times are shown in your time zone; change it and they move. Some days are fully booked, to show the jump to the next free day. Once booked, add it to your calendar.

Pick a time

How long

October 2026

SMTWTFS

Pick a day to see its times.

<script lang="ts">
	import Meeting from '#lib/blocks/booking/Meeting.svelte';
</script>

<Meeting
	title="Reading-room tour"
	host={{ name: 'Hunayn ibn Ishaq', role: 'Keeper of the translations' }}
	hours={{ timeZone: 'Asia/Baghdad', open: 9, close: 17, days: [0, 1, 2, 3, 4] }}
	location="Bayt al-Hikma, west reading room"
/>

Hotel stay

Dates and guests first, then the rooms free for those nights. Rooms too small for your party can't be chosen, and the total updates as you go.

Dates and guests

Check in and out

Under 12

<script lang="ts">
	import Hotel from '#lib/blocks/booking/Hotel.svelte';
</script>

<Hotel
	name="Dar al-Zahra guesthouse, Córdoba"
	taxRate={0.1}
	rooms={[
		{
			id: 'garden',
			name: 'Garden room',
			description: 'A double bed and a door onto the orange-tree courtyard.',
			rate: 9_500,
			sleeps: 2
		},
		{
			id: 'library',
			name: 'Library suite',
			description: 'A bedroom, a sitting room lined with books, and a daybed.',
			rate: 16_000,
			sleeps: 3
		},
		{
			id: 'family',
			name: 'Family rooms',
			description: 'Two connecting rooms around a small fountain.',
			rate: 21_000,
			sleeps: 5
		}
	]}
/>

Restaurant table

Times show in the restaurant's own time zone, and sold-out ones stay visible. Areas that can't seat your party are switched off.

Table

More than 8? Call +963 11 555 0142.

Day
Where to sit
<script lang="ts">
	import Table from '#lib/blocks/booking/Table.svelte';
</script>

<Table
	name="Bab Touma kitchen, Damascus"
	phone="+963 11 555 0142"
	hours={{ timeZone: 'Asia/Damascus', open: 12, close: 23, days: [0, 1, 2, 3, 4, 6] }}
	areas={[
		{
			value: 'hall',
			label: 'Dining hall',
			description: 'Under the painted ceiling, near the kitchen.',
			seats: 8
		},
		{
			value: 'courtyard',
			label: 'Courtyard',
			description: 'Around the fountain, under the lemon trees.',
			seats: 6
		},
		{
			value: 'iwan',
			label: 'Iwan',
			description: 'The raised alcove, cushions and low tables.',
			seats: 4
		}
	]}
/>

Appointment

A service, a person (or anyone available), then a time that fits the service's length.

Service

What are you coming in for?
<script lang="ts">
	import Appointment from '#lib/blocks/booking/Appointment.svelte';
</script>

<Appointment
	place="The bimaristan of al-Nuri, Damascus"
	hours={{ timeZone: 'Asia/Damascus', open: 8, close: 14, days: [0, 1, 2, 3, 4, 6] }}
	services={[
		{
			id: 'eyes',
			name: 'Eye examination',
			description: 'Sight, eyelids and a look at the lens.',
			minutes: 30,
			price: 4_000
		},
		{
			id: 'consult',
			name: 'General consultation',
			description: 'Talk it through with a physician.',
			minutes: 30,
			price: 3_500
		},
		{
			id: 'bath',
			name: 'Bath and massage',
			description: 'The hammam, then an hour with the masseur.',
			minutes: 90,
			price: 6_500
		}
	]}
	providers={[
		{ id: 'ammar', name: 'Ammar al-Mawsili', role: 'Eye physician' },
		{ id: 'ibn-nafis', name: 'Ibn al-Nafis', role: 'Physician' },
		{ id: 'rufaida', name: 'Rufaida al-Aslamia', role: 'Nurse and masseur' }
	]}
/>

Installation

npx sinaui add blocks/booking

Connecting your data

Availability comes from later() and the demo rules at the end of booking.ts. Replace those calls with requests to your booking service, and each flow's submit with the call that books. A failed request shows a message with Try again; a failed booking keeps everything filled in.

Keyboard

KeyDoes
Enter in a fieldContinue to the next step
Arrow keys in the timesMove between free times
TabMove through the fields, then Back and Continue

Accessibility

  • Moving to a step puts focus on its heading, so screen readers hear where they are.
  • Times are a group of radio buttons; booked ones are skipped and named as booked.
  • Loading, how many times are free, and a day with none are announced.
  • If a step has a problem, focus goes to the first field that needs fixing.
  • With reduced motion, steps change without sliding.
Introduction
Theming
Changelog
Button
Action button
Confirm button
Copy button
Hold to confirm
Split button
Calendar
Checkbox
Color picker
Combobox
Date picker
Date range picker
File drop
Form
Input
Mention input
Multi select
Number field
OTP input
Password input
Phone input
Radio cards
Radio group
Rich text editor
Search field
Segmented control
Select
Signature pad
Slider
Switch
Tag input
Upload
Wheel picker
Textarea
Time field
Toggle group
Toolbar
Alert dialog
Command palette
Context menu
Dialog
Drawer
Dropdown
Hover card
Popover
Tooltip
Announcement bar
Alert
Empty state
Meter
Progress
Rolling number
Skeleton
Spinner
Toast
Avatar
Avatar group
Badge
Card
Carousel
Chart
Chat
Code block
Data table
Folder
Kbd
Mind map
Resizable panels
Scroll area
Separator
Sortable
Table
Tag group
Virtual list
Audio player
Image
Image compare
Video
Visualizer
Breadcrumb
File tree
Menubar
Navigation menu
Pagination
Sidebar layout
Stepper
Tabs
Alarm clock
Clock
Dynamic island
Fluid orb
Hourglass
Stopwatch
Timer
AI chat
AI media
Auth
Board
Booking
Dashboard
File explorer
Messaging
Notifications
Settings
Team
Overview
Pricing
Agent chat
Calendar
Voice agent
Mail inbox
CMS
Blog
Marketing site
E-commerce
AI agents
SaaS landing page
SaaS app
Accordion
Dark theme
Light theme