Твои любимые блюда в исполнении

Готовим из того, что есть

Выберите блюдо, впишите продукты — и сайт покажет, сколько получится и что нужно докупить

Выбрать блюдо

Ничего не пропадёт, ничего не на глаз

Вы открываете рецепт, отмечаете, сколько продуктов уже есть дома, а калькулятор пересчитывает порции, список покупок и примерную стоимость докупки.

Как это работает

Шаг 1

Выберите блюдо

Шаг 2

Впишите продукты

Шаг 3

Получите расчёт

Учебный пример

Такой сайт вы соберёте сами

Это пример с практикума «Мастерская твоего первого сайта» — чтобы показать: сайт — это не только дизайн, а маленькая история, которую можно собрать своими руками даже в самом начале пути.

Базовый рецепт на ' + r.portions + ' порции

' + r.title + '

' + r.description + '

Приготовление

    ' + r.steps.map(function(s) { return '
  1. ' + s + '
  2. '; }).join("") + '

Калькулятор продуктов

' + ingredientRows(r) + '
ПродуктНужноЕсть домаЦена

Для граммов цена считается за 1 кг, для миллилитров — за 1 л, для штук — за 1 шт. Незаполненные продукты не ограничивают количество порций.

'; modal.classList.add("is-open"); modal.setAttribute("aria-hidden", "false"); document.documentElement.style.overflow = "hidden"; closeBtn.focus(); } function closeModal() { modal.classList.remove("is-open"); modal.setAttribute("aria-hidden", "true"); document.documentElement.style.overflow = ""; modalContent.innerHTML = ""; activeRecipe = null; } function readNum(attr, idx) { var inp = modalContent.querySelector('[' + attr + '="' + idx + '"]'); if (!inp || inp.value === "") return null; var v = Number(String(inp.value).replace(",", ".")); return Number.isFinite(v) && v >= 0 ? v : null; } function plural(n, f) { var m = Math.abs(n) % 100, m1 = m % 10; return m > 10 && m < 20 ? f[2] : m1 > 1 && m1 < 5 ? f[1] : m1 === 1 ? f[0] : f[2]; } function calculate() { if (!activeRecipe) return; var r = activeRecipe, minC = Infinity, limiter = "базовый рецепт", hasInput = false; r.ingredients.forEach(function(i, idx) { var h = readNum("data-have", idx); if (h !== null) { hasInput = true; var c = h / i[1]; if (c < minC) { minC = c; limiter = i[0].toLowerCase(); } } }); var exact = hasInput ? r.portions * minC : r.portions, whole = hasInput ? Math.floor(exact) : r.portions, enough = !hasInput || whole >= 1, target = hasInput ? Math.max(1, whole) : r.portions, factor = target / r.portions, totalCost = 0, hasMissing = false, hasUnpriced = false; var list = r.ingredients.map(function(i, idx) { var needed = i[1] * factor, have = readNum("data-have", idx), price = readNum("data-price", idx), available = have === null ? 0 : have, missing = Math.max(0, needed - available), cost = null; if (missing > 0) hasMissing = true; if (missing > 0 && price !== null) { cost = i[2] === "pcs" ? missing * price : missing / 1000 * price; totalCost += cost; } if (missing > 0 && price === null) hasUnpriced = true; var status = missing <= 0 ? "✓ хватает" : "докупить " + fmt(missing, i[2]) + (cost !== null ? " · " + money(cost) : ""); return '
  • ' + i[0] + 'нужно ' + fmt(needed, i[2]) + ' · ' + (have === null ? "не указано" : "есть " + fmt(available, i[2])) + ' · ' + status + '
  • '; }).join(""); var portionText = enough ? target + " " + plural(target, ["порция", "порции", "порций"]) : "Не хватает даже на одну порцию"; var shoppingTitle = hasInput && !enough ? "Список покупок для 1 порции" : "Список покупок"; var costText = hasMissing ? (totalCost > 0 ? (hasUnpriced ? "от " + money(totalCost) : money(totalCost)) : "без расчёта цены") : "0 ₽"; var res = modalContent.querySelector("[data-result]"); res.innerHTML = '
    Получится' + portionText + '
    Ограничивает' + (hasInput ? limiter : "Базовый рецепт") + '
    Докупка примерно' + costText + '

    ' + shoppingTitle + '

      ' + list + '
    '; res.classList.add("is-visible"); } renderCards(); cardsRoot.addEventListener("click", function(e) { var c = e.target.closest("[data-recipe]"); if (c) openRecipe(c.getAttribute("data-recipe")); }); modal.addEventListener("click", function(e) { if (e.target === modal) closeModal(); if (e.target.closest("[data-calc]")) calculate(); }); closeBtn.addEventListener("click", closeModal); document.addEventListener("keydown", function(e) { if (e.key === "Escape" && modal.classList.contains("is-open")) closeModal(); }); })();