// Pets app UI
Pet Food and Cat Care App UI Design in Expo
This pet food app UI blends a small shop with daily care. Owners get a home screen for their cat, a product page for its food, and a guide that shows how much to serve.
- Screens
- 3
- Built with
- Expo SDK 58
- Fonts
- DM Sans, Fraunces
- License
- Free (MIT)

Nourish Paws Pet Food app screens

Pet home
A green hero with a cat portrait, quick actions and a daily overview of meals eaten and water intake.

Food product
A pack shot with share and save actions, a recipe selector, three pack sizes, the price and rating, and four product benefits.

Feeding guide
A photo of a filled bowl above a daily guide by cat weight and a grid of happy customer photos.
Copy the Nourish Paws Pet Food 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
# Nourish Paws Pet Food: design artifact
This pet food app UI blends a small shop with daily care. Owners get a home screen for their cat, a product page for its food, and a guide that shows how much to serve.
- Type: Pets mobile app UI
- Screens: 3
- Artboard: 390 × 844
- Target: Expo SDK 58, Expo Router, TypeScript, expo-image, react-native-svg
- Live page: https://developermill.com/app-designs/pet-food-app-ui-design
## Design direction
Soft oat backgrounds and a deep olive green, with a serif headline over warm pet photography for a calm, premium feel.
- Hero with a large pet portrait and mood chip
- Quick actions for pets, bowls, shop and logs
- Pack size picker on the product page
- Weight-based feeding guide with customer photos
## Foundations
### Colour
| Token | Hex |
| --- | --- |
| Olive | `#4A5A36` |
| Dark moss | `#2F3A22` |
| Oat | `#F1ECDF` |
| Paper | `#FBF9F4` |
| Ink | `#22261D` |
| Sage grey | `#555A4C` |
### Typography
- Typefaces: DM Sans, Fraunces
- Common sizes (px): 11, 11.5, 12, 13, 14, 15, 24, 26
### Shape and depth
- Common corner radii (px): 12, 14, 18, 22, 24, 28
## Screens
### 1. Pet home
A green hero with a cat portrait, quick actions and a daily overview of meals eaten and water intake.
Preview: https://developermill.com/app-designs/pet-food-app-ui-design/home.webp
Copy on this screen:
- Happy Cat,
- Healthy Life
- Premium nutrition
- for your feline friend.
- Whiskers is Happy
- Quick Actions
- Add Pet
- Scan Bowl
- Shop Now
- Feeding Log
- Today's Overview
- Meals Eaten
- 2 / 2
- Great job!
- Water Intake
- 180 ml
- Keep it up!
- Home
- Shop
- Health
- Profile
### 2. Food product
A pack shot with share and save actions, a recipe selector, three pack sizes, the price and rating, and four product benefits.
Preview: https://developermill.com/app-designs/pet-food-app-ui-design/product.webp
Copy on this screen:
- Chicken & Salmon Recipe
- $24.99
- (128)
- Grain Free
- High Protein
- Natural
- Ingredients
- Vet
- Approved
- Add to Cart
### 3. Feeding guide
A photo of a filled bowl above a daily guide by cat weight and a grid of happy customer photos.
Preview: https://developermill.com/app-designs/pet-food-app-ui-design/feeding.webp
Copy on this screen:
- Feeding Guide
- Daily Feeding Guide
- Happy Customers
- Home
- Shop
- Health
- Profile
## Full source
The original artboards as HTML. Image URLs point at the hosted demo assets.
### Pet home (Main)
```html
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Nourish Paws Home</title>
<script src="./support.js"></script>
</head>
<body>
<x-dc>
<helmet>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=DM+Sans:wght@400;500;600;700&family=Fraunces:opsz,wght@9..144,400;9..144,500&display=swap">
<style>
body{margin:0}
a{color:#4A5A36;text-decoration:none}a:hover{color:#2F3A22}
button{font-family:inherit;cursor:pointer}
</style>
</helmet>
<div style="width: 390px; height: 844px; position: relative; overflow: hidden; background: #FBF9F4; font-family: 'DM Sans', system-ui, sans-serif; color: #22261D">
<div style="position: absolute; top: 0; left: 0; width: 390px; height: 500px; background: #525B42">
<img src="https://developermill.com/app-designs/pet-food-app-ui-design/assets/fa8a2f1b9b.jpg" alt="Tabby cat in an olive collar looking up" style="position: absolute; right: -20px; bottom: 0; width: 320px; height: 430px; object-fit: cover; object-position: center top">
<div style="position: absolute; top: 52px; left: 20px; right: 20px; display: flex; justify-content: space-between; align-items: center">
<img src="https://developermill.com/app-designs/pet-food-app-ui-design/assets/88e3f4c2e5.jpg" alt="Your profile" style="width: 44px; height: 44px; border-radius: 50%; box-sizing: border-box; border: 2px solid #FFFFFF; object-fit: cover">
<button aria-label="Notifications" style="width: 44px; height: 44px; border-radius: 50%; border: none; background: #FFFFFF; display: flex; align-items: center; justify-content: center; padding: 0">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="#22261D" 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>
</button>
</div>
<div style="position: absolute; top: 122px; left: 24px; width: 220px">
<h1 style="margin: 0; font-family: 'Fraunces', Georgia, serif; font-weight: 400; font-size: 31px; line-height: 1.15; color: #FFFFFF; letter-spacing: -0.3px">Happy Cat,<br>Healthy Life</h1>
<svg style="position: absolute; left: 186px; top: 44px" width="26" height="22" viewBox="0 0 26 22" fill="none" stroke="#FFFFFF" stroke-width="1.4" stroke-linecap="round" aria-hidden="true"><path d="M13 20C6 15 2 11 2 7a5 5 0 0 1 11-2 5 5 0 0 1 11 2c0 4-4 8-11 13z"></path></svg>
<p style="margin: 12px 0 0; font-size: 13px; line-height: 1.45; color: rgba(255,255,255,0.88)">Premium nutrition<br>for your feline friend.</p>
</div>
<div style="position: absolute; top: 408px; left: 20px; display: flex; align-items: center; gap: 8px; padding: 9px 16px 9px 12px; background: #F4EFE1; border-radius: 999px">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#4A5A36" stroke-width="1.8" stroke-linecap="round" aria-hidden="true"><circle cx="12" cy="12" r="9.5"></circle><path d="M8 14.5s1.5 2 4 2 4-2 4-2"></path><path d="M9 9.5h.01M15 9.5h.01"></path></svg>
<span style="font-size: 13px; font-weight: 500; color: #2D3325">Whiskers is Happy</span>
</div>
</div>
<div style="position: absolute; top: 466px; left: 0; width: 390px; bottom: 0; background: #FBF9F4; border-radius: 28px 28px 0 0; box-sizing: border-box; padding: 22px 20px 0">
<h2 style="margin: 0; font-size: 15px; font-weight: 600">Quick Actions</h2>
<div style="display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 8px; margin-top: 12px">
<button style="display: flex; flex-direction: column; align-items: center; gap: 6px; border: none; background: none; padding: 0; color: #4A4F42; font-size: 11.5px">
<span style="width: 54px; height: 54px; border-radius: 50%; background: #F1ECDF; display: flex; align-items: center; justify-content: center"><svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="#22261D" stroke-width="1.6" stroke-linecap="round" aria-hidden="true"><path d="M12 5v14M5 12h14"></path></svg></span>Add Pet</button>
<button style="display: flex; flex-direction: column; align-items: center; gap: 6px; border: none; background: none; padding: 0; color: #4A4F42; font-size: 11.5px">
<span style="width: 54px; height: 54px; border-radius: 50%; background: #F1ECDF; display: flex; align-items: center; justify-content: center"><svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="#22261D" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 8h3l2-3h6l2 3h3v11H4z"></path><circle cx="12" cy="13" r="3.5"></circle></svg></span>Scan Bowl</button>
<a href="Product.dc.html" style="display: flex; flex-direction: column; align-items: center; gap: 6px; color: #4A4F42; font-size: 11.5px">
<span style="width: 54px; height: 54px; border-radius: 50%; background: #F1ECDF; display: flex; align-items: center; justify-content: center"><svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="#22261D" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 4h2.5l2.2 10.5h10.6L20.5 7H7"></path><circle cx="9" cy="19" r="1.4"></circle><circle cx="17" cy="19" r="1.4"></circle></svg></span>Shop Now</a>
<a href="Feeding.dc.html" style="display: flex; flex-direction: column; align-items: center; gap: 6px; color: #4A4F42; font-size: 11.5px">
<span style="width: 54px; height: 54px; border-radius: 50%; background: #F1ECDF; display: flex; align-items: center; justify-content: center"><svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="#22261D" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><ellipse cx="12" cy="9" rx="8" ry="2.5"></ellipse><path d="M4 9l1.5 7c.3 1.5 3 2.5 6.5 2.5s6.2-1 6.5-2.5L20 9"></path></svg></span>Feeding Log</a>
</div>
<h2 style="margin: 22px 0 0; font-size: 15px; font-weight: 600">Today's Overview</h2>
<div style="display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; margin-top: 12px">
<div style="background: #F3EFE4; border-radius: 18px; padding: 14px 14px 12px; display: flex; flex-direction: column; gap: 6px">
<span style="font-size: 12px; color: #555A4C">Meals Eaten</span>
<div style="display: flex; align-items: center; justify-content: space-between">
<span style="font-size: 24px; font-weight: 500">2 / 2</span>
<svg width="34" height="22" viewBox="0 0 34 22" aria-hidden="true"><ellipse cx="17" cy="6" rx="15" ry="4" fill="#3E4A2E"></ellipse><path d="M2 6l3 11c.6 2 5.6 3.5 12 3.5s11.4-1.5 12-3.5L32 6z" fill="{{accent}}"></path></svg>
</div>
<span style="font-size: 11.5px; color: #555A4C">Great job!</span>
</div>
<div style="background: #F3EFE4; border-radius: 18px; padding: 14px 14px 12px; display: flex; flex-direction: column; gap: 6px">
<span style="font-size: 12px; color: #555A4C">Water Intake</span>
<div style="display: flex; align-items: center; justify-content: space-between">
<span style="font-size: 24px; font-weight: 500">180 ml</span>
<svg width="18" height="24" viewBox="0 0 18 24" aria-hidden="true"><path d="M9 1C9 1 1 11 1 15.5a8 8 0 0 0 16 0C17 11 9 1 9 1z" fill="#5B93CF"></path></svg>
</div>
<span style="font-size: 11.5px; color: #555A4C">Keep it up!</span>
</div>
</div>
</div>
<nav aria-label="Main" style="position: absolute; left: 0; bottom: 0; width: 390px; height: 78px; box-sizing: border-box; padding: 10px 16px 0; background: #FFFFFF; border-top: 1px solid #ECE8DD; display: grid; grid-template-columns: repeat(4, minmax(0, 1fr))">
<a href="Main.dc.html" style="display: flex; flex-direction: column; align-items: center; gap: 4px; font-size: 11px; font-weight: 600; color: {{accent}}">
<svg width="22" height="22" viewBox="0 0 24 24" fill="{{accent}}" aria-hidden="true"><path d="M3 10.5L12 3l9 7.5V20a1 1 0 0 1-1 1h-5v-6h-6v6H4a1 1 0 0 1-1-1z"></path></svg>Home</a>
<a href="Product.dc.html" style="display: flex; flex-direction: column; align-items: center; gap: 4px; font-size: 11px; color: #555A4C">
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linejoin="round" aria-hidden="true"><path d="M5 8h14l-1 13H6z"></path><path d="M9 8V6a3 3 0 0 1 6 0v2"></path></svg>Shop</a>
<a href="Feeding.dc.html" style="display: flex; flex-direction: column; align-items: center; gap: 4px; font-size: 11px; color: #555A4C">
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linejoin="round" aria-hidden="true"><path d="M12 20C5 15 2.5 11.5 2.5 8a4.7 4.7 0 0 1 9.5-1.5A4.7 4.7 0 0 1 21.5 8c0 3.5-2.5 7-9.5 12z"></path></svg>Health</a>
<a href="Main.dc.html" style="display: flex; flex-direction: column; align-items: center; gap: 4px; font-size: 11px; color: #555A4C">
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" aria-hidden="true"><circle cx="12" cy="8" r="4"></circle><path d="M4 21c1-4 4-6 8-6s7 2 8 6"></path></svg>Profile</a>
</nav>
</div>
</x-dc>
<script type="text/x-dc" data-dc-script data-props='{"accent":{"editor":"color","default":"#4A5A36","options":["#4A5A36","#3E5B4F","#6B5A3A"]},"$preview":{"width":390,"height":844}}'>
class Component extends DCLogic {
renderVals() {
return { accent: this.props.accent ?? '#4A5A36' };
}
}
</script>
</body>
</html>
```
### Food product (Product)
```html
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Nourish Paws Product</title>
<script src="./support.js"></script>
</head>
<body>
<x-dc>
<helmet>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=DM+Sans:wght@400;500;600;700&family=Fraunces:opsz,wght@9..144,400;9..144,500&display=swap">
<style>
body{margin:0}
a{color:#4A5A36;text-decoration:none}a:hover{color:#2F3A22}
button{font-family:inherit;cursor:pointer}
</style>
</helmet>
<div style="width: 390px; height: 844px; position: relative; overflow: hidden; background: #F8F4EE; font-family: 'DM Sans', system-ui, sans-serif; color: #22261D">
<div style="position: absolute; top: 52px; left: 20px; right: 20px; display: flex; justify-content: space-between; align-items: center; z-index: 2">
<a href="Main.dc.html" aria-label="Back" style="width: 44px; height: 44px; 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="#22261D" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M19 12H5M11 6l-6 6 6 6"></path></svg></a>
<button aria-label="More options" style="width: 44px; height: 44px; border-radius: 50%; border: none; background: #FFFFFF; display: flex; align-items: center; justify-content: center; padding: 0">
<svg width="20" height="20" viewBox="0 0 24 24" fill="#22261D" aria-hidden="true"><circle cx="5" cy="12" r="1.8"></circle><circle cx="12" cy="12" r="1.8"></circle><circle cx="19" cy="12" r="1.8"></circle></svg></button>
</div>
<img src="https://developermill.com/app-designs/pet-food-app-ui-design/assets/104d225b22.jpg" alt="Nourish Paws Chicken & Salmon cat food bag" style="position: absolute; top: 60px; left: 70px; width: 250px; height: 334px; object-fit: contain">
<div style="position: absolute; top: 386px; right: 44px; display: flex; gap: 10px">
<button aria-label="Find in store" style="width: 44px; height: 44px; border-radius: 50%; border: none; background: #FFFFFF; display: flex; align-items: center; justify-content: center; padding: 0">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="#22261D" stroke-width="1.7" stroke-linejoin="round" aria-hidden="true"><path d="M12 21s7-6.5 7-12a7 7 0 0 0-14 0c0 5.5 7 12 7 12z"></path><circle cx="12" cy="9" r="2.5"></circle></svg></button>
<button aria-label="Save to favourites" style="width: 44px; height: 44px; border-radius: 50%; border: none; background: {{accent}}; display: flex; align-items: center; justify-content: center; padding: 0">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="#FFFFFF" stroke-width="1.7" stroke-linejoin="round" aria-hidden="true"><path d="M12 20C5 15 2.5 11.5 2.5 8a4.7 4.7 0 0 1 9.5-1.5A4.7 4.7 0 0 1 21.5 8c0 3.5-2.5 7-9.5 12z"></path></svg></button>
<button aria-label="Add to cart" style="width: 44px; height: 44px; border-radius: 50%; border: none; background: #FFFFFF; display: flex; align-items: center; justify-content: center; padding: 0">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="#22261D" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 4h2.5l2.2 10.5h10.6L20.5 7H7"></path><circle cx="9" cy="19" r="1.4"></circle><circle cx="17" cy="19" r="1.4"></circle><path d="M14 9.5v-3M12.5 8h3"></path></svg></button>
</div>
<div style="position: absolute; top: 444px; left: 0; width: 390px; bottom: 0; background: #FBF9F4; border-radius: 28px 28px 0 0; box-sizing: border-box; padding: 20px 20px 0; display: flex; flex-direction: column; gap: 16px">
<button style="height: 48px; border-radius: 24px; border: none; background: {{accent}}; color: #FFFFFF; font-size: 14px; font-weight: 500; display: flex; align-items: center; justify-content: center; gap: 10px">Chicken & Salmon Recipe
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#FFFFFF" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 9l6 6 6-6"></path></svg></button>
<div role="radiogroup" aria-label="Bag size" style="display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 10px">
<sc-for list="{{sizes}}" as="s" hint-placeholder-count="3">
<button role="radio" aria-checked="{{s.checked}}" onClick="{{s.pick}}" style="{{s.style}}">{{s.label}}</button>
</sc-for>
</div>
<div style="display: flex; align-items: center; justify-content: space-between">
<span style="font-size: 26px; font-weight: 700; letter-spacing: -0.3px">$24.99</span>
<span style="display: flex; align-items: center; gap: 6px; font-size: 12.5px; color: #555A4C">
<svg role="img" aria-label="5 out of 5 stars" width="76" height="13" viewBox="0 0 76 13"><g fill="#3A3F33"><path d="M6.5 0l1.9 4.1 4.4.5-3.3 3 1 4.4-4-2.3-4 2.3 1-4.4-3.3-3 4.4-.5z"></path><path transform="translate(15.7)" d="M6.5 0l1.9 4.1 4.4.5-3.3 3 1 4.4-4-2.3-4 2.3 1-4.4-3.3-3 4.4-.5z"></path><path transform="translate(31.4)" d="M6.5 0l1.9 4.1 4.4.5-3.3 3 1 4.4-4-2.3-4 2.3 1-4.4-3.3-3 4.4-.5z"></path><path transform="translate(47.1)" d="M6.5 0l1.9 4.1 4.4.5-3.3 3 1 4.4-4-2.3-4 2.3 1-4.4-3.3-3 4.4-.5z"></path><path transform="translate(62.8)" d="M6.5 0l1.9 4.1 4.4.5-3.3 3 1 4.4-4-2.3-4 2.3 1-4.4-3.3-3 4.4-.5z"></path></g></svg>(128)</span>
</div>
<div style="display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 8px">
<div style="display: flex; flex-direction: column; align-items: center; gap: 6px; text-align: center; font-size: 11px; line-height: 1.3; color: #4A4F42">
<span style="width: 44px; height: 44px; border-radius: 50%; background: #F1ECDF; display: flex; align-items: center; justify-content: center"><svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="#22261D" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M5 20h14M7 20V10l5-5 5 5v10"></path><path d="M10 14l4 4M14 14l-4 4"></path></svg></span>Grain Free</div>
<div style="display: flex; flex-direction: column; align-items: center; gap: 6px; text-align: center; font-size: 11px; line-height: 1.3; color: #4A4F42">
<span style="width: 44px; height: 44px; border-radius: 50%; background: #F1ECDF; display: flex; align-items: center; justify-content: center"><svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="#22261D" stroke-width="1.5" aria-hidden="true"><circle cx="8" cy="10" r="4"></circle><circle cx="16" cy="10" r="4"></circle><circle cx="12" cy="16" r="4"></circle></svg></span>High Protein</div>
<div style="display: flex; flex-direction: column; align-items: center; gap: 6px; text-align: center; font-size: 11px; line-height: 1.3; color: #4A4F42">
<span style="width: 44px; height: 44px; border-radius: 50%; background: #F1ECDF; display: flex; align-items: center; justify-content: center"><svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="#22261D" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 21v-9"></path><path d="M12 12c0-4 3-7 7-7 0 4-3 7-7 7z"></path><path d="M12 15c0-3-2.5-5.5-6-5.5 0 3 2.5 5.5 6 5.5z"></path></svg></span>Natural<br>Ingredients</div>
<div style="display: flex; flex-direction: column; align-items: center; gap: 6px; text-align: center; font-size: 11px; line-height: 1.3; color: #4A4F42">
<span style="width: 44px; height: 44px; border-radius: 50%; background: #F1ECDF; display: flex; align-items: center; justify-content: center"><svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="#22261D" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 3l7 3v5c0 5-3 8-7 10-4-2-7-5-7-10V6z"></path><path d="M9 12l2 2 4-4"></path></svg></span>Vet<br>Approved</div>
</div>
<button style="height: 52px; border-radius: 26px; border: none; background: {{accent}}; color: #FFFFFF; font-size: 15px; font-weight: 600; margin-top: 4px">Add to Cart</button>
</div>
</div>
</x-dc>
<script type="text/x-dc" data-dc-script data-props='{"accent":{"editor":"color","default":"#4A5A36","options":["#4A5A36","#3E5B4F","#6B5A3A"]},"$preview":{"width":390,"height":844}}'>
class Component extends DCLogic {
constructor(props) {
super(props);
this.state = { size: '1.5 kg' };
}
renderVals() {
const accent = this.props.accent ?? '#4A5A36';
const base = 'height: 44px; border-radius: 22px; font-size: 14px; font-weight: 500; color: #22261D; ';
const sizes = ['1.5 kg', '3 kg', '7 kg'].map((label) => {
const on = this.state.size === label;
return {
label,
checked: on ? 'true' : 'false',
pick: () => this.setState({ size: label }),
style: base + (on ? 'background: #FFFFFF; border: 1.5px solid ' + accent + ';' : 'background: #F1ECDF; border: 1.5px solid transparent;')
};
});
return { accent, sizes };
}
}
</script>
</body>
</html>
```
### Feeding guide (Feeding)
```html
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Nourish Paws Feeding Guide</title>
<script src="./support.js"></script>
</head>
<body>
<x-dc>
<helmet>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=DM+Sans:wght@400;500;600;700&family=Fraunces:opsz,wght@9..144,400;9..144,500&display=swap">
<style>
body{margin:0}
a{color:#4A5A36;text-decoration:none}a:hover{color:#2F3A22}
button{font-family:inherit;cursor:pointer}
</style>
</helmet>
<div style="width: 390px; height: 844px; position: relative; overflow: hidden; background: #FBF9F4; font-family: 'DM Sans', system-ui, sans-serif; color: #22261D">
<img src="https://developermill.com/app-designs/pet-food-app-ui-design/assets/f37373270f.jpg" alt="Olive Nourish Paws bowl full of kibble on a wooden table" style="position: absolute; top: 0; left: 0; width: 390px; height: 400px; object-fit: cover; object-position: center 60%">
<div style="position: absolute; top: 52px; left: 20px; right: 20px; display: flex; justify-content: space-between; align-items: center">
<a href="Product.dc.html" aria-label="Back" style="width: 44px; height: 44px; 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="#22261D" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M19 12H5M11 6l-6 6 6 6"></path></svg></a>
<button aria-label="More options" style="width: 44px; height: 44px; border-radius: 50%; border: none; background: #FFFFFF; display: flex; align-items: center; justify-content: center; padding: 0">
<svg width="20" height="20" viewBox="0 0 24 24" fill="#22261D" aria-hidden="true"><circle cx="5" cy="12" r="1.8"></circle><circle cx="12" cy="12" r="1.8"></circle><circle cx="19" cy="12" r="1.8"></circle></svg></button>
</div>
<div style="position: absolute; top: 322px; right: 20px; display: flex; gap: 10px">
<button aria-label="Play sound" style="width: 44px; height: 44px; border-radius: 50%; border: none; background: #FFFFFF; display: flex; align-items: center; justify-content: center; padding: 0">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="#22261D" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M9 18V5l11-2v13"></path><circle cx="6.5" cy="18" r="2.5"></circle><circle cx="17.5" cy="16" r="2.5"></circle></svg></button>
<button aria-label="Take photo" style="width: 44px; height: 44px; border-radius: 50%; border: none; background: #FFFFFF; display: flex; align-items: center; justify-content: center; padding: 0">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="#22261D" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 8h3l2-3h6l2 3h3v11H4z"></path><circle cx="12" cy="13" r="3.5"></circle></svg></button>
</div>
<div style="position: absolute; top: 378px; left: 0; width: 390px; bottom: 0; background: #FBF9F4; border-radius: 28px 28px 0 0; box-sizing: border-box; padding: 18px 20px 0; display: flex; flex-direction: column">
<button style="height: 44px; border-radius: 22px; border: none; background: {{accent}}; color: #FFFFFF; font-size: 14px; font-weight: 500; display: flex; align-items: center; justify-content: center; gap: 10px">Feeding Guide
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#FFFFFF" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 9l6 6 6-6"></path></svg></button>
<h2 style="margin: 18px 0 0; font-size: 15px; font-weight: 600">Daily Feeding Guide</h2>
<div style="display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 8px; margin-top: 10px">
<sc-for list="{{guide}}" as="g" hint-placeholder-count="4">
<div style="background: #F3EFE4; border-radius: 14px; padding: 10px 4px; display: flex; flex-direction: column; align-items: center; gap: 4px">
<svg width="26" height="26" viewBox="0 0 24 24" aria-hidden="true"><path d="M7 22c-1-3 0-7 2-9-1-1-1.5-3-1-5L9 3l2.2 3h1.6L15 3l1 5c.5 2 0 4-1 5 2 2 3 6 2 9z" fill="{{accent}}"></path><path d="M17 21.5c2.5 0 4-1.5 4-4" fill="none" stroke="{{accent}}" stroke-width="1.6" stroke-linecap="round"></path></svg>
<span style="font-size: 11.5px; font-weight: 500">{{g.weight}}</span>
<span style="font-size: 11px; color: #555A4C">{{g.amount}}</span>
</div>
</sc-for>
</div>
<h2 style="margin: 18px 0 0; font-size: 15px; font-weight: 600">Happy Customers</h2>
<div style="display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; margin-top: 10px">
<img src="https://developermill.com/app-designs/pet-food-app-ui-design/assets/9b90c504e7.jpg" alt="Tabby eating from a Nourish Paws bowl" style="width: 100%; height: 96px; border-radius: 12px; object-fit: cover; display: block">
<img src="https://developermill.com/app-designs/pet-food-app-ui-design/assets/512fbe5d17.jpg" alt="Tabby wearing an olive bandana" style="width: 100%; height: 96px; border-radius: 12px; object-fit: cover; object-position: center 30%; display: block">
<img src="https://developermill.com/app-designs/pet-food-app-ui-design/assets/d386eb5742.jpg" alt="Close-up of a tabby looking up" style="width: 100%; height: 96px; border-radius: 12px; object-fit: cover; display: block">
<img src="https://developermill.com/app-designs/pet-food-app-ui-design/assets/5ae8680073.jpg" alt="Tabby relaxing on a blanket" style="width: 100%; height: 96px; border-radius: 12px; object-fit: cover; display: block">
</div>
</div>
<nav aria-label="Main" style="position: absolute; left: 0; bottom: 0; width: 390px; height: 78px; box-sizing: border-box; padding: 10px 16px 0; background: #FFFFFF; border-top: 1px solid #ECE8DD; display: grid; grid-template-columns: repeat(4, minmax(0, 1fr))">
<a href="Main.dc.html" style="display: flex; flex-direction: column; align-items: center; gap: 4px; font-size: 11px; font-weight: 600; color: {{accent}}">
<svg width="22" height="22" viewBox="0 0 24 24" fill="{{accent}}" aria-hidden="true"><path d="M3 10.5L12 3l9 7.5V20a1 1 0 0 1-1 1h-5v-6h-6v6H4a1 1 0 0 1-1-1z"></path></svg>Home</a>
<a href="Product.dc.html" style="display: flex; flex-direction: column; align-items: center; gap: 4px; font-size: 11px; color: #555A4C">
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linejoin="round" aria-hidden="true"><path d="M5 8h14l-1 13H6z"></path><path d="M9 8V6a3 3 0 0 1 6 0v2"></path></svg>Shop</a>
<a href="Feeding.dc.html" style="display: flex; flex-direction: column; align-items: center; gap: 4px; font-size: 11px; color: #555A4C">
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linejoin="round" aria-hidden="true"><path d="M12 20C5 15 2.5 11.5 2.5 8a4.7 4.7 0 0 1 9.5-1.5A4.7 4.7 0 0 1 21.5 8c0 3.5-2.5 7-9.5 12z"></path></svg>Health</a>
<a href="Main.dc.html" style="display: flex; flex-direction: column; align-items: center; gap: 4px; font-size: 11px; color: #555A4C">
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" aria-hidden="true"><circle cx="12" cy="8" r="4"></circle><path d="M4 21c1-4 4-6 8-6s7 2 8 6"></path></svg>Profile</a>
</nav>
</div>
</x-dc>
<script type="text/x-dc" data-dc-script data-props='{"accent":{"editor":"color","default":"#4A5A36","options":["#4A5A36","#3E5B4F","#6B5A3A"]},"$preview":{"width":390,"height":844}}'>
class Component extends DCLogic {
renderVals() {
return {
accent: this.props.accent ?? '#4A5A36',
guide: [
{ weight: '2–3 kg', amount: '30–40 g' },
{ weight: '3–5 kg', amount: '40–60 g' },
{ weight: '5–7 kg', amount: '60–80 g' },
{ weight: '7+ kg', amount: '80+ g' }
]
};
}
}
</script>
</body>
</html>
```SKILL.md
Save as .claude/skills/pet-food-app-ui/SKILL.md
--- name: pet-food-app-ui description: Build the Nourish Paws Pet Food mobile app UI in Expo React Native. Use when asked to create a pet food app UI or a new screen in this visual style. --- # Nourish Paws Pet Food app UI Build this pets app UI in an Expo Router project with TypeScript. Reference: https://developermill.com/app-designs/pet-food-app-ui-design ## Visual direction Soft oat backgrounds and a deep olive green, with a serif headline over warm pet photography for a calm, premium feel. Keep these signature details: - Hero with a large pet portrait and mood chip - Quick actions for pets, bowls, shop and logs - Pack size picker on the product page - Weight-based feeding guide with customer photos ## Set up ```bash npx expo install expo-image react-native-svg @expo-google-fonts/dm-sans @expo-google-fonts/fraunces expo-font ``` ## Tokens | Name | Hex | | --- | --- | | Olive | `#4A5A36` | | Dark moss | `#2F3A22` | | Oat | `#F1ECDF` | | Paper | `#FBF9F4` | | Ink | `#22261D` | | Sage grey | `#555A4C` | - Typefaces: DM Sans, Fraunces - Text sizes (px): 11, 11.5, 12, 13, 14, 15, 24, 26 - Corner radii (px): 12, 14, 18, 22, 24, 28 Define these once and reuse them. Do not add new colours. ## Screens 1. **Pet home.** A green hero with a cat portrait, quick actions and a daily overview of meals eaten and water intake. 2. **Food product.** A pack shot with share and save actions, a recipe selector, three pack sizes, the price and rating, and four product benefits. 3. **Feeding guide.** A photo of a filled bowl above a daily guide by cat weight and a grid of happy customer photos. ## 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 pet-food-app --template default cd pet-food-app rm -rf src npx expo install expo-image react-native-svg @expo-google-fonts/dm-sans @expo-google-fonts/fraunces expo-font
src/app/_layout.tsx
import {
DMSans_400Regular,
DMSans_500Medium,
DMSans_600SemiBold,
DMSans_700Bold,
} from '@expo-google-fonts/dm-sans';
import { Fraunces_400Regular } from '@expo-google-fonts/fraunces';
import { useFonts } from 'expo-font';
import { Stack } from 'expo-router';
export default function RootLayout() {
const [fontsLoaded] = useFonts({
DMSans_400Regular,
DMSans_500Medium,
DMSans_600SemiBold,
DMSans_700Bold,
Fraunces_400Regular,
});
if (!fontsLoaded) return null;
return <Stack screenOptions={{ headerShown: false }} />;
}src/app/index.tsx
import { Image } from 'expo-image';
import { router } from 'expo-router';
import { Pressable, Text, View } from 'react-native';
import Svg, { Circle, Ellipse, Path } from 'react-native-svg';
import { Screen } from '@/components/screen';
import { asset } from '@/lib/assets';
const accent = '#4A5A36';
export default function MainScreen() {
return (
<Screen background="#FBF9F4" statusBar="dark">
<View style={{ flex: 1 }}>
<View
style={{
position: 'absolute',
top: 0,
left: 0,
width: 390,
height: 500,
backgroundColor: '#525B42',
}}>
<Image
source={{ uri: asset('fa8a2f1b9b.jpg') }}
contentFit="cover"
contentPosition="top center"
accessibilityLabel="Tabby cat in an olive collar looking up"
style={{ position: 'absolute', right: -20, bottom: 0, width: 320, height: 430 }}
/>
<View
style={{
position: 'absolute',
top: 52,
left: 20,
right: 20,
justifyContent: 'space-between',
alignItems: 'center',
flexDirection: 'row',
}}>
<Image
source={{ uri: asset('88e3f4c2e5.jpg') }}
contentFit="cover"
accessibilityLabel="Your profile"
style={{
width: 44,
height: 44,
borderRadius: '50%',
borderWidth: 2,
borderColor: '#FFFFFF',
}}
/>
<Pressable
accessibilityRole="button"
accessibilityLabel="Notifications"
style={{
width: 44,
height: 44,
borderRadius: '50%',
borderWidth: 0,
backgroundColor: '#FFFFFF',
alignItems: 'center',
justifyContent: 'center',
padding: 0,
flexDirection: 'row',
}}>
<Svg
width="20"
height="20"
viewBox="0 0 24 24"
fill="none"
stroke="#22261D"
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>
</Pressable>
</View>
<View style={{ position: 'absolute', top: 122, left: 24, width: 220 }}>
<Text
style={{
margin: 0,
fontFamily: 'Fraunces_400Regular',
fontSize: 31,
color: '#FFFFFF',
letterSpacing: -0.3,
lineHeight: 35.65,
}}>
Happy Cat,{'\n'}Healthy Life
</Text>
<Svg
width="26"
height="22"
viewBox="0 0 26 22"
fill="none"
stroke="#FFFFFF"
strokeWidth="1.4"
strokeLinecap="round"
style={{ position: 'absolute', left: 186, top: 44, zIndex: 1 }}>
<Path d="M13 20C6 15 2 11 2 7a5 5 0 0 1 11-2 5 5 0 0 1 11 2c0 4-4 8-11 13z" />
</Svg>
<Text
style={{
marginTop: 12,
marginBottom: 0,
marginHorizontal: 0,
fontFamily: 'DMSans_400Regular',
fontSize: 13,
color: 'rgba(255,255,255,0.88)',
lineHeight: 18.85,
}}>
Premium nutrition{'\n'}for your feline friend.
</Text>
</View>
<View
style={{
position: 'absolute',
top: 408,
left: 20,
alignItems: 'center',
gap: 8,
paddingVertical: 9,
paddingLeft: 12,
paddingRight: 16,
backgroundColor: '#F4EFE1',
borderRadius: 999,
flexDirection: 'row',
}}>
<Svg
width="18"
height="18"
viewBox="0 0 24 24"
fill="none"
stroke="#4A5A36"
strokeWidth="1.8"
strokeLinecap="round">
<Circle cx="12" cy="12" r="9.5" />
<Path d="M8 14.5s1.5 2 4 2 4-2 4-2" />
<Path d="M9 9.5h.01M15 9.5h.01" />
</Svg>
<Text
style={{
flexShrink: 1,
fontFamily: 'DMSans_500Medium',
fontSize: 13,
color: '#2D3325',
}}>
Whiskers is Happy
</Text>
</View>
</View>
<View
style={{
position: 'absolute',
top: 466,
left: 0,
width: 390,
bottom: 0,
backgroundColor: '#FBF9F4',
borderTopLeftRadius: 28,
borderTopRightRadius: 28,
borderBottomRightRadius: 0,
borderBottomLeftRadius: 0,
paddingTop: 22,
paddingBottom: 0,
paddingHorizontal: 20,
}}>
<Text
style={{ margin: 0, fontFamily: 'DMSans_600SemiBold', fontSize: 15, color: '#22261D' }}>
Quick Actions
</Text>
<View style={{ marginTop: 12, flexDirection: 'row', gap: 8 }}>
<Pressable
accessibilityRole="button"
style={{ alignItems: 'center', gap: 6, borderWidth: 0, padding: 0, flex: 1 }}>
<View
style={{
width: 54,
height: 54,
borderRadius: '50%',
backgroundColor: '#F1ECDF',
alignItems: 'center',
justifyContent: 'center',
flexDirection: 'row',
}}>
<Svg
width="22"
height="22"
viewBox="0 0 24 24"
fill="none"
stroke="#22261D"
strokeWidth="1.6"
strokeLinecap="round">
<Path d="M12 5v14M5 12h14" />
</Svg>
</View>
<Text style={{ fontFamily: 'DMSans_400Regular', fontSize: 11.5, color: '#4A4F42' }}>
Add Pet
</Text>
</Pressable>
<Pressable
accessibilityRole="button"
style={{ alignItems: 'center', gap: 6, borderWidth: 0, padding: 0, flex: 1 }}>
<View
style={{
width: 54,
height: 54,
borderRadius: '50%',
backgroundColor: '#F1ECDF',
alignItems: 'center',
justifyContent: 'center',
flexDirection: 'row',
}}>
<Svg
width="22"
height="22"
viewBox="0 0 24 24"
fill="none"
stroke="#22261D"
strokeWidth="1.6"
strokeLinecap="round"
strokeLinejoin="round">
<Path d="M4 8h3l2-3h6l2 3h3v11H4z" />
<Circle cx="12" cy="13" r="3.5" />
</Svg>
</View>
<Text style={{ fontFamily: 'DMSans_400Regular', fontSize: 11.5, color: '#4A4F42' }}>
Scan Bowl
</Text>
</Pressable>
<Pressable
accessibilityRole="link"
onPress={() => router.navigate('/product')}
style={{ alignItems: 'center', gap: 6, flex: 1 }}>
<View
style={{
width: 54,
height: 54,
borderRadius: '50%',
backgroundColor: '#F1ECDF',
alignItems: 'center',
justifyContent: 'center',
flexDirection: 'row',
}}>
<Svg
width="22"
height="22"
viewBox="0 0 24 24"
fill="none"
stroke="#22261D"
strokeWidth="1.6"
strokeLinecap="round"
strokeLinejoin="round">
<Path d="M3 4h2.5l2.2 10.5h10.6L20.5 7H7" />
<Circle cx="9" cy="19" r="1.4" />
<Circle cx="17" cy="19" r="1.4" />
</Svg>
</View>
<Text style={{ fontFamily: 'DMSans_400Regular', fontSize: 11.5, color: '#4A4F42' }}>
Shop Now
</Text>
</Pressable>
<Pressable
accessibilityRole="link"
onPress={() => router.navigate('/feeding')}
style={{ alignItems: 'center', gap: 6, flex: 1 }}>
<View
style={{
width: 54,
height: 54,
borderRadius: '50%',
backgroundColor: '#F1ECDF',
alignItems: 'center',
justifyContent: 'center',
flexDirection: 'row',
}}>
<Svg
width="22"
height="22"
viewBox="0 0 24 24"
fill="none"
stroke="#22261D"
strokeWidth="1.6"
strokeLinecap="round"
strokeLinejoin="round">
<Ellipse cx="12" cy="9" rx="8" ry="2.5" />
<Path d="M4 9l1.5 7c.3 1.5 3 2.5 6.5 2.5s6.2-1 6.5-2.5L20 9" />
</Svg>
</View>
<Text style={{ fontFamily: 'DMSans_400Regular', fontSize: 11.5, color: '#4A4F42' }}>
Feeding Log
</Text>
</Pressable>
</View>
<Text
style={{
marginTop: 22,
marginBottom: 0,
marginHorizontal: 0,
fontFamily: 'DMSans_600SemiBold',
fontSize: 15,
color: '#22261D',
}}>
{"Today's Overview"}
</Text>
<View style={{ marginTop: 12, flexDirection: 'row', gap: 12 }}>
<View
style={{
backgroundColor: '#F3EFE4',
borderRadius: 18,
paddingTop: 14,
paddingBottom: 12,
paddingHorizontal: 14,
gap: 6,
flex: 1,
}}>
<Text style={{ fontFamily: 'DMSans_400Regular', fontSize: 12, color: '#555A4C' }}>
Meals Eaten
</Text>
<View
style={{
alignItems: 'center',
justifyContent: 'space-between',
flexDirection: 'row',
}}>
<Text
style={{
flexShrink: 1,
fontFamily: 'DMSans_500Medium',
fontSize: 24,
color: '#22261D',
}}>
2 / 2
</Text>
<Svg width="34" height="22" viewBox="0 0 34 22">
<Ellipse cx="17" cy="6" rx="15" ry="4" fill="#3E4A2E" />
<Path d="M2 6l3 11c.6 2 5.6 3.5 12 3.5s11.4-1.5 12-3.5L32 6z" fill={accent} />
</Svg>
</View>
<Text style={{ fontFamily: 'DMSans_400Regular', fontSize: 11.5, color: '#555A4C' }}>
Great job!
</Text>
</View>
<View
style={{
backgroundColor: '#F3EFE4',
borderRadius: 18,
paddingTop: 14,
paddingBottom: 12,
paddingHorizontal: 14,
gap: 6,
flex: 1,
}}>
<Text style={{ fontFamily: 'DMSans_400Regular', fontSize: 12, color: '#555A4C' }}>
Water Intake
</Text>
<View
style={{
alignItems: 'center',
justifyContent: 'space-between',
flexDirection: 'row',
}}>
<Text
style={{
flexShrink: 1,
fontFamily: 'DMSans_500Medium',
fontSize: 24,
color: '#22261D',
}}>
180 ml
</Text>
<Svg width="18" height="24" viewBox="0 0 18 24">
<Path d="M9 1C9 1 1 11 1 15.5a8 8 0 0 0 16 0C17 11 9 1 9 1z" fill="#5B93CF" />
</Svg>
</View>
<Text style={{ fontFamily: 'DMSans_400Regular', fontSize: 11.5, color: '#555A4C' }}>
Keep it up!
</Text>
</View>
</View>
</View>
<View
style={{
position: 'absolute',
left: 0,
bottom: 0,
width: 390,
height: 78,
paddingTop: 10,
paddingBottom: 0,
paddingHorizontal: 16,
backgroundColor: '#FFFFFF',
borderTopWidth: 1,
borderTopColor: '#ECE8DD',
flexDirection: 'row',
}}>
<Pressable
accessibilityRole="link"
onPress={() => router.navigate('/')}
style={{ alignItems: 'center', gap: 4, flex: 1 }}>
<Svg width="22" height="22" viewBox="0 0 24 24" fill={accent}>
<Path d="M3 10.5L12 3l9 7.5V20a1 1 0 0 1-1 1h-5v-6h-6v6H4a1 1 0 0 1-1-1z" />
</Svg>
<Text style={{ fontFamily: 'DMSans_600SemiBold', fontSize: 11, color: accent }}>
Home
</Text>
</Pressable>
<Pressable
accessibilityRole="link"
onPress={() => router.navigate('/product')}
style={{ alignItems: 'center', gap: 4, flex: 1 }}>
<Svg
width="22"
height="22"
viewBox="0 0 24 24"
fill="none"
stroke="#555A4C"
strokeWidth="1.6"
strokeLinejoin="round">
<Path d="M5 8h14l-1 13H6z" />
<Path d="M9 8V6a3 3 0 0 1 6 0v2" />
</Svg>
<Text style={{ fontFamily: 'DMSans_400Regular', fontSize: 11, color: '#555A4C' }}>
Shop
</Text>
</Pressable>
<Pressable
accessibilityRole="link"
onPress={() => router.navigate('/feeding')}
style={{ alignItems: 'center', gap: 4, flex: 1 }}>
<Svg
width="22"
height="22"
viewBox="0 0 24 24"
fill="none"
stroke="#555A4C"
strokeWidth="1.6"
strokeLinejoin="round">
<Path d="M12 20C5 15 2.5 11.5 2.5 8a4.7 4.7 0 0 1 9.5-1.5A4.7 4.7 0 0 1 21.5 8c0 3.5-2.5 7-9.5 12z" />
</Svg>
<Text style={{ fontFamily: 'DMSans_400Regular', fontSize: 11, color: '#555A4C' }}>
Health
</Text>
</Pressable>
<Pressable
accessibilityRole="link"
onPress={() => router.navigate('/')}
style={{ alignItems: 'center', gap: 4, flex: 1 }}>
<Svg
width="22"
height="22"
viewBox="0 0 24 24"
fill="none"
stroke="#555A4C"
strokeWidth="1.6"
strokeLinecap="round">
<Circle cx="12" cy="8" r="4" />
<Path d="M4 21c1-4 4-6 8-6s7 2 8 6" />
</Svg>
<Text style={{ fontFamily: 'DMSans_400Regular', fontSize: 11, color: '#555A4C' }}>
Profile
</Text>
</Pressable>
</View>
</View>
</Screen>
);
}src/app/feeding.tsx
import { Image } from 'expo-image';
import { router } from 'expo-router';
import { Pressable, Text, View } from 'react-native';
import Svg, { Circle, Path } from 'react-native-svg';
import { Screen } from '@/components/screen';
import { asset } from '@/lib/assets';
const accent = '#4A5A36';
const guide = [
{ weight: '2–3 kg', amount: '30–40 g' },
{ weight: '3–5 kg', amount: '40–60 g' },
{ weight: '5–7 kg', amount: '60–80 g' },
{ weight: '7+ kg', amount: '80+ g' },
];
export default function FeedingScreen() {
return (
<Screen background="#FBF9F4" statusBar="dark">
<View style={{ flex: 1 }}>
<Image
source={{ uri: asset('f37373270f.jpg') }}
contentFit="cover"
contentPosition={{ left: 'center', top: '60%' }}
accessibilityLabel="Olive Nourish Paws bowl full of kibble on a wooden table"
style={{ position: 'absolute', top: 0, left: 0, width: 390, height: 400 }}
/>
<View
style={{
position: 'absolute',
top: 52,
left: 20,
right: 20,
justifyContent: 'space-between',
alignItems: 'center',
flexDirection: 'row',
}}>
<Pressable
accessibilityRole="link"
accessibilityLabel="Back"
onPress={() => router.navigate('/product')}
style={{
width: 44,
height: 44,
borderRadius: '50%',
backgroundColor: '#FFFFFF',
alignItems: 'center',
justifyContent: 'center',
flexDirection: 'row',
}}>
<Svg
width="20"
height="20"
viewBox="0 0 24 24"
fill="none"
stroke="#22261D"
strokeWidth="1.8"
strokeLinecap="round"
strokeLinejoin="round">
<Path d="M19 12H5M11 6l-6 6 6 6" />
</Svg>
</Pressable>
<Pressable
accessibilityRole="button"
accessibilityLabel="More options"
style={{
width: 44,
height: 44,
borderRadius: '50%',
borderWidth: 0,
backgroundColor: '#FFFFFF',
alignItems: 'center',
justifyContent: 'center',
padding: 0,
flexDirection: 'row',
}}>
<Svg width="20" height="20" viewBox="0 0 24 24" fill="#22261D">
<Circle cx="5" cy="12" r="1.8" />
<Circle cx="12" cy="12" r="1.8" />
<Circle cx="19" cy="12" r="1.8" />
</Svg>
</Pressable>
</View>
<View style={{ position: 'absolute', top: 322, right: 20, gap: 10, flexDirection: 'row' }}>
<Pressable
accessibilityRole="button"
accessibilityLabel="Play sound"
style={{
width: 44,
height: 44,
borderRadius: '50%',
borderWidth: 0,
backgroundColor: '#FFFFFF',
alignItems: 'center',
justifyContent: 'center',
padding: 0,
flexDirection: 'row',
}}>
<Svg
width="20"
height="20"
viewBox="0 0 24 24"
fill="none"
stroke="#22261D"
strokeWidth="1.7"
strokeLinecap="round"
strokeLinejoin="round">
<Path d="M9 18V5l11-2v13" />
<Circle cx="6.5" cy="18" r="2.5" />
<Circle cx="17.5" cy="16" r="2.5" />
</Svg>
</Pressable>
<Pressable
accessibilityRole="button"
accessibilityLabel="Take photo"
style={{
width: 44,
height: 44,
borderRadius: '50%',
borderWidth: 0,
backgroundColor: '#FFFFFF',
alignItems: 'center',
justifyContent: 'center',
padding: 0,
flexDirection: 'row',
}}>
<Svg
width="20"
height="20"
viewBox="0 0 24 24"
fill="none"
stroke="#22261D"
strokeWidth="1.7"
strokeLinecap="round"
strokeLinejoin="round">
<Path d="M4 8h3l2-3h6l2 3h3v11H4z" />
<Circle cx="12" cy="13" r="3.5" />
</Svg>
</Pressable>
</View>
<View
style={{
position: 'absolute',
top: 378,
left: 0,
width: 390,
bottom: 0,
backgroundColor: '#FBF9F4',
borderTopLeftRadius: 28,
borderTopRightRadius: 28,
borderBottomRightRadius: 0,
borderBottomLeftRadius: 0,
paddingTop: 18,
paddingBottom: 0,
paddingHorizontal: 20,
}}>
<Pressable
accessibilityRole="button"
style={{
height: 44,
borderRadius: 22,
borderWidth: 0,
backgroundColor: accent,
alignItems: 'center',
justifyContent: 'center',
gap: 10,
flexDirection: 'row',
}}>
<Text
style={{
fontFamily: 'DMSans_500Medium',
fontSize: 14,
color: '#FFFFFF',
flexShrink: 1,
}}>
Feeding Guide
</Text>
<Svg
width="14"
height="14"
viewBox="0 0 24 24"
fill="none"
stroke="#FFFFFF"
strokeWidth="2.2"
strokeLinecap="round"
strokeLinejoin="round">
<Path d="M6 9l6 6 6-6" />
</Svg>
</Pressable>
<Text
style={{
marginTop: 18,
marginBottom: 0,
marginHorizontal: 0,
fontFamily: 'DMSans_600SemiBold',
fontSize: 15,
color: '#22261D',
}}>
Daily Feeding Guide
</Text>
<View style={{ marginTop: 10, flexDirection: 'row', gap: 8, flexWrap: 'wrap' }}>
{guide.map((g, index) => (
<View
key={index}
style={{
backgroundColor: '#F3EFE4',
borderRadius: 14,
paddingVertical: 10,
paddingHorizontal: 4,
alignItems: 'center',
gap: 4,
flexGrow: 1,
flexBasis: '21%',
}}>
<Svg width="26" height="26" viewBox="0 0 24 24">
<Path
d="M7 22c-1-3 0-7 2-9-1-1-1.5-3-1-5L9 3l2.2 3h1.6L15 3l1 5c.5 2 0 4-1 5 2 2 3 6 2 9z"
fill={accent}
/>
<Path
d="M17 21.5c2.5 0 4-1.5 4-4"
fill="none"
stroke={accent}
strokeWidth="1.6"
strokeLinecap="round"
/>
</Svg>
<Text style={{ fontFamily: 'DMSans_500Medium', fontSize: 11.5, color: '#22261D' }}>
{g.weight}
</Text>
<Text style={{ fontFamily: 'DMSans_400Regular', fontSize: 11, color: '#555A4C' }}>
{g.amount}
</Text>
</View>
))}
</View>
<Text
style={{
marginTop: 18,
marginBottom: 0,
marginHorizontal: 0,
fontFamily: 'DMSans_600SemiBold',
fontSize: 15,
color: '#22261D',
}}>
Happy Customers
</Text>
<View style={{ marginTop: 10, gap: 8 }}>
<View style={{ flexDirection: 'row', gap: 8 }}>
<Image
source={{ uri: asset('9b90c504e7.jpg') }}
contentFit="cover"
accessibilityLabel="Tabby eating from a Nourish Paws bowl"
style={{ width: '100%', height: 96, borderRadius: 12, flex: 1 }}
/>
<Image
source={{ uri: asset('512fbe5d17.jpg') }}
contentFit="cover"
contentPosition={{ left: 'center', top: '30%' }}
accessibilityLabel="Tabby wearing an olive bandana"
style={{ width: '100%', height: 96, borderRadius: 12, flex: 1 }}
/>
</View>
<View style={{ flexDirection: 'row', gap: 8 }}>
<Image
source={{ uri: asset('d386eb5742.jpg') }}
contentFit="cover"
accessibilityLabel="Close-up of a tabby looking up"
style={{ width: '100%', height: 96, borderRadius: 12, flex: 1 }}
/>
<Image
source={{ uri: asset('5ae8680073.jpg') }}
contentFit="cover"
accessibilityLabel="Tabby relaxing on a blanket"
style={{ width: '100%', height: 96, borderRadius: 12, flex: 1 }}
/>
</View>
</View>
</View>
<View
style={{
position: 'absolute',
left: 0,
bottom: 0,
width: 390,
height: 78,
paddingTop: 10,
paddingBottom: 0,
paddingHorizontal: 16,
backgroundColor: '#FFFFFF',
borderTopWidth: 1,
borderTopColor: '#ECE8DD',
flexDirection: 'row',
}}>
<Pressable
accessibilityRole="link"
onPress={() => router.navigate('/')}
style={{ alignItems: 'center', gap: 4, flex: 1 }}>
<Svg width="22" height="22" viewBox="0 0 24 24" fill={accent}>
<Path d="M3 10.5L12 3l9 7.5V20a1 1 0 0 1-1 1h-5v-6h-6v6H4a1 1 0 0 1-1-1z" />
</Svg>
<Text style={{ fontFamily: 'DMSans_600SemiBold', fontSize: 11, color: accent }}>
Home
</Text>
</Pressable>
<Pressable
accessibilityRole="link"
onPress={() => router.navigate('/product')}
style={{ alignItems: 'center', gap: 4, flex: 1 }}>
<Svg
width="22"
height="22"
viewBox="0 0 24 24"
fill="none"
stroke="#555A4C"
strokeWidth="1.6"
strokeLinejoin="round">
<Path d="M5 8h14l-1 13H6z" />
<Path d="M9 8V6a3 3 0 0 1 6 0v2" />
</Svg>
<Text style={{ fontFamily: 'DMSans_400Regular', fontSize: 11, color: '#555A4C' }}>
Shop
</Text>
</Pressable>
<Pressable
accessibilityRole="link"
onPress={() => router.navigate('/feeding')}
style={{ alignItems: 'center', gap: 4, flex: 1 }}>
<Svg
width="22"
height="22"
viewBox="0 0 24 24"
fill="none"
stroke="#555A4C"
strokeWidth="1.6"
strokeLinejoin="round">
<Path d="M12 20C5 15 2.5 11.5 2.5 8a4.7 4.7 0 0 1 9.5-1.5A4.7 4.7 0 0 1 21.5 8c0 3.5-2.5 7-9.5 12z" />
</Svg>
<Text style={{ fontFamily: 'DMSans_400Regular', fontSize: 11, color: '#555A4C' }}>
Health
</Text>
</Pressable>
<Pressable
accessibilityRole="link"
onPress={() => router.navigate('/')}
style={{ alignItems: 'center', gap: 4, flex: 1 }}>
<Svg
width="22"
height="22"
viewBox="0 0 24 24"
fill="none"
stroke="#555A4C"
strokeWidth="1.6"
strokeLinecap="round">
<Circle cx="12" cy="8" r="4" />
<Path d="M4 21c1-4 4-6 8-6s7 2 8 6" />
</Svg>
<Text style={{ fontFamily: 'DMSans_400Regular', fontSize: 11, color: '#555A4C' }}>
Profile
</Text>
</Pressable>
</View>
</View>
</Screen>
);
}src/app/product.tsx
import { Image } from 'expo-image';
import { router } from 'expo-router';
import { useState } from 'react';
import { Pressable, Text, View } from 'react-native';
import Svg, { Circle, G, Path } 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>>({ size: '1.5 kg' });
const setState = (patch: Record<string, any>) =>
setScreenState((current) => ({ ...current, ...patch }));
const accent = '#4A5A36';
const base =
'height: 44px; border-radius: 22px; font-size: 14px; font-weight: 500; color: #22261D; ';
const sizes = ['1.5 kg', '3 kg', '7 kg'].map((label) => {
const on = state.size === label;
return {
label,
checked: on ? 'true' : 'false',
pick: () => setState({ size: label }),
style:
base +
(on
? 'background: #FFFFFF; border: 1.5px solid ' + accent + ';'
: 'background: #F1ECDF; border: 1.5px solid transparent;'),
};
});
return { accent, sizes };
}
export default function ProductScreen() {
const { accent, sizes } = useScreen();
return (
<Screen background="#F8F4EE" statusBar="dark">
<View style={{ flex: 1 }}>
<View
style={{
position: 'absolute',
top: 52,
left: 20,
right: 20,
justifyContent: 'space-between',
alignItems: 'center',
zIndex: 2,
flexDirection: 'row',
}}>
<Pressable
accessibilityRole="link"
accessibilityLabel="Back"
onPress={() => router.navigate('/')}
style={{
width: 44,
height: 44,
borderRadius: '50%',
backgroundColor: '#FFFFFF',
alignItems: 'center',
justifyContent: 'center',
flexDirection: 'row',
}}>
<Svg
width="20"
height="20"
viewBox="0 0 24 24"
fill="none"
stroke="#22261D"
strokeWidth="1.8"
strokeLinecap="round"
strokeLinejoin="round">
<Path d="M19 12H5M11 6l-6 6 6 6" />
</Svg>
</Pressable>
<Pressable
accessibilityRole="button"
accessibilityLabel="More options"
style={{
width: 44,
height: 44,
borderRadius: '50%',
borderWidth: 0,
backgroundColor: '#FFFFFF',
alignItems: 'center',
justifyContent: 'center',
padding: 0,
flexDirection: 'row',
}}>
<Svg width="20" height="20" viewBox="0 0 24 24" fill="#22261D">
<Circle cx="5" cy="12" r="1.8" />
<Circle cx="12" cy="12" r="1.8" />
<Circle cx="19" cy="12" r="1.8" />
</Svg>
</Pressable>
</View>
<Image
source={{ uri: asset('104d225b22.jpg') }}
contentFit="contain"
accessibilityLabel="Nourish Paws Chicken & Salmon cat food bag"
style={{ position: 'absolute', top: 60, left: 70, width: 250, height: 334 }}
/>
<View style={{ position: 'absolute', top: 386, right: 44, gap: 10, flexDirection: 'row' }}>
<Pressable
accessibilityRole="button"
accessibilityLabel="Find in store"
style={{
width: 44,
height: 44,
borderRadius: '50%',
borderWidth: 0,
backgroundColor: '#FFFFFF',
alignItems: 'center',
justifyContent: 'center',
padding: 0,
flexDirection: 'row',
}}>
<Svg
width="20"
height="20"
viewBox="0 0 24 24"
fill="none"
stroke="#22261D"
strokeWidth="1.7"
strokeLinejoin="round">
<Path d="M12 21s7-6.5 7-12a7 7 0 0 0-14 0c0 5.5 7 12 7 12z" />
<Circle cx="12" cy="9" r="2.5" />
</Svg>
</Pressable>
<Pressable
accessibilityRole="button"
accessibilityLabel="Save to favourites"
style={{
width: 44,
height: 44,
borderRadius: '50%',
borderWidth: 0,
backgroundColor: accent,
alignItems: 'center',
justifyContent: 'center',
padding: 0,
flexDirection: 'row',
}}>
<Svg
width="20"
height="20"
viewBox="0 0 24 24"
fill="none"
stroke="#FFFFFF"
strokeWidth="1.7"
strokeLinejoin="round">
<Path d="M12 20C5 15 2.5 11.5 2.5 8a4.7 4.7 0 0 1 9.5-1.5A4.7 4.7 0 0 1 21.5 8c0 3.5-2.5 7-9.5 12z" />
</Svg>
</Pressable>
<Pressable
accessibilityRole="button"
accessibilityLabel="Add to cart"
style={{
width: 44,
height: 44,
borderRadius: '50%',
borderWidth: 0,
backgroundColor: '#FFFFFF',
alignItems: 'center',
justifyContent: 'center',
padding: 0,
flexDirection: 'row',
}}>
<Svg
width="20"
height="20"
viewBox="0 0 24 24"
fill="none"
stroke="#22261D"
strokeWidth="1.7"
strokeLinecap="round"
strokeLinejoin="round">
<Path d="M3 4h2.5l2.2 10.5h10.6L20.5 7H7" />
<Circle cx="9" cy="19" r="1.4" />
<Circle cx="17" cy="19" r="1.4" />
<Path d="M14 9.5v-3M12.5 8h3" />
</Svg>
</Pressable>
</View>
<View
style={{
position: 'absolute',
top: 444,
left: 0,
width: 390,
bottom: 0,
backgroundColor: '#FBF9F4',
borderTopLeftRadius: 28,
borderTopRightRadius: 28,
borderBottomRightRadius: 0,
borderBottomLeftRadius: 0,
paddingTop: 20,
paddingBottom: 0,
paddingHorizontal: 20,
gap: 16,
}}>
<Pressable
accessibilityRole="button"
style={{
height: 48,
borderRadius: 24,
borderWidth: 0,
backgroundColor: accent,
alignItems: 'center',
justifyContent: 'center',
gap: 10,
flexDirection: 'row',
}}>
<Text
style={{
fontFamily: 'DMSans_500Medium',
fontSize: 14,
color: '#FFFFFF',
flexShrink: 1,
}}>
{'Chicken & Salmon Recipe'}
</Text>
<Svg
width="14"
height="14"
viewBox="0 0 24 24"
fill="none"
stroke="#FFFFFF"
strokeWidth="2.2"
strokeLinecap="round"
strokeLinejoin="round">
<Path d="M6 9l6 6 6-6" />
</Svg>
</Pressable>
<View style={{ flexDirection: 'row', gap: 10, flexWrap: 'wrap' }}>
{sizes.map((s, index) => (
<Pressable
key={index}
accessibilityRole="button"
onPress={s.pick}
style={{
flexGrow: 1,
flexBasis: '26%',
alignItems: 'center',
justifyContent: 'center',
...css(s.style),
}}>
<Text
style={[
{
fontFamily: 'DMSans_400Regular',
fontSize: 13.33,
color: '#000000',
textAlign: 'center',
},
cssText(s.style),
]}>
{s.label}
</Text>
</Pressable>
))}
</View>
<View
style={{ alignItems: 'center', justifyContent: 'space-between', flexDirection: 'row' }}>
<Text
style={{
flexShrink: 1,
fontFamily: 'DMSans_700Bold',
fontSize: 26,
color: '#22261D',
letterSpacing: -0.3,
}}>
$24.99
</Text>
<View style={{ alignItems: 'center', gap: 6, flexShrink: 1, flexDirection: 'row' }}>
<Svg
width="76"
height="13"
viewBox="0 0 76 13"
accessibilityRole="image"
accessibilityLabel="5 out of 5 stars">
<G fill="#3A3F33">
<Path d="M6.5 0l1.9 4.1 4.4.5-3.3 3 1 4.4-4-2.3-4 2.3 1-4.4-3.3-3 4.4-.5z" />
<Path
transform="translate(15.7)"
d="M6.5 0l1.9 4.1 4.4.5-3.3 3 1 4.4-4-2.3-4 2.3 1-4.4-3.3-3 4.4-.5z"
/>
<Path
transform="translate(31.4)"
d="M6.5 0l1.9 4.1 4.4.5-3.3 3 1 4.4-4-2.3-4 2.3 1-4.4-3.3-3 4.4-.5z"
/>
<Path
transform="translate(47.1)"
d="M6.5 0l1.9 4.1 4.4.5-3.3 3 1 4.4-4-2.3-4 2.3 1-4.4-3.3-3 4.4-.5z"
/>
<Path
transform="translate(62.8)"
d="M6.5 0l1.9 4.1 4.4.5-3.3 3 1 4.4-4-2.3-4 2.3 1-4.4-3.3-3 4.4-.5z"
/>
</G>
</Svg>
<Text
style={{
fontFamily: 'DMSans_400Regular',
fontSize: 12.5,
color: '#555A4C',
flexShrink: 1,
}}>
(128)
</Text>
</View>
</View>
<View style={{ flexDirection: 'row', gap: 8 }}>
<View style={{ alignItems: 'center', gap: 6, flex: 1 }}>
<View
style={{
width: 44,
height: 44,
borderRadius: '50%',
backgroundColor: '#F1ECDF',
alignItems: 'center',
justifyContent: 'center',
flexDirection: 'row',
}}>
<Svg
width="22"
height="22"
viewBox="0 0 24 24"
fill="none"
stroke="#22261D"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round">
<Path d="M5 20h14M7 20V10l5-5 5 5v10" />
<Path d="M10 14l4 4M14 14l-4 4" />
</Svg>
</View>
<Text
style={{
fontFamily: 'DMSans_400Regular',
fontSize: 11,
color: '#4A4F42',
lineHeight: 14.3,
textAlign: 'center',
}}>
Grain Free
</Text>
</View>
<View style={{ alignItems: 'center', gap: 6, flex: 1 }}>
<View
style={{
width: 44,
height: 44,
borderRadius: '50%',
backgroundColor: '#F1ECDF',
alignItems: 'center',
justifyContent: 'center',
flexDirection: 'row',
}}>
<Svg
width="22"
height="22"
viewBox="0 0 24 24"
fill="none"
stroke="#22261D"
strokeWidth="1.5">
<Circle cx="8" cy="10" r="4" />
<Circle cx="16" cy="10" r="4" />
<Circle cx="12" cy="16" r="4" />
</Svg>
</View>
<Text
style={{
fontFamily: 'DMSans_400Regular',
fontSize: 11,
color: '#4A4F42',
lineHeight: 14.3,
textAlign: 'center',
}}>
High Protein
</Text>
</View>
<View style={{ alignItems: 'center', gap: 6, flex: 1 }}>
<View
style={{
width: 44,
height: 44,
borderRadius: '50%',
backgroundColor: '#F1ECDF',
alignItems: 'center',
justifyContent: 'center',
flexDirection: 'row',
}}>
<Svg
width="22"
height="22"
viewBox="0 0 24 24"
fill="none"
stroke="#22261D"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round">
<Path d="M12 21v-9" />
<Path d="M12 12c0-4 3-7 7-7 0 4-3 7-7 7z" />
<Path d="M12 15c0-3-2.5-5.5-6-5.5 0 3 2.5 5.5 6 5.5z" />
</Svg>
</View>
<Text
style={{
fontFamily: 'DMSans_400Regular',
fontSize: 11,
color: '#4A4F42',
lineHeight: 14.3,
textAlign: 'center',
}}>
Natural{'\n'}Ingredients
</Text>
</View>
<View style={{ alignItems: 'center', gap: 6, flex: 1 }}>
<View
style={{
width: 44,
height: 44,
borderRadius: '50%',
backgroundColor: '#F1ECDF',
alignItems: 'center',
justifyContent: 'center',
flexDirection: 'row',
}}>
<Svg
width="22"
height="22"
viewBox="0 0 24 24"
fill="none"
stroke="#22261D"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round">
<Path d="M12 3l7 3v5c0 5-3 8-7 10-4-2-7-5-7-10V6z" />
<Path d="M9 12l2 2 4-4" />
</Svg>
</View>
<Text
style={{
fontFamily: 'DMSans_400Regular',
fontSize: 11,
color: '#4A4F42',
lineHeight: 14.3,
textAlign: 'center',
}}>
Vet{'\n'}Approved
</Text>
</View>
</View>
<Pressable
accessibilityRole="button"
style={{
height: 52,
borderRadius: 26,
borderWidth: 0,
backgroundColor: accent,
marginTop: 4,
alignItems: 'center',
justifyContent: 'center',
}}>
<Text
style={{
fontFamily: 'DMSans_600SemiBold',
fontSize: 15,
color: '#FFFFFF',
textAlign: 'center',
}}>
Add to Cart
</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/pet-food-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: 'DMSans_400Regular', 500: 'DMSans_500Medium', 600: 'DMSans_600SemiBold', 700: 'DMSans_700Bold', 800: 'DMSans_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 olive and oat palette keeps it calm, and the quick actions and overview cards leave room for real pet data, which makes it a good base for a subscription food brand or a pet wellness app.
Soft oat backgrounds and a deep olive green, with a serif headline over warm pet photography for a calm, premium feel.
- Hero with a large pet portrait and mood chip
- Quick actions for pets, bowls, shop and logs
- Pack size picker on the product page
- Weight-based feeding guide with customer photos
Colour palette
Olive
#4A5A36
Dark moss
#2F3A22
Oat
#F1ECDF
Paper
#FBF9F4
Ink
#22261D
Sage grey
#555A4C
How to use this design
- 01
Pick what to copy
Copy the Nourish Paws Pet Food 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 size picker change state?
Yes. The 1.5 kg, 3 kg and 7 kg options are real state, so the selected pack is outlined when you tap it. Prices are sample data you can link to each size.
Can this work for dog food too?
Yes. Only the photos and copy are cat specific. Swap the images and the weight bands in the feeding guide and the same screens work for dogs.
Which fonts does the design use?
Fraunces sets the headlines and DM Sans handles everything else. Both load through Expo Google Fonts in the root layout.
Is the Nourish Paws Pet Food 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.


