// Health app UI
AI Health Coach App UI Design in Expo
This AI health coach app UI puts a person at the centre of the screen and surrounds them with the numbers that matter: an overall wellbeing score, four health indexes and practical recommendations.
- Screens
- 2
- Built with
- Expo SDK 58
- Fonts
- Urbanist
- License
- Free (MIT)

AI Health Coach app screens

Coach home
Greeting, global index card, wellbeing score over the coach portrait, a glass panel of AI recommendations and a pill tab bar.

Global index
A four-part ring chart with labelled scores above a dark sheet of steps, calories, active minutes and a histogram.
Copy the AI Health Coach 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
# AI Health Coach: design artifact
This AI health coach app UI puts a person at the centre of the screen and surrounds them with the numbers that matter: an overall wellbeing score, four health indexes and practical recommendations.
- Type: Health mobile app UI
- Screens: 2
- Artboard: 390 × 844
- Target: Expo SDK 58, Expo Router, TypeScript, expo-blur, expo-image, react-native-svg
- Live page: https://developermill.com/app-designs/ai-health-coach-app-ui-design
## Design direction
Cool sage greys with a lime accent, a full-height coach portrait, frosted glass panels and bright category cards.
- Frosted glass recommendations panel
- Segmented ring chart in SVG
- Horizontally scrolling recommendation cards
- Sculpted bottom sheet with an activity histogram
## Foundations
### Colour
| Token | Hex |
| --- | --- |
| Lime | `#D7F26B` |
| Carbon | `#0E0F0F` |
| Sage | `#D5DAD8` |
| Coral | `#F2845C` |
| Periwinkle | `#8FA9F5` |
| Seafoam | `#9ED9C9` |
### Typography
- Typefaces: Urbanist
- Common sizes (px): 10, 11, 12, 13, 14, 15, 19, 22
### Shape and depth
- Common corner radii (px): 3, 10, 14, 22, 28, 40
- Shadows: `0 10px 30px rgba(14, 15, 15, 0.08), inset 0 1px 0 rgba(255, 255, 255, 0.7)`
## Screens
### 1. Coach home
Greeting, global index card, wellbeing score over the coach portrait, a glass panel of AI recommendations and a pill tab bar.
Preview: https://developermill.com/app-designs/ai-health-coach-app-ui-design/home.webp
Copy on this screen:
- Welcome
- Freddy
- 2
- Global index
- PA
- N
- D
- MH
- General well-being
- 82
- %
- AI Recommendations
- See all
- Running
- Do 15 minutes of light jogging
- Nutrition
- Try 25g of protein in your breakfast
- Mental
- Do 2 minutes of deep breathing.
### 2. Global index
A four-part ring chart with labelled scores above a dark sheet of steps, calories, active minutes and a histogram.
Preview: https://developermill.com/app-designs/ai-health-coach-app-ui-design/globalindex.webp
Copy on this screen:
- Global index
- 82
- %
- General well-being
- Physical Activity
- 80
- Nutrition
- 75
- Dream
- 85
- Health mental
- 90
- Steps
- 6,742
- /8,000
- Calories
- 420 kcal
- Active minutes
- 32 min
- See coach recommendations
## Full source
The complete design source for each screen. Images point at hosted demo files.
### Main
```html
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>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=Urbanist:wght@400;500;600&display=swap" rel="stylesheet">
<style>
body{margin:0}
a{color:#2B302E;text-decoration:none}a:hover{color:#0E0F0F}
</style>
</helmet>
<div style="position: relative; width: 390px; height: 844px; overflow: hidden; background: #D5DAD8; font-family: 'Urbanist', system-ui, sans-serif; color: #0E0F0F">
<img src="https://developermill.com/app-designs/ai-health-coach-app-ui-design/assets/336ee9f7b2.jpg" alt="Your AI coach" style="position: absolute; top: 118px; left: 120px; width: 270px; height: 470px; object-fit: cover; object-position: top center">
<div style="position: absolute; top: 56px; left: 20px; right: 20px; display: flex; align-items: center; justify-content: space-between">
<div style="display: flex; align-items: center; gap: 12px">
<img src="https://developermill.com/app-designs/ai-health-coach-app-ui-design/assets/f0f3823a08.jpg" alt="Freddy" style="width: 48px; height: 48px; border-radius: 50%; object-fit: cover; border: 2px solid #FFFFFF; box-sizing: border-box">
<div style="display: flex; flex-direction: column; gap: 2px">
<span style="font-size: 13px; color: #4E5552">Welcome</span>
<span style="font-size: 20px; font-weight: 600">Freddy</span>
</div>
</div>
<div style="display: flex; gap: 10px">
<button aria-label="Notifications, 2 new" style="position: relative; width: 52px; height: 52px; border-radius: 50%; border: 0; background: #FFFFFF; display: flex; align-items: center; justify-content: center; cursor: pointer; color: #0E0F0F">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 8a6 6 0 0 1 12 0c0 7 3 9 3 9H3s3-2 3-9"></path><path d="M10.3 21a1.94 1.94 0 0 0 3.4 0"></path></svg>
<span style="position: absolute; top: -2px; right: 2px; min-width: 20px; height: 20px; border-radius: 10px; background: {{accent}}; font-family: 'Urbanist', sans-serif; font-size: 11px; font-weight: 600; display: flex; align-items: center; justify-content: center">2</span>
</button>
<button aria-label="Menu" style="width: 52px; height: 52px; border-radius: 50%; border: 0; background: #FFFFFF; display: flex; align-items: center; justify-content: center; cursor: pointer; color: #0E0F0F">
<svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><circle cx="8" cy="8" r="2"></circle><circle cx="16" cy="8" r="2"></circle><circle cx="8" cy="16" r="2"></circle><circle cx="16" cy="16" r="2"></circle></svg>
</button>
</div>
</div>
<div style="position: absolute; top: 150px; left: 20px; width: 168px; box-sizing: border-box; padding: 14px; background: #FFFFFF; border-radius: 22px; display: flex; flex-direction: column; gap: 12px">
<div style="display: flex; align-items: center; justify-content: space-between">
<span style="font-size: 14px; font-weight: 500">Global index</span>
<a href="GlobalIndex.dc.html" aria-label="Open global index" style="width: 36px; height: 36px; border-radius: 50%; background: #0E0F0F; color: #FFFFFF; display: flex; align-items: center; justify-content: center">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M7 17 17 7"></path><path d="M8 7h9v9"></path></svg>
</a>
</div>
<div style="display: flex; flex-direction: column; gap: 8px">
<div style="display: flex; align-items: center; gap: 8px"><span style="width: 22px; font-size: 11px; font-weight: 500">PA</span><div style="flex: 1; height: 6px; border-radius: 3px; background: #F0F2F1"><div style="width: 86%; height: 6px; border-radius: 3px; background: {{accent}}"></div></div></div>
<div style="display: flex; align-items: center; gap: 8px"><span style="width: 22px; font-size: 11px; font-weight: 500">N</span><div style="flex: 1; height: 6px; border-radius: 3px; background: #F0F2F1"><div style="width: 72%; height: 6px; border-radius: 3px; background: #7E9BF0"></div></div></div>
<div style="display: flex; align-items: center; gap: 8px"><span style="width: 22px; font-size: 11px; font-weight: 500">D</span><div style="flex: 1; height: 6px; border-radius: 3px; background: #F0F2F1"><div style="width: 80%; height: 6px; border-radius: 3px; background: #F2845C"></div></div></div>
<div style="display: flex; align-items: center; gap: 8px"><span style="width: 22px; font-size: 11px; font-weight: 500">MH</span><div style="flex: 1; height: 6px; border-radius: 3px; background: #F0F2F1"><div style="width: 90%; height: 6px; border-radius: 3px; background: #9ED9C9"></div></div></div>
</div>
</div>
<div style="position: absolute; top: 308px; left: 20px; display: flex; flex-direction: column; gap: 4px">
<span style="font-size: 13px; line-height: 16px; color: #3B403E; white-space: pre-line">General
well-being</span>
<div style="display: flex; align-items: baseline"><span style="font-size: 64px; font-weight: 500; letter-spacing: -2px; line-height: 1">82</span><span style="font-size: 26px; font-weight: 500">%</span></div>
</div>
<div style="position: absolute; top: 492px; left: 12px; right: 0; box-sizing: border-box; padding: 16px 0 16px 16px; background: rgba(255, 255, 255, 0.32); -webkit-backdrop-filter: blur(22px) saturate(160%); backdrop-filter: blur(22px) saturate(160%); border: 1px solid rgba(255, 255, 255, 0.65); border-right: 0; box-shadow: 0 10px 30px rgba(14, 15, 15, 0.08), inset 0 1px 0 rgba(255, 255, 255, 0.7); border-radius: 28px 0 0 28px; display: flex; flex-direction: column; gap: 12px">
<div style="display: flex; align-items: center; justify-content: space-between; padding-right: 20px">
<div style="display: flex; align-items: center; gap: 6px">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linejoin="round" aria-hidden="true"><path d="M12 3l1.8 5.2L19 10l-5.2 1.8L12 17l-1.8-5.2L5 10l5.2-1.8z"></path></svg>
<span style="font-size: 15px; font-weight: 500">AI Recommendations</span>
</div>
<a href="#" style="font-size: 13px; font-weight: 500">See all</a>
</div>
<div style="display: flex; gap: 10px; overflow-x: auto; padding-right: 16px">
<div style="flex: 0 0 150px; height: 150px; box-sizing: border-box; padding: 14px; border-radius: 22px; background: #F2845C; position: relative; color: #1F1410">
<span style="font-size: 14px; font-weight: 600">Running</span>
<button aria-label="Open running plan" style="position: absolute; top: 10px; right: 10px; width: 36px; height: 36px; border-radius: 50%; border: 0; background: #0E0F0F; color: #FFFFFF; display: flex; align-items: center; justify-content: center; cursor: pointer">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M7 17 17 7"></path><path d="M8 7h9v9"></path></svg>
</button>
<img src="https://developermill.com/app-designs/ai-health-coach-app-ui-design/assets/d25209e912.jpg" alt="Runner jogging" style="position: absolute; left: 4px; bottom: 6px; width: 66px; height: 92px; object-fit: cover; object-position: center 30%; border-radius: 14px">
<span style="position: absolute; right: 12px; bottom: 14px; width: 66px; font-size: 12px; line-height: 15px">Do 15 minutes of light jogging</span>
</div>
<div style="flex: 0 0 150px; height: 150px; box-sizing: border-box; padding: 14px; border-radius: 22px; background: {{accent}}; position: relative; color: #1C2108">
<span style="font-size: 14px; font-weight: 600">Nutrition</span>
<img src="https://developermill.com/app-designs/ai-health-coach-app-ui-design/assets/54c4b96167.jpg" alt="Protein bowl" style="position: absolute; left: 6px; bottom: 8px; width: 70px; height: 70px; border-radius: 50%; object-fit: cover">
<span style="position: absolute; right: 12px; bottom: 14px; width: 62px; font-size: 12px; line-height: 15px">Try 25g of protein in your breakfast</span>
</div>
<div style="flex: 0 0 150px; height: 150px; box-sizing: border-box; padding: 14px; border-radius: 22px; background: #8FA9F5; position: relative; color: #0F1630">
<span style="font-size: 14px; font-weight: 600">Mental</span>
<button aria-label="Open breathing exercise" style="position: absolute; top: 10px; right: 10px; width: 36px; height: 36px; border-radius: 50%; border: 0; background: #0E0F0F; color: #FFFFFF; display: flex; align-items: center; justify-content: center; cursor: pointer">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M7 17 17 7"></path><path d="M8 7h9v9"></path></svg>
</button>
<span style="position: absolute; left: 14px; bottom: 14px; right: 14px; font-size: 12px; line-height: 15px">Do 2 minutes of deep breathing.</span>
</div>
</div>
</div>
<nav aria-label="Main" style="position: absolute; bottom: 28px; left: 20px; right: 20px; height: 72px; box-sizing: border-box; padding: 8px; border-radius: 40px; background: #6F7472; display: flex; align-items: center; justify-content: space-between">
<a href="Main.dc.html" aria-label="Home" style="width: 56px; height: 56px; border-radius: 50%; background: #FFFFFF; color: #0E0F0F; display: flex; align-items: center; justify-content: center">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linejoin="round" aria-hidden="true"><path d="M3 10.5 12 3l9 7.5V20a1 1 0 0 1-1 1h-5v-6h-6v6H4a1 1 0 0 1-1-1z"></path></svg>
</a>
<a href="GlobalIndex.dc.html" aria-label="Stats" style="width: 56px; height: 56px; border-radius: 50%; background: #878C8A; color: #FFFFFF; display: flex; align-items: center; justify-content: center">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M5 20V10M12 20V4M19 20v-7"></path></svg>
</a>
<button aria-label="AI coach" style="width: 56px; height: 56px; border-radius: 50%; border: 0; background: {{accent}}; color: #0E0F0F; display: flex; align-items: center; justify-content: center; cursor: pointer">
<img src="https://developermill.com/app-designs/ai-health-coach-app-ui-design/assets/4527cf3df2.jpg" alt="" style="width: 30px; height: 30px; border-radius: 50%">
</button>
<button aria-label="Calendar" style="width: 56px; height: 56px; border-radius: 50%; border: 0; background: #878C8A; color: #FFFFFF; display: flex; align-items: center; justify-content: center; cursor: pointer">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" aria-hidden="true"><rect x="3" y="5" width="18" height="16" rx="2"></rect><path d="M16 3v4M8 3v4M3 10h18"></path></svg>
</button>
<button aria-label="Profile" style="width: 56px; height: 56px; border-radius: 50%; border: 0; background: #878C8A; color: #FFFFFF; display: flex; align-items: center; justify-content: center; cursor: pointer">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" aria-hidden="true"><circle cx="12" cy="8" r="4"></circle><path d="M4 21a8 8 0 0 1 16 0"></path></svg>
</button>
</nav>
</div>
</x-dc>
<script type="text/x-dc" data-dc-script data-props='{"accent":{"editor":"color","default":"#D7F26B","options":["#D7F26B","#9ED9C9","#F2845C","#8FA9F5"]},"$preview":{"width":390,"height":844}}'>
class Component extends DCLogic {
renderVals() {
return { accent: this.props.accent ?? '#D7F26B' };
}
}
</script>
</body>
</html>
```
### GlobalIndex
```html
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Global index</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=Urbanist:wght@400;500;600&display=swap" rel="stylesheet">
<style>
body{margin:0}
a{color:#0E0F0F;text-decoration:none}a:hover{color:#2B302E}
</style>
</helmet>
<div style="position: relative; width: 390px; height: 844px; overflow: hidden; background: #E8ECEA; font-family: 'Urbanist', system-ui, sans-serif; color: #0E0F0F">
<div style="position: absolute; top: 56px; left: 20px; right: 20px; display: flex; align-items: center; justify-content: space-between">
<a href="Main.dc.html" aria-label="Back" style="width: 52px; height: 52px; border-radius: 50%; background: #FFFFFF; display: flex; align-items: center; justify-content: center">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m15 18-6-6 6-6"></path></svg>
</a>
<h1 style="margin: 0; font-size: 19px; font-weight: 500">Global index</h1>
<button aria-label="Pick date" style="width: 52px; height: 52px; border-radius: 50%; border: 0; background: #FFFFFF; display: flex; align-items: center; justify-content: center; cursor: pointer; color: #0E0F0F">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" aria-hidden="true"><rect x="3" y="5" width="18" height="16" rx="2"></rect><path d="M16 3v4M8 3v4M3 10h18"></path></svg>
</button>
</div>
<div style="position: absolute; top: 128px; left: 12px; right: 12px; height: 312px; background: #F6F8F7; border-radius: 32px">
<svg role="img" aria-label="Well-being ring: physical activity 80%, nutrition 75%, health mental 90%, dream 85%" width="240" height="240" viewBox="0 0 300 300" style="position: absolute; top: 30px; left: 63px">
<circle cx="150" cy="150" r="95" fill="none" stroke="#A9DFD0" stroke-width="40" stroke-linecap="round" stroke-dasharray="94.5 596.9" transform="rotate(171.5 150 150)"></circle>
<circle cx="150" cy="150" r="95" fill="none" stroke="{{accent}}" stroke-width="40" stroke-linecap="round" stroke-dasharray="86.2 596.9" transform="rotate(261.5 150 150)"></circle>
<circle cx="150" cy="150" r="95" fill="none" stroke="#7E9BF0" stroke-width="40" stroke-linecap="round" stroke-dasharray="102.8 596.9" transform="rotate(346.5 150 150)"></circle>
<circle cx="150" cy="150" r="95" fill="none" stroke="#F2845C" stroke-width="40" stroke-linecap="round" stroke-dasharray="94.5 596.9" transform="rotate(81.5 150 150)"></circle>
<g fill="#FFFFFF" fill-opacity="0.85"><circle cx="77.2" cy="88.9" r="11"></circle><circle cx="197.5" cy="67.7" r="11"></circle><circle cx="232.3" cy="197.5" r="11"></circle><circle cx="133.5" cy="243.6" r="11"></circle></g>
<g fill="none" stroke="#0E0F0F" stroke-opacity="0.45" stroke-width="1.5"><circle cx="77.2" cy="88.9" r="4.5"></circle><circle cx="197.5" cy="67.7" r="4.5"></circle><circle cx="232.3" cy="197.5" r="4.5"></circle><circle cx="133.5" cy="243.6" r="4.5"></circle></g>
</svg>
<div style="position: absolute; top: 30px; left: 63px; width: 240px; height: 240px; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 2px">
<div style="display: flex; align-items: baseline"><span style="font-size: 48px; font-weight: 500; letter-spacing: -1.5px; line-height: 1">82</span><span style="font-size: 20px; font-weight: 500">%</span></div>
<span style="font-size: 11px; color: #555C59">General well-being</span>
</div>
<div style="position: absolute; top: 52px; left: 16px; display: flex; flex-direction: column; gap: 4px">
<span style="font-size: 11px; line-height: 13px; color: #3B403E; white-space: pre-line">Physical
Activity</span>
<span style="font-size: 15px; font-weight: 500">80<span style="font-size: 10px">%</span></span>
</div>
<div style="position: absolute; top: 52px; right: 16px; display: flex; flex-direction: column; align-items: flex-end; gap: 4px">
<span style="font-size: 11px; color: #3B403E">Nutrition</span>
<span style="font-size: 15px; font-weight: 500">75<span style="font-size: 10px">%</span></span>
</div>
<div style="position: absolute; bottom: 40px; left: 16px; display: flex; flex-direction: column; gap: 4px">
<span style="font-size: 11px; color: #3B403E">Dream</span>
<span style="font-size: 15px; font-weight: 500">85<span style="font-size: 10px">%</span></span>
</div>
<div style="position: absolute; bottom: 40px; right: 16px; display: flex; flex-direction: column; align-items: flex-end; gap: 4px">
<span style="font-size: 11px; line-height: 13px; color: #3B403E; text-align: right; white-space: pre-line">Health
mental</span>
<span style="font-size: 15px; font-weight: 500">90<span style="font-size: 10px">%</span></span>
</div>
</div>
<div style="position: absolute; top: 420px; left: 0; right: 0; bottom: 0; box-sizing: border-box; padding: 34px 20px 32px; background: #0E0F0F; border-radius: 34px 34px 0 0; color: #FFFFFF; display: flex; flex-direction: column">
<svg aria-hidden="true" width="150" height="18" viewBox="0 0 150 18" style="position: absolute; top: -17px; left: 120px"><path d="M0 18 C 22 18, 26 0, 48 0 H 102 C 124 0, 128 18, 150 18 Z" fill="#0E0F0F"></path></svg>
<div style="position: absolute; top: -4px; left: 167px; width: 56px; height: 4px; border-radius: 2px; background: #4A4E4C"></div>
<div style="display: flex; align-items: center; justify-content: space-between">
<h2 style="margin: 0; font-size: 19px; font-weight: 500">Physical Activity</h2>
<span style="font-size: 19px; font-weight: 500">80<span style="font-size: 12px">%</span></span>
</div>
<div style="margin-top: 24px; display: grid; grid-template-columns: repeat(3, minmax(0, 1fr))">
<div style="display: flex; flex-direction: column; gap: 6px; padding-right: 10px">
<span style="font-size: 12px; color: #A3A8A6">Steps</span>
<span style="font-size: 22px; font-weight: 500">6,742<span style="font-size: 13px; color: #8E9391">/8,000</span></span>
</div>
<div style="display: flex; flex-direction: column; gap: 6px; padding: 0 10px; border-left: 1px solid #2A2D2C">
<span style="font-size: 12px; color: #A3A8A6">Calories</span>
<span style="font-size: 22px; font-weight: 500">420 kcal</span>
</div>
<div style="display: flex; flex-direction: column; gap: 6px; padding-left: 10px; border-left: 1px solid #2A2D2C">
<span style="font-size: 12px; color: #A3A8A6">Active minutes</span>
<span style="font-size: 22px; font-weight: 500">32 min</span>
</div>
</div>
<div aria-hidden="true" style="margin-top: 32px; height: 110px; display: flex; align-items: flex-end; justify-content: space-between">
<sc-for list="{{bars}}" as="b" hint-placeholder-count="58">
<div style="width: 2px; border-radius: 1px; height: {{b.h}}; background: {{b.c}}"></div>
</sc-for>
</div>
<button style="margin-top: auto; height: 60px; border: 0; border-radius: 30px; background: {{accent}}; color: #0E0F0F; display: flex; align-items: center; justify-content: center; gap: 10px; font-family: 'Urbanist', sans-serif; font-size: 15px; font-weight: 600; cursor: pointer">
<img src="https://developermill.com/app-designs/ai-health-coach-app-ui-design/assets/4527cf3df2.jpg" alt="" style="width: 26px; height: 26px; border-radius: 50%">
See coach recommendations
</button>
</div>
</div>
</x-dc>
<script type="text/x-dc" data-dc-script data-props='{"accent":{"editor":"color","default":"#D7F26B","options":["#D7F26B","#9ED9C9","#F2845C","#8FA9F5"]},"$preview":{"width":390,"height":844}}'>
class Component extends DCLogic {
renderVals() {
var bars = [];
var n = 58;
for (var i = 0; i < n; i++) {
var t = i / (n - 1);
var jitter = ((i * 37) % 11) - 5;
var h = Math.max(8, Math.round(14 + t * t * 82 + jitter * (0.4 + t)));
if (h > 106) h = 106;
var a = (0.22 + t * 0.7).toFixed(2);
bars.push({ h: h + 'px', c: 'rgba(255,255,255,' + a + ')' });
}
return { accent: this.props.accent ?? '#D7F26B', bars: bars };
}
}
</script>
</body>
</html>
```SKILL.md
Save as .claude/skills/ai-health-coach-app-ui/SKILL.md
--- name: ai-health-coach-app-ui description: Build the AI Health Coach mobile app UI in Expo React Native. Use when asked to create a AI health coach app UI or a new screen in this visual style. --- # AI Health Coach app UI Build this UI in an Expo SDK 58 app with Expo Router and strict TypeScript. Match the spec below rather than inventing a new look. The finished reference lives at https://developermill.com/app-designs/ai-health-coach-app-ui-design ## Visual direction Cool sage greys with a lime accent, a full-height coach portrait, frosted glass panels and bright category cards. Keep these signature details: - Frosted glass recommendations panel - Segmented ring chart in SVG - Horizontally scrolling recommendation cards - Sculpted bottom sheet with an activity histogram ## Set up ```bash npx expo install expo-blur expo-image react-native-svg @expo-google-fonts/urbanist expo-font ``` ## Tokens | Token | Hex | | --- | --- | | Lime | `#D7F26B` | | Carbon | `#0E0F0F` | | Sage | `#D5DAD8` | | Coral | `#F2845C` | | Periwinkle | `#8FA9F5` | | Seafoam | `#9ED9C9` | - Typefaces: Urbanist - Common sizes (px): 10, 11, 12, 13, 14, 15, 19, 22 - Common corner radii (px): 3, 10, 14, 22, 28, 40 Define these once and reuse them. Do not add new colours. ## Screens Build one Expo Router route per screen, in this order: 1. **Coach home**: Greeting, global index card, wellbeing score over the coach portrait, a glass panel of AI recommendations and a pill tab bar. 2. **Global index**: A four-part ring chart with labelled scores above a dark sheet of steps, calories, active minutes and a histogram. ## Build rules - Design on a 390 × 844 artboard, then scale to the device width so layouts match on every phone. - Load each custom font weight by its own family name with expo-font. Do not rely on `fontWeight` with custom fonts. - Draw icons, charts and illustrations with react-native-svg rather than image files or chart libraries. - Use `Pressable` with an `accessibilityLabel` for every icon-only control, and keep touch targets at 44 px or larger. - Wire navigation with `router.navigate`, and keep sample data in constants at the top of each file so it is easy to replace. - Use expo-image for photos, with `contentFit` set explicitly. ## Done when - Every screen above exists as a route and they link together. - Colours, type and radii match the tokens exactly. - `npx tsc --noEmit` and `npx expo lint` both pass. - Layouts hold up on small and large phones.
Set up the project
npx create-expo-app@latest ai-health-coach-app --template default cd ai-health-coach-app rm -rf src npx expo install expo-blur expo-image react-native-svg @expo-google-fonts/urbanist expo-font
src/app/_layout.tsx
import {
Urbanist_400Regular,
Urbanist_500Medium,
Urbanist_600SemiBold,
} from '@expo-google-fonts/urbanist';
import { useFonts } from 'expo-font';
import { Stack } from 'expo-router';
export default function RootLayout() {
const [fontsLoaded] = useFonts({ Urbanist_400Regular, Urbanist_500Medium, Urbanist_600SemiBold });
if (!fontsLoaded) return null;
return <Stack screenOptions={{ headerShown: false }} />;
}src/app/globalindex.tsx
import { Image } from 'expo-image';
import { router } from 'expo-router';
import { Pressable, 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';
const accent = '#D7F26B';
const bars = [
{ h: '12px', c: 'rgba(255,255,255,0.22)' },
{ h: '14px', c: 'rgba(255,255,255,0.23)' },
{ h: '15px', c: 'rgba(255,255,255,0.24)' },
{ h: '12px', c: 'rgba(255,255,255,0.26)' },
{ h: '14px', c: 'rgba(255,255,255,0.27)' },
{ h: '17px', c: 'rgba(255,255,255,0.28)' },
{ h: '13px', c: 'rgba(255,255,255,0.29)' },
{ h: '16px', c: 'rgba(255,255,255,0.31)' },
{ h: '18px', c: 'rgba(255,255,255,0.32)' },
{ h: '15px', c: 'rgba(255,255,255,0.33)' },
{ h: '18px', c: 'rgba(255,255,255,0.34)' },
{ h: '14px', c: 'rgba(255,255,255,0.36)' },
{ h: '17px', c: 'rgba(255,255,255,0.37)' },
{ h: '20px', c: 'rgba(255,255,255,0.38)' },
{ h: '16px', c: 'rgba(255,255,255,0.39)' },
{ h: '20px', c: 'rgba(255,255,255,0.40)' },
{ h: '23px', c: 'rgba(255,255,255,0.42)' },
{ h: '19px', c: 'rgba(255,255,255,0.43)' },
{ h: '23px', c: 'rgba(255,255,255,0.44)' },
{ h: '27px', c: 'rgba(255,255,255,0.45)' },
{ h: '23px', c: 'rgba(255,255,255,0.47)' },
{ h: '27px', c: 'rgba(255,255,255,0.48)' },
{ h: '22px', c: 'rgba(255,255,255,0.49)' },
{ h: '27px', c: 'rgba(255,255,255,0.50)' },
{ h: '31px', c: 'rgba(255,255,255,0.51)' },
{ h: '26px', c: 'rgba(255,255,255,0.53)' },
{ h: '31px', c: 'rgba(255,255,255,0.54)' },
{ h: '36px', c: 'rgba(255,255,255,0.55)' },
{ h: '31px', c: 'rgba(255,255,255,0.56)' },
{ h: '36px', c: 'rgba(255,255,255,0.58)' },
{ h: '41px', c: 'rgba(255,255,255,0.59)' },
{ h: '36px', c: 'rgba(255,255,255,0.60)' },
{ h: '42px', c: 'rgba(255,255,255,0.61)' },
{ h: '37px', c: 'rgba(255,255,255,0.63)' },
{ h: '42px', c: 'rgba(255,255,255,0.64)' },
{ h: '48px', c: 'rgba(255,255,255,0.65)' },
{ h: '43px', c: 'rgba(255,255,255,0.66)' },
{ h: '49px', c: 'rgba(255,255,255,0.67)' },
{ h: '55px', c: 'rgba(255,255,255,0.69)' },
{ h: '49px', c: 'rgba(255,255,255,0.70)' },
{ h: '55px', c: 'rgba(255,255,255,0.71)' },
{ h: '62px', c: 'rgba(255,255,255,0.72)' },
{ h: '56px', c: 'rgba(255,255,255,0.74)' },
{ h: '63px', c: 'rgba(255,255,255,0.75)' },
{ h: '57px', c: 'rgba(255,255,255,0.76)' },
{ h: '64px', c: 'rgba(255,255,255,0.77)' },
{ h: '71px', c: 'rgba(255,255,255,0.78)' },
{ h: '65px', c: 'rgba(255,255,255,0.80)' },
{ h: '72px', c: 'rgba(255,255,255,0.81)' },
{ h: '80px', c: 'rgba(255,255,255,0.82)' },
{ h: '73px', c: 'rgba(255,255,255,0.83)' },
{ h: '81px', c: 'rgba(255,255,255,0.85)' },
{ h: '89px', c: 'rgba(255,255,255,0.86)' },
{ h: '82px', c: 'rgba(255,255,255,0.87)' },
{ h: '90px', c: 'rgba(255,255,255,0.88)' },
{ h: '84px', c: 'rgba(255,255,255,0.90)' },
{ h: '92px', c: 'rgba(255,255,255,0.91)' },
{ h: '100px', c: 'rgba(255,255,255,0.92)' },
];
export default function GlobalIndexScreen() {
return (
<Screen background="#E8ECEA" statusBar="dark">
<View style={{ flex: 1 }}>
<View
style={{
position: 'absolute',
top: 56,
left: 20,
right: 20,
alignItems: 'center',
justifyContent: 'space-between',
flexDirection: 'row',
}}>
<Pressable
accessibilityRole="link"
accessibilityLabel="Back"
onPress={() => router.navigate('/')}
style={{
width: 52,
height: 52,
borderRadius: '50%',
backgroundColor: '#FFFFFF',
alignItems: 'center',
justifyContent: 'center',
flexDirection: 'row',
}}>
<Svg
width="20"
height="20"
viewBox="0 0 24 24"
fill="none"
stroke="#0E0F0F"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round">
<Path d="m15 18-6-6 6-6" />
</Svg>
</Pressable>
<Text
style={{
margin: 0,
flexShrink: 1,
fontFamily: 'Urbanist_500Medium',
fontSize: 19,
color: '#0E0F0F',
}}>
Global index
</Text>
<Pressable
accessibilityRole="button"
accessibilityLabel="Pick date"
style={{
width: 52,
height: 52,
borderRadius: '50%',
borderWidth: 0,
backgroundColor: '#FFFFFF',
alignItems: 'center',
justifyContent: 'center',
flexDirection: 'row',
}}>
<Svg
width="20"
height="20"
viewBox="0 0 24 24"
fill="none"
stroke="#0E0F0F"
strokeWidth="1.8"
strokeLinecap="round">
<Rect x="3" y="5" width="18" height="16" rx="2" />
<Path d="M16 3v4M8 3v4M3 10h18" />
</Svg>
</Pressable>
</View>
<View
style={{
position: 'absolute',
top: 128,
left: 12,
right: 12,
height: 312,
backgroundColor: '#F6F8F7',
borderRadius: 32,
}}>
<Svg
width="240"
height="240"
viewBox="0 0 300 300"
accessibilityRole="image"
accessibilityLabel="Well-being ring: physical activity 80%, nutrition 75%, health mental 90%, dream 85%"
style={{ position: 'absolute', top: 30, left: 63 }}>
<Circle
cx="150"
cy="150"
r="95"
fill="none"
stroke="#A9DFD0"
strokeWidth="40"
strokeLinecap="round"
strokeDasharray="94.5 596.9"
transform="rotate(171.5 150 150)"
/>
<Circle
cx="150"
cy="150"
r="95"
fill="none"
stroke={accent}
strokeWidth="40"
strokeLinecap="round"
strokeDasharray="86.2 596.9"
transform="rotate(261.5 150 150)"
/>
<Circle
cx="150"
cy="150"
r="95"
fill="none"
stroke="#7E9BF0"
strokeWidth="40"
strokeLinecap="round"
strokeDasharray="102.8 596.9"
transform="rotate(346.5 150 150)"
/>
<Circle
cx="150"
cy="150"
r="95"
fill="none"
stroke="#F2845C"
strokeWidth="40"
strokeLinecap="round"
strokeDasharray="94.5 596.9"
transform="rotate(81.5 150 150)"
/>
<G fill="#FFFFFF" fillOpacity="0.85">
<Circle cx="77.2" cy="88.9" r="11" />
<Circle cx="197.5" cy="67.7" r="11" />
<Circle cx="232.3" cy="197.5" r="11" />
<Circle cx="133.5" cy="243.6" r="11" />
</G>
<G fill="none" stroke="#0E0F0F" strokeOpacity="0.45" strokeWidth="1.5">
<Circle cx="77.2" cy="88.9" r="4.5" />
<Circle cx="197.5" cy="67.7" r="4.5" />
<Circle cx="232.3" cy="197.5" r="4.5" />
<Circle cx="133.5" cy="243.6" r="4.5" />
</G>
</Svg>
<View
style={{
position: 'absolute',
top: 30,
left: 63,
width: 240,
height: 240,
alignItems: 'center',
justifyContent: 'center',
gap: 2,
}}>
<View style={{ alignItems: 'baseline', flexDirection: 'row' }}>
<Text
style={{
flexShrink: 1,
fontFamily: 'Urbanist_500Medium',
fontSize: 48,
color: '#0E0F0F',
letterSpacing: -1.5,
lineHeight: 48,
}}>
82
</Text>
<Text
style={{
flexShrink: 1,
fontFamily: 'Urbanist_500Medium',
fontSize: 20,
color: '#0E0F0F',
}}>
%
</Text>
</View>
<Text style={{ fontFamily: 'Urbanist_400Regular', fontSize: 11, color: '#555C59' }}>
General well-being
</Text>
</View>
<View style={{ position: 'absolute', top: 52, left: 16, gap: 4 }}>
<Text
style={{
fontFamily: 'Urbanist_400Regular',
fontSize: 11,
color: '#3B403E',
lineHeight: 13,
}}>
Physical Activity
</Text>
<Text style={{ fontFamily: 'Urbanist_500Medium', fontSize: 15, color: '#0E0F0F' }}>
80<Text style={{ fontSize: 10 }}>%</Text>
</Text>
</View>
<View
style={{ position: 'absolute', top: 52, right: 16, alignItems: 'flex-end', gap: 4 }}>
<Text style={{ fontFamily: 'Urbanist_400Regular', fontSize: 11, color: '#3B403E' }}>
Nutrition
</Text>
<Text style={{ fontFamily: 'Urbanist_500Medium', fontSize: 15, color: '#0E0F0F' }}>
75<Text style={{ fontSize: 10 }}>%</Text>
</Text>
</View>
<View style={{ position: 'absolute', bottom: 40, left: 16, gap: 4 }}>
<Text style={{ fontFamily: 'Urbanist_400Regular', fontSize: 11, color: '#3B403E' }}>
Dream
</Text>
<Text style={{ fontFamily: 'Urbanist_500Medium', fontSize: 15, color: '#0E0F0F' }}>
85<Text style={{ fontSize: 10 }}>%</Text>
</Text>
</View>
<View
style={{ position: 'absolute', bottom: 40, right: 16, alignItems: 'flex-end', gap: 4 }}>
<Text
style={{
fontFamily: 'Urbanist_400Regular',
fontSize: 11,
color: '#3B403E',
lineHeight: 13,
textAlign: 'right',
}}>
Health mental
</Text>
<Text style={{ fontFamily: 'Urbanist_500Medium', fontSize: 15, color: '#0E0F0F' }}>
90<Text style={{ fontSize: 10 }}>%</Text>
</Text>
</View>
</View>
<View
style={{
position: 'absolute',
top: 420,
left: 0,
right: 0,
bottom: 0,
paddingTop: 34,
paddingBottom: 32,
paddingHorizontal: 20,
backgroundColor: '#0E0F0F',
borderTopLeftRadius: 34,
borderTopRightRadius: 34,
borderBottomRightRadius: 0,
borderBottomLeftRadius: 0,
}}>
<Svg
width="150"
height="18"
viewBox="0 0 150 18"
style={{ position: 'absolute', top: -17, left: 120, zIndex: 1 }}>
<Path d="M0 18 C 22 18, 26 0, 48 0 H 102 C 124 0, 128 18, 150 18 Z" fill="#0E0F0F" />
</Svg>
<View
style={{
position: 'absolute',
top: -4,
left: 167,
width: 56,
height: 4,
borderRadius: 2,
backgroundColor: '#4A4E4C',
zIndex: 1,
}}
/>
<View
style={{ alignItems: 'center', justifyContent: 'space-between', flexDirection: 'row' }}>
<Text
style={{
margin: 0,
flexShrink: 1,
fontFamily: 'Urbanist_500Medium',
fontSize: 19,
color: '#FFFFFF',
}}>
Physical Activity
</Text>
<Text
style={{
flexShrink: 1,
fontFamily: 'Urbanist_500Medium',
fontSize: 19,
color: '#FFFFFF',
}}>
80<Text style={{ fontSize: 12 }}>%</Text>
</Text>
</View>
<View style={{ marginTop: 24, flexDirection: 'row' }}>
<View style={{ gap: 6, paddingRight: 10, flex: 1 }}>
<Text style={{ fontFamily: 'Urbanist_400Regular', fontSize: 12, color: '#A3A8A6' }}>
Steps
</Text>
<Text style={{ fontFamily: 'Urbanist_500Medium', fontSize: 22, color: '#FFFFFF' }}>
6,742<Text style={{ fontSize: 13, color: '#8E9391' }}>/8,000</Text>
</Text>
</View>
<View
style={{
gap: 6,
paddingVertical: 0,
paddingHorizontal: 10,
borderLeftWidth: 1,
borderLeftColor: '#2A2D2C',
flex: 1,
}}>
<Text style={{ fontFamily: 'Urbanist_400Regular', fontSize: 12, color: '#A3A8A6' }}>
Calories
</Text>
<Text style={{ fontFamily: 'Urbanist_500Medium', fontSize: 22, color: '#FFFFFF' }}>
420 kcal
</Text>
</View>
<View
style={{
gap: 6,
paddingLeft: 10,
borderLeftWidth: 1,
borderLeftColor: '#2A2D2C',
flex: 1,
}}>
<Text style={{ fontFamily: 'Urbanist_400Regular', fontSize: 12, color: '#A3A8A6' }}>
Active minutes
</Text>
<Text style={{ fontFamily: 'Urbanist_500Medium', fontSize: 22, color: '#FFFFFF' }}>
32 min
</Text>
</View>
</View>
<View
style={{
marginTop: 32,
height: 110,
alignItems: 'flex-end',
justifyContent: 'space-between',
flexDirection: 'row',
}}>
{bars.map((b, index) => (
<View
key={index}
style={{ width: 2, borderRadius: 1, height: parseFloat(b.h), backgroundColor: b.c }}
/>
))}
</View>
<Pressable
accessibilityRole="button"
style={{
marginTop: 'auto',
height: 60,
borderWidth: 0,
borderRadius: 30,
backgroundColor: accent,
alignItems: 'center',
justifyContent: 'center',
gap: 10,
flexDirection: 'row',
}}>
<Image
source={{ uri: asset('4527cf3df2.jpg') }}
contentFit="fill"
style={{ width: 26, height: 26, borderRadius: '50%' }}
/>
<Text
style={{
fontFamily: 'Urbanist_600SemiBold',
fontSize: 15,
color: '#0E0F0F',
flexShrink: 1,
}}>
See coach recommendations
</Text>
</Pressable>
</View>
</View>
</Screen>
);
}src/app/index.tsx
import { BlurView } from 'expo-blur';
import { Image } from 'expo-image';
import { router } from 'expo-router';
import { Pressable, ScrollView, StyleSheet, Text, View } from 'react-native';
import Svg, { Circle, Path, Rect } from 'react-native-svg';
import { Screen } from '@/components/screen';
import { asset } from '@/lib/assets';
const accent = '#D7F26B';
export default function MainScreen() {
return (
<Screen background="#D5DAD8" statusBar="dark">
<View style={{ flex: 1 }}>
<Image
source={{ uri: asset('336ee9f7b2.jpg') }}
contentFit="cover"
contentPosition="top center"
accessibilityLabel="Your AI coach"
style={{ position: 'absolute', top: 118, left: 120, width: 270, height: 470 }}
/>
<View
style={{
position: 'absolute',
top: 56,
left: 20,
right: 20,
alignItems: 'center',
justifyContent: 'space-between',
flexDirection: 'row',
}}>
<View style={{ alignItems: 'center', gap: 12, flexShrink: 1, flexDirection: 'row' }}>
<Image
source={{ uri: asset('f0f3823a08.jpg') }}
contentFit="cover"
accessibilityLabel="Freddy"
style={{
width: 48,
height: 48,
borderRadius: '50%',
borderWidth: 2,
borderColor: '#FFFFFF',
}}
/>
<View style={{ gap: 2, flexShrink: 1 }}>
<Text style={{ fontFamily: 'Urbanist_400Regular', fontSize: 13, color: '#4E5552' }}>
Welcome
</Text>
<Text style={{ fontFamily: 'Urbanist_600SemiBold', fontSize: 20, color: '#0E0F0F' }}>
Freddy
</Text>
</View>
</View>
<View style={{ gap: 10, flexShrink: 1, flexDirection: 'row' }}>
<Pressable
accessibilityRole="button"
accessibilityLabel="Notifications, 2 new"
style={{
width: 52,
height: 52,
borderRadius: '50%',
borderWidth: 0,
backgroundColor: '#FFFFFF',
alignItems: 'center',
justifyContent: 'center',
flexDirection: 'row',
}}>
<Svg
width="20"
height="20"
viewBox="0 0 24 24"
fill="none"
stroke="#0E0F0F"
strokeWidth="1.8"
strokeLinecap="round"
strokeLinejoin="round">
<Path d="M6 8a6 6 0 0 1 12 0c0 7 3 9 3 9H3s3-2 3-9" />
<Path d="M10.3 21a1.94 1.94 0 0 0 3.4 0" />
</Svg>
<View
style={{
position: 'absolute',
top: -2,
right: 2,
minWidth: 20,
height: 20,
borderRadius: 10,
backgroundColor: accent,
alignItems: 'center',
justifyContent: 'center',
flexDirection: 'row',
}}>
<Text
style={{
fontFamily: 'Urbanist_600SemiBold',
fontSize: 11,
color: '#0E0F0F',
flexShrink: 1,
}}>
2
</Text>
</View>
</Pressable>
<Pressable
accessibilityRole="button"
accessibilityLabel="Menu"
style={{
width: 52,
height: 52,
borderRadius: '50%',
borderWidth: 0,
backgroundColor: '#FFFFFF',
alignItems: 'center',
justifyContent: 'center',
flexDirection: 'row',
}}>
<Svg width="18" height="18" viewBox="0 0 24 24" fill="#0E0F0F">
<Circle cx="8" cy="8" r="2" />
<Circle cx="16" cy="8" r="2" />
<Circle cx="8" cy="16" r="2" />
<Circle cx="16" cy="16" r="2" />
</Svg>
</Pressable>
</View>
</View>
<View
style={{
position: 'absolute',
top: 150,
left: 20,
width: 168,
padding: 14,
backgroundColor: '#FFFFFF',
borderRadius: 22,
gap: 12,
}}>
<View
style={{ alignItems: 'center', justifyContent: 'space-between', flexDirection: 'row' }}>
<Text
style={{
flexShrink: 1,
fontFamily: 'Urbanist_500Medium',
fontSize: 14,
color: '#0E0F0F',
}}>
Global index
</Text>
<Pressable
accessibilityRole="link"
accessibilityLabel="Open global index"
onPress={() => router.navigate('/globalindex')}
style={{
width: 36,
height: 36,
borderRadius: '50%',
backgroundColor: '#0E0F0F',
alignItems: 'center',
justifyContent: 'center',
flexDirection: 'row',
}}>
<Svg
width="16"
height="16"
viewBox="0 0 24 24"
fill="none"
stroke="#FFFFFF"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round">
<Path d="M7 17 17 7" />
<Path d="M8 7h9v9" />
</Svg>
</Pressable>
</View>
<View style={{ gap: 8 }}>
<View style={{ alignItems: 'center', gap: 8, flexDirection: 'row' }}>
<Text
style={{
width: 22,
fontFamily: 'Urbanist_500Medium',
fontSize: 11,
color: '#0E0F0F',
flexShrink: 1,
}}>
PA
</Text>
<View style={{ flex: 1, height: 6, borderRadius: 3, backgroundColor: '#F0F2F1' }}>
<View
style={{ width: '86%', height: 6, borderRadius: 3, backgroundColor: accent }}
/>
</View>
</View>
<View style={{ alignItems: 'center', gap: 8, flexDirection: 'row' }}>
<Text
style={{
width: 22,
fontFamily: 'Urbanist_500Medium',
fontSize: 11,
color: '#0E0F0F',
flexShrink: 1,
}}>
N
</Text>
<View style={{ flex: 1, height: 6, borderRadius: 3, backgroundColor: '#F0F2F1' }}>
<View
style={{ width: '72%', height: 6, borderRadius: 3, backgroundColor: '#7E9BF0' }}
/>
</View>
</View>
<View style={{ alignItems: 'center', gap: 8, flexDirection: 'row' }}>
<Text
style={{
width: 22,
fontFamily: 'Urbanist_500Medium',
fontSize: 11,
color: '#0E0F0F',
flexShrink: 1,
}}>
D
</Text>
<View style={{ flex: 1, height: 6, borderRadius: 3, backgroundColor: '#F0F2F1' }}>
<View
style={{ width: '80%', height: 6, borderRadius: 3, backgroundColor: '#F2845C' }}
/>
</View>
</View>
<View style={{ alignItems: 'center', gap: 8, flexDirection: 'row' }}>
<Text
style={{
width: 22,
fontFamily: 'Urbanist_500Medium',
fontSize: 11,
color: '#0E0F0F',
flexShrink: 1,
}}>
MH
</Text>
<View style={{ flex: 1, height: 6, borderRadius: 3, backgroundColor: '#F0F2F1' }}>
<View
style={{ width: '90%', height: 6, borderRadius: 3, backgroundColor: '#9ED9C9' }}
/>
</View>
</View>
</View>
</View>
<View style={{ position: 'absolute', top: 308, left: 20, gap: 4 }}>
<Text
style={{
fontFamily: 'Urbanist_400Regular',
fontSize: 13,
color: '#3B403E',
lineHeight: 16,
}}>
General well-being
</Text>
<View style={{ alignItems: 'baseline', flexDirection: 'row' }}>
<Text
style={{
flexShrink: 1,
fontFamily: 'Urbanist_500Medium',
fontSize: 64,
color: '#0E0F0F',
letterSpacing: -2,
lineHeight: 64,
}}>
82
</Text>
<Text
style={{
flexShrink: 1,
fontFamily: 'Urbanist_500Medium',
fontSize: 26,
color: '#0E0F0F',
}}>
%
</Text>
</View>
</View>
<View
style={{
position: 'absolute',
top: 492,
left: 12,
right: 0,
paddingVertical: 16,
paddingLeft: 16,
paddingRight: 0,
backgroundColor: 'rgba(255, 255, 255, 0.32)',
borderWidth: 1,
borderColor: 'rgba(255, 255, 255, 0.65)',
borderRightWidth: 0,
boxShadow: '0 10px 30px rgba(14, 15, 15, 0.08), inset 0 1px 0 rgba(255, 255, 255, 0.7)',
borderTopLeftRadius: 28,
borderTopRightRadius: 0,
borderBottomRightRadius: 0,
borderBottomLeftRadius: 28,
gap: 12,
overflow: 'hidden',
}}>
<BlurView intensity={44} tint="light" style={StyleSheet.absoluteFill} />
<View
style={{
alignItems: 'center',
justifyContent: 'space-between',
paddingRight: 20,
flexDirection: 'row',
}}>
<View style={{ alignItems: 'center', gap: 6, flexShrink: 1, flexDirection: 'row' }}>
<Svg
width="16"
height="16"
viewBox="0 0 24 24"
fill="none"
stroke="#0E0F0F"
strokeWidth="1.8"
strokeLinejoin="round">
<Path d="M12 3l1.8 5.2L19 10l-5.2 1.8L12 17l-1.8-5.2L5 10l5.2-1.8z" />
</Svg>
<Text
style={{
flexShrink: 1,
fontFamily: 'Urbanist_500Medium',
fontSize: 15,
color: '#0E0F0F',
}}>
AI Recommendations
</Text>
</View>
<Pressable accessibilityRole="link" style={{ flexShrink: 1 }}>
<Text style={{ fontFamily: 'Urbanist_500Medium', fontSize: 13, color: '#2B302E' }}>
See all
</Text>
</Pressable>
</View>
<ScrollView
horizontal
showsHorizontalScrollIndicator={false}
contentContainerStyle={{ gap: 10, paddingRight: 16 }}>
<View
style={{
flexGrow: 0,
flexShrink: 0,
flexBasis: 150,
height: 150,
padding: 14,
borderRadius: 22,
backgroundColor: '#F2845C',
}}>
<Text style={{ fontFamily: 'Urbanist_400Regular', fontSize: 16, color: '#1F1410' }}>
<Text style={{ fontFamily: 'Urbanist_600SemiBold', fontSize: 14 }}>Running</Text>
</Text>
<Pressable
accessibilityRole="button"
accessibilityLabel="Open running plan"
style={{
position: 'absolute',
top: 10,
right: 10,
width: 36,
height: 36,
borderRadius: '50%',
borderWidth: 0,
backgroundColor: '#0E0F0F',
alignItems: 'center',
justifyContent: 'center',
flexDirection: 'row',
}}>
<Svg
width="16"
height="16"
viewBox="0 0 24 24"
fill="none"
stroke="#FFFFFF"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round">
<Path d="M7 17 17 7" />
<Path d="M8 7h9v9" />
</Svg>
</Pressable>
<Image
source={{ uri: asset('d25209e912.jpg') }}
contentFit="cover"
contentPosition={{ left: 'center', top: '30%' }}
accessibilityLabel="Runner jogging"
style={{
position: 'absolute',
left: 4,
bottom: 6,
width: 66,
height: 92,
borderRadius: 14,
}}
/>
<Text
style={{
position: 'absolute',
right: 12,
bottom: 14,
width: 66,
fontFamily: 'Urbanist_400Regular',
fontSize: 12,
color: '#1F1410',
lineHeight: 15,
}}>
Do 15 minutes of light jogging
</Text>
</View>
<View
style={{
flexGrow: 0,
flexShrink: 0,
flexBasis: 150,
height: 150,
padding: 14,
borderRadius: 22,
backgroundColor: accent,
}}>
<Text style={{ fontFamily: 'Urbanist_400Regular', fontSize: 16, color: '#1C2108' }}>
<Text style={{ fontFamily: 'Urbanist_600SemiBold', fontSize: 14 }}>Nutrition</Text>
</Text>
<Image
source={{ uri: asset('54c4b96167.jpg') }}
contentFit="cover"
accessibilityLabel="Protein bowl"
style={{
position: 'absolute',
left: 6,
bottom: 8,
width: 70,
height: 70,
borderRadius: '50%',
}}
/>
<Text
style={{
position: 'absolute',
right: 12,
bottom: 14,
width: 62,
fontFamily: 'Urbanist_400Regular',
fontSize: 12,
color: '#1C2108',
lineHeight: 15,
}}>
Try 25g of protein in your breakfast
</Text>
</View>
<View
style={{
flexGrow: 0,
flexShrink: 0,
flexBasis: 150,
height: 150,
padding: 14,
borderRadius: 22,
backgroundColor: '#8FA9F5',
}}>
<Text style={{ fontFamily: 'Urbanist_400Regular', fontSize: 16, color: '#0F1630' }}>
<Text style={{ fontFamily: 'Urbanist_600SemiBold', fontSize: 14 }}>Mental</Text>
</Text>
<Pressable
accessibilityRole="button"
accessibilityLabel="Open breathing exercise"
style={{
position: 'absolute',
top: 10,
right: 10,
width: 36,
height: 36,
borderRadius: '50%',
borderWidth: 0,
backgroundColor: '#0E0F0F',
alignItems: 'center',
justifyContent: 'center',
flexDirection: 'row',
}}>
<Svg
width="16"
height="16"
viewBox="0 0 24 24"
fill="none"
stroke="#FFFFFF"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round">
<Path d="M7 17 17 7" />
<Path d="M8 7h9v9" />
</Svg>
</Pressable>
<Text
style={{
position: 'absolute',
left: 14,
bottom: 14,
right: 14,
fontFamily: 'Urbanist_400Regular',
fontSize: 12,
color: '#0F1630',
lineHeight: 15,
}}>
Do 2 minutes of deep breathing.
</Text>
</View>
</ScrollView>
</View>
<View
style={{
position: 'absolute',
bottom: 28,
left: 20,
right: 20,
height: 72,
padding: 8,
borderRadius: 40,
backgroundColor: '#6F7472',
alignItems: 'center',
justifyContent: 'space-between',
flexDirection: 'row',
}}>
<Pressable
accessibilityRole="link"
accessibilityLabel="Home"
onPress={() => router.navigate('/')}
style={{
width: 56,
height: 56,
borderRadius: '50%',
backgroundColor: '#FFFFFF',
alignItems: 'center',
justifyContent: 'center',
flexDirection: 'row',
}}>
<Svg
width="20"
height="20"
viewBox="0 0 24 24"
fill="none"
stroke="#0E0F0F"
strokeWidth="1.8"
strokeLinejoin="round">
<Path d="M3 10.5 12 3l9 7.5V20a1 1 0 0 1-1 1h-5v-6h-6v6H4a1 1 0 0 1-1-1z" />
</Svg>
</Pressable>
<Pressable
accessibilityRole="link"
accessibilityLabel="Stats"
onPress={() => router.navigate('/globalindex')}
style={{
width: 56,
height: 56,
borderRadius: '50%',
backgroundColor: '#878C8A',
alignItems: 'center',
justifyContent: 'center',
flexDirection: 'row',
}}>
<Svg
width="20"
height="20"
viewBox="0 0 24 24"
fill="none"
stroke="#FFFFFF"
strokeWidth="2"
strokeLinecap="round">
<Path d="M5 20V10M12 20V4M19 20v-7" />
</Svg>
</Pressable>
<Pressable
accessibilityRole="button"
accessibilityLabel="AI coach"
style={{
width: 56,
height: 56,
borderRadius: '50%',
borderWidth: 0,
backgroundColor: accent,
alignItems: 'center',
justifyContent: 'center',
flexDirection: 'row',
}}>
<Image
source={{ uri: asset('4527cf3df2.jpg') }}
contentFit="fill"
style={{ width: 30, height: 30, borderRadius: '50%' }}
/>
</Pressable>
<Pressable
accessibilityRole="button"
accessibilityLabel="Calendar"
style={{
width: 56,
height: 56,
borderRadius: '50%',
borderWidth: 0,
backgroundColor: '#878C8A',
alignItems: 'center',
justifyContent: 'center',
flexDirection: 'row',
}}>
<Svg
width="20"
height="20"
viewBox="0 0 24 24"
fill="none"
stroke="#FFFFFF"
strokeWidth="1.8"
strokeLinecap="round">
<Rect x="3" y="5" width="18" height="16" rx="2" />
<Path d="M16 3v4M8 3v4M3 10h18" />
</Svg>
</Pressable>
<Pressable
accessibilityRole="button"
accessibilityLabel="Profile"
style={{
width: 56,
height: 56,
borderRadius: '50%',
borderWidth: 0,
backgroundColor: '#878C8A',
alignItems: 'center',
justifyContent: 'center',
flexDirection: 'row',
}}>
<Svg
width="20"
height="20"
viewBox="0 0 24 24"
fill="none"
stroke="#FFFFFF"
strokeWidth="1.8"
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/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 x 844 artboard, then scaled to the device width.
const ARTBOARD_WIDTH = 390;
const ARTBOARD_HEIGHT = 844;
type Props = {
background: string;
statusBar: 'light' | 'dark';
children: ReactNode;
};
export function Screen({ background, statusBar, 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(ARTBOARD_HEIGHT * scale, height) }}>
<StatusBar style={statusBar} />
<View
style={{
width: ARTBOARD_WIDTH,
height: ARTBOARD_HEIGHT,
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/ai-health-coach-app-ui-design/assets';
export const asset = (name: string) => `${ASSET_BASE}/${name}`;About this design
The second screen breaks that score into a segmented ring chart with an activity panel, so it suits fitness, wellness and habit products alike.
Cool sage greys with a lime accent, a full-height coach portrait, frosted glass panels and bright category cards.
- Frosted glass recommendations panel
- Segmented ring chart in SVG
- Horizontally scrolling recommendation cards
- Sculpted bottom sheet with an activity histogram
Colour palette
Lime
#D7F26B
Carbon
#0E0F0F
Sage
#D5DAD8
Coral
#F2845C
Periwinkle
#8FA9F5
Seafoam
#9ED9C9
How to use this design
- 01
Pick what to copy
Copy the AI Health Coach 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 this include the AI part?
It includes the interface for it. Recommendations are sample content, so you connect your own model or API to generate them.
Is the AI Health Coach 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.



