Coach — Meal Log
An energetic dark-themed calorie and macro tracker where clients log foods into breakfast, lunch, dinner and snack sections. A glowing electric-lime progress bar tracks the running daily total against the goal, animated macro rings break down protein, carbs and fat, and every entry can be removed with instant recalculation. Add foods through a compact form with quick-pick chips, watch the ring pulse toward the target, and get a motivational toast when the plan is on point.
MCP
Code
:root {
--bg: #0f1115;
--surface: #171a21;
--surface-2: #1f232c;
--lime: #c2f542;
--lime-d: #a3d62f;
--orange: #ff6b35;
--ink: #f2f4f0;
--muted: #9aa0a6;
--line: rgba(255, 255, 255, 0.10);
--line-2: rgba(255, 255, 255, 0.18);
--white: #fff;
--r-sm: 8px;
--r-md: 14px;
--r-lg: 20px;
--protein: #c2f542;
--carbs: #35c8ff;
--fat: #ff6b35;
}
* { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body {
margin: 0;
font-family: Inter, system-ui, -apple-system, sans-serif;
line-height: 1.5;
color: var(--ink);
background:
radial-gradient(900px 500px at 85% -10%, rgba(194, 245, 66, 0.10), transparent 60%),
radial-gradient(700px 460px at -5% 10%, rgba(255, 107, 53, 0.08), transparent 55%),
var(--bg);
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
min-height: 100vh;
padding: 28px 16px 60px;
}
.app {
max-width: 780px;
margin: 0 auto;
display: flex;
flex-direction: column;
gap: 18px;
}
/* ---------- TOPBAR ---------- */
.topbar {
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
}
.brand { display: flex; align-items: center; gap: 14px; }
.brand-mark {
width: 44px; height: 44px;
display: grid; place-items: center;
border-radius: var(--r-md);
background: linear-gradient(140deg, var(--lime), var(--lime-d));
color: #10240a;
font-size: 20px; font-weight: 800;
box-shadow: 0 8px 24px rgba(194, 245, 66, 0.28);
}
.topbar h1 {
margin: 0;
font-size: 22px;
font-weight: 800;
letter-spacing: 0.06em;
text-transform: uppercase;
}
.sub { margin: 0; color: var(--muted); font-size: 13px; }
.streak {
display: flex; align-items: baseline; gap: 6px;
padding: 8px 14px;
background: var(--surface);
border: 1px solid var(--line);
border-radius: 999px;
}
.streak .flame { font-size: 15px; }
.streak strong { font-size: 17px; font-weight: 800; color: var(--orange); }
.streak-lbl { font-size: 12px; color: var(--muted); text-transform: uppercase; letter-spacing: 0.04em; }
/* ---------- SUMMARY ---------- */
.summary {
background: linear-gradient(180deg, var(--surface), #14171d);
border: 1px solid var(--line);
border-radius: var(--r-lg);
padding: 22px;
display: flex;
flex-direction: column;
gap: 20px;
}
.cals {
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
flex-wrap: wrap;
}
.cal-figure { display: flex; align-items: baseline; gap: 10px; }
.cal-remain {
font-size: 52px;
font-weight: 800;
letter-spacing: -0.02em;
line-height: 1;
color: var(--lime);
text-shadow: 0 0 26px rgba(194, 245, 66, 0.35);
font-variant-numeric: tabular-nums;
}
.cal-remain.over { color: var(--orange); text-shadow: 0 0 26px rgba(255, 107, 53, 0.4); }
.cal-unit { font-size: 14px; color: var(--muted); text-transform: uppercase; letter-spacing: 0.08em; font-weight: 600; }
.cal-meta { display: flex; gap: 22px; }
.cal-row { display: flex; flex-direction: column; }
.cal-row span { font-size: 11px; color: var(--muted); text-transform: uppercase; letter-spacing: 0.06em; }
.cal-row strong { font-size: 18px; font-weight: 700; font-variant-numeric: tabular-nums; }
.goalbar-wrap { display: flex; flex-direction: column; gap: 8px; }
.goalbar {
height: 14px;
background: var(--surface-2);
border-radius: 999px;
overflow: hidden;
border: 1px solid var(--line);
}
.goalbar-fill {
height: 100%;
width: 0%;
border-radius: 999px;
background: linear-gradient(90deg, var(--lime-d), var(--lime));
box-shadow: 0 0 18px rgba(194, 245, 66, 0.5);
transition: width .5s cubic-bezier(.2,.7,.2,1), background .3s;
}
.goalbar.over .goalbar-fill {
background: linear-gradient(90deg, var(--orange), #ffab4a);
box-shadow: 0 0 18px rgba(255, 107, 53, 0.55);
}
.goalbar-note { margin: 0; font-size: 13px; color: var(--muted); }
.goalbar-note.over { color: var(--orange); font-weight: 600; }
.macros {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 12px;
}
.macro {
background: var(--surface-2);
border: 1px solid var(--line);
border-radius: var(--r-md);
padding: 12px 14px;
display: flex;
flex-direction: column;
gap: 8px;
}
.macro-top { display: flex; align-items: center; gap: 7px; font-size: 12px; color: var(--muted); text-transform: uppercase; letter-spacing: 0.04em; font-weight: 600; }
.macro-dot { width: 9px; height: 9px; border-radius: 50%; }
.macro[data-macro="protein"] .macro-dot { background: var(--protein); }
.macro[data-macro="carbs"] .macro-dot { background: var(--carbs); }
.macro[data-macro="fat"] .macro-dot { background: var(--fat); }
.macro strong { font-size: 20px; font-weight: 800; font-variant-numeric: tabular-nums; }
.macro-track { height: 5px; background: rgba(255,255,255,0.07); border-radius: 999px; overflow: hidden; }
.macro-fill { display: block; height: 100%; width: 0%; border-radius: 999px; transition: width .5s cubic-bezier(.2,.7,.2,1); }
.macro[data-macro="protein"] .macro-fill { background: var(--protein); }
.macro[data-macro="carbs"] .macro-fill { background: var(--carbs); }
.macro[data-macro="fat"] .macro-fill { background: var(--fat); }
/* ---------- COMPOSER ---------- */
.composer {
background: var(--surface);
border: 1px solid var(--line);
border-radius: var(--r-lg);
padding: 18px;
}
#foodForm {
display: grid;
grid-template-columns: 2fr 1fr 0.6fr 0.6fr 0.6fr 1.1fr auto;
gap: 10px;
align-items: end;
}
.field { display: flex; flex-direction: column; gap: 5px; min-width: 0; }
.field label {
font-size: 11px;
color: var(--muted);
text-transform: uppercase;
letter-spacing: 0.06em;
font-weight: 600;
padding-left: 2px;
}
.field input,
.field select {
width: 100%;
background: var(--surface-2);
border: 1px solid var(--line-2);
color: var(--ink);
border-radius: var(--r-sm);
padding: 10px 11px;
font: inherit;
font-size: 14px;
transition: border-color .15s, box-shadow .15s;
}
.field input::placeholder { color: #5f656d; }
.field input:focus,
.field select:focus,
.add-btn:focus-visible {
outline: none;
border-color: var(--lime);
box-shadow: 0 0 0 3px rgba(194, 245, 66, 0.25);
}
.field select { appearance: none; cursor: pointer; }
.add-btn {
height: 40px;
padding: 0 20px;
border: none;
border-radius: var(--r-sm);
background: linear-gradient(140deg, var(--lime), var(--lime-d));
color: #10240a;
font-weight: 800;
font-size: 14px;
letter-spacing: 0.04em;
text-transform: uppercase;
cursor: pointer;
box-shadow: 0 6px 18px rgba(194, 245, 66, 0.3);
transition: transform .12s, box-shadow .2s, filter .15s;
white-space: nowrap;
}
.add-btn:hover { filter: brightness(1.06); box-shadow: 0 8px 26px rgba(194, 245, 66, 0.45); }
.add-btn:active { transform: translateY(1px) scale(.98); }
.quickpicks {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 8px;
margin-top: 14px;
}
.qp-label { font-size: 12px; color: var(--muted); text-transform: uppercase; letter-spacing: 0.05em; }
.qp {
border: 1px solid var(--line-2);
background: var(--surface-2);
color: var(--ink);
font: inherit;
font-size: 12.5px;
font-weight: 500;
padding: 6px 12px;
border-radius: 999px;
cursor: pointer;
transition: border-color .15s, color .15s, transform .1s, background .15s;
}
.qp:hover { border-color: var(--lime); color: var(--lime); }
.qp:active { transform: scale(.95); }
.qp:focus-visible { outline: none; border-color: var(--lime); box-shadow: 0 0 0 3px rgba(194, 245, 66, 0.25); }
/* ---------- MEALS ---------- */
.meals { display: flex; flex-direction: column; gap: 14px; }
.meal-section {
background: var(--surface);
border: 1px solid var(--line);
border-radius: var(--r-lg);
overflow: hidden;
}
.meal-head {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
padding: 14px 18px;
border-bottom: 1px solid var(--line);
}
.meal-title { display: flex; align-items: center; gap: 10px; }
.meal-icon { font-size: 17px; }
.meal-title h2 {
margin: 0;
font-size: 14px;
font-weight: 800;
text-transform: uppercase;
letter-spacing: 0.08em;
}
.meal-subtotal {
font-size: 12px;
font-weight: 700;
color: var(--lime);
background: rgba(194, 245, 66, 0.12);
border: 1px solid rgba(194, 245, 66, 0.25);
padding: 4px 11px;
border-radius: 999px;
font-variant-numeric: tabular-nums;
}
.meal-subtotal.empty { color: var(--muted); background: transparent; border-color: var(--line); }
.rows { list-style: none; margin: 0; padding: 6px; display: flex; flex-direction: column; gap: 4px; }
.row {
display: flex;
align-items: center;
gap: 12px;
padding: 10px 12px;
border-radius: var(--r-md);
transition: background .15s;
}
.row:hover { background: var(--surface-2); }
.row-main { flex: 1; min-width: 0; }
.row-name { font-size: 14px; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.row-macros { display: flex; gap: 6px; margin-top: 3px; flex-wrap: wrap; }
.chip {
font-size: 10.5px;
font-weight: 600;
padding: 2px 7px;
border-radius: 999px;
text-transform: uppercase;
letter-spacing: 0.03em;
background: rgba(255,255,255,0.06);
color: var(--muted);
}
.chip.p { color: var(--protein); background: rgba(194,245,66,0.12); }
.chip.c { color: var(--carbs); background: rgba(53,200,255,0.12); }
.chip.f { color: var(--fat); background: rgba(255,107,53,0.12); }
.row-cal { font-size: 15px; font-weight: 800; font-variant-numeric: tabular-nums; }
.row-cal small { font-size: 10px; color: var(--muted); font-weight: 600; margin-left: 2px; }
.row-del {
width: 30px; height: 30px;
flex: none;
border: 1px solid var(--line-2);
background: transparent;
color: var(--muted);
border-radius: 8px;
cursor: pointer;
font-size: 16px;
line-height: 1;
display: grid; place-items: center;
transition: color .15s, border-color .15s, background .15s, transform .1s;
}
.row-del:hover { color: var(--orange); border-color: var(--orange); background: rgba(255,107,53,0.1); }
.row-del:active { transform: scale(.9); }
.row-del:focus-visible { outline: none; border-color: var(--orange); box-shadow: 0 0 0 3px rgba(255,107,53,0.25); }
.empty-row { padding: 16px 12px; color: #5f656d; font-size: 13px; text-align: center; }
@keyframes rowIn { from { opacity: 0; transform: translateY(-6px); } to { opacity: 1; transform: none; } }
.row.enter { animation: rowIn .28s ease; }
/* ---------- TOAST ---------- */
.toast {
position: fixed;
left: 50%;
bottom: 26px;
transform: translateX(-50%) translateY(20px);
background: var(--surface-2);
border: 1px solid var(--lime);
color: var(--ink);
padding: 12px 20px;
border-radius: 999px;
font-size: 14px;
font-weight: 600;
box-shadow: 0 12px 40px rgba(0,0,0,0.5), 0 0 24px rgba(194,245,66,0.2);
opacity: 0;
pointer-events: none;
transition: opacity .25s, transform .25s;
z-index: 50;
max-width: 90vw;
text-align: center;
}
.toast.show { opacity: 1; transform: translateX(-50%) translateY(0); }
.toast.warn { border-color: var(--orange); box-shadow: 0 12px 40px rgba(0,0,0,0.5), 0 0 24px rgba(255,107,53,0.25); }
/* ---------- RESPONSIVE ---------- */
@media (max-width: 720px) {
#foodForm { grid-template-columns: 1fr 1fr 1fr; }
.field-name { grid-column: 1 / -1; }
.field-meal { grid-column: 1 / 3; }
.add-btn { grid-column: 3 / 4; height: 100%; min-height: 40px; }
}
@media (max-width: 520px) {
body { padding: 18px 12px 48px; }
.macros { grid-template-columns: 1fr; }
.cal-remain { font-size: 42px; }
.cal-meta { gap: 16px; }
#foodForm { grid-template-columns: 1fr 1fr; }
.field-name { grid-column: 1 / -1; }
.field-cal { grid-column: 1 / -1; }
.field-meal { grid-column: 1 / -1; }
.add-btn { grid-column: 1 / -1; padding: 12px; }
.topbar h1 { font-size: 19px; }
}(function () {
"use strict";
var GOAL = 2100;
var MEALS = [
{ id: "breakfast", label: "Breakfast", icon: "🌅" },
{ id: "lunch", label: "Lunch", icon: "🥗" },
{ id: "dinner", label: "Dinner", icon: "🍽️" },
{ id: "snack", label: "Snack", icon: "🍎" }
];
var QUICKPICKS = [
{ name: "Oatmeal + berries", cal: 320, p: 11, c: 58, f: 6, meal: "breakfast" },
{ name: "Grilled chicken", cal: 280, p: 52, c: 0, f: 7, meal: "lunch" },
{ name: "Protein shake", cal: 160, p: 30, c: 6, f: 2, meal: "snack" },
{ name: "Brown rice cup", cal: 215, p: 5, c: 45, f: 2, meal: "lunch" },
{ name: "Greek yogurt", cal: 130, p: 17, c: 9, f: 3, meal: "snack" },
{ name: "Salmon fillet", cal: 360, p: 34, c: 0, f: 24, meal: "dinner" }
];
// seed data
var items = [
{ id: uid(), name: "Scrambled eggs + toast", cal: 380, p: 22, c: 30, f: 18, meal: "breakfast" },
{ id: uid(), name: "Black coffee", cal: 5, p: 0, c: 0, f: 0, meal: "breakfast" },
{ id: uid(), name: "Chicken & quinoa bowl", cal: 520, p: 45, c: 48, f: 14, meal: "lunch" }
];
var displayCal = null; // for count-up animation
// ---------- DOM refs ----------
var $ = function (id) { return document.getElementById(id); };
var mealsEl = $("meals");
var form = $("foodForm");
var quickEl = $("quickpicks");
var toastEl = $("toast");
var toastTimer;
function uid() { return "f" + Math.random().toString(36).slice(2, 9); }
// ---------- Build static meal sections ----------
function buildSections() {
MEALS.forEach(function (m) {
var sec = document.createElement("section");
sec.className = "meal-section";
sec.innerHTML =
'<div class="meal-head">' +
'<div class="meal-title"><span class="meal-icon" aria-hidden="true">' + m.icon + '</span>' +
'<h2>' + m.label + '</h2></div>' +
'<span class="meal-subtotal empty" id="sub-' + m.id + '">0 kcal</span>' +
'</div>' +
'<ul class="rows" id="rows-' + m.id + '"></ul>';
mealsEl.appendChild(sec);
});
}
function buildQuickpicks() {
QUICKPICKS.forEach(function (q) {
var b = document.createElement("button");
b.type = "button";
b.className = "qp";
b.textContent = q.name;
b.setAttribute("aria-label", "Add " + q.name + ", " + q.cal + " calories");
b.addEventListener("click", function () {
addItem({ name: q.name, cal: q.cal, p: q.p, c: q.c, f: q.f, meal: q.meal }, true);
});
quickEl.appendChild(b);
});
}
// ---------- Render ----------
function esc(s) {
return String(s).replace(/[&<>"']/g, function (c) {
return { "&": "&", "<": "<", ">": ">", '"': """, "'": "'" }[c];
});
}
function render(newId) {
MEALS.forEach(function (m) {
var ul = $("rows-" + m.id);
var sub = $("sub-" + m.id);
var list = items.filter(function (it) { return it.meal === m.id; });
var total = list.reduce(function (a, it) { return a + it.cal; }, 0);
sub.textContent = total + " kcal";
sub.classList.toggle("empty", total === 0);
ul.innerHTML = "";
if (!list.length) {
var li = document.createElement("li");
li.className = "empty-row";
li.textContent = "No food logged yet.";
ul.appendChild(li);
return;
}
list.forEach(function (it) {
var li = document.createElement("li");
li.className = "row" + (it.id === newId ? " enter" : "");
var macros = "";
if (it.p) macros += '<span class="chip p">' + it.p + 'g P</span>';
if (it.c) macros += '<span class="chip c">' + it.c + 'g C</span>';
if (it.f) macros += '<span class="chip f">' + it.f + 'g F</span>';
li.innerHTML =
'<div class="row-main">' +
'<div class="row-name">' + esc(it.name) + '</div>' +
(macros ? '<div class="row-macros">' + macros + '</div>' : '') +
'</div>' +
'<div class="row-cal">' + it.cal + '<small>kcal</small></div>' +
'<button class="row-del" aria-label="Remove ' + esc(it.name) + '" data-id="' + it.id + '">×</button>';
ul.appendChild(li);
});
});
updateSummary();
}
// ---------- Summary ----------
function updateSummary() {
var food = items.reduce(function (a, it) { return a + it.cal; }, 0);
var p = items.reduce(function (a, it) { return a + (it.p || 0); }, 0);
var c = items.reduce(function (a, it) { return a + (it.c || 0); }, 0);
var f = items.reduce(function (a, it) { return a + (it.f || 0); }, 0);
var remain = GOAL - food;
var over = remain < 0;
$("calGoal").textContent = GOAL;
$("calFood").textContent = food;
var remEl = $("calRemain");
remEl.classList.toggle("over", over);
remEl.textContent = over ? "+" + Math.abs(remain) : remain;
remEl.nextElementSibling.textContent = over ? "kcal over" : "kcal left";
// goal bar
var pct = Math.min(100, Math.round((food / GOAL) * 100));
var bar = $("goalbar");
var fill = $("goalFill");
fill.style.width = pct + "%";
fill.setAttribute("aria-valuenow", food);
bar.classList.toggle("over", over);
var note = $("goalNote");
note.classList.toggle("over", over);
if (food === 0) note.textContent = "Let's fuel up — nothing logged yet.";
else if (over) note.textContent = "Over goal by " + Math.abs(remain) + " kcal — ease up on the next meal.";
else if (pct >= 85) note.textContent = pct + "% of goal — dialed in. Great work!";
else note.textContent = pct + "% of goal logged · " + remain + " kcal to go.";
// macros (approx targets for fills)
setMacro("Protein", p, 160);
setMacro("Carbs", c, 230);
setMacro("Fat", f, 70);
}
function setMacro(key, val, target) {
$("m" + key).textContent = val + "g";
$("m" + key + "Fill").style.width = Math.min(100, Math.round((val / target) * 100)) + "%";
}
// ---------- Actions ----------
function addItem(data, quick) {
var it = {
id: uid(),
name: data.name.trim(),
cal: Math.max(0, Math.round(+data.cal || 0)),
p: Math.max(0, Math.round(+data.p || 0)),
c: Math.max(0, Math.round(+data.c || 0)),
f: Math.max(0, Math.round(+data.f || 0)),
meal: data.meal
};
if (!it.name) { toast("Give the food a name.", true); return; }
items.push(it);
render(it.id);
var mealLabel = MEALS.filter(function (m) { return m.id === it.meal; })[0].label;
toast((quick ? "Quick-added " : "Logged ") + it.name + " → " + mealLabel);
}
function removeItem(id) {
var it = items.filter(function (x) { return x.id === id; })[0];
items = items.filter(function (x) { return x.id !== id; });
render();
if (it) toast("Removed " + it.name + " · −" + it.cal + " kcal", true);
}
// ---------- Toast ----------
function toast(msg, warn) {
clearTimeout(toastTimer);
toastEl.textContent = msg;
toastEl.classList.toggle("warn", !!warn);
toastEl.classList.add("show");
toastTimer = setTimeout(function () { toastEl.classList.remove("show"); }, 2400);
}
// ---------- Events ----------
form.addEventListener("submit", function (e) {
e.preventDefault();
var fd = new FormData(form);
addItem({
name: fd.get("name"),
cal: fd.get("cal"),
p: fd.get("p"),
c: fd.get("c"),
f: fd.get("f"),
meal: fd.get("meal")
}, false);
form.reset();
$("fName").focus();
});
mealsEl.addEventListener("click", function (e) {
var btn = e.target.closest(".row-del");
if (btn) removeItem(btn.getAttribute("data-id"));
});
// ---------- Init ----------
buildSections();
buildQuickpicks();
render();
})();<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Coach — Meal Log</title>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet" />
<link rel="stylesheet" href="style.css" />
</head>
<body>
<main class="app" role="main">
<header class="topbar">
<div class="brand">
<span class="brand-mark" aria-hidden="true">◆</span>
<div>
<h1>Fuel Log</h1>
<p class="sub">Tuesday · Day 12 of Shred Block</p>
</div>
</div>
<div class="streak" title="Logging streak">
<span class="flame" aria-hidden="true">🔥</span>
<strong>12</strong><span class="streak-lbl">day streak</span>
</div>
</header>
<!-- DAILY SUMMARY -->
<section class="summary" aria-label="Daily nutrition summary">
<div class="cals">
<div class="cal-figure">
<span class="cal-remain" id="calRemain" aria-live="polite">2100</span>
<span class="cal-unit">kcal left</span>
</div>
<div class="cal-meta">
<div class="cal-row"><span>Goal</span><strong id="calGoal">2100</strong></div>
<div class="cal-row"><span>Food</span><strong id="calFood">0</strong></div>
</div>
</div>
<div class="goalbar-wrap">
<div class="goalbar" id="goalbar">
<div class="goalbar-fill" id="goalFill" role="progressbar" aria-valuemin="0" aria-valuemax="2100" aria-valuenow="0" aria-label="Calories consumed toward goal"></div>
</div>
<p class="goalbar-note" id="goalNote">Let's fuel up — nothing logged yet.</p>
</div>
<div class="macros" role="group" aria-label="Macro totals">
<div class="macro" data-macro="protein">
<div class="macro-top"><span class="macro-dot"></span><span>Protein</span></div>
<strong id="mProtein">0g</strong>
<div class="macro-track"><span class="macro-fill" id="mProteinFill"></span></div>
</div>
<div class="macro" data-macro="carbs">
<div class="macro-top"><span class="macro-dot"></span><span>Carbs</span></div>
<strong id="mCarbs">0g</strong>
<div class="macro-track"><span class="macro-fill" id="mCarbsFill"></span></div>
</div>
<div class="macro" data-macro="fat">
<div class="macro-top"><span class="macro-dot"></span><span>Fat</span></div>
<strong id="mFat">0g</strong>
<div class="macro-track"><span class="macro-fill" id="mFatFill"></span></div>
</div>
</div>
</section>
<!-- ADD FORM -->
<section class="composer" aria-label="Add food">
<form id="foodForm" autocomplete="off">
<div class="field field-name">
<label for="fName">Food</label>
<input id="fName" name="name" type="text" placeholder="e.g. Grilled chicken" required />
</div>
<div class="field field-cal">
<label for="fCal">Calories</label>
<input id="fCal" name="cal" type="number" min="0" max="4000" inputmode="numeric" placeholder="0" required />
</div>
<div class="field field-macro">
<label for="fP">P</label>
<input id="fP" name="p" type="number" min="0" max="300" inputmode="numeric" placeholder="0" />
</div>
<div class="field field-macro">
<label for="fC">C</label>
<input id="fC" name="c" type="number" min="0" max="400" inputmode="numeric" placeholder="0" />
</div>
<div class="field field-macro">
<label for="fF">F</label>
<input id="fF" name="f" type="number" min="0" max="200" inputmode="numeric" placeholder="0" />
</div>
<div class="field field-meal">
<label for="fMeal">Meal</label>
<select id="fMeal" name="meal">
<option value="breakfast">Breakfast</option>
<option value="lunch">Lunch</option>
<option value="dinner">Dinner</option>
<option value="snack">Snack</option>
</select>
</div>
<button type="submit" class="add-btn">Add<span aria-hidden="true"> +</span></button>
</form>
<div class="quickpicks" id="quickpicks" aria-label="Quick add foods">
<span class="qp-label">Quick add:</span>
</div>
</section>
<!-- MEAL SECTIONS -->
<section class="meals" id="meals" aria-label="Meal sections"></section>
</main>
<div class="toast" id="toast" role="status" aria-live="polite"></div>
<script src="script.js"></script>
</body>
</html>The Coach Meal Log is a focused nutrition-tracking panel for fitness coaching apps. A bold uppercase header sits above a live daily summary: a big remaining-calorie readout, a glowing lime goal bar that fills as food is logged, and three macro pills for protein, carbs and fat. Clients pick a meal section, type a food name and calories, optionally set macros, and hit add — the totals recalculate instantly with a smooth count-up animation.
Each meal section (Breakfast, Lunch, Dinner, Snack) shows its own subtotal badge and a stack of entry rows with calorie and macro chips. Rows have hover and focus states and a remove button that pulls the item back out of the running total. Quick-pick chips seed common foods so logging feels fast, and the progress bar shifts to an orange warning glow when the goal is exceeded.
Everything is vanilla JS with no dependencies: state lives in a single array, the UI re-renders from it, and a small toast helper surfaces motivational feedback. The layout is fully responsive down to 360px, keyboard-operable, and uses accessible labels and live regions so screen readers hear the updated totals.