Квиз подбора способа перевозки для Tilda
Четыре вопроса о грузе и маршруте, по сумме очков блок предлагает сборную отправку, отдельную машину или экспресс с вилкой срока и порядком цены.
<section class="vbkpp">
<div class="vbkpp-wrap">
<div class="vbkpp-head">
<h2 class="vbkpp-h2">Подберём способ отправки за четыре вопроса</h2>
<p class="vbkpp-lead">Спросим про груз, объём, маршрут и срок. В ответ покажем подходящий способ перевозки, вилку срока и порядок цены.</p>
</div>
<div class="vbkpp-box">
<ol class="vbkpp-steps" id="vbkpp-steps"></ol>
<div class="vbkpp-stage" id="vbkpp-stage" aria-live="polite"></div>
<div class="vbkpp-foot">
<button type="button" class="vbkpp-back" id="vbkpp-back" hidden>Вернуться на шаг назад</button>
<span class="vbkpp-num" id="vbkpp-num"></span>
</div>
</div>
</div>
</section>
<style>
.vbkpp, .vbkpp *, .vbkpp *::before, .vbkpp *::after { box-sizing: border-box; }
.vbkpp {
--acc: #1c5f8b; --acc-dark: #164a6d; --ink: #16202a; --muted: #5b6a78; --line: #dbe3ea; --soft: #f1f5f9; --warm: #c8761c;
--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;
}
.vbkpp-wrap { max-width: 940px; margin: 0 auto; padding: 0 clamp(0px, 2vw, 40px); }
.vbkpp-head { max-width: 42em; margin-bottom: clamp(20px, 3vw, 32px); }
.vbkpp-h2 { font-family: var(--fh); font-size: clamp(26px, 3.4vw, 40px); line-height: 1.1; font-weight: 700; letter-spacing: -.022em; margin: 0 0 12px; }
.vbkpp-lead { font-size: 16px; line-height: 1.6; color: var(--muted); margin: 0; }
.vbkpp-box { background: #fff; border: 1px solid var(--line); border-radius: 22px; padding: clamp(22px, 3vw, 40px); }
.vbkpp-steps { list-style: none; margin: 0 0 26px; padding: 0; display: flex; align-items: center; gap: 8px; }
.vbkpp-steps li { flex: 1 1 0; display: flex; align-items: center; gap: 8px; font-size: 12px; color: #93a2b0; }
.vbkpp-steps i { flex: 0 0 26px; width: 26px; height: 26px; border-radius: 50%; border: 1.5px solid var(--line);
display: flex; align-items: center; justify-content: center; font-style: normal; font-size: 12px; font-weight: 600;
background: #fff; transition: .2s ease; }
.vbkpp-steps s { flex: 1 1 auto; height: 2px; background: var(--line); text-decoration: none; border-radius: 2px; transition: background .25s ease; }
.vbkpp-steps li.vbkpp-st--on i { border-color: var(--acc); color: var(--acc); }
.vbkpp-steps li.vbkpp-st--done i { border-color: var(--acc); background: var(--acc); color: #fff; }
.vbkpp-steps li.vbkpp-st--done s { background: var(--acc); }
.vbkpp-q { font-family: var(--fh); font-size: clamp(19px, 2.1vw, 26px); line-height: 1.26; font-weight: 700; margin: 0 0 20px; letter-spacing: -.015em; }
.vbkpp-opts { display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 10px; }
.vbkpp-opt { display: block; width: 100%; text-align: left; border: 1.5px solid var(--line); margin: 0; padding: 16px 18px;
-webkit-appearance: none; appearance: none; font: inherit; cursor: pointer; border-radius: 14px;
background: #fff !important; color: var(--ink) !important; transition: .18s ease; }
.vbkpp-opt:hover { border-color: var(--acc); background: var(--soft) !important; }
.vbkpp-opt b { display: block; font-size: 15.5px; font-weight: 600; line-height: 1.35; margin-bottom: 5px; }
.vbkpp-opt span { display: block; font-size: 13px; line-height: 1.5; color: var(--muted); }
.vbkpp-foot { display: flex; align-items: center; gap: 16px; margin-top: 24px; }
.vbkpp-back { border: 0; margin: 0; padding: 0; -webkit-appearance: none; appearance: none; background: none !important;
cursor: pointer; font: inherit; font-size: 14px; color: var(--muted) !important; text-decoration: underline; text-underline-offset: 3px; }
.vbkpp-back:hover { color: var(--acc) !important; }
.vbkpp-num { margin-left: auto; font-size: 13px; color: #93a2b0; }
.vbkpp-res__tag { display: inline-block; font-size: 12px; font-weight: 600; letter-spacing: .02em; color: var(--warm);
background: #fcf3e8; border-radius: 999px; padding: 7px 14px; margin-bottom: 14px; }
.vbkpp-res__ttl { font-family: var(--fh); font-size: clamp(23px, 2.9vw, 33px); line-height: 1.12; font-weight: 700; letter-spacing: -.02em; margin: 0 0 12px; }
.vbkpp-res__txt { font-size: 16px; line-height: 1.65; color: var(--muted); margin: 0 0 22px; max-width: 44em; }
.vbkpp-res__grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: 10px; margin-bottom: 22px; }
.vbkpp-res__grid div { background: var(--soft); border-radius: 14px; padding: 16px 18px; }
.vbkpp-res__grid b { display: block; font-family: var(--fh); font-size: 19px; font-weight: 700; letter-spacing: -.01em; }
.vbkpp-res__grid span { display: block; font-size: 12.5px; line-height: 1.45; color: var(--muted); margin-top: 5px; }
.vbkpp-fit { list-style: none; margin: 0 0 24px; padding: 0; display: grid; gap: 8px; }
.vbkpp-fit li { position: relative; padding-left: 22px; font-size: 14.5px; line-height: 1.55; color: var(--ink); }
.vbkpp-fit li::before { content: ''; position: absolute; left: 2px; top: 7px; width: 7px; height: 7px; border-radius: 50%; background: var(--acc); }
.vbkpp-acts { display: flex; flex-wrap: wrap; gap: 10px; }
.vbkpp-cta, .vbkpp-cta:link, .vbkpp-cta:visited, .vbkpp-cta:active, .vbkpp-cta:focus { display: inline-flex; align-items: center; justify-content: center;
min-height: 54px; padding: 0 28px; border-radius: 12px; font-size: 16px; font-weight: 600;
background: var(--acc); color: #fff !important; text-decoration: none !important; transition: background .2s ease; }
.vbkpp-cta:hover { background: var(--acc-dark); color: #fff !important; }
.vbkpp-again { min-height: 54px; padding: 0 24px; border: 1.5px solid var(--line); margin: 0; -webkit-appearance: none; appearance: none;
font: inherit; font-size: 16px; cursor: pointer; border-radius: 12px; background: #fff !important; color: var(--ink) !important; }
.vbkpp-again:hover { border-color: var(--acc); }
@media (max-width: 620px) {
.vbkpp-steps li { font-size: 0; gap: 5px; }
.vbkpp-opts { grid-template-columns: minmax(0, 1fr); }
.vbkpp-acts .vbkpp-cta, .vbkpp-acts .vbkpp-again { width: 100%; }
}
@media (prefers-reduced-motion: reduce) { .vbkpp * { transition: none !important; } }
</style>
<script>
(function () {
/* ШАГИ: p - очки способам перевозки sbor, mash, expr */
var STEPS = [
{ l: 'Груз', q: 'Что нужно отправить?', opts: [
{ t: 'Коробки, мешки, паллеты', d: 'Тарный груз без особых требований', p: { sbor: 2, mash: 0, expr: 1 } },
{ t: 'Оборудование или станок', d: 'Тяжёлое место, аккуратная погрузка', p: { sbor: 0, mash: 2, expr: 0 } },
{ t: 'Стройматериалы', d: 'Длинномер, профиль, плита, смеси', p: { sbor: 1, mash: 2, expr: 0 } },
{ t: 'Хрупкое или температурный груз', d: 'Стекло, электроника, продукты', p: { sbor: 0, mash: 1, expr: 2 } }
]},
{ l: 'Объём', q: 'Какой объём у отправки?', opts: [
{ t: 'До одного кубометра', d: 'Несколько коробок до 200 килограммов', p: { sbor: 2, mash: 0, expr: 2 } },
{ t: 'От одного до пяти кубометров', d: 'Три-четыре паллеты, до полутора тонн', p: { sbor: 2, mash: 1, expr: 1 } },
{ t: 'От пяти до пятнадцати кубометров', d: 'Половина фуры или пятитонник', p: { sbor: 1, mash: 2, expr: 0 } },
{ t: 'Больше пятнадцати кубометров', d: 'Полная загрузка машины', p: { sbor: 0, mash: 3, expr: 0 } }
]},
{ l: 'Маршрут', q: 'Откуда и куда идёт груз?', opts: [
{ t: 'Внутри города или области', d: 'Плечо до 200 километров', p: { sbor: 1, mash: 2, expr: 1 } },
{ t: 'Между городами маршрутной сети', d: 'Оба адреса рядом с терминалами', p: { sbor: 3, mash: 0, expr: 1 } },
{ t: 'В отдалённый район или посёлок', d: 'Нужен подвоз до адреса', p: { sbor: 1, mash: 2, expr: 0 } },
{ t: 'Несколько точек в одном рейсе', d: 'Забор или выгрузка по адресам', p: { sbor: 0, mash: 3, expr: 0 } }
]},
{ l: 'Срок', q: 'Насколько срочно нужен груз на месте?', opts: [
{ t: 'Срок некритичный', d: 'Подождём несколько дней ради цены', p: { sbor: 3, mash: 0, expr: 0 } },
{ t: 'Нужно к конкретной дате', d: 'Есть окно выгрузки у получателя', p: { sbor: 1, mash: 2, expr: 1 } },
{ t: 'Нужно завтра или послезавтра', d: 'Простой линии или срыв монтажа', p: { sbor: 0, mash: 1, expr: 3 } },
{ t: 'Груз идёт без перегрузов', d: 'Одна машина от двери до двери', p: { sbor: 0, mash: 3, expr: 1 } }
]}
];
/* СПОСОБЫ ПЕРЕВОЗКИ: вилка срока и цены */
var WAYS = {
sbor: { ttl: 'Сборный груз', tag: 'Подходящий способ', href: '#sbornye-gruzy',
txt: 'Отправка едет в одной машине с грузами других клиентов, поэтому рейс делится на всех. Груз принимают на терминале, маркируют и ставят на ближайший рейс по расписанию.',
facts: [['2-6 рабочих дней', 'срок по сети терминалов'], ['от 1 900 до 12 000 ₽', 'отправка до 2 м³'], ['от 20 кг', 'минимальный объём']],
fit: ['Забор с адреса и выдача на терминале входят в тариф', 'Движение отправки видно в личном кабинете'] },
mash: { ttl: 'Отдельная машина', tag: 'Подходящий способ', href: '#otdelnaya-mashina',
txt: 'Машина подаётся под ваш груз и идёт от адреса до адреса без перегрузов на терминалах. Так возят оборудование, длинномер и рейсы с несколькими точками погрузки.',
facts: [['1-4 дня', 'в зависимости от плеча'], ['от 28 000 до 120 000 ₽', 'по маршруту и кузову'], ['до 20 тонн', 'грузоподъёмность фуры']],
fit: ['Кузов под груз: тент, изотерм, рефрижератор, борт', 'Два часа простоя под погрузкой входят в рейс'] },
expr: { ttl: 'Экспресс-доставка', tag: 'Подходящий способ', href: '#ekspress',
txt: 'Груз идёт прямым рейсом с приоритетом на терминале и выдаётся в первый час работы склада. Так возят запчасти для остановленной линии и хрупкие места.',
facts: [['1-2 дня', 'по маршрутной сети'], ['от 4 500 до 26 000 ₽', 'отправка до 1,5 м³'], ['до 18:00', 'приём заявки в день отправки']],
fit: ['Дата приезда закрепляется в заявке до погрузки', 'Температурный режим для продуктов и лекарств'] }
};
var roots = document.querySelectorAll('.vbkpp');
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('#vbkpp-stage');
var steps = root.querySelector('#vbkpp-steps');
var back = root.querySelector('#vbkpp-back');
var num = root.querySelector('#vbkpp-num');
var idx = 0, score, trail;
function reset() { idx = 0; score = { sbor: 0, mash: 0, expr: 0 }; trail = []; }
reset();
steps.innerHTML = STEPS.map(function (s, i) {
return '<li data-i="' + i + '"><i>' + (i + 1) + '<\/i><span>' + s.l + '<\/span>' +
(i < STEPS.length - 1 ? '<s><\/s>' : '') + '<\/li>';
}).join('');
function marks(done) {
var li = steps.querySelectorAll('li');
for (var j = 0; j < li.length; j++) {
li[j].className = j < done ? 'vbkpp-st--done' : (j === done ? 'vbkpp-st--on' : '');
}
}
function drawStep() {
var s = STEPS[idx];
marks(idx);
back.hidden = idx === 0;
num.textContent = 'Шаг ' + (idx + 1) + ' из ' + STEPS.length;
stage.innerHTML = '<h3 class="vbkpp-q">' + s.q + '<\/h3><div class="vbkpp-opts">' +
s.opts.map(function (o, i) {
return '<button type="button" class="vbkpp-opt" data-i="' + i + '"><b>' + o.t + '<\/b><span>' + o.d + '<\/span><\/button>';
}).join('') + '<\/div>';
var bs = stage.querySelectorAll('.vbkpp-opt');
for (var j = 0; j < bs.length; j++) bs[j].addEventListener('click', pick);
function pick() {
var o = s.opts[+this.getAttribute('data-i')];
trail.push(o.p);
for (var k in o.p) if (o.p.hasOwnProperty(k)) score[k] += o.p[k];
idx++;
if (idx < STEPS.length) drawStep(); else drawResult();
}
}
function drawResult() {
marks(STEPS.length);
back.hidden = false;
num.textContent = 'Результат подбора';
var win = 'sbor';
for (var k in score) if (score.hasOwnProperty(k) && score[k] > score[win]) win = k;
var w = WAYS[win];
stage.innerHTML = '<div><span class="vbkpp-res__tag">' + w.tag + '<\/span>' +
'<h3 class="vbkpp-res__ttl">' + w.ttl + '<\/h3>' +
'<p class="vbkpp-res__txt">' + w.txt + '<\/p>' +
'<div class="vbkpp-res__grid">' + w.facts.map(function (f) {
return '<div><b>' + f[0] + '<\/b><span>' + f[1] + '<\/span><\/div>';
}).join('') + '<\/div>' +
'<ul class="vbkpp-fit">' + w.fit.map(function (t) { return '<li>' + t + '<\/li>'; }).join('') + '<\/ul>' +
'<div class="vbkpp-acts"><a class="vbkpp-cta" href="' + w.href + '">Оставить груз на расчёт<\/a>' +
'<button type="button" class="vbkpp-again">Пройти заново<\/button><\/div><\/div>';
var again = stage.querySelector('.vbkpp-again');
if (again) again.addEventListener('click', function () { reset(); drawStep(); });
}
back.addEventListener('click', function () {
if (idx >= STEPS.length) { idx = STEPS.length - 1; }
else if (idx > 0) { idx--; }
else { return; }
var p = trail.pop();
if (p) for (var k in p) if (p.hasOwnProperty(k)) score[k] -= p[k];
drawStep();
});
drawStep();
}
})();
</script> Вставляется целиком в блок T123 «HTML-код». Внутри уже есть стили и скрипт, подключать ничего не нужно.
Как вставить блок в Tilda
- Скопируйте кодКнопка «Скопировать код» выше кладёт всё содержимое в буфер обмена.
- Добавьте блок T123В редакторе страницы: «Библиотека блоков» → «Другое» → T123 «HTML-код».
- Вставьте и сохранитеНажмите «Контент», вставьте код в поле, сохраните и обновите страницу.
- ОпубликуйтеВ превью скрипты Tilda работают не всегда — проверяйте на опубликованной странице.
Подробная инструкция с граблями — как вставить вайб-блок в Tilda.
Что менять под себя
| Где в коде | За что отвечает |
|---|---|
массив STEPS в начале скрипта | вопросы, подписи шагов, тексты ответов и очки за каждый |
объект WAYS | способы перевозки: описание, вилка срока и цены, состав тарифа |
href внутри WAYS | куда ведёт кнопка результата для каждого способа отправки |
--acc и --warm | цвет дорожки прогресса, кнопки результата и плашки над заголовком |
Квиз подбора перевозки: зачем он нужен
Отправитель редко знает, что ему выгоднее: поставить груз в сборный рейс или заказать машину целиком. Квиз спрашивает про тип груза, объём, маршрут и срочность, а затем показывает подходящий способ перевозки. В результате видны вилка срока, порядок цены и три пункта о том, что входит в тариф. Это снимает половину вопросов до звонка диспетчеру.
Прогресс показан нумерованной дорожкой из четырёх шагов с подписями, пройденные шаги закрашиваются. Кнопка возврата пересчитывает очки обратно, поэтому ответ можно поменять даже с экрана результата. Вопросы и способы перевозки лежат в двух структурах в начале скрипта, добавление пятого шага сводится к одному объекту.
Вопросы по этому блоку
-
Ключи объекта WAYS должны совпадать с ключами очков в ответах. Если способов станет четыре, добавьте ключ в объект, пропишите очки в ответах и поправьте начальное значение счётчика в функции reset.
-
Дорожка строится по длине массива STEPS, поэтому число шагов может быть любым. На узких экранах подписи скрываются и остаются только кружки с номерами, пять или шесть шагов помещаются без переноса.
-
Ответы остаются в браузере и наружу не уходят, запросов к сторонним сервисам блок не делает. Если нужна передача в заявку, поставьте после квиза блок с формой Tilda и подставьте выбранный способ в скрытое поле.
-
Да. Код на этой странице копируется целиком, без регистрации и подписки. Указывать авторство не нужно, использовать можно в коммерческих проектах.
-
Нет. Все классы внутри имеют собственный префикс vbkpp, поэтому не пересекаются ни со стилями Tilda, ни с другими блоками. Скрипт привязан к своему элементу и запускается один раз, даже если блок вставлен дважды.
-
Раскладка перестраивается под узкие экраны, размеры заданы через clamp, поля ввода имеют шрифт 16 пикселей — iOS не будет зумить страницу при фокусе. Отдельной мобильной версии подключать не нужно.
Другие квизы
Ещё блоки для логистики и грузоперевозок
Собрать сайт целиком, а не по блокам
Дровосеки делают сайты на Tilda под ключ: структура, тексты, дизайн, вёрстка и вайб-блоки под задачи бизнеса.