Задать вопрос директору
Telegram
VK
Звонок

Калькулятор кейтеринга для сайта на Tilda

Считает бюджет мероприятия по числу гостей, формату и длительности, показывает цену на одного человека и раскладывает итог на еду, посуду, работу официантов и логистику.

Живое демо — блок работает прямо здесь

Код блока

<section class="vbkkt">
  <div class="vbkkt-wrap">
    <div class="vbkkt-head">
      <h2 class="vbkkt-h2">Прикиньте бюджет банкета</h2>
      <p class="vbkkt-lead">Задайте число гостей, формат и длительность, и калькулятор соберёт смету и покажет, во сколько выйдет один гость.</p>
    </div>

    <div class="vbkkt-grid">
      <div class="vbkkt-panel">
        <div class="vbkkt-field">
          <div class="vbkkt-lbl"><span>Гостей</span><b id="vbkkt-gnum">60</b></div>
          <input type="range" id="vbkkt-guests" min="10" max="300" step="5" value="60" aria-label="Количество гостей">
          <div class="vbkkt-ticks"><i>10</i><i>150</i><i>300</i></div>
        </div>

        <div class="vbkkt-field">
          <span class="vbkkt-cap">Формат</span>
          <div class="vbkkt-opts" id="vbkkt-fmt" role="group" aria-label="Формат мероприятия"></div>
        </div>

        <div class="vbkkt-field">
          <span class="vbkkt-cap">Длительность</span>
          <div class="vbkkt-opts vbkkt-opts--row" id="vbkkt-hours" role="group" aria-label="Длительность мероприятия"></div>
        </div>

        <div class="vbkkt-field">
          <span class="vbkkt-cap">Что добавить</span>
          <div class="vbkkt-opts" id="vbkkt-extra" role="group" aria-label="Дополнительные услуги"></div>
        </div>
      </div>

      <aside class="vbkkt-out">
        <div class="vbkkt-sum">
          <span>Бюджет мероприятия</span>
          <b id="vbkkt-total">0 ₽</b>
        </div>
        <div class="vbkkt-per">
          <span>На одного гостя</span>
          <b id="vbkkt-per">0 ₽</b>
        </div>
        <div class="vbkkt-bar" id="vbkkt-bar" aria-hidden="true"></div>
        <ul class="vbkkt-rows" id="vbkkt-rows"></ul>
        <p class="vbkkt-note">Расчёт ориентировочный: итог зависит от меню, площадки и дня недели. Смету с раскладкой по блюдам присылаем после короткого разговора о поводе и вкусах гостей.</p>
        <a class="vbkkt-cta" href="#zayavka">Обсудить меню</a>
      </aside>
    </div>
  </div>
</section>

<style>
.vbkkt, .vbkkt *, .vbkkt *::before, .vbkkt *::after { box-sizing: border-box; }
.vbkkt {
  --acc: #8a5a2b; --acc-dark: #6f4720; --ink: #24201b; --muted: #6d645a; --line: #e5ddd1; --soft: #faf6ef; --bg: #f6f1e8;
  --c1: #8a5a2b; --c2: #c09155; --c3: #6f7d5a; --c4: #b9ac97;
  --f: var(--t-text-font, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Arial, sans-serif);
  --fh: var(--t-headline-font, var(--f));
  background: var(--bg); color: var(--ink); font-family: var(--f); padding: clamp(36px, 5vw, 82px) 20px;
}
.vbkkt-wrap { max-width: 1160px; margin: 0 auto; padding: 0 clamp(0px, 2vw, 40px); }
.vbkkt-head { max-width: 44em; margin-bottom: clamp(22px, 3vw, 36px); }
.vbkkt-h2 { font-family: var(--fh); font-size: clamp(26px, 3.4vw, 40px); line-height: 1.12; font-weight: 700; letter-spacing: -.02em; margin: 0 0 12px; }
.vbkkt-lead { font-size: 16px; line-height: 1.65; color: var(--muted); margin: 0; }

.vbkkt-grid { display: grid; grid-template-columns: minmax(0, 1.25fr) minmax(0, .9fr); gap: 18px; align-items: start; }
.vbkkt-panel { background: #fff; border: 1px solid var(--line); border-radius: 22px; padding: clamp(20px, 2.4vw, 30px); display: grid; gap: 26px; }
.vbkkt-lbl { display: flex; justify-content: space-between; align-items: baseline; margin-bottom: 12px; font-size: 13px; color: var(--muted); }
.vbkkt-lbl b { font-family: var(--fh); font-size: 24px; color: var(--ink); }
.vbkkt-cap { display: block; font-size: 13px; color: var(--muted); margin-bottom: 11px; }

.vbkkt input[type=range] { -webkit-appearance: none; appearance: none; width: 100%; height: 4px; border-radius: 4px; background: var(--line); outline: none; }
.vbkkt input[type=range]::-webkit-slider-thumb { -webkit-appearance: none; appearance: none; width: 24px; height: 24px; border-radius: 50%;
  background: var(--acc); border: 4px solid #fff; box-shadow: 0 1px 6px rgba(36,32,27,.25); cursor: pointer; }
.vbkkt input[type=range]::-moz-range-thumb { width: 18px; height: 18px; border-radius: 50%; background: var(--acc); border: 3px solid #fff; cursor: pointer; }
.vbkkt-ticks { display: flex; justify-content: space-between; margin-top: 7px; font-size: 11px; color: #a89e91; }
.vbkkt-ticks i { font-style: normal; }

.vbkkt-opts { display: grid; grid-template-columns: repeat(auto-fit, minmax(165px, 1fr)); gap: 9px; }
.vbkkt-opts--row { grid-template-columns: repeat(auto-fit, minmax(88px, 1fr)); }
.vbkkt-opt { -webkit-appearance: none; appearance: none; margin: 0; cursor: pointer; font: inherit; font-size: 16px; line-height: 1.3; text-align: left;
  padding: 13px 15px; border: 1.5px solid var(--line); border-radius: 13px; background: #fff !important; color: var(--ink) !important;
  transition: border-color .18s ease, background .18s ease; }
.vbkkt-opt:hover { border-color: var(--acc); }
.vbkkt-opt[aria-pressed="true"] { border-color: var(--acc); background: var(--soft) !important; font-weight: 600; }
.vbkkt-opt i { display: block; font-style: normal; font-size: 12.5px; font-weight: 400; color: var(--muted); margin-top: 4px; }
.vbkkt-opts--row .vbkkt-opt { text-align: center; }

.vbkkt-out { background: #fff; border: 1px solid var(--line); border-radius: 22px; padding: clamp(20px, 2.4vw, 28px); position: sticky; top: 20px; }
.vbkkt-sum span, .vbkkt-per span { display: block; font-size: 12.5px; color: var(--muted); margin-bottom: 6px; }
.vbkkt-sum b { display: block; font-family: var(--fh); font-size: clamp(26px, 2.9vw, 34px); line-height: 1.05; font-weight: 700; color: var(--acc); letter-spacing: -.02em; }
.vbkkt-per { padding-top: 16px; margin-top: 16px; border-top: 1px solid var(--line); }
.vbkkt-per b { display: block; font-family: var(--fh); font-size: 21px; font-weight: 600; }

.vbkkt-bar { display: flex; height: 8px; border-radius: 8px; overflow: hidden; background: var(--soft); margin: 18px 0 16px; }
.vbkkt-bar i { display: block; height: 100%; width: 0; transition: width .4s cubic-bezier(.22,1,.36,1); }
.vbkkt-rows { list-style: none; margin: 0; padding: 0; display: grid; gap: 11px; }
.vbkkt-rows li { display: flex; align-items: flex-start; gap: 10px; font-size: 13.5px; line-height: 1.45; color: var(--muted); }
.vbkkt-rows em { flex: 0 0 9px; width: 9px; height: 9px; border-radius: 3px; margin-top: 4px; }
.vbkkt-rows span { flex: 1; }
.vbkkt-rows span i { display: block; font-style: normal; font-size: 11.5px; color: #a89e91; margin-top: 2px; }
.vbkkt-rows b { color: var(--ink); font-weight: 600; white-space: nowrap; }
.vbkkt-note { margin: 18px 0 0; font-size: 12.5px; line-height: 1.6; color: var(--muted); }
.vbkkt-cta, .vbkkt-cta:link, .vbkkt-cta:visited { display: block; margin-top: 16px; text-align: center; text-decoration: none;
  background: var(--acc) !important; color: #fff !important; font-size: 15px; font-weight: 600; border-radius: 12px; padding: 15px 18px;
  transition: background .18s ease; }
.vbkkt-cta:hover { background: var(--acc-dark) !important; }

@media (max-width: 900px) { .vbkkt-grid { grid-template-columns: minmax(0, 1fr); } .vbkkt-out { position: static; } }
@media (prefers-reduced-motion: reduce) { .vbkkt-bar i, .vbkkt-opt, .vbkkt-cta { transition: none !important; } }
</style>

<script>
(function () {
  /* ── ТАРИФЫ И КОЭФФИЦИЕНТЫ: правьте только этот объект ── */
  var CFG = {
    formats: [
      { id: 'furshet', name: 'Фуршет', hint: 'канапе и закуски, гости стоят', per: 1850 },
      { id: 'banket',  name: 'Банкет',  hint: 'посадка, горячее и салаты',     per: 3200 },
      { id: 'coffee',  name: 'Кофе-брейк', hint: 'выпечка, кофе, чай',         per: 780 }
    ],
    hours: [
      { h: 2, k: 1 },
      { h: 3, k: 1.14 },
      { h: 4, k: 1.28 },
      { h: 6, k: 1.5 }
    ],
    dishes: 290,          /* аренда посуды и текстиля на гостя, ₽ */
    waiterGuests: 20,     /* сколько гостей на одного официанта */
    waiterShift: 4200,    /* смена официанта до 4 часов, ₽ */
    waiterExtra: 900,     /* доплата за каждый час сверх четырёх, ₽ */
    logisticBase: 4500,   /* подача, сборка и вывоз, ₽ */
    logisticGuest: 45     /* та же статья, добавка на гостя, ₽ */
  };

  var roots = document.querySelectorAll('.vbkkt');
  for (var i = 0; i < roots.length; i++) init(roots[i]);

  function init(root) {
    if (root.__vb) return; root.__vb = 1;
    var $ = function (s) { return root.querySelector(s); };
    var st = { guests: 60, format: 'furshet', hours: 3, dishes: true, waiters: true };

    function fmt(n) { return String(Math.round(n / 10) * 10).replace(/\B(?=(\d{3})+(?!\d))/g, ' ') + ' ₽'; }
    function guestWord(n) { var m = n % 100, k = n % 10; return (m > 10 && m < 20) || k === 0 || k > 4 ? 'гостей' : k === 1 ? 'гость' : 'гостя'; }
    function hourWord(n) { return n === 1 ? 'час' : n < 5 ? 'часа' : 'часов'; }

    $('#vbkkt-fmt').innerHTML = CFG.formats.map(function (f) {
      return '<button type="button" class="vbkkt-opt" data-id="' + f.id + '" aria-pressed="' + (f.id === st.format) + '">' +
        f.name + '<i>' + f.hint + ', ' + f.per + ' ₽ с гостя<\/i><\/button>';
    }).join('');

    $('#vbkkt-hours').innerHTML = CFG.hours.map(function (o) {
      return '<button type="button" class="vbkkt-opt" data-h="' + o.h + '" aria-pressed="' + (o.h === st.hours) + '">' +
        o.h + ' ' + hourWord(o.h) + '<\/button>';
    }).join('');

    $('#vbkkt-extra').innerHTML =
      '<button type="button" class="vbkkt-opt" data-x="dishes" aria-pressed="true">Посуда и текстиль<i>тарелки, приборы, скатерти<\/i><\/button>' +
      '<button type="button" class="vbkkt-opt" data-x="waiters" aria-pressed="true">Официанты<i>один человек на ' + CFG.waiterGuests + ' гостей<\/i><\/button>';

    function press(box, el) {
      var all = box.querySelectorAll('.vbkkt-opt');
      for (var k = 0; k < all.length; k++) all[k].setAttribute('aria-pressed', String(all[k] === el));
    }

    $('#vbkkt-fmt').addEventListener('click', function (e) {
      var b = e.target.closest('.vbkkt-opt'); if (!b) return;
      st.format = b.getAttribute('data-id'); press(this, b); render();
    });
    $('#vbkkt-hours').addEventListener('click', function (e) {
      var b = e.target.closest('.vbkkt-opt'); if (!b) return;
      st.hours = +b.getAttribute('data-h'); press(this, b); render();
    });
    $('#vbkkt-extra').addEventListener('click', function (e) {
      var b = e.target.closest('.vbkkt-opt'); if (!b) return;
      var key = b.getAttribute('data-x');
      st[key] = !st[key];
      b.setAttribute('aria-pressed', String(st[key]));
      render();
    });
    $('#vbkkt-guests').addEventListener('input', function () { st.guests = +this.value; render(); });

    function render() {
      var f = CFG.formats.filter(function (o) { return o.id === st.format; })[0];
      var hk = CFG.hours.filter(function (o) { return o.h === st.hours; })[0].k;
      var rows = [];

      var food = f.per * st.guests * hk;
      rows.push({ n: 'Еда и напитки', d: f.name + ', ' + st.hours + ' ' + hourWord(st.hours), v: food, c: 'var(--c1)' });

      if (st.dishes) {
        var dish = CFG.dishes * st.guests;
        rows.push({ n: 'Посуда и текстиль', d: CFG.dishes + ' ₽ на гостя', v: dish, c: 'var(--c2)' });
      }
      if (st.waiters) {
        var cnt = Math.max(1, Math.ceil(st.guests / CFG.waiterGuests));
        var extra = Math.max(0, st.hours - 4) * CFG.waiterExtra;
        var wage = cnt * (CFG.waiterShift + extra);
        rows.push({ n: 'Работа официантов', d: cnt + ' человек в смене', v: wage, c: 'var(--c3)' });
      }
      var log = CFG.logisticBase + CFG.logisticGuest * st.guests;
      rows.push({ n: 'Подача и вывоз', d: 'доставка, сборка стола, уборка', v: log, c: 'var(--c4)' });

      var total = rows.reduce(function (s, r) { return s + r.v; }, 0);

      $('#vbkkt-gnum').textContent = st.guests + ' ' + guestWord(st.guests);
      $('#vbkkt-total').textContent = fmt(total);
      $('#vbkkt-per').textContent = fmt(total / st.guests);
      $('#vbkkt-bar').innerHTML = rows.map(function (r) {
        return '<i style="background:' + r.c + '; width:' + (r.v / total * 100).toFixed(1) + '%"><\/i>';
      }).join('');
      $('#vbkkt-rows').innerHTML = rows.map(function (r) {
        return '<li><em style="background:' + r.c + '"><\/em><span>' + r.n + '<i>' + r.d + '<\/i><\/span><b>' + fmt(r.v) + '<\/b><\/li>';
      }).join('');
    }

    render();
  }
})();
</script>

Вставляется целиком в блок T123 «HTML-код». Внутри уже есть стили и скрипт, подключать ничего не нужно.

Как вставить блок в Tilda

  1. Скопируйте кодКнопка «Скопировать код» выше кладёт всё содержимое в буфер обмена.
  2. Добавьте блок T123В редакторе страницы: «Библиотека блоков» → «Другое» → T123 «HTML-код».
  3. Вставьте и сохранитеНажмите «Контент», вставьте код в поле, сохраните и обновите страницу.
  4. ОпубликуйтеВ превью скрипты Tilda работают не всегда — проверяйте на опубликованной странице.

Подробная инструкция с граблями — как вставить вайб-блок в Tilda.

Что менять под себя

Где в кодеЗа что отвечает
объект CFG в начале скриптацены форматов на гостя, поправки за длительность, ставки посуды и официантов
поля logisticBase и logisticGuestстоимость подачи, сборки и вывоза: фиксированная часть и добавка на гостя
атрибуты min, max и value у ползункаграницы числа гостей и значение по умолчанию
--c1, --c2, --c3, --c4 на корнецвета сегментов полосы и точек в списке статей
ссылка «Обсудить меню»адрес якоря к форме заявки на вашей странице

Калькулятор кейтеринга: зачем он нужен

Первый вопрос заказчика банкета звучит одинаково: во сколько это обойдётся на человека. Пока ответа нет, разговор стоит на месте, и менеджер тратит время на переписку ради одной цифры. Калькулятор отвечает за него: посетитель двигает число гостей, выбирает формат и сразу видит вилку бюджета. В заявку такой человек приходит уже с ожиданиями, близкими к вашему прайсу.

Итог складывается из четырёх статей, и каждая видна отдельной строкой с пояснением, откуда взялась сумма. Полоса над списком показывает доли статей в бюджете, поэтому заметно, что половину суммы держит еда, а официанты стоят меньше, чем кажется. Все ставки лежат в объекте CFG в начале скрипта: цена формата на гостя, поправка на длительность, аренда посуды, смена официанта и логистика. Отправки данных наружу нет, вся арифметика идёт в браузере, кнопка внизу ведёт к вашей форме на странице.

Вопросы по этому блоку

  • Допишите объект в массив formats внутри CFG: название, короткое пояснение и цену на гостя. Кнопка появится сама, разметку трогать не нужно.

  • Итоговая сумма делится на текущее число гостей, включая посуду, официантов и логистику. Поэтому при маленьких заказах цена на человека выше: фиксированная часть логистики распределяется на меньшее число людей.

  • Нет, форм внутри блока нет. Кнопка ведёт к форме на странице, а результат посетитель называет менеджеру или переносит в поле комментария.

  • Да. Код на этой странице копируется целиком, без регистрации и подписки. Указывать авторство не нужно, использовать можно в коммерческих проектах.

  • Нет. Все классы внутри имеют собственный префикс vbkkt, поэтому не пересекаются ни со стилями Tilda, ни с другими блоками. Скрипт привязан к своему элементу и запускается один раз, даже если блок вставлен дважды.

  • Раскладка перестраивается под узкие экраны, размеры заданы через clamp, поля ввода имеют шрифт 16 пикселей — iOS не будет зумить страницу при фокусе. Отдельной мобильной версии подключать не нужно.

Другие калькуляторы

Ещё блоки для ресторанов и доставки

Собрать сайт целиком, а не по блокам

Дровосеки делают сайты на Tilda под ключ: структура, тексты, дизайн, вёрстка и вайб-блоки под задачи бизнеса.