If you’re here, you’ve successfully built your first web app—the daily calorie calculator. Awesome job! You took empty files and turned them into a working, interactive tool. That’s a huge step, and you should be proud.
In the first lesson, we promised things would get even more interesting. We promised to teach our app to understand food by connecting it to an “external brain.” It’s time to deliver on that promise and transform our simple calculator into a genuinely smart and useful food diary.
In this tutorial, we’ll do two things at once:
1. Seriously upgrade our app: We’ll add a food diary, connect to a nutrition database, add a unit converter, and create a responsive layout.
2. Learn the art of prompting: At each major step, I’ll show you the exact prompt (request) I gave my AI assistant (Gemini) to get the results we need.
Your main skill as a vibe coder isn’t memorizing hundreds of commands; it’s learning how to clearly and effectively give instructions to your AI co-pilot. Let’s learn by doing.
Part 1: Connecting to the “External Brain” (API)
Our app knows nothing about food. To get that knowledge, we need to connect it to an external service that has a massive nutrition database. This connection is made through an API (Application Programming Interface).
What’s an API? Think of it as a waiter in a restaurant. 🍽️
• Your App is you, the customer. You want to order a dish (information).
• The External Service (like the NutritionIX database) is the kitchen. It has all the ingredients (data) and does all the complex work.
• The API is the waiter. You give the waiter a clear order (“I want the nutrition info for 100g of chicken”). The waiter (API) takes the order to the kitchen, gets the result, and brings it back to you.
You don’t need to know how the kitchen works; you just need to know how to order. That’s what we’re going to teach our app.
Our First Prompt:
Me: “Hey! I want to upgrade my calorie calculator for beginners. I need it to get nutrition data from the internet. I’ve heard I need an API for that. Can you recommend a good API with an easy sign-up and a free version, and explain how to get the keys? We’ll also need to add an input field in the HTML for the food.”
Result: The AI recommended the NutritionIX API and provided the following instructions.
Step 1: Get Your API Keys
1. Go to https://www.nutritionix.com/business/api and click “GET YOUR API KEY”.
2. Sign up. When asked for a website, you can just enter “Personal Project.”
3. After you log in, you’ll land on your Dashboard. Copy the two keys you see there: Application ID and API Key. Save them somewhere safe.
Part 2: Building the Full Interface (HTML & CSS)
Now that we have access to the “kitchen,” we need to update our “menu”—the app’s user interface.
Our Second Prompt:
Me: “Awesome, I have the keys. Now, let’s build the complete UI for the final version of the app. I need you to add:
1. A switcher for Metric/Imperial units.
2. New input fields for feet/inches.
3. A whole section for the food diary with a list and a summary block (consumed/remaining).
4. Make it all display in two nice columns on desktop. Please provide the complete, final code for index.html and style.css.”
Result: The AI generated the complete files for our interface.
Step 2: Replace Your index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>VibeCalorie Tracker</title> <link rel="stylesheet" href="style.css"> </head> <body> <div class="container"> <div class="calculator"> <h1>Daily Calorie Goal</h1> <div class="unit-switcher"> <button id="metric-btn" class="unit-btn active">Metric (kg, cm)</button> <button id="imperial-btn" class="unit-btn">Imperial (lbs, ft, in)</button> </div> <p>Age:</p> <input id="age" type="number" min="1" placeholder="e.g., 25"> <p>Gender:</p> <input type="radio" name="gender" value="male" checked> Male <input type="radio" name="gender" value="female"> Female <p id="weight-label">Weight (kg):</p> <input id="weight" type="number" min="1" placeholder="e.g., 70"> <p id="height-label">Height (cm):</p> <div id="metric-height" class="height-inputs"> <input id="height-cm" type="number" min="1" placeholder="e.g., 180"> </div> <div id="imperial-height" class="height-inputs hidden"> <input id="height-ft" type="number" min="1" placeholder="ft"> <input id="height-in" type="number" min="0" placeholder="in"> </div> <p>Activity Level:</p> <select id="activity"> <option value="1.2">Sedentary</option> <option value="1.375">Lightly active</option> <option value="1.55">Moderately active</option> <option value="1.725">Very active</option> </select> <div class="button-group"> <button id="calculate-btn">Calculate Goal</button> <button id="clear-goal-btn" class="btn-secondary">Clear Fields</button> </div> <h2 id="result-container">Your daily goal: <span id="result">0</span> kcal</h2> </div> <div class="food-tracker"> <h1>Add Food to Diary</h1> <p id="food-example-label">Type in what you ate (e.g., "1 apple and 100g chicken breast")</p> <textarea id="food-input" placeholder="Enter food..."></textarea> <div class="button-group"> <button id="add-food-btn">Add Food</button> <button id="clear-diary-btn" class="btn-secondary">Clear Diary</button> </div> <div class="food-log"> <h2 class="food-log-title">Today's Diary:</h2> <ul id="food-list"></ul> </div> <div class="summary"> <div class="summary-item"> <span>Total Consumed:</span> <span id="total-calories">0</span> kcal </div> <div class="summary-item remaining"> <span>Calories Remaining:</span> <span id="remaining-calories">0</span> kcal </div> </div> </div> </div> <script src="script.js"></script> </body> </html>
Step 3: Replace Your style.css
/* General Body Styles */ body { font-family: Arial, sans-serif; background-color: #f4f7f6; display: flex; justify-content: center; align-items: flex-start; padding: 40px 15px; min-height: 100vh; margin: 0; box-sizing: border-box; } .container { width: 100%; max-width: 400px; /* Default max-width for mobile */ } .calculator, .food-tracker { background-color: white; padding: 25px 30px; border-radius: 10px; box-shadow: 0 4px 10px rgba(0,0,0,0.1); margin-bottom: 30px; } /* Styles for the unit switcher */ .unit-switcher { display: flex; justify-content: center; margin-bottom: 20px; } .unit-btn { background-color: #e9ecef; color: #495057; border: 1px solid #ced4da; padding: 8px 15px; margin: 0; cursor: pointer; font-size: 14px; width: 50%; transition: background-color 0.2s, color 0.2s; } .unit-btn:first-child { border-radius: 5px 0 0 5px; } .unit-btn:last-child { border-radius: 0 5px 5px 0; border-left: none; } .unit-btn.active { background-color: #5c67f2; color: white; border-color: #5c67f2; } h1 { text-align: center; color: #333; font-size: 1.8em; } p { color: #555; margin-top: 15px; margin-bottom: 5px; } input[type="number"], select, textarea { width: 100%; padding: 10px; margin-top: 4px; border-radius: 4px; border: 1px solid #ddd; box-sizing: border-box; font-family: Arial, sans-serif; font-size: 1em; } /* Styles for height inputs */ .height-inputs { display: flex; gap: 10px; } .height-inputs.hidden { display: none; } textarea { resize: vertical; min-height: 80px; } button { width: 100%; padding: 12px; color: white; border: none; border-radius: 5px; cursor: pointer; font-size: 16px; transition: background-color 0.2s; } /* Button group for side-by-side buttons */ .button-group { display: flex; gap: 10px; margin-top: 20px; } /* Style for secondary/clear buttons */ .btn-secondary { background-color: #6c757d; /* A neutral grey color */ } .btn-secondary:hover { background-color: #5a6268; } #calculate-btn { background-color: #5c67f2; } #calculate-btn:hover { background-color: #4a54c4; } #add-food-btn { background-color: #28a745; } #add-food-btn:hover { background-color: #218838; } #result-container { text-align: center; margin-top: 20px; font-size: 1.2em; color: #333; } .food-log { margin-top: 25px; } .food-log-title { font-size: 1.2em; color: #333; border-bottom: 1px solid #eee; padding-bottom: 5px; } #food-list { list-style-type: none; padding: 0; margin-top: 10px; } #food-list li { padding: 8px 0; border-bottom: 1px solid #f0f0f0; } .summary { margin-top: 25px; padding-top: 15px; border-top: 2px solid #5c67f2; } .summary-item { display: flex; justify-content: space-between; font-size: 1.1em; padding: 5px 0; } .summary-item.remaining { font-weight: bold; color: #28a745; } /* DESKTOP LAYOUT (for wider screens) */ @media (min-width: 800px) { .container { max-width: 850px; display: flex; gap: 30px; align-items: flex-start; } .calculator, .food-tracker { flex: 1; width: 50%; margin-bottom: 0; } }
Part 3: Bringing the App to Life (JavaScript)
The interface is ready. Now it’s time to write the “brain.” This is the most complex task for the AI, so the prompt needs to be crystal clear.
Our Third Prompt:
Me: “Awesome, the UI is done. Now let’s write all the logic in script.js. It needs to do the following:
1. Store my API keys at the top.
2. Manage the unit switcher and update the UI accordingly.
3. Convert lbs/ft/in to kg/cm before calculating the goal.
4. On ‘Add Food’ click, send a request to the NutritionIX API.
5. Add the returned food items to the list on the page.
6. Keep a running total of consumed calories and the remaining balance, updating the summary block.
7. Add clear buttons for each section. Please provide the final, complete code for script.js with all functions and English comments.”
Result: The AI generated the final “brain” for our application.
Step 4: Replace Your script.js
Remember to paste YOUR API keys at the top!
// --- NUTRITIONIX API CREDENTIALS --- const NUTRITIONIX_APP_ID = 'YOUR_APP_ID_HERE'; // <-- PASTE YOUR APP ID HERE const NUTRITIONIX_API_KEY = 'YOUR_API_KEY_HERE'; // <-- PASTE YOUR API KEY HERE const NUTRITIONIX_API_URL = 'https://trackapi.nutritionix.com/v2/natural/nutrients'; document.addEventListener('DOMContentLoaded', () => { // --- STATE MANAGEMENT --- // Variables to hold the application's data let dailyGoal = 0; let totalCalories = 0; let currentUnitSystem = 'metric'; // --- ELEMENTS --- // A central place to get all interactive elements from the page const calculateBtn = document.getElementById('calculate-btn'); const resultSpan = document.getElementById('result'); const addFoodBtn = document.getElementById('add-food-btn'); const foodList = document.getElementById('food-list'); const foodInput = document.getElementById('food-input'); const totalCaloriesSpan = document.getElementById('total-calories'); const remainingCaloriesSpan = document.getElementById('remaining-calories'); const metricBtn = document.getElementById('metric-btn'); const imperialBtn = document.getElementById('imperial-btn'); const weightLabel = document.getElementById('weight-label'); const weightInput = document.getElementById('weight'); const heightLabel = document.getElementById('height-label'); const metricHeightDiv = document.getElementById('metric-height'); const imperialHeightDiv = document.getElementById('imperial-height'); const foodExampleLabel = document.getElementById('food-example-label'); const clearGoalBtn = document.getElementById('clear-goal-btn'); const clearDiaryBtn = document.getElementById('clear-diary-btn'); // --- EVENT LISTENERS --- // Connect functions to button clicks calculateBtn.addEventListener('click', calculateAndSetGoal); addFoodBtn.addEventListener('click', addFood); metricBtn.addEventListener('click', () => switchUnitSystem('metric')); imperialBtn.addEventListener('click', () => switchUnitSystem('imperial')); clearGoalBtn.addEventListener('click', clearGoalFields); clearDiaryBtn.addEventListener('click', clearDiary); // --- FUNCTIONS --- // Handles switching between Metric and Imperial UI function switchUnitSystem(system) { currentUnitSystem = system; if (system === 'metric') { metricBtn.classList.add('active'); imperialBtn.classList.remove('active'); weightLabel.textContent = 'Weight (kg):'; weightInput.placeholder = 'e.g., 70'; heightLabel.textContent = 'Height (cm):'; foodExampleLabel.textContent = 'Type in what you ate (e.g., "1 apple and 100g chicken breast")'; metricHeightDiv.classList.remove('hidden'); imperialHeightDiv.classList.add('hidden'); } else { imperialBtn.classList.add('active'); metricBtn.classList.remove('active'); weightLabel.textContent = 'Weight (lbs):'; weightInput.placeholder = 'e.g., 155'; heightLabel.textContent = 'Height (ft, in):'; foodExampleLabel.textContent = 'Type in what you ate (e.g., "1 apple and 3.5oz chicken breast")'; imperialHeightDiv.classList.remove('hidden'); metricHeightDiv.classList.add('hidden'); } } // Calculates the daily calorie goal based on user input function calculateAndSetGoal() { let weightInKg; let heightInCm; // Convert imperial units to metric before calculation if needed if (currentUnitSystem === 'imperial') { const heightFt = parseFloat(document.getElementById('height-ft').value) || 0; const heightIn = parseFloat(document.getElementById('height-in').value) || 0; const weightLbs = parseFloat(document.getElementById('weight').value) || 0; weightInKg = weightLbs * 0.453592; heightInCm = (heightFt * 12 + heightIn) * 2.54; } else { weightInKg = parseFloat(document.getElementById('weight').value) || 0; heightInCm = parseFloat(document.getElementById('height-cm').value) || 0; } const age = parseInt(document.getElementById('age').value) || 0; const gender = document.querySelector('input[name="gender"]:checked').value; const activity = parseFloat(document.getElementById('activity').value); // Improved validation if (!age || !weightInKg || !heightInCm) { alert("Please fill in all the required fields: age, weight, and height."); return; } if (age <= 0 || weightInKg <= 0 || heightInCm <= 0) { alert("Age, weight, and height must be positive numbers."); return; } let bmr; if (gender === 'male') { bmr = (10 * weightInKg) + (6.25 * heightInCm) - (5 * age) + 5; } else { bmr = (10 * weightInKg) + (6.25 * heightInCm) - (5 * age) - 161; } const tdee = Math.round(bmr * activity); dailyGoal = tdee; // Save goal to state resultSpan.textContent = dailyGoal; updateSummary(); } // Clears the input fields in the goal calculator function clearGoalFields() { document.getElementById('age').value = ''; document.getElementById('weight').value = ''; document.getElementById('height-cm').value = ''; document.getElementById('height-ft').value = ''; document.getElementById('height-in').value = ''; resultSpan.textContent = '0'; dailyGoal = 0; updateSummary(); } // Clears the entire food diary section function clearDiary() { if (confirm('Are you sure you want to clear the entire food diary?')) { foodList.innerHTML = ''; foodInput.value = ''; totalCalories = 0; updateSummary(); } } // Handles the logic for adding a new food item async function addFood() { const query = foodInput.value; if (query.trim() === '') { alert('Please enter a food.'); return; } try { const data = await fetchFoodData(query); // Add calories from each found food to the total data.foods.forEach(food => { totalCalories += food.nf_calories; }); displayFoods(data.foods); // Show food on the list updateSummary(); // Update totals foodInput.value = ''; // Clear input field } catch (error) { console.error('Error in addFood:', error); alert('Something went wrong. Could not process food.'); } } // Sends the request to the NutritionIX API async function fetchFoodData(query) { const response = await fetch(NUTRITIONIX_API_URL, { method: 'POST', headers: { 'Content-Type': 'application/json', 'x-app-id': NUTRITIONIX_APP_ID, 'x-app-key': NUTRITIONIX_API_KEY, }, body: JSON.stringify({ query: query }), }); if (!response.ok) { const errorData = await response.json(); throw new Error(errorData.message || 'API request failed!'); } return await response.json(); } // Renders the list of food items on the page function displayFoods(foods) { foods.forEach(food => { const listItem = document.createElement('li'); const foodInfo = `${food.serving_qty} ${food.food_name} - ${food.nf_calories.toFixed(0)} kcal`; listItem.textContent = foodInfo; foodList.appendChild(listItem); }); } // Updates the summary section with total and remaining calories function updateSummary() { const remaining = dailyGoal - totalCalories; totalCaloriesSpan.textContent = totalCalories.toFixed(0); remainingCaloriesSpan.textContent = remaining.toFixed(0); const remainingContainer = remainingCaloriesSpan.closest('.summary-item'); if (remaining < 0) { remainingContainer.style.color = '#dc3545'; // Red for negative } else { remainingContainer.style.color = '#28a745'; // Green for positive } } });
Part 4: The Art of the Prompt & Final Launch
Now that the app is finished, let’s reflect on why our prompts worked. A good prompt gives clear instructions.
❌ Bad Prompt: “it doesn’t work”
✅ Good Prompt: “I have an error. When I click ‘Add Food’, the console shows this: TypeError: …non ISO-8859-1 code point… Can you help me find the problem in my code?”
The more context and detail you give your AI assistant, the more accurate and helpful its response will be. Your main job isn’t to write the code, but to turn your great idea into a clear request.
Final Launch:
1. Make sure you’ve pasted your API keys into script.js.
2. Save all three files.
3. Open index.html in your browser.
Test all the features: calculate your goal in both metric and imperial, add food, clear the fields, and resize your browser window to see the layout change.
Congratulations! You didn’t just write code; you directed a full development cycle for a complex, interactive application using AI as your co-pilot.
➡️ Go back to Lesson 1: Build Your First Web App
Leave a Reply