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

Квиз подбора вида уборки для Tilda

Четыре шага о помещении и о том, что мешает, на выходе рекомендованный вид уборки с вилкой цены под выбранную площадь и временем работы бригады.

  • ТипКвиз
  • НишаКлининг
  • Вес кода13.7 КБ
  • Заголовок H1нет, только H2
  • Форма Tildaнет
  • Интерактив
  • Считает цену
Живое демо — блок работает прямо здесь

Код блока

<section class="vbkpu">
  <div class="vbkpu-wrap">
    <div class="vbkpu-head">
      <h2 class="vbkpu-h2">Какая уборка вам нужна</h2>
      <p class="vbkpu-lead">Четыре вопроса о помещении и о том, что мешает. В ответ покажем вид уборки, вилку цены и время.</p>
    </div>

    <div class="vbkpu-panel">
      <div class="vbkpu-track" id="vbkpu-track" aria-hidden="true"></div>
      <div class="vbkpu-meta">
        <span class="vbkpu-num" id="vbkpu-num">Шаг 1 из 4</span>
        <button type="button" class="vbkpu-back" id="vbkpu-back" hidden>Вернуться назад</button>
      </div>
      <div class="vbkpu-stage" id="vbkpu-stage" aria-live="polite"></div>
    </div>
  </div>
</section>

<style>
.vbkpu, .vbkpu *, .vbkpu *::before, .vbkpu *::after { box-sizing: border-box; }
.vbkpu {
  --acc: #0f8f7b; --acc-dark: #0b7263; --ink: #16211f; --muted: #5b6f6b; --line: #dde9e6; --soft: #f1f7f5;
  --f: var(--t-text-font, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Arial, sans-serif);
  --fh: var(--t-headline-font, var(--f));
  background: var(--soft); color: var(--ink); font-family: var(--f); padding: clamp(36px, 5vw, 80px) 20px;
}
.vbkpu-wrap { max-width: 920px; margin: 0 auto; padding: 0 clamp(0px, 2vw, 40px); }
.vbkpu-head { max-width: 40em; margin-bottom: clamp(20px, 2.6vw, 32px); }
.vbkpu-h2 { font-family: var(--fh); font-size: clamp(26px, 3.4vw, 40px); line-height: 1.1; font-weight: 700; letter-spacing: -.02em; margin: 0 0 12px; }
.vbkpu-lead { font-size: 16px; line-height: 1.6; color: var(--muted); margin: 0; }

.vbkpu-panel { background: #fff; border: 1px solid var(--line); border-radius: 26px; padding: clamp(22px, 3vw, 40px); }
.vbkpu-track { display: grid; grid-template-columns: repeat(4, 1fr); gap: 6px; margin-bottom: 16px; }
.vbkpu-track i { display: block; height: 5px; border-radius: 5px; background: var(--line); transition: background .3s ease; }
.vbkpu-track i.vbkpu-on { background: var(--acc); }
.vbkpu-meta { display: flex; align-items: center; gap: 16px; margin-bottom: 20px; }
.vbkpu-num { font-size: 13px; font-weight: 600; letter-spacing: .02em; color: var(--acc); }
.vbkpu-back { margin: 0 0 0 auto; padding: 0; border: 0; -webkit-appearance: none; appearance: none; background: none !important;
  cursor: pointer; font: inherit; font-size: 13.5px; color: var(--muted) !important; text-decoration: underline; }
.vbkpu-back:hover { color: var(--acc) !important; }

.vbkpu-q { font-family: var(--fh); font-size: clamp(20px, 2.2vw, 26px); line-height: 1.25; font-weight: 700; letter-spacing: -.015em; margin: 0 0 6px; }
.vbkpu-hint { font-size: 14px; line-height: 1.55; color: var(--muted); margin: 0 0 18px; }
.vbkpu-opts { display: grid; grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); gap: 10px; }
.vbkpu-opt { display: flex; align-items: flex-start; gap: 12px; width: 100%; min-height: 62px; padding: 15px 17px; margin: 0;
  border: 1.5px solid var(--line); -webkit-appearance: none; appearance: none; font: inherit; cursor: pointer; text-align: left;
  border-radius: 15px; background: #fff !important; color: var(--ink) !important; transition: .18s ease; }
.vbkpu-opt:hover { border-color: var(--acc); background: var(--soft) !important; }
.vbkpu-opt em { flex: 0 0 20px; height: 20px; margin-top: 2px; border: 1.5px solid var(--line); border-radius: 50%; }
.vbkpu-opt:hover em { border-color: var(--acc); box-shadow: inset 0 0 0 4px var(--acc); }
.vbkpu-opt span { font-size: 15.5px; line-height: 1.45; }
.vbkpu-opt span i { display: block; font-style: normal; font-size: 12.5px; color: var(--muted); margin-top: 4px; }

.vbkpu-res__tag { display: inline-block; font-size: 12px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase;
  color: var(--acc); background: var(--soft); border-radius: 999px; padding: 6px 14px; margin-bottom: 14px; }
.vbkpu-res__ttl { font-family: var(--fh); font-size: clamp(23px, 2.8vw, 32px); line-height: 1.15; font-weight: 700; letter-spacing: -.02em; margin: 0 0 10px; }
.vbkpu-res__txt { font-size: 15.5px; line-height: 1.65; color: var(--muted); margin: 0 0 20px; }
.vbkpu-facts { display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: 10px; margin-bottom: 18px; }
.vbkpu-facts div { background: var(--soft); border-radius: 15px; padding: 16px 18px; }
.vbkpu-facts b { display: block; font-family: var(--fh); font-size: 20px; font-weight: 700; letter-spacing: -.02em; }
.vbkpu-facts span { display: block; font-size: 12.5px; color: var(--muted); margin-top: 5px; }
.vbkpu-list { list-style: none; margin: 0 0 22px; padding: 0; display: grid; gap: 9px; }
.vbkpu-list li { position: relative; padding-left: 26px; font-size: 14.5px; line-height: 1.5; color: var(--muted); }
.vbkpu-list li::before { content: ''; position: absolute; left: 4px; top: 6px; width: 6px; height: 11px;
  border: solid var(--acc); border-width: 0 2px 2px 0; transform: rotate(45deg); }
.vbkpu-acts { display: flex; flex-wrap: wrap; gap: 10px; }
.vbkpu-cta, .vbkpu-cta:link, .vbkpu-cta:visited, .vbkpu-cta:active, .vbkpu-cta:focus { display: inline-flex; align-items: center; justify-content: center;
  min-height: 54px; padding: 0 28px; border-radius: 13px; font-size: 16px; font-weight: 600;
  background: var(--acc); color: #fff !important; text-decoration: none !important; }
.vbkpu-cta:hover { background: var(--acc-dark); color: #fff !important; }
.vbkpu-again { min-height: 54px; padding: 0 24px; margin: 0; border: 1.5px solid var(--line); -webkit-appearance: none; appearance: none;
  font: inherit; font-size: 16px; cursor: pointer; border-radius: 13px; background: #fff !important; color: var(--ink) !important; }
.vbkpu-again:hover { border-color: var(--acc); }

@media (prefers-reduced-motion: reduce) { .vbkpu * { transition: none !important; } }
</style>

<script>
(function () {
  var STEPS = [
    { q: 'Что убираем?', h: 'От него зависит состав работ.', opts: [
      { t: 'Квартиру', s: 'мебель и техника на месте', p: { sup: 1, gen: 1, rep: 0, off: 0 } },
      { t: 'Дом или таунхаус', s: 'два этажа и лестница', p: { sup: 1, gen: 2, rep: 0, off: 0 } },
      { t: 'Офис или салон', s: 'рабочие места и зона гостей', p: { sup: 0, gen: 0, rep: 0, off: 3 } },
      { t: 'После ремонта', s: 'пыль и следы клея', p: { sup: 0, gen: 0, rep: 4, off: 0 } }
    ]},
    { q: 'Какая площадь?', h: 'Задаёт цену и состав бригады.', opts: [
      { t: 'До 40 м²', s: 'студия', a: 0, p: { sup: 1, gen: 0, rep: 0, off: 0 } },
      { t: 'От 40 до 60 м²', s: 'двухкомнатная', a: 1, p: { sup: 1, gen: 1, rep: 0, off: 0 } },
      { t: 'От 60 до 90 м²', s: 'трёхкомнатная', a: 2, p: { sup: 0, gen: 1, rep: 0, off: 1 } },
      { t: 'Больше 90 м²', s: 'дом', a: 3, p: { sup: 0, gen: 1, rep: 1, off: 1 } }
    ]},
    { q: 'Что беспокоит сильнее всего?', h: 'Главная причина вызвать клинеров.', opts: [
      { t: 'Пыль и полы', s: 'порядок разъехался за неделю', p: { sup: 3, gen: 0, rep: 0, off: 1 } },
      { t: 'Налёт и жир на кухне', s: 'обычной уборкой не берётся', p: { sup: 0, gen: 3, rep: 0, off: 0 } },
      { t: 'Строительная пыль везде', s: 'на шкафах и в углах', p: { sup: 0, gen: 0, rep: 4, off: 0 } },
      { t: 'Мутные окна', s: 'стёкла, рамы, подоконники', p: { sup: 1, gen: 2, rep: 1, off: 1 } }
    ]},
    { q: 'Как часто нужна уборка?', h: 'По графику считаем по абонементу.', opts: [
      { t: 'Один раз', s: 'разовый выезд', p: { sup: 1, gen: 2, rep: 1, off: 0 } },
      { t: 'Раз в неделю', s: 'график и свой клинер', p: { sup: 3, gen: 0, rep: 0, off: 1 } },
      { t: 'Два раза в месяц', s: 'между генеральными', p: { sup: 2, gen: 1, rep: 0, off: 1 } },
      { t: 'Каждый рабочий день', s: 'офис или салон', p: { sup: 0, gen: 0, rep: 0, off: 4 } }
    ]}
  ];

  var AREAS = [ [30, 40], [40, 60], [60, 90], [90, 130] ];

  var KIND = {
    sup: { ttl: 'Поддерживающая уборка', href: '#raschet', rate: [60, 85], hour: 0.07, crew: 'Один клинер',
      txt: 'Регулярный формат: полы, пыль, санузел, кухня и вынос мусора. Держит порядок между генеральными уборками.',
      list: ['Полы, пыль и зеркала', 'Санузел и кухня'] },
    gen: { ttl: 'Генеральная уборка', href: '#raschet', rate: [130, 175], hour: 0.14, crew: 'Двое клинеров',
      txt: 'Отодвигаем мебель, моем внутри шкафов и техники, снимаем налёт и жир.',
      list: ['Внутри шкафов и техники', 'Налёт, окна и двери'] },
    rep: { ttl: 'Уборка после ремонта', href: '#raschet', rate: [180, 240], hour: 0.19, crew: 'Бригада из трёх человек',
      txt: 'Строительная пыль оседает трижды, поэтому проходим помещение в три круга с промышленным пылесосом.',
      list: ['Три прохода по поверхностям', 'Плёнка, клей и краска'] },
    off: { ttl: 'Обслуживание офиса по графику', href: '#raschet', rate: [45, 70], hour: 0.05, crew: 'Клинер по расписанию',
      txt: 'При потоке людей выгоднее график: клинер приходит в удобное время и работает по регламенту.',
      list: ['Рабочие места и санузлы', 'Перегородки и входная группа'] }
  };

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

  function init(root) {
    if (root.__vb) return; root.__vb = 1;
    var stage = root.querySelector('#vbkpu-stage'), track = root.querySelector('#vbkpu-track');
    var num = root.querySelector('#vbkpu-num'), back = root.querySelector('#vbkpu-back');
    var idx = 0, score, trail, area;

    track.innerHTML = new Array(STEPS.length + 1).join('<i><\/i>');
    var cells = track.querySelectorAll('i');

    function money(n) { return String(Math.round(n / 100) * 100).replace(/\B(?=(\d{3})+(?!\d))/g, ' '); }

    function reset() { score = { sup: 0, gen: 0, rep: 0, off: 0 }; trail = []; area = 1; idx = 0; }

    function marks(done) {
      for (var c = 0; c < cells.length; c++) cells[c].className = c < done ? 'vbkpu-on' : '';
    }

    function drawStep() {
      var s = STEPS[idx];
      marks(idx);
      num.textContent = 'Шаг ' + (idx + 1) + ' из ' + STEPS.length;
      back.hidden = idx === 0;
      var h = '<h3 class="vbkpu-q">' + s.q + '<\/h3><p class="vbkpu-hint">' + s.h + '<\/p><div class="vbkpu-opts">';
      for (var j = 0; j < s.opts.length; j++) {
        h += '<button type="button" class="vbkpu-opt" data-i="' + j + '"><em><\/em><span>' + s.opts[j].t +
          '<i>' + s.opts[j].s + '<\/i><\/span><\/button>';
      }
      stage.innerHTML = h + '<\/div>';
      var bs = stage.querySelectorAll('.vbkpu-opt');
      for (var b = 0; b < bs.length; b++) {
        bs[b].addEventListener('click', function () {
          var o = STEPS[idx].opts[+this.getAttribute('data-i')];
          trail.push({ p: o.p, a: area });
          for (var k in o.p) { if (o.p.hasOwnProperty(k)) score[k] += o.p[k]; }
          if (typeof o.a === 'number') area = o.a;
          idx++;
          if (idx < STEPS.length) drawStep(); else drawResult();
        });
      }
    }

    function drawResult() {
      marks(STEPS.length);
      num.textContent = 'Готово';
      back.hidden = false;
      var win = 'sup', k;
      for (k in score) { if (score.hasOwnProperty(k) && score[k] > score[win]) win = k; }
      var d = KIND[win], ar = AREAS[area];
      var lo = money(d.rate[0] * ar[0]), hi = money(d.rate[1] * ar[1]);
      var t1 = Math.max(2, Math.round(ar[0] * d.hour)), t2 = Math.max(t1 + 1, Math.round(ar[1] * d.hour));
      var h = '<span class="vbkpu-res__tag">Рекомендуем<\/span><h3 class="vbkpu-res__ttl">' + d.ttl + '<\/h3>' +
        '<p class="vbkpu-res__txt">' + d.txt + '<\/p><div class="vbkpu-facts">' +
        '<div><b>' + lo + ' - ' + hi + ' ₽<\/b><span>вилка цены на вашу площадь<\/span><\/div>' +
        '<div><b>' + t1 + '-' + t2 + ' ч<\/b><span>часов работы на объекте<\/span><\/div>' +
        '<div><b>' + d.crew + '<\/b><span>состав бригады<\/span><\/div><\/div><ul class="vbkpu-list">';
      for (var j = 0; j < d.list.length; j++) h += '<li>' + d.list[j] + '<\/li>';
      h += '<\/ul><div class="vbkpu-acts"><a class="vbkpu-cta" href="' + d.href + '">Перейти к расчёту<\/a>' +
        '<button type="button" class="vbkpu-again">Ответить заново<\/button><\/div>';
      stage.innerHTML = h;
      var again = stage.querySelector('.vbkpu-again');
      if (again) again.addEventListener('click', function () { reset(); drawStep(); });
    }

    back.addEventListener('click', function () {
      if (idx === 0) return;
      idx--;
      var last = trail.pop();
      if (last) {
        for (var k in last.p) { if (last.p.hasOwnProperty(k)) score[k] -= last.p[k]; }
        area = last.a;
      }
      drawStep();
    });

    reset();
    drawStep();
  }
})();
</script>

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

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

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

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

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

Где в кодеЗа что отвечает
массив STEPS в начале скриптавопросы, подсказки, варианты ответов и очки за каждый
объект KINDвиды уборки: описание, ставка за м², норма часов, состав бригады
массив AREASграницы диапазонов площади, по ним считается вилка цены
--acc и --soft в стиляхцвет прогресса, кнопки результата и фона секции

Квиз подбора уборки: зачем он нужен

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

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

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

  • В объекте KIND у каждого формата есть поле rate с двумя числами: минимальная и максимальная ставка за квадратный метр. Вилка получается умножением этих ставок на границы диапазона площади из массива AREAS.

  • Да, добавьте объект в массив STEPS. Полоса прогресса рисуется по длине массива, поэтому лишних правок в стилях не потребуется. Следите, чтобы ключи очков в новых ответах совпадали с ключами объекта KIND.

  • Ответы остаются в браузере, запросов к сторонним сервисам блок не делает. Если нужна передача в заявку, поставьте после квиза блок с формой Tilda и подставьте выбранный вид уборки в скрытое поле.

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

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

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

Другие квизы

Ещё блоки для клининговых компаний

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

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