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

Блок отзывов клиентов для салона красоты

Карусель на три карточки с оценкой, услугой и датой. Листается свайпом, стрелками и точками, на паузу встаёт при наведении.

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

Код блока

<section class="vboks">
  <div class="vboks-wrap">
    <div class="vboks-head">
      <h2 class="vboks-h2">Что говорят клиентки</h2>
      <div class="vboks-arrows">
        <button type="button" class="vboks-arrow" data-dir="-1" aria-label="Предыдущий отзыв">
          <svg viewBox="0 0 20 20" aria-hidden="true" focusable="false"><path d="M12 4l-6 6 6 6" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/></svg>
        </button>
        <button type="button" class="vboks-arrow" data-dir="1" aria-label="Следующий отзыв">
          <svg viewBox="0 0 20 20" aria-hidden="true" focusable="false"><path d="M8 4l6 6-6 6" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/></svg>
        </button>
      </div>
    </div>

    <div class="vboks-view">
      <div class="vboks-track" id="vboks-track"></div>
    </div>
    <div class="vboks-dots" id="vboks-dots" role="tablist" aria-label="Навигация по отзывам"></div>
  </div>
</section>

<style>
.vboks, .vboks *, .vboks *::before, .vboks *::after { box-sizing: border-box; }
.vboks {
  --acc: #b9705f; --ink: #33272a; --muted: #7a6a6c; --line: #ece1dd; --soft: #faf3f1; --star: #e0a33a;
  --f: var(--t-text-font, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Arial, sans-serif);
  --fh: var(--t-headline-font, var(--f));
  background: #fff; color: var(--ink); font-family: var(--f); padding: clamp(36px, 5vw, 80px) 20px; overflow: hidden;
}
.vboks-wrap { max-width: 1240px; margin: 0 auto; padding: 0 clamp(0px, 2vw, 40px); }
.vboks-head { display: flex; align-items: flex-end; justify-content: space-between; gap: 20px; margin-bottom: clamp(22px, 2.6vw, 34px); }
.vboks-h2 { font-family: var(--fh); font-size: clamp(26px, 3.4vw, 42px); line-height: 1.1; font-weight: 700; letter-spacing: -.02em; margin: 0; }
.vboks-arrows { display: flex; gap: 8px; flex: 0 0 auto; }
.vboks-arrow { width: 46px; height: 46px; display: flex; align-items: center; justify-content: center; border: 1.5px solid var(--line);
  margin: 0; -webkit-appearance: none; appearance: none; cursor: pointer; border-radius: 50%; background: #fff !important; color: var(--ink) !important; transition: .18s ease; }
.vboks-arrow:hover { border-color: var(--acc); color: var(--acc) !important; }
.vboks-arrow svg { width: 20px; height: 20px; }

.vboks-view { overflow: hidden; margin: 0 -8px; }
.vboks-track { display: flex; gap: 0; transition: transform .45s cubic-bezier(.22,1,.36,1); will-change: transform; }
.vboks-card { flex: 0 0 auto; padding: 0 8px; }
.vboks-card__in { height: 100%; background: var(--soft); border-radius: 22px; padding: clamp(20px, 2.2vw, 28px); display: flex; flex-direction: column; }
.vboks-stars { display: flex; gap: 3px; margin-bottom: 14px; }
.vboks-stars svg { width: 16px; height: 16px; color: var(--star); }
.vboks-stars svg.is-off { color: #ded0cb; }
.vboks-txt { flex: 1; margin: 0 0 18px; font-size: 15px; line-height: 1.65; color: var(--ink); }
.vboks-who { display: flex; align-items: center; gap: 12px; padding-top: 16px; border-top: 1px solid var(--line); }
.vboks-ava { flex: 0 0 44px; width: 44px; height: 44px; border-radius: 50%; display: flex; align-items: center; justify-content: center;
  font-size: 15px; font-weight: 600; color: #fff; }
.vboks-name { display: block; font-size: 15px; font-weight: 600; }
.vboks-meta { display: block; font-size: 12.5px; color: var(--muted); margin-top: 2px; }

.vboks-dots { display: flex; justify-content: center; gap: 7px; margin-top: 22px; }
.vboks-dot { width: 8px; height: 8px; padding: 0; border: 0; margin: 0; -webkit-appearance: none; appearance: none; cursor: pointer;
  border-radius: 50%; background: #ded0cb !important; transition: .2s ease; }
.vboks-dot.is-on { background: var(--acc) !important; width: 24px; border-radius: 999px; }

@media (max-width: 640px) { .vboks-head { flex-direction: column; align-items: flex-start; } }
@media (prefers-reduced-motion: reduce) { .vboks-track { transition: none !important; } }
</style>

<script>
(function () {
  /* ── ОТЗЫВЫ ── */
  var REVIEWS = [
    { name: 'Марина', text: 'Пришла с обломанными ногтями после отпуска, была уверена, что придётся всё снимать. Мастер восстановила три ногтя и сделала форму так, что разницы не видно. Держится четвёртую неделю.', rate: 5, service: 'Маникюр, укрепление', master: 'Ольга', date: 'март' },
    { name: 'Ксения', text: 'Долго не решалась на окрашивание — боялась уйти в рыжину. Показали палитру, объяснили, почему нужен предварительный смыв, и сделали ровно то, что обсуждали. Никаких сюрпризов в зеркале.', rate: 5, service: 'Окрашивание', master: 'Дина', date: 'февраль' },
    { name: 'Алина', text: 'Записалась на чистку, ждала боли и красного лица на два дня. По факту вышла и сразу поехала на встречу. Косметолог честно сказала, что за один раз всё не уберём, и расписала курс.', rate: 5, service: 'Чистка лица', master: 'Вера', date: 'март' },
    { name: 'Ирина', text: 'Единственный салон, где меня не пересаживают и не бегают между креслами. Полтора часа спокойно, кофе, разговоры по желанию. За это и хожу второй год.', rate: 5, service: 'Педикюр', master: 'Ольга', date: 'январь' },
    { name: 'Полина', text: 'Брови делали впервые, очень боялась «шнурков». Мастер нарисовала форму карандашом, показала в зеркало, и только потом начала. Получилось естественно, коллеги не заметили, а муж заметил.', rate: 4, service: 'Коррекция бровей', master: 'Дина', date: 'февраль' },
    { name: 'Юлия', text: 'Опаздывала на сорок минут из-за пробки, была уверена, что откажут. Перезвонили сами, предложили сдвинуть запись и не потеряли моё время. Мелочь, а показательно.', rate: 5, service: 'Стрижка и укладка', master: 'Вера', date: 'март' }
  ];
  var AUTOPLAY = 5200;   /* интервал автопрокрутки в мс, 0 — выключить */
  var PER_VIEW = 3;      /* карточек в кадре на широком экране */
  var COLORS = ['#b9705f', '#8a7a6b', '#a8846f', '#7d8b7a', '#a0748a', '#8b7f9b'];

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

  function init(root) {
    if (root.__vb) return; root.__vb = 1;
    var track = root.querySelector('#vboks-track'), dotsBox = root.querySelector('#vboks-dots');
    var pos = 0, per = PER_VIEW, timer = null;

    function star(on) {
      return '<svg viewBox="0 0 16 16" class="' + (on ? '' : 'is-off') + '" aria-hidden="true" focusable="false">' +
        '<path d="M8 1.6l1.9 3.9 4.3.6-3.1 3 .7 4.3L8 11.4 4.2 13.4l.7-4.3-3.1-3 4.3-.6z" fill="currentColor"/><\/svg>';
    }

    track.innerHTML = REVIEWS.map(function (r, i) {
      var stars = '';
      for (var s = 1; s <= 5; s++) stars += star(s <= r.rate);
      return '<div class="vboks-card"><div class="vboks-card__in">' +
        '<div class="vboks-stars" aria-label="Оценка ' + r.rate + ' из 5">' + stars + '<\/div>' +
        '<p class="vboks-txt">' + r.text + '<\/p>' +
        '<div class="vboks-who"><span class="vboks-ava" style="background:' + COLORS[i % COLORS.length] + '">' + r.name.charAt(0) + '<\/span>' +
        '<span><b class="vboks-name">' + r.name + '<\/b><span class="vboks-meta">' + r.service + ' · мастер ' + r.master + ' · ' + r.date + '<\/span><\/span><\/div>' +
        '<\/div><\/div>';
    }).join('');

    var cards = track.querySelectorAll('.vboks-card');

    function measure() {
      var w = root.clientWidth;
      per = w < 620 ? 1 : w < 980 ? 2 : PER_VIEW;
      cards.forEach(function (c) { c.style.width = (100 / per) + '%'; });
      var pages = Math.max(1, REVIEWS.length - per + 1);
      if (pos > pages - 1) pos = pages - 1;
      drawDots(pages);
      move();
    }

    function drawDots(pages) {
      dotsBox.innerHTML = '';
      for (var i = 0; i < pages; i++) {
        var b = document.createElement('button');
        b.type = 'button'; b.className = 'vboks-dot' + (i === pos ? ' is-on' : '');
        b.setAttribute('aria-label', 'Отзыв ' + (i + 1));
        (function (i) { b.addEventListener('click', function () { pos = i; move(); }); })(i);
        dotsBox.appendChild(b);
      }
    }

    function move() {
      track.style.transform = 'translateX(' + (-pos * (100 / per)) + '%)';
      dotsBox.querySelectorAll('.vboks-dot').forEach(function (d, i) { d.classList.toggle('is-on', i === pos); });
    }

    function step(dir) {
      var pages = Math.max(1, REVIEWS.length - per + 1);
      pos = (pos + dir + pages) % pages;
      move();
    }

    root.querySelectorAll('.vboks-arrow').forEach(function (b) {
      b.addEventListener('click', function () { step(+b.dataset.dir); restart(); });
    });

    /* свайп пальцем */
    var x0 = null;
    track.addEventListener('touchstart', function (e) { x0 = e.touches[0].clientX; stop(); }, { passive: true });
    track.addEventListener('touchend', function (e) {
      if (x0 === null) return;
      var dx = e.changedTouches[0].clientX - x0;
      if (Math.abs(dx) > 40) step(dx < 0 ? 1 : -1);
      x0 = null; restart();
    }, { passive: true });

    function play() { if (AUTOPLAY > 0) timer = setInterval(function () { step(1); }, AUTOPLAY); }
    function stop() { if (timer) { clearInterval(timer); timer = null; } }
    function restart() { stop(); play(); }

    root.addEventListener('mouseenter', stop);
    root.addEventListener('mouseleave', play);
    root.addEventListener('focusin', stop);

    window.addEventListener('resize', measure);
    measure();
    play();
  }
})();
</script>

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

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

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

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

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

Где в кодеЗа что отвечает
массив REVIEWS в скриптеимя, текст, оценка, услуга и дата отзыва
AUTOPLAYинтервал автопрокрутки в миллисекундах, 0 — выключить
--accцвет звёзд и активной точки

Отзывы клиентов салона: зачем он нужен

Отзыв работает, когда в нём есть деталь: какая услуга, какой мастер, когда. Карточка в блоке рассчитана на это — под текстом стоит строка «услуга, мастер, дата», а не абстрактное «Всё понравилось».

Аватары нарисованы SVG-заглушками с инициалами: пока фото нет, блок выглядит законченным, а когда фото появятся — меняется один тег. Автопрокрутка останавливается, если человек начал читать.

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

  • В карточке замените SVG-аватар на тег картинки со ссылкой на файл. Если сайт на Tilda, загрузите фото в медиабиблиотеку и возьмите адрес с их CDN — так картинка будет отдаваться быстро.

  • Да, в скрипте есть переменная PER_VIEW — поставьте 1. Ширина карточек пересчитается автоматически.

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

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

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

Другие блоки отзывов

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

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

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