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

Квиз подбора врача для сайта клиники

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

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

Код блока

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

    <div class="vbkvr-box">
      <ol class="vbkvr-rail" id="vbkvr-rail"></ol>
      <div class="vbkvr-stage" id="vbkvr-stage" aria-live="polite"></div>
      <div class="vbkvr-foot">
        <button type="button" class="vbkvr-back" id="vbkvr-back" hidden>Назад</button>
        <span class="vbkvr-num" id="vbkvr-num"></span>
      </div>
    </div>
  </div>
</section>

<style>
.vbkvr, .vbkvr *, .vbkvr *::before, .vbkvr *::after { box-sizing: border-box; }
.vbkvr { --acc: #1c7f8c; --acc-dark: #166874; --ink: #1d2b2e; --muted: #5f7276; --line: #dfe8e9; --soft: #f2f7f7; --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;
}
.vbkvr-wrap { max-width: 940px; margin: 0 auto; padding: 0 clamp(0px, 2vw, 40px); }
.vbkvr-head { max-width: 44em; margin-bottom: clamp(20px, 2.6vw, 30px); }
.vbkvr-h2 { font-family: var(--fh); font-size: clamp(25px, 3.2vw, 38px); line-height: 1.14; font-weight: 600; letter-spacing: -.02em; margin: 0 0 12px; }
.vbkvr-lead { font-size: 16px; line-height: 1.65; color: var(--muted); margin: 0; }
.vbkvr-box { background: #fff; border: 1px solid var(--line); border-radius: 20px; padding: clamp(22px, 3vw, 40px); }
.vbkvr-rail { display: grid; grid-template-columns: repeat(4, 1fr); gap: 8px; list-style: none; margin: 0 0 clamp(22px, 2.6vw, 32px); padding: 0; }
.vbkvr-rail li { font-size: 12px; color: #93a4a7; padding-top: 10px; border-top: 3px solid var(--line); transition: .3s ease; }
.vbkvr-rail li.is-now { color: var(--acc); font-weight: 600; border-top-color: var(--acc); }
.vbkvr-rail li.is-done { color: var(--muted); border-top-color: var(--acc); }
.vbkvr-q { font-family: var(--fh); font-size: clamp(19px, 2.2vw, 25px); line-height: 1.28; font-weight: 600; margin: 0 0 18px; }
.vbkvr-opts { display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 10px; }
.vbkvr-opt { display: flex; align-items: flex-start; gap: 12px; width: 100%; border: 1px solid var(--line); margin: 0; -webkit-appearance: none; appearance: none; font: inherit; cursor: pointer; text-align: left; background: #fff !important; color: var(--ink) !important; border-radius: 14px; padding: 15px 17px; transition: .18s ease; }
.vbkvr-opt:hover { border-color: var(--acc); background: var(--soft) !important; }
.vbkvr-mark { flex: 0 0 18px; width: 18px; height: 18px; margin-top: 2px; border-radius: 50%; border: 2px solid #c3d3d5; transition: .18s ease; }
.vbkvr-opt:hover .vbkvr-mark { border-color: var(--acc); box-shadow: inset 0 0 0 4px var(--acc); }
.vbkvr-opt span { font-size: 15px; line-height: 1.45; }
.vbkvr-foot { display: flex; align-items: center; gap: 16px; margin-top: 22px; padding-top: 18px; border-top: 1px solid var(--line); }
.vbkvr-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; }
.vbkvr-back:hover { color: var(--acc) !important; }
.vbkvr-num { margin-left: auto; font-size: 13px; color: var(--muted); white-space: nowrap; }
.vbkvr-res { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: clamp(18px, 2.4vw, 30px); }
.vbkvr-tag { display: inline-block; font-size: 11.5px; font-weight: 600; color: var(--acc); background: var(--soft); border-radius: 999px; padding: 6px 13px; margin-bottom: 14px; }
.vbkvr-rttl { font-family: var(--fh); font-size: clamp(22px, 2.6vw, 30px); line-height: 1.16; font-weight: 600; margin: 0 0 12px }
.vbkvr-rtxt { font-size: 15.5px; line-height: 1.65; color: var(--muted); margin: 0 0 18px }
.vbkvr-when { font-size: 14px; background: var(--soft); border-radius: 12px; padding: 12px 15px; margin: 0; }
.vbkvr-doc { background: var(--soft); border-radius: 16px; padding: clamp(18px, 2.2vw, 26px); }
.vbkvr-doc__top { display: flex; align-items: center; gap: 14px; margin-bottom: 15px; }
.vbkvr-ava { flex: 0 0 54px; width: 54px; height: 54px; border-radius: 50%; }
.vbkvr-doc__name { font-family: var(--fh); font-size: 16.5px; font-weight: 600; margin: 0 0 3px; }
.vbkvr-doc__role { font-size: 13px; color: var(--muted); margin: 0 }
.vbkvr-facts { list-style: none; margin: 0 0 20px; padding: 0; display: grid; gap: 10px; }
.vbkvr-facts li { display: flex; justify-content: space-between; gap: 12px; font-size: 14px; padding-bottom: 10px; border-bottom: 1px solid var(--line); }
.vbkvr-facts li:last-child { border-bottom: 0; padding-bottom: 0; }
.vbkvr-facts b { font-weight: 600; text-align: right; }
.vbkvr-acts { display: grid; gap: 9px; }
.vbkvr-cta, .vbkvr-cta:link, .vbkvr-cta:visited, .vbkvr-cta:active, .vbkvr-cta:focus { display: flex; align-items: center; justify-content: center; min-height: 52px; border-radius: 12px; font-size: 16px; font-weight: 600; background: var(--acc); color: #fff !important; text-decoration: none !important; }
.vbkvr-cta:hover { background: var(--acc-dark); color: #fff !important; }
.vbkvr-again { min-height: 46px; border: 1px solid var(--line); margin: 0; appearance: none; font: inherit; font-size: 15px; cursor: pointer; border-radius: 12px; background: #fff !important; color: var(--ink) !important; }
.vbkvr-again:hover { border-color: var(--acc); }
@media (max-width: 600px) { .vbkvr-rail li { font-size: 0; } }
@media (prefers-reduced-motion: reduce) { .vbkvr * { transition: none !important; } }
</style>

<script>
(function () {
  var CTA_HREF = '#zapis';
  /* ── ШАГИ: p — очки направлениям, slot — удобное время ── */
  var STEPS = [
    { label: 'Жалоба', q: 'Что беспокоит сильнее всего?', opts: [
    { t: 'Головная боль, головокружение, онемение', p: { nev: 3 } },
    { t: 'Давление, перебои в сердце, одышка при нагрузке', p: { kar: 3 } },
    { t: 'Боль в животе, тяжесть после еды, изжога', p: { gas: 3 } },
    { t: 'Слабость, температура, кашель', p: { ter: 3 } }
   ]},
    { label: 'Срок', q: 'Как давно это продолжается?', opts: [
    { t: 'Несколько дней', p: { ter: 2 } },
    { t: 'Две или три недели', p: { ter: 1, nev: 1, kar: 1, gas: 1 } },
    { t: 'Больше месяца', p: { nev: 2, kar: 2, gas: 2 } },
    { t: 'Возвращается уже не первый год', p: { nev: 3, kar: 3, gas: 3 } }
   ]},
    { label: 'Опыт', q: 'Обращались с этим к врачу раньше?', opts: [
    { t: 'Иду впервые', p: { ter: 3 } },
    { t: 'Был у терапевта, направил дальше', p: { nev: 2, kar: 2, gas: 2 } },
    { t: 'Наблюдаюсь, нужен повторный приём', p: { nev: 2, kar: 2, gas: 2 } },
    { t: 'Обследовался в другом месте', p: { nev: 1, kar: 1, gas: 1 } }
   ]},
    { label: 'Время', q: 'Когда удобно прийти?', opts: [
    { t: 'Будни до обеда', slot: 'будни с 08:30 до 13:00' },
    { t: 'Будни после работы', slot: 'будни с 17:00 до 20:30' },
    { t: 'В выходной', slot: 'суббота с 09:00 до 15:00' },
    { t: 'Чем раньше, тем лучше', slot: 'ближайшее свободное окно' }
   ]}
  ];
  /* ── НАПРАВЛЕНИЯ ── */
  var LINES = {
    ter: { ttl: 'Терапевт', c: '#d7e5e6',
     txt: 'Приём начинают с терапевта: он собирает картину, назначает базовые анализы и решает, нужен ли профильный врач.',
     doc: 'Елена Прохорова', role: 'Терапевт, стаж 16 лет',
     facts: [['Длительность приёма', '40 минут'], ['Первичный приём', '2 400 ₽'], ['Взять с собой', 'выписки и список лекарств']] },
    nev: { ttl: 'Невролог', c: '#dfe0ec',
     txt: 'Головные боли, головокружение и онемение ведёт невролог. Врач проверит рефлексы, посмотрит шейный отдел и направит на снимок.',
     doc: 'Мария Столярова', role: 'Невролог, стаж 14 лет',
     facts: [['Длительность приёма', '50 минут'], ['Первичный приём', '3 100 ₽'], ['Взять с собой', 'снимки и заключения']] },
    kar: { ttl: 'Кардиолог', c: '#e7dcdc',
     txt: 'Давление, перебои и одышку смотрит кардиолог. На приёме снимают кардиограмму и решают, нужно ли суточное наблюдение.',
     doc: 'Андрей Головин', role: 'Кардиолог, стаж 19 лет',
     facts: [['Длительность приёма', '45 минут'], ['Приём с кардиограммой', '3 400 ₽'], ['Взять с собой', 'дневник давления за неделю']] },
    gas: { ttl: 'Гастроэнтеролог', c: '#e3e5d8',
     txt: 'Тяжесть, изжогу и боли в животе ведёт гастроэнтеролог. Врач уточнит питание, посмотрит анализы и назначит обследование.',
     doc: 'Ирина Батурина', role: 'Гастроэнтеролог, стаж 12 лет',
     facts: [['Длительность приёма', '45 минут'], ['Первичный приём', '3 200 ₽'], ['Взять с собой', 'анализы за полгода']] }
  };
  var roots = document.querySelectorAll('.vbkvr');
  for (var i = 0; i < roots.length; i++) init(roots[i]);
  function init(root) {
    if (root.__vb) return; root.__vb = 1;
    var idx = 0, score = { ter: 0, nev: 0, kar: 0, gas: 0 }, trail = [], slot = '';
    var rail = root.querySelector('#vbkvr-rail'), stage = root.querySelector('#vbkvr-stage');
    var back = root.querySelector('#vbkvr-back'), num = root.querySelector('#vbkvr-num');
    rail.innerHTML = STEPS.map(function (s) { return '<li>' + s.label + '<\/li>'; }).join('');
    var marks = rail.querySelectorAll('li');
    function paintRail(done) {
      for (var i = 0; i < marks.length; i++) marks[i].className = i < done ? 'is-done' : (i === done ? 'is-now' : '');
    }
    function drawStep() {
      var s = STEPS[idx];
      paintRail(idx);
      num.textContent = 'Вопрос ' + (idx + 1) + ' из ' + STEPS.length;
      back.hidden = idx === 0;
      stage.innerHTML = '<h3 class="vbkvr-q">' + s.q + '<\/h3><div class="vbkvr-opts">' +
        s.opts.map(function (o, i) {
          return '<button type="button" class="vbkvr-opt" data-i="' + i + '">' +
            '<span class="vbkvr-mark" aria-hidden="true"><\/span><span>' + o.t + '<\/span><\/button>';
        }).join('') + '<\/div>';
      var btns = stage.querySelectorAll('.vbkvr-opt');
      for (var k = 0; k < btns.length; k++) btns[k].addEventListener('click', pick);
      function pick() {
        var o = s.opts[+this.getAttribute('data-i')];
        trail.push({ p: o.p, slot: slot });
        if (o.p) for (var k2 in o.p) score[k2] += o.p[k2];
        if (o.slot) slot = o.slot;
        idx++;
        if (idx < STEPS.length) drawStep(); else drawResult();
      }
    }
    function avatar(c, ini) {
      return '<svg class="vbkvr-ava" viewBox="0 0 54 54" role="img" aria-label="Портрет врача" focusable="false">' +
        '<circle cx="27" cy="27" r="27" fill="' + c + '"/>' +
        '<text x="27" y="33" text-anchor="middle" font-family="inherit" font-size="17" font-weight="600" fill="#41565a">' + ini + '<\/text><\/svg>';
    }
    function drawResult() {
      paintRail(STEPS.length);
      num.textContent = 'Результат';
      back.hidden = false;
      var win = 'ter', best = -1;
      for (var key in score) { if (score[key] > best) { best = score[key]; win = key; } }
      var L = LINES[win];
      var ini = L.doc.split(' ').map(function (p) { return p.charAt(0); }).join('');
      stage.innerHTML = '<div class="vbkvr-res"><div>' +
        '<span class="vbkvr-tag">Направление<\/span>' +
        '<h3 class="vbkvr-rttl">' + L.ttl + '<\/h3>' +
        '<p class="vbkvr-rtxt">' + L.txt + '<\/p>' +
        '<p class="vbkvr-when">Удобное время: ' + (slot || 'ближайшее свободное окно') + '<\/p>' +
        '<\/div><div class="vbkvr-doc">' +
        '<div class="vbkvr-doc__top">' + avatar(L.c, ini) +
        '<div><p class="vbkvr-doc__name">' + L.doc + '<\/p><p class="vbkvr-doc__role">' + L.role + '<\/p><\/div><\/div>' +
        '<ul class="vbkvr-facts">' + L.facts.map(function (f) {
          return '<li><span>' + f[0] + '<\/span><b>' + f[1] + '<\/b><\/li>';
        }).join('') + '<\/ul>' +
        '<div class="vbkvr-acts"><a class="vbkvr-cta" href="' + CTA_HREF + '">Записаться<\/a>' +
        '<button type="button" class="vbkvr-again">Пройти заново<\/button><\/div><\/div><\/div>';
      var again = stage.querySelector('.vbkvr-again');
      if (again) again.addEventListener('click', function () {
        idx = 0; score = { ter: 0, nev: 0, kar: 0, gas: 0 }; trail = []; slot = ''; drawStep();
      });
    }
    back.addEventListener('click', function () {
      if (idx === 0) return;
      idx--;
      var last = trail.pop();
      if (last) {
        if (last.p) for (var k3 in last.p) score[k3] -= last.p[k3];
        slot = last.slot;
      }
      drawStep();
    });
    drawStep();
  }
})();
</script>

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

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

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

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

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

Где в кодеЗа что отвечает
массив STEPS в начале скриптавопросы, варианты ответа и очки направлениям
поле slot у последнего шагачасы приёма, которые подставляются в результат
объект LINESнаправления, врачи, длительность и стоимость приёма
переменная CTA_HREFкуда ведёт кнопка «Записаться»: якорь или виджет записи
подписи в railназвания шагов над вопросами

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

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

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

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

  • Допишите ключ в объект LINES, добавьте его в начальный счёт score и раздайте очки в вариантах ответа. Разметку править не нужно, результат соберётся автоматически.

  • Удалите последний объект массива STEPS. Строка с часами приёма подставит запасной текст «ближайшее свободное окно», остальное продолжит работать.

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

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

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

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

Другие квизы

Ещё блоки для медицины и стоматологии

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

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