// Health app UI
Doctor Appointment App UI Design in Expo
This doctor appointment app UI puts the next visit first. Patients see a confirmed appointment on the home screen, then browse specialities, nearby clinics and top doctors.
- Screens
- 2
- Built with
- Expo SDK 58
- Fonts
- Inter
- License
- Free (MIT)

Doctor Appointment app screens

Patient home
A greeting, search, the next appointment, speciality shortcuts, nearby clinics and top doctors above the tab bar.

Doctor and booking
A clinic photo, doctor card with stats, month, day picker, time slots, directions and a booking action.
Copy the Doctor Appointment kit
Use the artifact as a design brief, the skill to generate the UI with an AI agent, or the code to run it right away.
Design artifact
The full design: tokens, screens and source
# Doctor Appointment: design artifact
This doctor appointment app UI puts the next visit first. Patients see a confirmed appointment on the home screen, then browse specialities, nearby clinics and top doctors.
- Type: Health mobile app UI
- Screens: 2
- Artboard: 390 × 844
- Target: Expo SDK 58, Expo Router, TypeScript, expo-image, expo-linear-gradient, react-native-svg
- Live page: https://developermill.com/app-designs/doctor-appointment-app-ui-design
## Design direction
Clinical white and soft blue-grey with a confident blue gradient, rounded cards and calm pastel speciality icons.
- Upcoming appointment card with video action
- Speciality shortcuts and nearby clinics
- Day and time slot pickers with disabled slots
- Small route preview with directions
## Foundations
### Colour
| Token | Hex |
| --- | --- |
| Clinic blue | `#2563EB` |
| Deep blue | `#1D4ED8` |
| Ice | `#EEF4FF` |
| Care green | `#12A06B` |
| Navy ink | `#0F172A` |
| Slate | `#64748B` |
### Typography
- Typefaces: Inter
- Common sizes (px): 10.5, 11, 12, 12.5, 13, 15, 16, 17
### Shape and depth
- Common corner radii (px): 3, 6, 12, 14, 18, 22
- Shadows: `0 4px 14px rgba(15,23,42,0.05)`, `0 4px 12px rgba(15,23,42,0.12)`, `0 2px 8px rgba(15,23,42,0.04)`, `0 14px 28px rgba(37,99,235,0.32)`
## Screens
### 1. Patient home
A greeting, search, the next appointment, speciality shortcuts, nearby clinics and top doctors above the tab bar.
Preview: https://developermill.com/app-designs/doctor-appointment-app-ui-design/home.webp
Copy on this screen:
- MS
- Hello, Meera
- How are you today?
- Search
- AR
- Dr. Arvind Raman
- Cardiologist · KG Heart Clinic
- Fri, 3 Oct
- 10:30 AM
- Confirmed
- Specialities
- See all
- Cardio
- Dental
- Eye
- Child
- Clinics near you
- Map view
- Open till 9 PM
- KG Heart Clinic
- 4.9
- Race Course · 1.2 km
- ECG
- Lab
- Vision Eye Care
- 4.8
- Gandhipuram · 2.4 km
- Top doctors
- View all
- KI
- Dr. Kavya Iyer
- Dermatologist · 9 yrs
- ₹500
- Home
### 2. Doctor and booking
A clinic photo, doctor card with stats, month, day picker, time slots, directions and a booking action.
Preview: https://developermill.com/app-designs/doctor-appointment-app-ui-design/booking.webp
Copy on this screen:
- AR
- Dr. Arvind Raman
- Cardiologist · MBBS, MD, DM
- 2.4k+
- Patients
- 14 yrs
- Experience
- 4.9
- Rating
- 860
- Reviews
- October 2026
- Available slots
- 6 open
- KG Heart Clinic, Race Course
- 1.2 km · 6 min drive
- Directions
- Consultation
- ₹800
- Book appointment
## Full source
The original artboards as HTML. Image URLs point at the hosted demo assets.
### Patient home (Main)
```html
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Doctor App – Home</title>
<script src="./support.js"></script>
</head>
<body>
<x-dc>
<helmet>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet">
<style>
body{margin:0}
a{color:#2563EB;text-decoration:none}a:hover{color:#1D4ED8}
button{font-family:inherit}
</style>
</helmet>
<div style="width: 390px; height: 844px; box-sizing: border-box; position: relative; overflow: hidden; background: #F7F9FC; font-family: Inter, system-ui, sans-serif; color: #0F172A">
<div style="display: flex; justify-content: space-between; align-items: center; padding: 16px 28px 0 32px; height: 38px; box-sizing: border-box">
<span style="font-size: 15px; font-weight: 600">9:41</span>
<div style="display: flex; gap: 6px; align-items: center">
<svg width="18" height="12" viewBox="0 0 18 12" aria-hidden="true"><rect x="0" y="8" width="3" height="4" rx="1" fill="#0F172A"/><rect x="5" y="5.5" width="3" height="6.5" rx="1" fill="#0F172A"/><rect x="10" y="3" width="3" height="9" rx="1" fill="#0F172A"/><rect x="15" y="0" width="3" height="12" rx="1" fill="#0F172A"/></svg>
<svg width="16" height="12" viewBox="0 0 16 12" aria-hidden="true"><path d="M8 11.5l2.2-2.6a3.2 3.2 0 0 0-4.4 0zM3.6 6.9a6.3 6.3 0 0 1 8.8 0l1.5-1.7a8.6 8.6 0 0 0-11.8 0zM.6 3.5a10.6 10.6 0 0 1 14.8 0L16.8 2A12.8 12.8 0 0 0-.8 2z" fill="#0F172A"/></svg>
<svg width="26" height="12" viewBox="0 0 26 12" aria-hidden="true"><rect x="0.5" y="0.5" width="22" height="11" rx="3" fill="none" stroke="#0F172A" opacity="0.4"/><rect x="2" y="2" width="19" height="8" rx="2" fill="#0F172A"/><rect x="24" y="4" width="1.6" height="4" rx="0.8" fill="#0F172A" opacity="0.4"/></svg>
</div>
</div>
<div style="padding: 14px 20px 0; display: flex; flex-direction: column; gap: 16px">
<div style="display: flex; align-items: center; gap: 12px">
<div style="width: 44px; height: 44px; border-radius: 22px; background: linear-gradient(135deg,#60A5FA,#2563EB); color: #fff; font-weight: 700; font-size: 15px; display: flex; align-items: center; justify-content: center">MS</div>
<div style="flex-grow: 1; display: flex; flex-direction: column; gap: 2px">
<span style="font-size: 12px; color: #64748B">Hello, Meera</span>
<span style="font-size: 19px; font-weight: 700; letter-spacing: -0.3px">How are you today?</span>
</div>
<button aria-label="Notifications" style="width: 44px; height: 44px; border-radius: 14px; border: 1px solid #E2E8F0; background: #fff; display: flex; align-items: center; justify-content: center; position: relative; padding: 0">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="#0F172A" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M18 8a6 6 0 0 0-12 0c0 7-3 9-3 9h18s-3-2-3-9"/><path d="M13.7 21a2 2 0 0 1-3.4 0"/></svg>
<span style="position: absolute; top: 10px; right: 11px; width: 8px; height: 8px; border-radius: 4px; background: #EF4444; border: 2px solid #fff"></span>
</button>
</div>
<div style="display: flex; align-items: center; gap: 10px; background: #fff; border: 1px solid #E2E8F0; border-radius: 16px; padding: 6px 6px 6px 14px; box-shadow: 0 2px 8px rgba(15,23,42,0.04)">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#64748B" stroke-width="2" stroke-linecap="round" aria-hidden="true"><circle cx="11" cy="11" r="7"/><path d="M21 21l-4.3-4.3"/></svg>
<label for="search" style="position: absolute; left: -9999px">Search</label>
<input id="search" placeholder="Search doctor, clinic, symptom" style="flex-grow: 1; border: none; outline: none; font-family: inherit; font-size: 14px; color: #0F172A; background: transparent; min-width: 0">
<button aria-label="Filters" style="width: 40px; height: 40px; border-radius: 12px; border: none; background: #2563EB; display: flex; align-items: center; justify-content: center; padding: 0">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M4 21v-7M4 10V3M12 21v-9M12 8V3M20 21v-5M20 12V3M1 14h6M9 8h6M17 16h6"/></svg>
</button>
</div>
<div style="background: linear-gradient(135deg,#3B82F6 0%,#2563EB 55%,#1D4ED8 100%); border-radius: 22px; padding: 18px; color: #fff; display: flex; flex-direction: column; gap: 16px; box-shadow: 0 14px 28px rgba(37,99,235,0.32)">
<div style="display: flex; align-items: center; gap: 12px">
<div style="width: 48px; height: 48px; border-radius: 24px; background: rgba(255,255,255,0.18); border: 1.5px solid rgba(255,255,255,0.5); font-weight: 700; font-size: 15px; display: flex; align-items: center; justify-content: center">AR</div>
<div style="flex-grow: 1; display: flex; flex-direction: column; gap: 3px">
<span style="font-size: 16px; font-weight: 700">Dr. Arvind Raman</span>
<span style="font-size: 12px; color: rgba(255,255,255,0.88)">Cardiologist · KG Heart Clinic</span>
</div>
<button aria-label="Start video consult" style="width: 44px; height: 44px; border-radius: 14px; border: none; background: #fff; display: flex; align-items: center; justify-content: center; padding: 0">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="#2563EB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M23 7l-7 5 7 5z"/><rect x="1" y="5" width="15" height="14" rx="2"/></svg>
</button>
</div>
<div style="display: flex; align-items: center; gap: 16px; background: rgba(255,255,255,0.14); border-radius: 14px; padding: 11px 14px; font-size: 12.5px; font-weight: 500">
<span style="display: flex; align-items: center; gap: 6px"><svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="2" stroke-linecap="round" aria-hidden="true"><rect x="3" y="4" width="18" height="18" rx="2"/><path d="M16 2v4M8 2v4M3 10h18"/></svg>Fri, 3 Oct</span>
<span style="display: flex; align-items: center; gap: 6px"><svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="2" stroke-linecap="round" aria-hidden="true"><circle cx="12" cy="12" r="10"/><path d="M12 6v6l4 2"/></svg>10:30 AM</span>
<span style="display: flex; align-items: center; gap: 6px; margin-left: auto"><svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M20 6L9 17l-5-5"/></svg>Confirmed</span>
</div>
</div>
<div style="display: flex; flex-direction: column; gap: 12px">
<div style="display: flex; justify-content: space-between; align-items: baseline">
<h2 style="margin: 0; font-size: 17px; font-weight: 700">Specialities</h2>
<a href="#" style="font-size: 13px; font-weight: 600">See all</a>
</div>
<div style="display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 12px">
<div style="display: flex; flex-direction: column; align-items: center; gap: 7px">
<div style="width: 58px; height: 58px; border-radius: 18px; background: #FEECEC; display: flex; align-items: center; justify-content: center"><svg width="26" height="26" viewBox="0 0 24 24" fill="none" stroke="#E5484D" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M20.8 4.6a5.5 5.5 0 0 0-7.8 0L12 5.7l-1-1.1a5.5 5.5 0 0 0-7.8 7.8L12 21.2l8.8-8.8a5.5 5.5 0 0 0 0-7.8z"/><path d="M3.5 12h4l1.5-3 3 6 1.5-3h7"/></svg></div>
<span style="font-size: 12.5px; font-weight: 500">Cardio</span>
</div>
<div style="display: flex; flex-direction: column; align-items: center; gap: 7px">
<div style="width: 58px; height: 58px; border-radius: 18px; background: #E8F0FE; display: flex; align-items: center; justify-content: center"><svg width="26" height="26" viewBox="0 0 24 24" fill="none" stroke="#2563EB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M7 3c-2.5 0-4 2-4 4.5 0 3 1.5 4 2 7 .4 2.6.8 6.5 2.5 6.5s1.6-4.5 2.5-6c.5-.8 1.5-.8 2 0 .9 1.5.8 6 2.5 6s2.1-3.9 2.5-6.5c.5-3 2-4 2-7C21 5 19.5 3 17 3c-2 0-3 1-5 1S9 3 7 3z"/></svg></div>
<span style="font-size: 12.5px; font-weight: 500">Dental</span>
</div>
<div style="display: flex; flex-direction: column; align-items: center; gap: 7px">
<div style="width: 58px; height: 58px; border-radius: 18px; background: #E3F6EE; display: flex; align-items: center; justify-content: center"><svg width="26" height="26" viewBox="0 0 24 24" fill="none" stroke="#12A06B" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/></svg></div>
<span style="font-size: 12.5px; font-weight: 500">Eye</span>
</div>
<div style="display: flex; flex-direction: column; align-items: center; gap: 7px">
<div style="width: 58px; height: 58px; border-radius: 18px; background: #FFF1DE; display: flex; align-items: center; justify-content: center"><svg width="26" height="26" viewBox="0 0 24 24" fill="none" stroke="#E0851B" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="13" r="8"/><path d="M12 5c0-1.5 1-2.5 2.5-2.5"/><path d="M9 11.5h.01M15 11.5h.01"/><path d="M9.5 15.5a3.5 3.5 0 0 0 5 0"/></svg></div>
<span style="font-size: 12.5px; font-weight: 500">Child</span>
</div>
</div>
</div>
<div style="display: flex; flex-direction: column; gap: 12px">
<div style="display: flex; justify-content: space-between; align-items: baseline">
<h2 style="margin: 0; font-size: 17px; font-weight: 700">Clinics near you</h2>
<a href="#" style="font-size: 13px; font-weight: 600">Map view</a>
</div>
<div style="display: flex; gap: 12px; margin-right: -20px">
<div style="width: 196px; flex-shrink: 0; background: #fff; border-radius: 18px; overflow: hidden; border: 1px solid #EEF2F7; box-shadow: 0 4px 14px rgba(15,23,42,0.05)">
<div style="height: 90px; position: relative">
<img src="https://developermill.com/app-designs/doctor-appointment-app-ui-design/assets/3341cf7574.jpg" alt="KG Heart Clinic treatment room" style="width: 100%; height: 100%; object-fit: cover; display: block">
<span style="position: absolute; top: 8px; left: 8px; background: #fff; color: #0E8A5B; font-size: 10.5px; font-weight: 600; padding: 4px 8px; border-radius: 999px; display: flex; align-items: center; gap: 4px"><span style="width: 6px; height: 6px; border-radius: 3px; background: #12A06B"></span>Open till 9 PM</span>
</div>
<div style="padding: 10px 12px 12px; display: flex; flex-direction: column; gap: 6px">
<div style="display: flex; justify-content: space-between; align-items: center">
<span style="font-size: 14px; font-weight: 700">KG Heart Clinic</span>
<span style="font-size: 12px; font-weight: 600; display: flex; align-items: center; gap: 3px"><svg width="12" height="12" viewBox="0 0 24 24" fill="#F5A524" aria-hidden="true"><path d="M12 2l3.1 6.3 6.9 1-5 4.9 1.2 6.8L12 17.8 5.8 21l1.2-6.8-5-4.9 6.9-1z"/></svg>4.9</span>
</div>
<span style="font-size: 11.5px; color: #64748B; display: flex; align-items: center; gap: 4px"><svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="#64748B" stroke-width="2" aria-hidden="true"><path d="M21 10c0 7-9 13-9 13S3 17 3 10a9 9 0 0 1 18 0z"/><circle cx="12" cy="10" r="3"/></svg>Race Course · 1.2 km</span>
<div style="display: flex; gap: 5px">
<span style="font-size: 10.5px; font-weight: 600; color: #1D4ED8; background: #EEF4FF; padding: 3px 7px; border-radius: 6px">Cardio</span>
<span style="font-size: 10.5px; font-weight: 600; color: #1D4ED8; background: #EEF4FF; padding: 3px 7px; border-radius: 6px">ECG</span>
<span style="font-size: 10.5px; font-weight: 600; color: #1D4ED8; background: #EEF4FF; padding: 3px 7px; border-radius: 6px">Lab</span>
</div>
</div>
</div>
<div style="width: 196px; flex-shrink: 0; background: #fff; border-radius: 18px; overflow: hidden; border: 1px solid #EEF2F7; box-shadow: 0 4px 14px rgba(15,23,42,0.05)">
<div style="height: 90px; position: relative">
<img src="https://developermill.com/app-designs/doctor-appointment-app-ui-design/assets/4737dbf3f8.jpg" alt="Vision Eye Care exam room" style="width: 100%; height: 100%; object-fit: cover; display: block">
<span style="position: absolute; top: 8px; left: 8px; background: #fff; color: #0E8A5B; font-size: 10.5px; font-weight: 600; padding: 4px 8px; border-radius: 999px; display: flex; align-items: center; gap: 4px"><span style="width: 6px; height: 6px; border-radius: 3px; background: #12A06B"></span>Open till 9 PM</span>
</div>
<div style="padding: 10px 12px 12px; display: flex; flex-direction: column; gap: 6px">
<div style="display: flex; justify-content: space-between; align-items: center">
<span style="font-size: 14px; font-weight: 700">Vision Eye Care</span>
<span style="font-size: 12px; font-weight: 600; display: flex; align-items: center; gap: 3px"><svg width="12" height="12" viewBox="0 0 24 24" fill="#F5A524" aria-hidden="true"><path d="M12 2l3.1 6.3 6.9 1-5 4.9 1.2 6.8L12 17.8 5.8 21l1.2-6.8-5-4.9 6.9-1z"/></svg>4.8</span>
</div>
<span style="font-size: 11.5px; color: #64748B; display: flex; align-items: center; gap: 4px"><svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="#64748B" stroke-width="2" aria-hidden="true"><path d="M21 10c0 7-9 13-9 13S3 17 3 10a9 9 0 0 1 18 0z"/><circle cx="12" cy="10" r="3"/></svg>Gandhipuram · 2.4 km</span>
<div style="display: flex; gap: 5px">
<span style="font-size: 10.5px; font-weight: 600; color: #1D4ED8; background: #EEF4FF; padding: 3px 7px; border-radius: 6px">Cardio</span>
<span style="font-size: 10.5px; font-weight: 600; color: #1D4ED8; background: #EEF4FF; padding: 3px 7px; border-radius: 6px">ECG</span>
<span style="font-size: 10.5px; font-weight: 600; color: #1D4ED8; background: #EEF4FF; padding: 3px 7px; border-radius: 6px">Lab</span>
</div>
</div>
</div>
</div>
</div>
<div style="display: flex; flex-direction: column; gap: 12px">
<div style="display: flex; justify-content: space-between; align-items: baseline">
<h2 style="margin: 0; font-size: 17px; font-weight: 700">Top doctors</h2>
<a href="#" style="font-size: 13px; font-weight: 600">View all</a>
</div>
<div style="display: flex; align-items: center; gap: 12px; background: #fff; border: 1px solid #EEF2F7; border-radius: 18px; padding: 12px">
<div style="width: 46px; height: 46px; border-radius: 23px; background: linear-gradient(135deg,#FDBA74,#F97316); color: #fff; font-weight: 700; font-size: 15px; display: flex; align-items: center; justify-content: center">KI</div>
<div style="flex-grow: 1; display: flex; flex-direction: column; gap: 3px">
<span style="font-size: 15px; font-weight: 700">Dr. Kavya Iyer</span>
<span style="font-size: 12px; color: #64748B">Dermatologist · 9 yrs</span>
</div>
<span style="font-size: 16px; font-weight: 700">₹500</span>
</div>
</div>
</div>
<nav aria-label="Main" style="position: absolute; left: 16px; right: 16px; bottom: 26px; height: 68px; background: #fff; border-radius: 26px; box-shadow: 0 10px 30px rgba(15,23,42,0.14); display: flex; align-items: center; justify-content: space-between; padding: 0 22px 0 10px; box-sizing: border-box">
<a href="#" style="display: flex; align-items: center; gap: 8px; background: #2563EB; color: #fff; padding: 0 20px; height: 48px; border-radius: 18px; font-size: 14px; font-weight: 600">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 10l9-7 9 7v10a2 2 0 0 1-2 2h-4v-7H9v7H5a2 2 0 0 1-2-2z"/></svg>Home
</a>
<a href="#" aria-label="Appointments" style="width: 44px; height: 44px; display: flex; align-items: center; justify-content: center"><svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="#64748B" stroke-width="2" stroke-linecap="round" aria-hidden="true"><rect x="3" y="4" width="18" height="18" rx="2"/><path d="M16 2v4M8 2v4M3 10h18"/></svg></a>
<a href="#" aria-label="Messages" style="width: 44px; height: 44px; display: flex; align-items: center; justify-content: center"><svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="#64748B" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M21 11.5a8.4 8.4 0 0 1-12.4 7.4L3 21l2.1-5.6A8.5 8.5 0 1 1 21 11.5z"/></svg></a>
<a href="#" aria-label="Profile" style="width: 44px; height: 44px; display: flex; align-items: center; justify-content: center"><svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="#64748B" stroke-width="2" stroke-linecap="round" aria-hidden="true"><circle cx="12" cy="8" r="4"/><path d="M4 21a8 8 0 0 1 16 0"/></svg></a>
</nav>
<div style="position: absolute; bottom: 8px; left: 50%; margin-left: -67px; width: 134px; height: 5px; border-radius: 3px; background: #0F172A"></div>
</div>
</x-dc>
<script type="text/x-dc" data-dc-script data-props='{"$preview":{"width":390,"height":844}}'>
class Component extends DCLogic {
renderVals() {
return {};
}
}
</script>
</body>
</html>
```
### Doctor and booking (Detail)
```html
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Doctor App – Doctor detail</title>
<script src="./support.js"></script>
</head>
<body>
<x-dc>
<helmet>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet">
<style>
body{margin:0}
a{color:#2563EB;text-decoration:none}a:hover{color:#1D4ED8}
button{font-family:inherit;cursor:pointer}
</style>
</helmet>
<div style="width: 390px; height: 844px; box-sizing: border-box; position: relative; overflow: hidden; background: #F7F9FC; font-family: Inter, system-ui, sans-serif; color: #0F172A">
<div style="position: absolute; top: 0; left: 0; right: 0; height: 250px">
<img src="https://developermill.com/app-designs/doctor-appointment-app-ui-design/assets/d8eca69c65.jpg" alt="Clinic consultation room" style="width: 100%; height: 100%; object-fit: cover; display: block">
<div style="position: absolute; inset: 0; background: linear-gradient(180deg, rgba(255,255,255,0.35) 0%, rgba(255,255,255,0) 30%)"></div>
</div>
<div style="position: relative; display: flex; justify-content: space-between; align-items: center; padding: 16px 28px 0 32px; height: 38px; box-sizing: border-box">
<span style="font-size: 15px; font-weight: 600">9:41</span>
<div style="display: flex; gap: 6px; align-items: center">
<svg width="18" height="12" viewBox="0 0 18 12" aria-hidden="true"><rect x="0" y="8" width="3" height="4" rx="1" fill="#0F172A"/><rect x="5" y="5.5" width="3" height="6.5" rx="1" fill="#0F172A"/><rect x="10" y="3" width="3" height="9" rx="1" fill="#0F172A"/><rect x="15" y="0" width="3" height="12" rx="1" fill="#0F172A"/></svg>
<svg width="26" height="12" viewBox="0 0 26 12" aria-hidden="true"><rect x="0.5" y="0.5" width="22" height="11" rx="3" fill="none" stroke="#0F172A" opacity="0.4"/><rect x="2" y="2" width="19" height="8" rx="2" fill="#0F172A"/></svg>
</div>
</div>
<div style="position: relative; display: flex; justify-content: space-between; padding: 14px 20px 0">
<a href="Main.dc.html" aria-label="Back" style="width: 44px; height: 44px; border-radius: 14px; background: #fff; display: flex; align-items: center; justify-content: center; box-shadow: 0 4px 12px rgba(15,23,42,0.12)">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="#0F172A" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M15 18l-6-6 6-6"/></svg>
</a>
<button aria-label="Save doctor" style="width: 44px; height: 44px; border-radius: 14px; background: #fff; border: none; display: flex; align-items: center; justify-content: center; box-shadow: 0 4px 12px rgba(15,23,42,0.12); padding: 0">
<svg width="20" height="20" viewBox="0 0 24 24" fill="#E5484D" aria-hidden="true"><path d="M20.8 4.6a5.5 5.5 0 0 0-7.8 0L12 5.7l-1-1.1a5.5 5.5 0 0 0-7.8 7.8L12 21.2l8.8-8.8a5.5 5.5 0 0 0 0-7.8z"/></svg>
</button>
</div>
<div style="position: absolute; top: 196px; left: 16px; right: 16px; background: #fff; border-radius: 22px; padding: 16px; box-shadow: 0 12px 30px rgba(15,23,42,0.10); display: flex; flex-direction: column; gap: 14px">
<div style="display: flex; align-items: center; gap: 12px">
<div style="width: 52px; height: 52px; border-radius: 26px; background: linear-gradient(135deg,#60A5FA,#2563EB); color: #fff; font-weight: 700; font-size: 16px; display: flex; align-items: center; justify-content: center">AR</div>
<div style="flex-grow: 1; display: flex; flex-direction: column; gap: 3px">
<span style="font-size: 17px; font-weight: 700; display: flex; align-items: center; gap: 6px">Dr. Arvind Raman
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#12A06B" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" role="img" aria-label="Verified"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/><path d="M9 12l2 2 4-4"/></svg>
</span>
<span style="font-size: 12px; color: #64748B">Cardiologist · MBBS, MD, DM</span>
</div>
<button aria-label="Message doctor" style="width: 44px; height: 44px; border-radius: 14px; border: none; background: #EEF4FF; display: flex; align-items: center; justify-content: center; padding: 0">
<svg width="19" height="19" viewBox="0 0 24 24" fill="none" stroke="#2563EB" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M21 11.5a8.4 8.4 0 0 1-12.4 7.4L3 21l2.1-5.6A8.5 8.5 0 1 1 21 11.5z"/></svg>
</button>
</div>
<div style="display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 4px; border-top: 1px solid #EEF2F7; padding-top: 12px; text-align: center">
<div style="display: flex; flex-direction: column; gap: 2px"><span style="font-size: 16px; font-weight: 700">2.4k+</span><span style="font-size: 11px; color: #64748B">Patients</span></div>
<div style="display: flex; flex-direction: column; gap: 2px"><span style="font-size: 16px; font-weight: 700">14 yrs</span><span style="font-size: 11px; color: #64748B">Experience</span></div>
<div style="display: flex; flex-direction: column; gap: 2px"><span style="font-size: 16px; font-weight: 700">4.9</span><span style="font-size: 11px; color: #64748B">Rating</span></div>
<div style="display: flex; flex-direction: column; gap: 2px"><span style="font-size: 16px; font-weight: 700">860</span><span style="font-size: 11px; color: #64748B">Reviews</span></div>
</div>
</div>
<div style="position: absolute; top: 368px; left: 20px; right: 20px; display: flex; flex-direction: column; gap: 14px">
<div style="display: flex; justify-content: space-between; align-items: center">
<h2 style="margin: 0; font-size: 17px; font-weight: 700">October 2026</h2>
<div style="display: flex; gap: 4px">
<button aria-label="Previous week" style="width: 28px; height: 28px; border: none; background: transparent; padding: 0; display: flex; align-items: center; justify-content: center"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#64748B" stroke-width="2.4" stroke-linecap="round" aria-hidden="true"><path d="M15 18l-6-6 6-6"/></svg></button>
<button aria-label="Next week" style="width: 28px; height: 28px; border: none; background: transparent; padding: 0; display: flex; align-items: center; justify-content: center"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#0F172A" stroke-width="2.4" stroke-linecap="round" aria-hidden="true"><path d="M9 18l6-6-6-6"/></svg></button>
</div>
</div>
<div style="display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); gap: 8px">
<sc-for list="{{days}}" as="d" hint-placeholder-count="5">
<button onClick="{{d.pick}}" style="{{d.style}}">
<span style="font-size: 11px; font-weight: 500; opacity: 0.8">{{d.dow}}</span>
<span style="font-size: 18px; font-weight: 700">{{d.num}}</span>
</button>
</sc-for>
</div>
<div style="display: flex; justify-content: space-between; align-items: baseline">
<h2 style="margin: 0; font-size: 17px; font-weight: 700">Available slots</h2>
<span style="font-size: 12px; color: #64748B">6 open</span>
</div>
<div style="display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 8px">
<sc-for list="{{slots}}" as="s" hint-placeholder-count="6">
<button onClick="{{s.pick}}" disabled="{{s.disabled}}" style="{{s.style}}">{{s.label}}</button>
</sc-for>
</div>
<div style="background: #fff; border: 1px solid #EEF2F7; border-radius: 18px; overflow: hidden">
<div style="height: 92px; position: relative; background: #EEF1F5">
<svg width="350" height="92" viewBox="0 0 350 92" aria-hidden="true" style="display: block">
<rect width="350" height="92" fill="#EEF1F5"/>
<g fill="#FFFFFF">
<rect x="0" y="34" width="350" height="9"/><rect x="0" y="70" width="350" height="7"/>
<rect x="58" y="0" width="8" height="92"/><rect x="138" y="0" width="9" height="92"/><rect x="226" y="0" width="8" height="92"/><rect x="300" y="0" width="7" height="92"/>
</g>
<rect x="246" y="48" width="46" height="18" rx="3" fill="#D7EFD9"/><rect x="310" y="6" width="34" height="24" rx="3" fill="#D7EFD9"/><rect x="76" y="48" width="52" height="18" rx="3" fill="#E2E6EC"/>
<path d="M28 52 H100 V38 H180 V18 H218" fill="none" stroke="#2563EB" stroke-width="5" stroke-linecap="round" stroke-linejoin="round"/>
<circle cx="28" cy="52" r="5" fill="#fff" stroke="#2563EB" stroke-width="3"/>
<circle cx="218" cy="18" r="11" fill="#2563EB"/><path d="M218 13v10M213 18h10" stroke="#fff" stroke-width="2.4" stroke-linecap="round"/>
</svg>
</div>
<div style="display: flex; align-items: center; justify-content: space-between; padding: 10px 12px">
<div style="display: flex; flex-direction: column; gap: 2px">
<span style="font-size: 13px; font-weight: 700">KG Heart Clinic, Race Course</span>
<span style="font-size: 11px; color: #64748B">1.2 km · 6 min drive</span>
</div>
<a href="#" style="display: flex; align-items: center; gap: 5px; background: #EEF4FF; padding: 0 12px; height: 34px; border-radius: 10px; font-size: 12px; font-weight: 600">
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="#2563EB" stroke-width="2.2" stroke-linejoin="round" aria-hidden="true"><path d="M3 11l19-9-9 19-2-8z"/></svg>Directions
</a>
</div>
</div>
</div>
<div style="position: absolute; left: 0; right: 0; bottom: 0; height: 104px; background: #fff; border-top: 1px solid #EEF2F7; display: flex; align-items: flex-start; justify-content: space-between; padding: 16px 20px 0; box-sizing: border-box">
<div style="display: flex; flex-direction: column; gap: 2px">
<span style="font-size: 12px; color: #64748B">Consultation</span>
<span style="font-size: 22px; font-weight: 800; letter-spacing: -0.3px">₹800</span>
</div>
<a href="#" style="height: 54px; padding: 0 30px; border-radius: 18px; background: #2563EB; color: #fff; font-size: 15px; font-weight: 600; display: flex; align-items: center; box-shadow: 0 10px 22px rgba(37,99,235,0.35)">Book appointment</a>
</div>
<div style="position: absolute; bottom: 8px; left: 50%; margin-left: -67px; width: 134px; height: 5px; border-radius: 3px; background: #0F172A"></div>
</div>
</x-dc>
<script type="text/x-dc" data-dc-script data-props='{"$preview":{"width":390,"height":844}}'>
class Component extends DCLogic {
constructor(props) {
super(props);
this.state = { day: 3, slot: '10:30 AM' };
}
renderVals() {
const days = [['Wed',1],['Thu',2],['Fri',3],['Sat',4],['Sun',5]].map(([dow, num]) => {
const on = this.state.day === num;
return {
dow, num,
pick: () => this.setState({ day: num }),
style: 'display: flex; flex-direction: column; align-items: center; gap: 4px; padding: 10px 0; border-radius: 16px; font-family: inherit; ' +
(on ? 'background: #2563EB; color: #fff; border: 1px solid #2563EB; box-shadow: 0 8px 18px rgba(37,99,235,0.3)' : 'background: #fff; color: #0F172A; border: 1px solid #E2E8F0')
};
});
const slots = ['09:30 AM','10:00 AM','10:30 AM','11:30 AM','04:30 PM','05:00 PM'].map((label) => {
const disabled = label === '10:00 AM';
const on = this.state.slot === label;
let look = 'background: #fff; color: #0F172A; border: 1px solid #E2E8F0';
if (disabled) look = 'background: #F8FAFC; color: #94A3B8; border: 1px solid #EEF2F7; text-decoration: line-through; cursor: default';
else if (on) look = 'background: #EEF4FF; color: #1D4ED8; border: 1.5px solid #2563EB';
return {
label, disabled,
pick: () => { if (!disabled) this.setState({ slot: label }); },
style: 'height: 44px; border-radius: 12px; font-family: inherit; font-size: 13px; font-weight: 600; ' + look
};
});
return { days, slots };
}
}
</script>
</body>
</html>
```SKILL.md
Save as .claude/skills/doctor-appointment-app-ui/SKILL.md
--- name: doctor-appointment-app-ui description: Build the Doctor Appointment mobile app UI in Expo React Native. Use when asked to create a doctor appointment app UI or a new screen in this visual style. --- # Doctor Appointment app UI Build this health app UI in an Expo Router project with TypeScript. Reference: https://developermill.com/app-designs/doctor-appointment-app-ui-design ## Visual direction Clinical white and soft blue-grey with a confident blue gradient, rounded cards and calm pastel speciality icons. Keep these signature details: - Upcoming appointment card with video action - Speciality shortcuts and nearby clinics - Day and time slot pickers with disabled slots - Small route preview with directions ## Set up ```bash npx expo install expo-image expo-linear-gradient react-native-svg @expo-google-fonts/inter expo-font ``` ## Tokens | Name | Hex | | --- | --- | | Clinic blue | `#2563EB` | | Deep blue | `#1D4ED8` | | Ice | `#EEF4FF` | | Care green | `#12A06B` | | Navy ink | `#0F172A` | | Slate | `#64748B` | - Typefaces: Inter - Text sizes (px): 10.5, 11, 12, 12.5, 13, 15, 16, 17 - Corner radii (px): 3, 6, 12, 14, 18, 22 Define these once and reuse them. Do not add new colours. ## Screens 1. **Patient home.** A greeting, search, the next appointment, speciality shortcuts, nearby clinics and top doctors above the tab bar. 2. **Doctor and booking.** A clinic photo, doctor card with stats, month, day picker, time slots, directions and a booking action. ## Build rules - Draw each screen on a 390 × 844 artboard and scale it to the device width. - Load each custom font weight by family name with expo-font. Do not set fontWeight with custom fonts. - Draw icons, charts and illustrations with react-native-svg. - Use Pressable with an accessibilityLabel for controls, with at least a 44px target. - Navigate with router.navigate and keep sample data as constants at the top of the file. - Use expo-image with contentFit for photos. ## Done when - All routes are linked and reachable. - Colours, type and radii match the tokens above. - `npx tsc --noEmit` and `npx expo lint` pass. - The layout holds up on small and large phones.
Set up the project
npx create-expo-app@latest doctor-appointment-app --template default cd doctor-appointment-app rm -rf src npx expo install expo-image expo-linear-gradient react-native-svg @expo-google-fonts/inter expo-font
src/app/_layout.tsx
import {
Inter_400Regular,
Inter_500Medium,
Inter_600SemiBold,
Inter_700Bold,
Inter_800ExtraBold,
} from '@expo-google-fonts/inter';
import { useFonts } from 'expo-font';
import { Stack } from 'expo-router';
export default function RootLayout() {
const [fontsLoaded] = useFonts({
Inter_400Regular,
Inter_500Medium,
Inter_600SemiBold,
Inter_700Bold,
Inter_800ExtraBold,
});
if (!fontsLoaded) return null;
return <Stack screenOptions={{ headerShown: false }} />;
}src/app/index.tsx
import { Image } from 'expo-image';
import { LinearGradient } from 'expo-linear-gradient';
import { Pressable, StyleSheet, Text, TextInput, View } from 'react-native';
import Svg, { Circle, Path, Rect } from 'react-native-svg';
import { Screen } from '@/components/screen';
import { asset } from '@/lib/assets';
export default function MainScreen() {
return (
<Screen background="#F7F9FC" statusBar="dark">
<View style={{ flex: 1 }}>
<View
style={{
justifyContent: 'space-between',
alignItems: 'center',
paddingTop: 16,
paddingBottom: 0,
paddingLeft: 32,
paddingRight: 28,
height: 38,
flexDirection: 'row',
}}
/>
<View style={{ paddingTop: 14, paddingBottom: 0, paddingHorizontal: 20, gap: 16 }}>
<View style={{ alignItems: 'center', gap: 12, flexDirection: 'row' }}>
<View
style={{
width: 44,
height: 44,
borderRadius: 22,
alignItems: 'center',
justifyContent: 'center',
overflow: 'hidden',
flexDirection: 'row',
}}>
<LinearGradient
colors={['#60A5FA', '#2563EB']}
locations={[0, 1]}
start={{ x: 0.146, y: 0.146 }}
end={{ x: 0.854, y: 0.854 }}
style={StyleSheet.absoluteFill}
/>
<Text
style={{ fontFamily: 'Inter_700Bold', fontSize: 15, color: '#fff', flexShrink: 1 }}>
MS
</Text>
</View>
<View style={{ flexGrow: 1, gap: 2, flexShrink: 1 }}>
<Text style={{ fontFamily: 'Inter_400Regular', fontSize: 12, color: '#64748B' }}>
Hello, Meera
</Text>
<Text
style={{
fontFamily: 'Inter_700Bold',
fontSize: 19,
color: '#0F172A',
letterSpacing: -0.3,
}}>
How are you today?
</Text>
</View>
<Pressable
accessibilityRole="button"
accessibilityLabel="Notifications"
style={{
width: 44,
height: 44,
borderRadius: 14,
borderWidth: 1,
borderColor: '#E2E8F0',
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
padding: 0,
flexDirection: 'row',
}}>
<Svg
width="20"
height="20"
viewBox="0 0 24 24"
fill="none"
stroke="#0F172A"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round">
<Path d="M18 8a6 6 0 0 0-12 0c0 7-3 9-3 9h18s-3-2-3-9" />
<Path d="M13.7 21a2 2 0 0 1-3.4 0" />
</Svg>
<View
style={{
position: 'absolute',
top: 10,
right: 11,
width: 12,
height: 12,
borderRadius: 4,
backgroundColor: '#EF4444',
borderWidth: 2,
borderColor: '#fff',
}}
/>
</Pressable>
</View>
<View
style={{
alignItems: 'center',
gap: 10,
backgroundColor: '#fff',
borderWidth: 1,
borderColor: '#E2E8F0',
borderRadius: 16,
paddingVertical: 6,
paddingLeft: 14,
paddingRight: 6,
boxShadow: '0 2px 8px rgba(15,23,42,0.04)',
flexDirection: 'row',
}}>
<Svg
width="18"
height="18"
viewBox="0 0 24 24"
fill="none"
stroke="#64748B"
strokeWidth="2"
strokeLinecap="round">
<Circle cx="11" cy="11" r="7" />
<Path d="M21 21l-4.3-4.3" />
</Svg>
<TextInput
placeholder="Search doctor, clinic, symptom"
placeholderTextColor="#8A8A8A"
style={{
flexGrow: 1,
borderWidth: 0,
minWidth: 0,
flexShrink: 1,
fontFamily: 'Inter_400Regular',
fontSize: 14,
color: '#0F172A',
}}
/>
<Pressable
accessibilityRole="button"
accessibilityLabel="Filters"
style={{
width: 40,
height: 40,
borderRadius: 12,
borderWidth: 0,
backgroundColor: '#2563EB',
alignItems: 'center',
justifyContent: 'center',
padding: 0,
flexDirection: 'row',
}}>
<Svg
width="18"
height="18"
viewBox="0 0 24 24"
fill="none"
stroke="#fff"
strokeWidth="2"
strokeLinecap="round">
<Path d="M4 21v-7M4 10V3M12 21v-9M12 8V3M20 21v-5M20 12V3M1 14h6M9 8h6M17 16h6" />
</Svg>
</Pressable>
</View>
<View
style={{
borderRadius: 22,
padding: 18,
gap: 16,
boxShadow: '0 14px 28px rgba(37,99,235,0.32)',
overflow: 'hidden',
}}>
<LinearGradient
colors={['#3B82F6', '#2563EB', '#1D4ED8']}
locations={[0, 0.55, 1]}
start={{ x: 0.146, y: 0.146 }}
end={{ x: 0.854, y: 0.854 }}
style={StyleSheet.absoluteFill}
/>
<View style={{ alignItems: 'center', gap: 12, flexDirection: 'row' }}>
<View
style={{
width: 51,
height: 51,
borderRadius: 24,
backgroundColor: 'rgba(255,255,255,0.18)',
borderWidth: 1.5,
borderColor: 'rgba(255,255,255,0.5)',
alignItems: 'center',
justifyContent: 'center',
flexDirection: 'row',
}}>
<Text
style={{
fontFamily: 'Inter_700Bold',
fontSize: 15,
color: '#fff',
flexShrink: 1,
}}>
AR
</Text>
</View>
<View style={{ flexGrow: 1, gap: 3, flexShrink: 1 }}>
<Text style={{ fontFamily: 'Inter_700Bold', fontSize: 16, color: '#fff' }}>
Dr. Arvind Raman
</Text>
<Text
style={{
fontFamily: 'Inter_400Regular',
fontSize: 12,
color: 'rgba(255,255,255,0.88)',
}}>
Cardiologist · KG Heart Clinic
</Text>
</View>
<Pressable
accessibilityRole="button"
accessibilityLabel="Start video consult"
style={{
width: 44,
height: 44,
borderRadius: 14,
borderWidth: 0,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
padding: 0,
flexDirection: 'row',
}}>
<Svg
width="20"
height="20"
viewBox="0 0 24 24"
fill="none"
stroke="#2563EB"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round">
<Path d="M23 7l-7 5 7 5z" />
<Rect x="1" y="5" width="15" height="14" rx="2" />
</Svg>
</Pressable>
</View>
<View
style={{
alignItems: 'center',
gap: 16,
backgroundColor: 'rgba(255,255,255,0.14)',
borderRadius: 14,
paddingVertical: 11,
paddingHorizontal: 14,
flexDirection: 'row',
}}>
<View style={{ alignItems: 'center', gap: 6, flexShrink: 1, flexDirection: 'row' }}>
<Svg
width="15"
height="15"
viewBox="0 0 24 24"
fill="none"
stroke="#fff"
strokeWidth="2"
strokeLinecap="round">
<Rect x="3" y="4" width="18" height="18" rx="2" />
<Path d="M16 2v4M8 2v4M3 10h18" />
</Svg>
<Text
style={{
fontFamily: 'Inter_500Medium',
fontSize: 12.5,
color: '#fff',
flexShrink: 1,
}}>
Fri, 3 Oct
</Text>
</View>
<View style={{ alignItems: 'center', gap: 6, flexShrink: 1, flexDirection: 'row' }}>
<Svg
width="15"
height="15"
viewBox="0 0 24 24"
fill="none"
stroke="#fff"
strokeWidth="2"
strokeLinecap="round">
<Circle cx="12" cy="12" r="10" />
<Path d="M12 6v6l4 2" />
</Svg>
<Text
style={{
fontFamily: 'Inter_500Medium',
fontSize: 12.5,
color: '#fff',
flexShrink: 1,
}}>
10:30 AM
</Text>
</View>
<View
style={{
alignItems: 'center',
gap: 6,
marginLeft: 'auto',
flexShrink: 1,
flexDirection: 'row',
}}>
<Svg
width="15"
height="15"
viewBox="0 0 24 24"
fill="none"
stroke="#fff"
strokeWidth="2.4"
strokeLinecap="round"
strokeLinejoin="round">
<Path d="M20 6L9 17l-5-5" />
</Svg>
<Text
style={{
fontFamily: 'Inter_500Medium',
fontSize: 12.5,
color: '#fff',
flexShrink: 1,
}}>
Confirmed
</Text>
</View>
</View>
</View>
<View style={{ gap: 12 }}>
<View
style={{
justifyContent: 'space-between',
alignItems: 'baseline',
flexDirection: 'row',
}}>
<Text
style={{
margin: 0,
flexShrink: 1,
fontFamily: 'Inter_700Bold',
fontSize: 17,
color: '#0F172A',
}}>
Specialities
</Text>
<Pressable accessibilityRole="link" style={{ flexShrink: 1 }}>
<Text style={{ fontFamily: 'Inter_600SemiBold', fontSize: 13, color: '#2563EB' }}>
See all
</Text>
</Pressable>
</View>
<View style={{ flexDirection: 'row', gap: 12 }}>
<View style={{ alignItems: 'center', gap: 7, flex: 1 }}>
<View
style={{
width: 58,
height: 58,
borderRadius: 18,
backgroundColor: '#FEECEC',
alignItems: 'center',
justifyContent: 'center',
flexDirection: 'row',
}}>
<Svg
width="26"
height="26"
viewBox="0 0 24 24"
fill="none"
stroke="#E5484D"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round">
<Path d="M20.8 4.6a5.5 5.5 0 0 0-7.8 0L12 5.7l-1-1.1a5.5 5.5 0 0 0-7.8 7.8L12 21.2l8.8-8.8a5.5 5.5 0 0 0 0-7.8z" />
<Path d="M3.5 12h4l1.5-3 3 6 1.5-3h7" />
</Svg>
</View>
<Text style={{ fontFamily: 'Inter_500Medium', fontSize: 12.5, color: '#0F172A' }}>
Cardio
</Text>
</View>
<View style={{ alignItems: 'center', gap: 7, flex: 1 }}>
<View
style={{
width: 58,
height: 58,
borderRadius: 18,
backgroundColor: '#E8F0FE',
alignItems: 'center',
justifyContent: 'center',
flexDirection: 'row',
}}>
<Svg
width="26"
height="26"
viewBox="0 0 24 24"
fill="none"
stroke="#2563EB"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round">
<Path d="M7 3c-2.5 0-4 2-4 4.5 0 3 1.5 4 2 7 .4 2.6.8 6.5 2.5 6.5s1.6-4.5 2.5-6c.5-.8 1.5-.8 2 0 .9 1.5.8 6 2.5 6s2.1-3.9 2.5-6.5c.5-3 2-4 2-7C21 5 19.5 3 17 3c-2 0-3 1-5 1S9 3 7 3z" />
</Svg>
</View>
<Text style={{ fontFamily: 'Inter_500Medium', fontSize: 12.5, color: '#0F172A' }}>
Dental
</Text>
</View>
<View style={{ alignItems: 'center', gap: 7, flex: 1 }}>
<View
style={{
width: 58,
height: 58,
borderRadius: 18,
backgroundColor: '#E3F6EE',
alignItems: 'center',
justifyContent: 'center',
flexDirection: 'row',
}}>
<Svg
width="26"
height="26"
viewBox="0 0 24 24"
fill="none"
stroke="#12A06B"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round">
<Path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z" />
<Circle cx="12" cy="12" r="3" />
</Svg>
</View>
<Text style={{ fontFamily: 'Inter_500Medium', fontSize: 12.5, color: '#0F172A' }}>
Eye
</Text>
</View>
<View style={{ alignItems: 'center', gap: 7, flex: 1 }}>
<View
style={{
width: 58,
height: 58,
borderRadius: 18,
backgroundColor: '#FFF1DE',
alignItems: 'center',
justifyContent: 'center',
flexDirection: 'row',
}}>
<Svg
width="26"
height="26"
viewBox="0 0 24 24"
fill="none"
stroke="#E0851B"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round">
<Circle cx="12" cy="13" r="8" />
<Path d="M12 5c0-1.5 1-2.5 2.5-2.5" />
<Path d="M9 11.5h.01M15 11.5h.01" />
<Path d="M9.5 15.5a3.5 3.5 0 0 0 5 0" />
</Svg>
</View>
<Text style={{ fontFamily: 'Inter_500Medium', fontSize: 12.5, color: '#0F172A' }}>
Child
</Text>
</View>
</View>
</View>
<View style={{ gap: 12 }}>
<View
style={{
justifyContent: 'space-between',
alignItems: 'baseline',
flexDirection: 'row',
}}>
<Text
style={{
margin: 0,
flexShrink: 1,
fontFamily: 'Inter_700Bold',
fontSize: 17,
color: '#0F172A',
}}>
Clinics near you
</Text>
<Pressable accessibilityRole="link" style={{ flexShrink: 1 }}>
<Text style={{ fontFamily: 'Inter_600SemiBold', fontSize: 13, color: '#2563EB' }}>
Map view
</Text>
</Pressable>
</View>
<View style={{ gap: 12, marginRight: -20, flexDirection: 'row' }}>
<View
style={{
width: 198,
flexShrink: 0,
backgroundColor: '#fff',
borderRadius: 18,
overflow: 'hidden',
borderWidth: 1,
borderColor: '#EEF2F7',
boxShadow: '0 4px 14px rgba(15,23,42,0.05)',
}}>
<View style={{ height: 90 }}>
<Image
source={{ uri: asset('3341cf7574.jpg') }}
contentFit="cover"
accessibilityLabel="KG Heart Clinic treatment room"
style={{ width: '100%', height: '100%' }}
/>
<View
style={{
position: 'absolute',
top: 8,
left: 8,
backgroundColor: '#fff',
paddingVertical: 4,
paddingHorizontal: 8,
borderRadius: 999,
alignItems: 'center',
gap: 4,
flexDirection: 'row',
}}>
<View
style={{ width: 6, height: 6, borderRadius: 3, backgroundColor: '#12A06B' }}
/>
<Text
style={{
fontFamily: 'Inter_600SemiBold',
fontSize: 10.5,
color: '#0E8A5B',
flexShrink: 1,
}}>
Open till 9 PM
</Text>
</View>
</View>
<View style={{ paddingTop: 10, paddingBottom: 12, paddingHorizontal: 12, gap: 6 }}>
<View
style={{
justifyContent: 'space-between',
alignItems: 'center',
flexDirection: 'row',
}}>
<Text
style={{
flexShrink: 1,
fontFamily: 'Inter_700Bold',
fontSize: 14,
color: '#0F172A',
}}>
KG Heart Clinic
</Text>
<View
style={{ alignItems: 'center', gap: 3, flexShrink: 1, flexDirection: 'row' }}>
<Svg width="12" height="12" viewBox="0 0 24 24" fill="#F5A524">
<Path d="M12 2l3.1 6.3 6.9 1-5 4.9 1.2 6.8L12 17.8 5.8 21l1.2-6.8-5-4.9 6.9-1z" />
</Svg>
<Text
style={{
fontFamily: 'Inter_600SemiBold',
fontSize: 12,
color: '#0F172A',
flexShrink: 1,
}}>
4.9
</Text>
</View>
</View>
<View style={{ alignItems: 'center', gap: 4, flexDirection: 'row' }}>
<Svg
width="12"
height="12"
viewBox="0 0 24 24"
fill="none"
stroke="#64748B"
strokeWidth="2">
<Path d="M21 10c0 7-9 13-9 13S3 17 3 10a9 9 0 0 1 18 0z" />
<Circle cx="12" cy="10" r="3" />
</Svg>
<Text
style={{
fontFamily: 'Inter_400Regular',
fontSize: 11.5,
color: '#64748B',
flexShrink: 1,
}}>
Race Course · 1.2 km
</Text>
</View>
<View style={{ gap: 5, flexDirection: 'row' }}>
<Text
style={{
backgroundColor: '#EEF4FF',
paddingVertical: 3,
paddingHorizontal: 7,
borderRadius: 6,
flexShrink: 1,
fontFamily: 'Inter_600SemiBold',
fontSize: 10.5,
color: '#1D4ED8',
}}>
Cardio
</Text>
<Text
style={{
backgroundColor: '#EEF4FF',
paddingVertical: 3,
paddingHorizontal: 7,
borderRadius: 6,
flexShrink: 1,
fontFamily: 'Inter_600SemiBold',
fontSize: 10.5,
color: '#1D4ED8',
}}>
ECG
</Text>
<Text
style={{
backgroundColor: '#EEF4FF',
paddingVertical: 3,
paddingHorizontal: 7,
borderRadius: 6,
flexShrink: 1,
fontFamily: 'Inter_600SemiBold',
fontSize: 10.5,
color: '#1D4ED8',
}}>
Lab
</Text>
</View>
</View>
</View>
<View
style={{
width: 198,
flexShrink: 0,
backgroundColor: '#fff',
borderRadius: 18,
overflow: 'hidden',
borderWidth: 1,
borderColor: '#EEF2F7',
boxShadow: '0 4px 14px rgba(15,23,42,0.05)',
}}>
<View style={{ height: 90 }}>
<Image
source={{ uri: asset('4737dbf3f8.jpg') }}
contentFit="cover"
accessibilityLabel="Vision Eye Care exam room"
style={{ width: '100%', height: '100%' }}
/>
<View
style={{
position: 'absolute',
top: 8,
left: 8,
backgroundColor: '#fff',
paddingVertical: 4,
paddingHorizontal: 8,
borderRadius: 999,
alignItems: 'center',
gap: 4,
flexDirection: 'row',
}}>
<View
style={{ width: 6, height: 6, borderRadius: 3, backgroundColor: '#12A06B' }}
/>
<Text
style={{
fontFamily: 'Inter_600SemiBold',
fontSize: 10.5,
color: '#0E8A5B',
flexShrink: 1,
}}>
Open till 9 PM
</Text>
</View>
</View>
<View style={{ paddingTop: 10, paddingBottom: 12, paddingHorizontal: 12, gap: 6 }}>
<View
style={{
justifyContent: 'space-between',
alignItems: 'center',
flexDirection: 'row',
}}>
<Text
style={{
flexShrink: 1,
fontFamily: 'Inter_700Bold',
fontSize: 14,
color: '#0F172A',
}}>
Vision Eye Care
</Text>
<View
style={{ alignItems: 'center', gap: 3, flexShrink: 1, flexDirection: 'row' }}>
<Svg width="12" height="12" viewBox="0 0 24 24" fill="#F5A524">
<Path d="M12 2l3.1 6.3 6.9 1-5 4.9 1.2 6.8L12 17.8 5.8 21l1.2-6.8-5-4.9 6.9-1z" />
</Svg>
<Text
style={{
fontFamily: 'Inter_600SemiBold',
fontSize: 12,
color: '#0F172A',
flexShrink: 1,
}}>
4.8
</Text>
</View>
</View>
<View style={{ alignItems: 'center', gap: 4, flexDirection: 'row' }}>
<Svg
width="12"
height="12"
viewBox="0 0 24 24"
fill="none"
stroke="#64748B"
strokeWidth="2">
<Path d="M21 10c0 7-9 13-9 13S3 17 3 10a9 9 0 0 1 18 0z" />
<Circle cx="12" cy="10" r="3" />
</Svg>
<Text
style={{
fontFamily: 'Inter_400Regular',
fontSize: 11.5,
color: '#64748B',
flexShrink: 1,
}}>
Gandhipuram · 2.4 km
</Text>
</View>
<View style={{ gap: 5, flexDirection: 'row' }}>
<Text
style={{
backgroundColor: '#EEF4FF',
paddingVertical: 3,
paddingHorizontal: 7,
borderRadius: 6,
flexShrink: 1,
fontFamily: 'Inter_600SemiBold',
fontSize: 10.5,
color: '#1D4ED8',
}}>
Cardio
</Text>
<Text
style={{
backgroundColor: '#EEF4FF',
paddingVertical: 3,
paddingHorizontal: 7,
borderRadius: 6,
flexShrink: 1,
fontFamily: 'Inter_600SemiBold',
fontSize: 10.5,
color: '#1D4ED8',
}}>
ECG
</Text>
<Text
style={{
backgroundColor: '#EEF4FF',
paddingVertical: 3,
paddingHorizontal: 7,
borderRadius: 6,
flexShrink: 1,
fontFamily: 'Inter_600SemiBold',
fontSize: 10.5,
color: '#1D4ED8',
}}>
Lab
</Text>
</View>
</View>
</View>
</View>
</View>
<View style={{ gap: 12 }}>
<View
style={{
justifyContent: 'space-between',
alignItems: 'baseline',
flexDirection: 'row',
}}>
<Text
style={{
margin: 0,
flexShrink: 1,
fontFamily: 'Inter_700Bold',
fontSize: 17,
color: '#0F172A',
}}>
Top doctors
</Text>
<Pressable accessibilityRole="link" style={{ flexShrink: 1 }}>
<Text style={{ fontFamily: 'Inter_600SemiBold', fontSize: 13, color: '#2563EB' }}>
View all
</Text>
</Pressable>
</View>
<View
style={{
alignItems: 'center',
gap: 12,
backgroundColor: '#fff',
borderWidth: 1,
borderColor: '#EEF2F7',
borderRadius: 18,
padding: 12,
flexDirection: 'row',
}}>
<View
style={{
width: 46,
height: 46,
borderRadius: 23,
alignItems: 'center',
justifyContent: 'center',
overflow: 'hidden',
flexDirection: 'row',
}}>
<LinearGradient
colors={['#FDBA74', '#F97316']}
locations={[0, 1]}
start={{ x: 0.146, y: 0.146 }}
end={{ x: 0.854, y: 0.854 }}
style={StyleSheet.absoluteFill}
/>
<Text
style={{
fontFamily: 'Inter_700Bold',
fontSize: 15,
color: '#fff',
flexShrink: 1,
}}>
KI
</Text>
</View>
<View style={{ flexGrow: 1, gap: 3, flexShrink: 1 }}>
<Text style={{ fontFamily: 'Inter_700Bold', fontSize: 15, color: '#0F172A' }}>
Dr. Kavya Iyer
</Text>
<Text style={{ fontFamily: 'Inter_400Regular', fontSize: 12, color: '#64748B' }}>
Dermatologist · 9 yrs
</Text>
</View>
<Text
style={{
flexShrink: 1,
fontFamily: 'Inter_700Bold',
fontSize: 16,
color: '#0F172A',
}}>
₹500
</Text>
</View>
</View>
</View>
<View
style={{
position: 'absolute',
left: 16,
right: 16,
bottom: 26,
height: 68,
backgroundColor: '#fff',
borderRadius: 26,
boxShadow: '0 10px 30px rgba(15,23,42,0.14)',
alignItems: 'center',
justifyContent: 'space-between',
paddingVertical: 0,
paddingLeft: 10,
paddingRight: 22,
flexDirection: 'row',
}}>
<Pressable
accessibilityRole="link"
style={{
alignItems: 'center',
gap: 8,
backgroundColor: '#2563EB',
paddingVertical: 0,
paddingHorizontal: 20,
height: 48,
borderRadius: 18,
flexShrink: 1,
flexDirection: 'row',
}}>
<Svg
width="18"
height="18"
viewBox="0 0 24 24"
fill="none"
stroke="#fff"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round">
<Path d="M3 10l9-7 9 7v10a2 2 0 0 1-2 2h-4v-7H9v7H5a2 2 0 0 1-2-2z" />
</Svg>
<Text
style={{
fontFamily: 'Inter_600SemiBold',
fontSize: 14,
color: '#fff',
flexShrink: 1,
}}>
Home
</Text>
</Pressable>
<Pressable
accessibilityRole="link"
accessibilityLabel="Appointments"
style={{
width: 44,
height: 44,
alignItems: 'center',
justifyContent: 'center',
flexDirection: 'row',
}}>
<Svg
width="22"
height="22"
viewBox="0 0 24 24"
fill="none"
stroke="#64748B"
strokeWidth="2"
strokeLinecap="round">
<Rect x="3" y="4" width="18" height="18" rx="2" />
<Path d="M16 2v4M8 2v4M3 10h18" />
</Svg>
</Pressable>
<Pressable
accessibilityRole="link"
accessibilityLabel="Messages"
style={{
width: 44,
height: 44,
alignItems: 'center',
justifyContent: 'center',
flexDirection: 'row',
}}>
<Svg
width="22"
height="22"
viewBox="0 0 24 24"
fill="none"
stroke="#64748B"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round">
<Path d="M21 11.5a8.4 8.4 0 0 1-12.4 7.4L3 21l2.1-5.6A8.5 8.5 0 1 1 21 11.5z" />
</Svg>
</Pressable>
<Pressable
accessibilityRole="link"
accessibilityLabel="Profile"
style={{
width: 44,
height: 44,
alignItems: 'center',
justifyContent: 'center',
flexDirection: 'row',
}}>
<Svg
width="22"
height="22"
viewBox="0 0 24 24"
fill="none"
stroke="#64748B"
strokeWidth="2"
strokeLinecap="round">
<Circle cx="12" cy="8" r="4" />
<Path d="M4 21a8 8 0 0 1 16 0" />
</Svg>
</Pressable>
</View>
</View>
</Screen>
);
}src/app/detail.tsx
import { Image } from 'expo-image';
import { LinearGradient } from 'expo-linear-gradient';
import { router } from 'expo-router';
import { useState } from 'react';
import { Pressable, StyleSheet, Text, View } from 'react-native';
import Svg, { Circle, G, Path, Rect } from 'react-native-svg';
import { Screen } from '@/components/screen';
import { asset } from '@/lib/assets';
import { css, cssText } from '@/lib/css';
// Screen state, and the values the layout below reads from it.
function useScreen() {
const [state, setScreenState] = useState<Record<string, any>>({ day: 3, slot: '10:30 AM' });
const setState = (patch: Record<string, any>) =>
setScreenState((current) => ({ ...current, ...patch }));
const days = [
['Wed', 1],
['Thu', 2],
['Fri', 3],
['Sat', 4],
['Sun', 5],
].map(([dow, num]) => {
const on = state.day === num;
return {
dow,
num,
pick: () => setState({ day: num }),
style:
'display: flex; flex-direction: column; align-items: center; gap: 4px; padding: 10px 0; border-radius: 16px; font-family: inherit; ' +
(on
? 'background: #2563EB; color: #fff; border: 1px solid #2563EB; box-shadow: 0 8px 18px rgba(37,99,235,0.3)'
: 'background: #fff; color: #0F172A; border: 1px solid #E2E8F0'),
};
});
const slots = ['09:30 AM', '10:00 AM', '10:30 AM', '11:30 AM', '04:30 PM', '05:00 PM'].map(
(label) => {
const disabled = label === '10:00 AM';
const on = state.slot === label;
let look = 'background: #fff; color: #0F172A; border: 1px solid #E2E8F0';
if (disabled)
look =
'background: #F8FAFC; color: #94A3B8; border: 1px solid #EEF2F7; text-decoration: line-through; cursor: default';
else if (on) look = 'background: #EEF4FF; color: #1D4ED8; border: 1.5px solid #2563EB';
return {
label,
disabled,
pick: () => {
if (!disabled) setState({ slot: label });
},
style:
'height: 44px; border-radius: 12px; font-family: inherit; font-size: 13px; font-weight: 600; ' +
look,
};
},
);
return { days, slots };
}
export default function DetailScreen() {
const { days, slots } = useScreen();
return (
<Screen background="#F7F9FC" statusBar="dark">
<View style={{ flex: 1 }}>
<View style={{ position: 'absolute', top: 0, left: 0, right: 0, height: 250 }}>
<Image
source={{ uri: asset('d8eca69c65.jpg') }}
contentFit="cover"
accessibilityLabel="Clinic consultation room"
style={{ width: '100%', height: '100%' }}
/>
<View style={{ position: 'absolute', top: 0, right: 0, bottom: 0, left: 0 }}>
<LinearGradient
colors={['rgba(255,255,255,0.35)', 'rgba(255,255,255,0)']}
locations={[0, 0.3]}
start={{ x: 0.5, y: 0 }}
end={{ x: 0.5, y: 1 }}
style={StyleSheet.absoluteFill}
/>
</View>
</View>
<View
style={{
justifyContent: 'space-between',
alignItems: 'center',
paddingTop: 16,
paddingBottom: 0,
paddingLeft: 32,
paddingRight: 28,
height: 38,
flexDirection: 'row',
}}
/>
<View
style={{
justifyContent: 'space-between',
paddingTop: 14,
paddingBottom: 0,
paddingHorizontal: 20,
flexDirection: 'row',
}}>
<Pressable
accessibilityRole="link"
accessibilityLabel="Back"
onPress={() => router.navigate('/')}
style={{
width: 44,
height: 44,
borderRadius: 14,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
boxShadow: '0 4px 12px rgba(15,23,42,0.12)',
flexDirection: 'row',
}}>
<Svg
width="20"
height="20"
viewBox="0 0 24 24"
fill="none"
stroke="#0F172A"
strokeWidth="2.2"
strokeLinecap="round"
strokeLinejoin="round">
<Path d="M15 18l-6-6 6-6" />
</Svg>
</Pressable>
<Pressable
accessibilityRole="button"
accessibilityLabel="Save doctor"
style={{
width: 44,
height: 44,
borderRadius: 14,
backgroundColor: '#fff',
borderWidth: 0,
alignItems: 'center',
justifyContent: 'center',
boxShadow: '0 4px 12px rgba(15,23,42,0.12)',
padding: 0,
flexDirection: 'row',
}}>
<Svg width="20" height="20" viewBox="0 0 24 24" fill="#E5484D">
<Path d="M20.8 4.6a5.5 5.5 0 0 0-7.8 0L12 5.7l-1-1.1a5.5 5.5 0 0 0-7.8 7.8L12 21.2l8.8-8.8a5.5 5.5 0 0 0 0-7.8z" />
</Svg>
</Pressable>
</View>
<View
style={{
position: 'absolute',
top: 196,
left: 16,
right: 16,
backgroundColor: '#fff',
borderRadius: 22,
padding: 16,
boxShadow: '0 12px 30px rgba(15,23,42,0.10)',
gap: 14,
}}>
<View style={{ alignItems: 'center', gap: 12, flexDirection: 'row' }}>
<View
style={{
width: 52,
height: 52,
borderRadius: 26,
alignItems: 'center',
justifyContent: 'center',
overflow: 'hidden',
flexDirection: 'row',
}}>
<LinearGradient
colors={['#60A5FA', '#2563EB']}
locations={[0, 1]}
start={{ x: 0.146, y: 0.146 }}
end={{ x: 0.854, y: 0.854 }}
style={StyleSheet.absoluteFill}
/>
<Text
style={{ fontFamily: 'Inter_700Bold', fontSize: 16, color: '#fff', flexShrink: 1 }}>
AR
</Text>
</View>
<View style={{ flexGrow: 1, gap: 3, flexShrink: 1 }}>
<View style={{ alignItems: 'center', gap: 6, flexDirection: 'row' }}>
<Text
style={{
fontFamily: 'Inter_700Bold',
fontSize: 17,
color: '#0F172A',
flexShrink: 1,
}}>
Dr. Arvind Raman
</Text>
<Svg
width="16"
height="16"
viewBox="0 0 24 24"
fill="none"
stroke="#12A06B"
strokeWidth="2.2"
strokeLinecap="round"
strokeLinejoin="round"
accessibilityRole="image"
accessibilityLabel="Verified">
<Path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z" />
<Path d="M9 12l2 2 4-4" />
</Svg>
</View>
<Text style={{ fontFamily: 'Inter_400Regular', fontSize: 12, color: '#64748B' }}>
Cardiologist · MBBS, MD, DM
</Text>
</View>
<Pressable
accessibilityRole="button"
accessibilityLabel="Message doctor"
style={{
width: 44,
height: 44,
borderRadius: 14,
borderWidth: 0,
backgroundColor: '#EEF4FF',
alignItems: 'center',
justifyContent: 'center',
padding: 0,
flexDirection: 'row',
}}>
<Svg
width="19"
height="19"
viewBox="0 0 24 24"
fill="none"
stroke="#2563EB"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round">
<Path d="M21 11.5a8.4 8.4 0 0 1-12.4 7.4L3 21l2.1-5.6A8.5 8.5 0 1 1 21 11.5z" />
</Svg>
</Pressable>
</View>
<View
style={{
borderTopWidth: 1,
borderTopColor: '#EEF2F7',
paddingTop: 12,
flexDirection: 'row',
gap: 4,
}}>
<View style={{ gap: 2, flex: 1 }}>
<Text
style={{
fontFamily: 'Inter_700Bold',
fontSize: 16,
color: '#0F172A',
textAlign: 'center',
}}>
2.4k+
</Text>
<Text
style={{
fontFamily: 'Inter_400Regular',
fontSize: 11,
color: '#64748B',
textAlign: 'center',
}}>
Patients
</Text>
</View>
<View style={{ gap: 2, flex: 1 }}>
<Text
style={{
fontFamily: 'Inter_700Bold',
fontSize: 16,
color: '#0F172A',
textAlign: 'center',
}}>
14 yrs
</Text>
<Text
style={{
fontFamily: 'Inter_400Regular',
fontSize: 11,
color: '#64748B',
textAlign: 'center',
}}>
Experience
</Text>
</View>
<View style={{ gap: 2, flex: 1 }}>
<Text
style={{
fontFamily: 'Inter_700Bold',
fontSize: 16,
color: '#0F172A',
textAlign: 'center',
}}>
4.9
</Text>
<Text
style={{
fontFamily: 'Inter_400Regular',
fontSize: 11,
color: '#64748B',
textAlign: 'center',
}}>
Rating
</Text>
</View>
<View style={{ gap: 2, flex: 1 }}>
<Text
style={{
fontFamily: 'Inter_700Bold',
fontSize: 16,
color: '#0F172A',
textAlign: 'center',
}}>
860
</Text>
<Text
style={{
fontFamily: 'Inter_400Regular',
fontSize: 11,
color: '#64748B',
textAlign: 'center',
}}>
Reviews
</Text>
</View>
</View>
</View>
<View style={{ position: 'absolute', top: 368, left: 20, right: 20, gap: 14 }}>
<View
style={{ justifyContent: 'space-between', alignItems: 'center', flexDirection: 'row' }}>
<Text
style={{
margin: 0,
flexShrink: 1,
fontFamily: 'Inter_700Bold',
fontSize: 17,
color: '#0F172A',
}}>
October 2026
</Text>
<View style={{ gap: 4, flexShrink: 1, flexDirection: 'row' }}>
<Pressable
accessibilityRole="button"
accessibilityLabel="Previous week"
style={{
width: 28,
height: 28,
borderWidth: 0,
padding: 0,
alignItems: 'center',
justifyContent: 'center',
flexDirection: 'row',
}}>
<Svg
width="16"
height="16"
viewBox="0 0 24 24"
fill="none"
stroke="#64748B"
strokeWidth="2.4"
strokeLinecap="round">
<Path d="M15 18l-6-6 6-6" />
</Svg>
</Pressable>
<Pressable
accessibilityRole="button"
accessibilityLabel="Next week"
style={{
width: 28,
height: 28,
borderWidth: 0,
padding: 0,
alignItems: 'center',
justifyContent: 'center',
flexDirection: 'row',
}}>
<Svg
width="16"
height="16"
viewBox="0 0 24 24"
fill="none"
stroke="#0F172A"
strokeWidth="2.4"
strokeLinecap="round">
<Path d="M9 18l6-6-6-6" />
</Svg>
</Pressable>
</View>
</View>
<View style={{ flexDirection: 'row', gap: 8, flexWrap: 'wrap' }}>
{days.map((d, index) => (
<Pressable
key={index}
accessibilityRole="button"
onPress={d.pick}
style={{
alignItems: 'center',
gap: 4,
flexGrow: 1,
flexBasis: '17%',
...css(d.style),
}}>
<Text
style={{
opacity: 0.8,
fontFamily: 'Inter_500Medium',
fontSize: 11,
color: '#000000',
}}>
{d.dow}
</Text>
<Text style={{ fontFamily: 'Inter_700Bold', fontSize: 18, color: '#000000' }}>
{d.num}
</Text>
</Pressable>
))}
</View>
<View
style={{
justifyContent: 'space-between',
alignItems: 'baseline',
flexDirection: 'row',
}}>
<Text
style={{
margin: 0,
flexShrink: 1,
fontFamily: 'Inter_700Bold',
fontSize: 17,
color: '#0F172A',
}}>
Available slots
</Text>
<Text
style={{
flexShrink: 1,
fontFamily: 'Inter_400Regular',
fontSize: 12,
color: '#64748B',
}}>
6 open
</Text>
</View>
<View style={{ flexDirection: 'row', gap: 8, flexWrap: 'wrap' }}>
{slots.map((s, index) => (
<Pressable
key={index}
accessibilityRole="button"
onPress={s.pick}
disabled={s.disabled}
style={{
flexGrow: 1,
flexBasis: '26%',
alignItems: 'center',
justifyContent: 'center',
...css(s.style),
}}>
<Text
style={[
{
fontFamily: 'Inter_400Regular',
fontSize: 13.33,
color: '#000000',
textAlign: 'center',
},
cssText(s.style),
]}>
{s.label}
</Text>
</Pressable>
))}
</View>
<View
style={{
backgroundColor: '#fff',
borderWidth: 1,
borderColor: '#EEF2F7',
borderRadius: 18,
overflow: 'hidden',
}}>
<View style={{ height: 92, backgroundColor: '#EEF1F5' }}>
<Svg width="350" height="92" viewBox="0 0 350 92">
<Rect width="350" height="92" fill="#EEF1F5" />
<G fill="#FFFFFF">
<Rect x="0" y="34" width="350" height="9" />
<Rect x="0" y="70" width="350" height="7" />
<Rect x="58" y="0" width="8" height="92" />
<Rect x="138" y="0" width="9" height="92" />
<Rect x="226" y="0" width="8" height="92" />
<Rect x="300" y="0" width="7" height="92" />
</G>
<Rect x="246" y="48" width="46" height="18" rx="3" fill="#D7EFD9" />
<Rect x="310" y="6" width="34" height="24" rx="3" fill="#D7EFD9" />
<Rect x="76" y="48" width="52" height="18" rx="3" fill="#E2E6EC" />
<Path
d="M28 52 H100 V38 H180 V18 H218"
fill="none"
stroke="#2563EB"
strokeWidth="5"
strokeLinecap="round"
strokeLinejoin="round"
/>
<Circle cx="28" cy="52" r="5" fill="#fff" stroke="#2563EB" strokeWidth="3" />
<Circle cx="218" cy="18" r="11" fill="#2563EB" />
<Path
d="M218 13v10M213 18h10"
stroke="#fff"
strokeWidth="2.4"
strokeLinecap="round"
/>
</Svg>
</View>
<View
style={{
alignItems: 'center',
justifyContent: 'space-between',
paddingVertical: 10,
paddingHorizontal: 12,
flexDirection: 'row',
}}>
<View style={{ gap: 2, flexShrink: 1 }}>
<Text style={{ fontFamily: 'Inter_700Bold', fontSize: 13, color: '#0F172A' }}>
KG Heart Clinic, Race Course
</Text>
<Text style={{ fontFamily: 'Inter_400Regular', fontSize: 11, color: '#64748B' }}>
1.2 km · 6 min drive
</Text>
</View>
<Pressable
accessibilityRole="link"
style={{
alignItems: 'center',
gap: 5,
backgroundColor: '#EEF4FF',
paddingVertical: 0,
paddingHorizontal: 12,
height: 34,
borderRadius: 10,
flexShrink: 1,
flexDirection: 'row',
}}>
<Svg
width="13"
height="13"
viewBox="0 0 24 24"
fill="none"
stroke="#2563EB"
strokeWidth="2.2"
strokeLinejoin="round">
<Path d="M3 11l19-9-9 19-2-8z" />
</Svg>
<Text
style={{
fontFamily: 'Inter_600SemiBold',
fontSize: 12,
color: '#2563EB',
flexShrink: 1,
}}>
Directions
</Text>
</Pressable>
</View>
</View>
</View>
<View
style={{
position: 'absolute',
left: 0,
right: 0,
bottom: 0,
height: 104,
backgroundColor: '#fff',
borderTopWidth: 1,
borderTopColor: '#EEF2F7',
alignItems: 'flex-start',
justifyContent: 'space-between',
paddingTop: 16,
paddingBottom: 0,
paddingHorizontal: 20,
flexDirection: 'row',
}}>
<View style={{ gap: 2, flexShrink: 1 }}>
<Text style={{ fontFamily: 'Inter_400Regular', fontSize: 12, color: '#64748B' }}>
Consultation
</Text>
<Text
style={{
fontFamily: 'Inter_800ExtraBold',
fontSize: 22,
color: '#0F172A',
letterSpacing: -0.3,
}}>
₹800
</Text>
</View>
<Pressable
accessibilityRole="link"
style={{
height: 54,
paddingVertical: 0,
paddingHorizontal: 30,
borderRadius: 18,
backgroundColor: '#2563EB',
alignItems: 'center',
boxShadow: '0 10px 22px rgba(37,99,235,0.35)',
flexShrink: 1,
flexDirection: 'row',
}}>
<Text
style={{
fontFamily: 'Inter_600SemiBold',
fontSize: 15,
color: '#fff',
flexShrink: 1,
}}>
Book appointment
</Text>
</Pressable>
</View>
</View>
</Screen>
);
}src/components/screen.tsx
import { StatusBar } from 'expo-status-bar';
import type { ReactNode } from 'react';
import { ScrollView, useWindowDimensions, View } from 'react-native';
// Every screen is drawn on a 390 wide artboard, then scaled to the device width.
const ARTBOARD_WIDTH = 390;
type Props = {
background: string;
statusBar: 'light' | 'dark';
height?: number;
children: ReactNode;
};
export function Screen({ background, statusBar, height: artboardHeight = 844, children }: Props) {
const { width, height } = useWindowDimensions();
const scale = width / ARTBOARD_WIDTH;
return (
<ScrollView
bounces={false}
showsVerticalScrollIndicator={false}
style={{ flex: 1, backgroundColor: background }}
contentContainerStyle={{ height: Math.max(artboardHeight * scale, height) }}>
<StatusBar style={statusBar} />
<View
style={{
width: ARTBOARD_WIDTH,
height: artboardHeight,
overflow: 'hidden',
transformOrigin: 'left top',
transform: [{ scale }],
}}>
{children}
</View>
</ScrollView>
);
}src/lib/assets.ts
// Demo images are hosted by DeveloperMill. Point this at your own CDN, or swap to require().
const ASSET_BASE = 'https://developermill.com/app-designs/doctor-appointment-app-ui-design/assets';
export const asset = (name: string) => `${ASSET_BASE}/${name}`;src/lib/css.ts
import type { TextStyle, ViewStyle } from 'react-native';
// A few styles change with state, so they are kept as short CSS strings from the design.
const FACES: Record<number, string> = {400: 'Inter_400Regular', 500: 'Inter_500Medium', 600: 'Inter_600SemiBold', 700: 'Inter_700Bold', 800: 'Inter_800ExtraBold'};
const px = (value: string) => (value.endsWith('%') ? (value as `${number}%`) : parseFloat(value));
function declarations(source: string) {
return source
.split(';')
.map((rule) => rule.split(':').map((part) => part.trim()))
.filter(([name, value]) => name && value);
}
export function css(source: string): ViewStyle {
const style: Record<string, unknown> = {};
for (const [name, ...rest] of declarations(source)) {
const value = rest.join(':');
if (name === 'background' || name === 'background-color') {
style.backgroundColor = value;
} else if (name === 'border') {
const [width, , ...color] = value.split(' ');
if (value === 'none' || value === '0') style.borderWidth = 0;
else {
style.borderWidth = parseFloat(width);
style.borderColor = color.join(' ');
}
} else if (name === 'display' && value === 'flex') {
style.flexDirection = style.flexDirection ?? 'row';
} else if (name === 'flex-direction') {
style.flexDirection = value;
} else if (name === 'align-items') {
style.alignItems = value;
} else if (name === 'justify-content') {
style.justifyContent = value;
} else if (name === 'box-shadow') {
style.boxShadow = value;
} else if (name === 'position') {
style.position = value === 'absolute' ? 'absolute' : 'relative';
} else if (name === 'opacity') {
style.opacity = parseFloat(value);
} else if (name === 'gap' || name === 'width' || name === 'height' || name === 'left' || name === 'top') {
style[name] = px(value);
} else if (name === 'border-radius') {
style.borderRadius = px(value);
} else if (name === 'padding') {
const [vertical, horizontal = vertical] = value.split(' ');
style.paddingVertical = px(vertical);
style.paddingHorizontal = px(horizontal);
} else if (name === 'white-space' && value === 'nowrap') {
style.flexShrink = 0;
} else if (name === 'flex-shrink') {
style.flexShrink = parseFloat(value);
} else if (name === 'flex') {
if (value === 'none') style.flexGrow = 0;
else style.flex = parseFloat(value);
}
}
return style as ViewStyle;
}
export function cssText(source: string): TextStyle {
const style: Record<string, unknown> = {};
for (const [name, value] of declarations(source)) {
if (name === 'color') style.color = value;
if (name === 'font-size') style.fontSize = parseFloat(value);
if (name === 'font-weight' && FACES[Number(value)]) style.fontFamily = FACES[Number(value)];
if (name === 'text-decoration' && value.startsWith('line-through')) {
style.textDecorationLine = 'line-through';
}
}
return style as TextStyle;
}About this design
The booking screen has real state: pick a day and a time slot, and unavailable times are struck through and cannot be chosen. The clear blue system also suits clinics, labs and telemedicine products.
Clinical white and soft blue-grey with a confident blue gradient, rounded cards and calm pastel speciality icons.
- Upcoming appointment card with video action
- Speciality shortcuts and nearby clinics
- Day and time slot pickers with disabled slots
- Small route preview with directions
Colour palette
Clinic blue
#2563EB
Deep blue
#1D4ED8
Ice
#EEF4FF
Care green
#12A06B
Navy ink
#0F172A
Slate
#64748B
How to use this design
- 01
Pick what to copy
Copy the Doctor Appointment artifact, skill or code from this page.
- 02
Run it or generate it
Paste the code into a new Expo app and start it, or give the skill to your AI coding agent.
- 03
Make it yours
Replace the demo images and sample content, then connect your own data.
Frequently asked questions
Does the slot picker work?
Yes. Tapping a day or time selects it, and the unavailable 10:00 AM slot is disabled and struck through.
Can it support video visits?
The appointment card already has a video action. Connect it to a calling SDK such as Agora or Daily.
Is the code ready for patient data?
It is a front-end UI. Before storing medical data, add secure authentication and follow the rules that apply in your country, such as HIPAA.
Is the Doctor Appointment design free to use?
Yes. The design, skill and Expo code are free and MIT licensed, including for commercial apps. Replace the demo images and sample content with your own before you ship.
What is the difference between the artifact, skill and code?
The artifact is the full design: tokens, screens and source. The skill is a SKILL.md that teaches an AI coding agent to build the UI. The code is the finished Expo app you can paste in and run.
Which Expo version does the code use?
It targets Expo SDK 58 with Expo Router and TypeScript. It uses packages installed through npx expo install, with no custom native code.



