Блок отзывов покупателей товара
Плитки отзывов с оценкой в звёздах, названием купленной позиции и сроком использования. Фильтр по оценке считает количество отзывов сам.
<section class="vbopt">
<div class="vbopt-wrap">
<div class="vbopt-head">
<div class="vbopt-head__t">
<h2 class="vbopt-h2">Как посуда живёт после покупки</h2>
<p class="vbopt-lead">Отзыв приходит через месяц после доставки, второй раз спрашиваем через полгода. В карточке видно, что человек купил, сколько уже пользуется и оформлял ли заказ на сайте.</p>
</div>
<div class="vbopt-avg">
<b id="vbopt-score">0,0</b>
<span id="vbopt-cap">оценка по отзывам</span>
</div>
</div>
<div class="vbopt-filter" id="vbopt-filter" role="group" aria-label="Отбор отзывов по оценке"></div>
<div class="vbopt-grid" id="vbopt-grid"></div>
<p class="vbopt-empty" id="vbopt-empty" hidden>С такой оценкой отзывов пока нет.</p>
</div>
</section>
<style>
.vbopt, .vbopt *, .vbopt *::before, .vbopt *::after { box-sizing: border-box; }
.vbopt {
--acc: #9c2b45; --ink: #1e1c1b; --muted: #6b6561; --line: #e4e0dc; --soft: #f7f5f2; --star: #d9a441; --ok: #3f7a52;
--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(38px, 5.2vw, 86px) 20px;
}
.vbopt-wrap { max-width: 1220px; margin: 0 auto; padding: 0 clamp(0px, 2vw, 38px); }
.vbopt-head { display: flex; flex-wrap: wrap; align-items: flex-end; justify-content: space-between; gap: 22px 40px; margin-bottom: 24px; }
.vbopt-head__t { max-width: 44em; }
.vbopt-h2 { font-family: var(--fh); font-size: clamp(26px, 3.3vw, 40px); line-height: 1.08; font-weight: 700; letter-spacing: -.02em; margin: 0 0 12px; }
.vbopt-lead { font-size: 15.5px; line-height: 1.62; color: var(--muted); margin: 0; }
.vbopt-avg { display: flex; align-items: center; gap: 14px; background: var(--soft); border-radius: 18px; padding: 16px 22px; }
.vbopt-avg b { font-family: var(--fh); font-size: 40px; line-height: 1; font-weight: 700; color: var(--acc); }
.vbopt-avg span { font-size: 13px; line-height: 1.4; color: var(--muted); max-width: 10em; }
.vbopt-filter { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 18px; }
.vbopt-chip { border: 1.5px solid var(--line); margin: 0; padding: 10px 17px; appearance: none; font: inherit; font-size: 14px;
cursor: pointer; border-radius: 999px; background: #fff !important; color: var(--ink) !important; transition: border-color .18s ease; }
.vbopt-chip:hover { border-color: var(--acc); }
.vbopt-chip i { font-style: normal; color: var(--muted); margin-left: 6px; font-size: 13px; }
.vbopt-chip--on { border-color: var(--acc); background: var(--acc) !important; color: #fff !important; }
.vbopt-chip--on i { color: #f0d3da; }
.vbopt-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(290px, 1fr)); gap: 12px; }
.vbopt-card { border: 1px solid var(--line); border-radius: 20px; padding: clamp(20px, 2.2vw, 26px); display: flex; flex-direction: column; }
.vbopt-top { display: flex; align-items: center; gap: 12px; margin-bottom: 12px; }
.vbopt-ava { flex: 0 0 42px; width: 42px; height: 42px; border-radius: 13px; background: var(--soft); display: flex;
align-items: center; justify-content: center; font-size: 16px; font-weight: 700; color: var(--acc); }
.vbopt-nm { display: block; font-size: 15px; font-weight: 600; }
.vbopt-use { display: block; font-size: 12.5px; color: var(--muted); margin-top: 2px; }
.vbopt-stars { display: flex; gap: 2px; margin-bottom: 10px; }
.vbopt-stars svg { width: 17px; height: 17px; color: var(--star); }
.vbopt-stars svg.vbopt-off { color: var(--line); }
.vbopt-good { display: block; font-size: 13px; font-weight: 600; color: var(--acc); margin-bottom: 9px; }
.vbopt-card p { margin: 0 0 16px; font-size: 14.5px; line-height: 1.62; color: var(--muted); }
.vbopt-mark { display: inline-flex; align-items: center; gap: 7px; margin-top: auto; border-top: 1px solid var(--line);
padding-top: 13px; font-size: 12.5px; color: var(--ok); }
.vbopt-mark svg { width: 15px; height: 15px; }
.vbopt-mark--no { color: #9a938e; }
.vbopt-empty { margin: 22px 0 0; font-size: 15px; color: var(--muted); }
@media (max-width: 560px) { .vbopt-avg { width: 100%; } }
@media (prefers-reduced-motion: reduce) { .vbopt * { transition: none !important; } }
</style>
<script>
(function () {
/* ── ОТЗЫВЫ: rate — оценка, site — заказ оформлен на сайте ── */
var REVIEWS = [
{ n: 'Марина К.', g: 'Столовый комплект «Гладь» на 4 персоны', rate: 5, use: 'пользуется 8 месяцев', site: true,
t: 'Брала на новоселье и боялась, что тарелки окажутся тонкими. Они тяжёлые, край ровный, за восемь месяцев ни одного скола, хотя моем в машине каждый день. Оттенок у всех предметов совпал, это было главным.' },
{ n: 'Дмитрий Р.', g: 'Кастрюля стальная, 3 литра', rate: 5, use: 'пользуется год и 2 месяца', site: true,
t: 'Варю в ней каждый день, дно не повело даже после сильного нагрева. Каша на медленном огне не пригорает, крышка сидит плотно. Ручки греются, если долго держать крышку закрытой, но это ожидаемо для стали.' },
{ n: 'Ольга В.', g: 'Набор для завтрака: 2 кружки и 2 тарелки', rate: 4, use: 'пользуется 3 месяца', site: true,
t: 'Набор приехал за два дня, коробка целая, внутри пенопласт под каждый предмет. Кружка держит тепло дольше обычной. Снял бы полбалла за то, что ручка узковата для крупной ладони, в остальном претензий нет.' },
{ n: 'Сергей П.', g: 'Три банки для хранения, 0,7 литра', rate: 5, use: 'пользуется 7 месяцев', site: true,
t: 'Пересыпал крупы и кофе, запах наружу не идёт. Кольцо в крышке плотное, банка открывается с лёгким щелчком. Заказал ещё три штуки через полгода, приехали такие же, разницы между партиями не заметил.' },
{ n: 'Анна Т.', g: 'Доска разделочная, дуб', rate: 4, use: 'пользуется 5 месяцев', site: false,
t: 'Купила в магазине партнёра, поэтому про сайт сказать нечего. Доска плотная, торцы обработаны маслом, после мойки не встала домиком. Раз в месяц прохожу маслом сама, дерево держится хорошо.' },
{ n: 'Ирина Л.', g: 'Кружка «Слой», 350 мл, 2 штуки', rate: 3, use: 'пользуется 2 месяца', site: true,
t: 'Кружки пришли из разных партий, и оттенок глазури заметно отличается: одна светлее. Поддержка предложила замену, но ждать новую отправку я не стала. Само изделие крепкое, вопрос только к подбору партии в заказе.' },
{ n: 'Павел М.', g: 'Тарелки обеденные, 27 см, 6 штук', rate: 5, use: 'пользуется год и 4 месяца', site: true,
t: 'Взял шесть тарелок для семьи, за полтора года разбили одну по своей вине. Докупил ровно такую же, встала в стопку без зазора. Ставлю в духовку под запеканку, глазурь не потрескалась.' }
];
var roots = document.querySelectorAll('.vbopt');
for (var i = 0; i < roots.length; i++) init(roots[i]);
function init(root) {
if (root.__vb) return; root.__vb = 1;
var grid = root.querySelector('#vbopt-grid'), fbox = root.querySelector('#vbopt-filter');
var empty = root.querySelector('#vbopt-empty');
var STAR = 'M8.5 1.4l2 4.2 4.6.6-3.4 3.2.9 4.6-4.1-2.3-4.1 2.3.9-4.6L1.9 6.2l4.6-.6z';
var cur = 0;
var sum = 0;
for (var a = 0; a < REVIEWS.length; a++) sum += REVIEWS[a].rate;
root.querySelector('#vbopt-score').textContent = (sum / REVIEWS.length).toFixed(1).replace('.', ',');
root.querySelector('#vbopt-cap').textContent = 'средняя оценка по ' + REVIEWS.length + ' отзывам за год';
function count(r) {
var c = 0;
for (var k = 0; k < REVIEWS.length; k++) { if (r === 0 || REVIEWS[k].rate === r) c++; }
return c;
}
function stars(rate) {
var out = '<div class="vbopt-stars" aria-label="Оценка ' + rate + ' из 5">';
for (var s = 1; s <= 5; s++) {
out += '<svg viewBox="0 0 17 17" fill="currentColor" class="' + (s <= rate ? '' : 'vbopt-off') +
'" aria-hidden="true" focusable="false"><path d="' + STAR + '"/><\/svg>';
}
return out + '<\/div>';
}
function drawFilter() {
var opts = [[0, 'Все отзывы'], [5, 'Пять звёзд'], [4, 'Четыре звезды'], [3, 'Три звезды']];
var out = '';
for (var f = 0; f < opts.length; f++) {
out += '<button type="button" class="vbopt-chip' + (opts[f][0] === cur ? ' vbopt-chip--on' : '') +
'" data-r="' + opts[f][0] + '">' + opts[f][1] + '<i>' + count(opts[f][0]) + '<\/i><\/button>';
}
fbox.innerHTML = out;
var chips = fbox.querySelectorAll('.vbopt-chip');
for (var c = 0; c < chips.length; c++) {
chips[c].addEventListener('click', function () {
cur = parseInt(this.getAttribute('data-r'), 10);
drawFilter(); drawList();
});
}
}
function drawList() {
var out = '', shown = 0;
for (var r = 0; r < REVIEWS.length; r++) {
var v = REVIEWS[r];
if (cur !== 0 && v.rate !== cur) continue;
shown++;
var mark = v.site
? '<span class="vbopt-mark"><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="M3 8.5l3 3 7-7.5"/><\/svg>Заказ оформлен на сайте<\/span>'
: '<span class="vbopt-mark vbopt-mark--no"><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><circle cx="8" cy="8" r="6"/><path d="M8 5v4M8 11.2v.1"/><\/svg>Покупка в магазине партнёра<\/span>';
out += '<article class="vbopt-card"><div class="vbopt-top"><span class="vbopt-ava">' + v.n.charAt(0) + '<\/span>' +
'<span><b class="vbopt-nm">' + v.n + '<\/b><span class="vbopt-use">' + v.use + '<\/span><\/span><\/div>' +
stars(v.rate) + '<span class="vbopt-good">' + v.g + '<\/span><p>' + v.t + '<\/p>' + mark + '<\/article>';
}
grid.innerHTML = out;
empty.hidden = shown > 0;
}
drawFilter();
drawList();
}
})();
</script> Вставляется целиком в блок T123 «HTML-код». Внутри уже есть стили и скрипт, подключать ничего не нужно.
Как вставить блок в Tilda
- Скопируйте кодКнопка «Скопировать код» выше кладёт всё содержимое в буфер обмена.
- Добавьте блок T123В редакторе страницы: «Библиотека блоков» → «Другое» → T123 «HTML-код».
- Вставьте и сохранитеНажмите «Контент», вставьте код в поле, сохраните и обновите страницу.
- ОпубликуйтеВ превью скрипты Tilda работают не всегда — проверяйте на опубликованной странице.
Подробная инструкция с граблями — как вставить вайб-блок в Tilda.
Что менять под себя
| Где в коде | За что отвечает |
|---|---|
REVIEWS в начале скрипта | имя, купленная позиция, оценка, срок использования, текст |
поле site у отзыва | отметка о заказе на сайте или покупке у партнёра |
массив opts в функции фильтра | набор кнопок отбора по оценке |
--star в начале стилей | цвет закрашенных звёзд отдельно от фирменного цвета |
Отзывы покупателей товара: зачем он нужен
Отзыв про товар работает иначе, чем отзыв про услугу: покупателю важно, что именно взяли, сколько уже пользуются и как вещь ведёт себя со временем. Каждая плитка собрана вокруг этих трёх полей, звёзды нарисованы вектором, а внизу стоит отметка о том, оформлялся ли заказ на сайте. Средняя оценка в правом верхнем углу считается из массива, вручную её править не нужно.
Фильтр над сеткой отбирает отзывы по числу звёзд и показывает количество рядом с каждой кнопкой. Оставленный в наборе отзыв на три звезды выглядит убедительнее ровной пятёрки у всех: заметно, что подборка не отфильтрована владельцем. Если по выбранной оценке ничего нет, вместо пустого места появляется короткая строка.
Вопросы по этому блоку
-
Удалите тег div с идентификатором фильтра из разметки и вызов drawFilter в конце скрипта. Список отзывов после этого выводится целиком и продолжает работать.
-
Да, она берётся из массива REVIEWS и округляется до одного знака. Подпись рядом тоже подставляет число отзывов, поэтому после правки списка цифры сойдутся.
-
Вместо буквы в кружке подставьте тег img с адресом из медиабиблиотеки Tilda и задайте ему ширину и высоту 42 пикселя. Скругление наследуется от стиля контейнера.
-
Да. Код на этой странице копируется целиком, без регистрации и подписки. Указывать авторство не нужно, использовать можно в коммерческих проектах.
-
Нет. Все классы внутри имеют собственный префикс vbopt, поэтому не пересекаются ни со стилями Tilda, ни с другими блоками. Скрипт привязан к своему элементу и запускается один раз, даже если блок вставлен дважды.
-
Раскладка перестраивается под узкие экраны, размеры заданы через clamp, поля ввода имеют шрифт 16 пикселей — iOS не будет зумить страницу при фокусе. Отдельной мобильной версии подключать не нужно.
Другие блоки отзывов
Ещё блоки для товарных брендов
Собрать сайт целиком, а не по блокам
Дровосеки делают сайты на Tilda под ключ: структура, тексты, дизайн, вёрстка и вайб-блоки под задачи бизнеса.