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

Отзывы гостей базы отдыха

Лента отзывов в три колонки: в карточке видно, кто приезжал, каким составом, в каком доме и в каком сезоне, а чипы сверху оставляют отзывы нужного времени года.

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

Код блока

<section class="vbogb">
  <div class="vbogb-wrap">
    <div class="vbogb-top">
      <div class="vbogb-head">
        <h2 class="vbogb-h2">Гости о домах и территории</h2>
        <p class="vbogb-lead">Анкету присылаем на второй день после выезда. В карточке видно, какой дом снимали, в каком сезоне и с кем приезжали, поэтому отзыв легко примерить на свою поездку.</p>
      </div>
      <div class="vbogb-score">
        <b id="vbogb-avg">0,0</b>
        <div>
          <span class="vbogb-stars" id="vbogb-top-stars" role="img" aria-label="Средняя оценка гостей"></span>
          <span class="vbogb-score__t" id="vbogb-cnt"></span>
        </div>
      </div>
    </div>

    <div class="vbogb-chips" id="vbogb-chips" role="group" aria-label="Отбор отзывов по сезону"></div>
    <div class="vbogb-feed" id="vbogb-feed"></div>
    <p class="vbogb-empty" id="vbogb-empty" hidden>За этот сезон отзывов пока нет, выберите другой.</p>
  </div>
</section>

<style>
.vbogb, .vbogb *, .vbogb *::before, .vbogb *::after { box-sizing: border-box; }
.vbogb {
  --acc: #4f7d43; --star: #d8a13c; --ink: #1e2a20; --muted: #63755f; --line: #e0e8d9; --soft: #f4f8ef;
  --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;
}
.vbogb-wrap { max-width: 1220px; margin: 0 auto; padding: 0 clamp(0px, 2vw, 40px); }
.vbogb-top { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: clamp(20px, 3vw, 44px); align-items: end; margin-bottom: clamp(20px, 2.6vw, 30px); }
.vbogb-head { max-width: 44em; }
.vbogb-h2 { font-family: var(--fh); font-size: clamp(25px, 3.3vw, 40px); line-height: 1.1; font-weight: 700; letter-spacing: -.022em; margin: 0 0 12px; }
.vbogb-lead { font-size: 16px; line-height: 1.6; color: var(--muted); margin: 0; }

.vbogb-score { display: flex; align-items: center; gap: 14px; background: var(--soft); border-radius: 20px; padding: 18px 22px; }
.vbogb-score b { font-family: var(--fh); font-size: clamp(34px, 4vw, 46px); line-height: 1; font-weight: 700; color: var(--acc); }
.vbogb-score__t { display: block; font-size: 12.5px; line-height: 1.45; color: var(--muted); margin-top: 6px; max-width: 15em; }
.vbogb-stars { display: inline-flex; gap: 2px; }
.vbogb-stars svg { width: 16px; height: 16px; color: #d3ddca; }
.vbogb-stars svg.vbogb-on { color: var(--star); }

.vbogb-chips { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: clamp(16px, 2vw, 24px); }
.vbogb-chip { -webkit-appearance: none; appearance: none; margin: 0; padding: 11px 18px; border: 1.5px solid var(--line); border-radius: 999px;
  cursor: pointer; font: inherit; font-size: 14px; background: #fff !important; color: var(--muted) !important; transition: .18s ease; }
.vbogb-chip:hover { border-color: var(--acc); color: var(--ink) !important; }
.vbogb-chip i { font-style: normal; opacity: .65; margin-left: 6px; }
.vbogb-chip--on, .vbogb-chip--on:hover { background: var(--acc) !important; border-color: var(--acc); color: #fff !important; font-weight: 600; }

.vbogb-feed { column-count: 3; column-gap: clamp(12px, 1.6vw, 20px); }
.vbogb-card { break-inside: avoid; page-break-inside: avoid; border: 1px solid var(--line); border-radius: 20px;
  padding: 22px 24px; margin: 0 0 clamp(12px, 1.6vw, 20px); background: #fff; }
.vbogb-card__top { display: flex; align-items: center; gap: 12px; margin-bottom: 12px; }
.vbogb-ava { flex: 0 0 42px; width: 42px; height: 42px; border-radius: 14px; background: var(--soft); display: flex; align-items: center;
  justify-content: center; font-family: var(--fh); font-size: 16px; font-weight: 700; color: var(--acc); }
.vbogb-card__n { display: block; font-size: 15px; font-weight: 600; }
.vbogb-card__w { display: block; font-size: 12.5px; color: var(--muted); margin-top: 3px; }
.vbogb-card__meta { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-bottom: 12px; }
.vbogb-tag { font-size: 11.5px; color: var(--acc); background: var(--soft); border-radius: 999px; padding: 5px 11px; }
.vbogb-tag--season { color: #8a6a2c; background: #faf3e6; }
.vbogb-card p { margin: 0; font-size: 14.5px; line-height: 1.65; color: var(--muted); }
.vbogb-empty { margin: 0; font-size: 15px; color: var(--muted); }

@media (max-width: 1000px) { .vbogb-feed { column-count: 2; } }
@media (max-width: 860px) { .vbogb-top { grid-template-columns: minmax(0, 1fr); align-items: start; } }
@media (max-width: 620px) { .vbogb-feed { column-count: 1; } }
@media (prefers-reduced-motion: reduce) { .vbogb * { transition: none !important; } }
</style>

<script>
(function () {
  /* ── ОТЗЫВЫ: who - с кем приезжали, house - дом, season - сезон, rate - звёзды ── */
  var REVIEWS = [
    { name: 'Ирина и Павел', who: 'вдвоём на годовщину', house: 'Купол «Лесной»', season: 'Зима', rate: 5,
      text: 'Брали купол на две ночи в конце января. Снег вокруг, панорамное окно от пола, вечером топили купель и сидели в ней под фонарём. Дорогу до самого дома чистят, легковая машина прошла без вопросов.' },
    { name: 'Семья Кузнецовых', who: 'с двумя детьми 5 и 9 лет', house: 'Домик «Кедровый»', season: 'Лето', rate: 5,
      text: 'Спуск к воде пологий, мелко далеко от берега, поэтому за детьми можно смотреть с террасы. Качели и песочница заняли младшего на весь день. Посуды хватило, готовили сами, в магазин ездили в село за десять минут.' },
    { name: 'Денис Р.', who: 'компанией из шести человек', house: 'Дом «Банный»', season: 'Осень', rate: 5,
      text: 'Печь протопили к нашему приезду, в парной было ровно то, что нужно после дороги. Веники и дрова лежали в предбаннике. Единственное, о чём стоит знать: купель на террасе греется дольше, чем кажется, лучше сказать про неё заранее.' },
    { name: 'Марина Т.', who: 'выезд отдела, 11 человек', house: 'Большой дом «Причал»', season: 'Весна', rate: 4,
      text: 'Зал вместил всех, для ноутбуков хватило розеток, интернет держал видеосвязь. Из минусов: вторая кухня маленькая для такой компании, готовить на одиннадцать человек в ней тесно. Мангальную зону администратор подготовил заранее.' },
    { name: 'Алексей и Ольга', who: 'вдвоём с собакой', house: 'Домик «Кедровый»', season: 'Осень', rate: 5,
      text: 'Приятно, что про питомца спросили при бронировании и назвали цену сразу, а на месте выдали миски и коврик. Территория огорожена, поэтому пса отпускали побегать. Соседний дом стоял пустой, тишина полная.' },
    { name: 'Гости из Тюмени', who: 'три пары друзей', house: 'Сруб «Березняк»', season: 'Лето', rate: 5,
      text: 'Беседка со столом на десять мест решает всё: ужинали там три вечера подряд. Дрова колотые, разжигается быстро, мангал большой. Утром доносился только звук с воды. Приедем той же компанией в июле.' },
    { name: 'Виктор С.', who: 'с родителями и сестрой', house: 'Сруб «Березняк»', season: 'Зима', rate: 4,
      text: 'Дом тёплый, полы с подогревом в санузле, на втором этаже прохладнее градуса на три. Родителям было удобно на первом. Выезд продлили до трёх часов дня, доплату посчитали ровно по прайсу без разговоров.' },
    { name: 'Екатерина Л.', who: 'вдвоём с подругой', house: 'Купол «Лесной»', season: 'Весна', rate: 5,
      text: 'Ехали смотреть на ледоход и остались довольны видом. Завтраки заказывали оба утра, привозили к девяти, сырники тёплые. Чек прислали на почту в тот же день, залог вернули при выезде наличными.' }
  ];

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

  function init(root) {
    if (root.__vb) return; root.__vb = 1;
    var feed = root.querySelector('#vbogb-feed'), chips = root.querySelector('#vbogb-chips');
    var empty = root.querySelector('#vbogb-empty'), cur = 'Все';

    function stars(r) {
      var out = '<span class="vbogb-stars" role="img" aria-label="Оценка ' + r + ' из 5">';
      for (var s = 1; s <= 5; s++) {
        out += '<svg viewBox="0 0 20 20" class="' + (s <= r ? 'vbogb-on' : '') + '" aria-hidden="true" focusable="false">' +
          '<path fill="currentColor" d="M10 1.6l2.5 5.2 5.7.8-4.1 4 1 5.7-5.1-2.7-5.1 2.7 1-5.7-4.1-4 5.7-.8z"/><\/svg>';
      }
      return out + '<\/span>';
    }

    var sum = 0, order = ['Все', 'Зима', 'Весна', 'Лето', 'Осень'], counts = { 'Все': REVIEWS.length };
    for (var r1 = 0; r1 < REVIEWS.length; r1++) {
      sum += REVIEWS[r1].rate;
      counts[REVIEWS[r1].season] = (counts[REVIEWS[r1].season] || 0) + 1;
    }
    var avg = sum / REVIEWS.length;
    root.querySelector('#vbogb-avg').textContent = avg.toFixed(1).replace('.', ',');
    root.querySelector('#vbogb-top-stars').innerHTML = stars(Math.round(avg));
    root.querySelector('#vbogb-cnt').textContent = 'средняя оценка по ' + REVIEWS.length + ' анкетам за последний год';

    var chipHtml = '';
    for (var c = 0; c < order.length; c++) {
      if (!counts[order[c]]) continue;
      chipHtml += '<button type="button" class="vbogb-chip' + (order[c] === cur ? ' vbogb-chip--on' : '') +
        '" data-s="' + order[c] + '">' + order[c] + '<i>' + counts[order[c]] + '<\/i><\/button>';
    }
    chips.innerHTML = chipHtml;

    function draw() {
      var out = '', shown = 0;
      for (var n = 0; n < REVIEWS.length; n++) {
        var rv = REVIEWS[n];
        if (cur !== 'Все' && rv.season !== cur) continue;
        shown++;
        out += '<article class="vbogb-card"><div class="vbogb-card__top">' +
          '<span class="vbogb-ava">' + rv.name.charAt(0) + '<\/span>' +
          '<span><b class="vbogb-card__n">' + rv.name + '<\/b>' +
          '<span class="vbogb-card__w">' + rv.who + '<\/span><\/span><\/div>' +
          '<div class="vbogb-card__meta"><span class="vbogb-tag">' + rv.house + '<\/span>' +
          '<span class="vbogb-tag vbogb-tag--season">' + rv.season + '<\/span>' + stars(rv.rate) + '<\/div>' +
          '<p>' + rv.text + '<\/p><\/article>';
      }
      feed.innerHTML = out;
      empty.hidden = shown > 0;
    }

    var btns = chips.querySelectorAll('.vbogb-chip');
    for (var b = 0; b < btns.length; b++) {
      btns[b].addEventListener('click', function () {
        for (var k = 0; k < btns.length; k++) btns[k].className = 'vbogb-chip';
        this.className = 'vbogb-chip vbogb-chip--on';
        cur = this.getAttribute('data-s');
        draw();
      });
    }

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

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

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

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

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

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

Где в кодеЗа что отвечает
REVIEWS в начале скриптаимя, состав компании, дом, сезон, оценка и текст каждого отзыва
массив order в функции initпорядок и набор чипов отбора по сезонам
текст в vbogb-cntподпись под средней оценкой про число анкет и период
column-count у лентысколько колонок держать на широком экране
--star в начале стилейцвет закрашенных звёзд в оценке

Отзывы гостей базы отдыха: зачем он нужен

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

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

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

  • Да, поменяйте поле season на house в условии отбора и подставьте в массив order названия домов. Счётчики в чипах соберутся по тому же полю.

  • Скрипт складывает поле rate всех отзывов и делит на их число, затем округляет до одного знака и меняет точку на запятую. Отдельно вписывать её не нужно.

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

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

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

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

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

Ещё блоки для аренды домов

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

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