Отзывы гостей базы отдыха
Лента отзывов в три колонки: в карточке видно, кто приезжал, каким составом, в каком доме и в каком сезоне, а чипы сверху оставляют отзывы нужного времени года.
<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
- Скопируйте кодКнопка «Скопировать код» выше кладёт всё содержимое в буфер обмена.
- Добавьте блок T123В редакторе страницы: «Библиотека блоков» → «Другое» → T123 «HTML-код».
- Вставьте и сохранитеНажмите «Контент», вставьте код в поле, сохраните и обновите страницу.
- ОпубликуйтеВ превью скрипты Tilda работают не всегда — проверяйте на опубликованной странице.
Подробная инструкция с граблями — как вставить вайб-блок в Tilda.
Что менять под себя
| Где в коде | За что отвечает |
|---|---|
REVIEWS в начале скрипта | имя, состав компании, дом, сезон, оценка и текст каждого отзыва |
массив order в функции init | порядок и набор чипов отбора по сезонам |
текст в vbogb-cnt | подпись под средней оценкой про число анкет и период |
column-count у ленты | сколько колонок держать на широком экране |
--star в начале стилей | цвет закрашенных звёзд в оценке |
Отзывы гостей базы отдыха: зачем он нужен
Отзыв про летний домик с пологим спуском к воде мало говорит тому, кто ищет дом на январские выходные. Поэтому в каждой карточке стоит сезон, дом и состав компании, а чипы сверху отбирают отзывы по времени года и показывают, сколько их в каждом сезоне. Средняя оценка и число анкет считаются по массиву, менять цифры руками не нужно.
Карточки выложены в три колонки потоком, поэтому короткие и длинные отзывы стоят рядом без пустот, а на планшете колонок становится две, на телефоне одна. Оценка нарисована пятью звёздами векторной графикой, закрашенные и пустые отличаются цветом. Если в выбранном сезоне отзывов нет, вместо ленты появляется спокойная строка с подсказкой. Тексты отзывов лежат массивом REVIEWS в начале скрипта.
Вопросы по этому блоку
-
Да, поменяйте поле season на house в условии отбора и подставьте в массив order названия домов. Счётчики в чипах соберутся по тому же полю.
-
Скрипт складывает поле rate всех отзывов и делит на их число, затем округляет до одного знака и меняет точку на запятую. Отдельно вписывать её не нужно.
-
Восемь штук держат ленту плотной на широком экране. Если отзывов больше пятнадцати, разумно оставить самые подробные, чтобы блок не растягивал страницу.
-
Да. Код на этой странице копируется целиком, без регистрации и подписки. Указывать авторство не нужно, использовать можно в коммерческих проектах.
-
Нет. Все классы внутри имеют собственный префикс vbogb, поэтому не пересекаются ни со стилями Tilda, ни с другими блоками. Скрипт привязан к своему элементу и запускается один раз, даже если блок вставлен дважды.
-
Раскладка перестраивается под узкие экраны, размеры заданы через clamp, поля ввода имеют шрифт 16 пикселей — iOS не будет зумить страницу при фокусе. Отдельной мобильной версии подключать не нужно.
Другие блоки отзывов
Ещё блоки для аренды домов
Собрать сайт целиком, а не по блокам
Дровосеки делают сайты на Tilda под ключ: структура, тексты, дизайн, вёрстка и вайб-блоки под задачи бизнеса.