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

Блок отзывов работодателей для кадрового агентства

Плитки отзывов от компаний: сфера, закрытые позиции, число вышедших людей и срок подбора, фильтр по формату услуги и две сводные цифры над лентой.

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

Код блока

<section class="vbor">
  <div class="vbor-wrap">
    <div class="vbor-head">
      <div>
        <h2 class="vbor-h2">Что говорят компании, которым мы искали людей</h2>
        <p class="vbor-lead">Отзывы собраны после закрытия заявки и подтверждены подписанными актами. Указываем позиции, число вышедших людей и срок работы по каждой заявке.</p>
      </div>
      <ul class="vbor-sum" id="vbor-sum"></ul>
    </div>

    <div class="vbor-filter" id="vbor-filter"></div>
    <div class="vbor-grid" id="vbor-grid" aria-live="polite"></div>
    <p class="vbor-count" id="vbor-count"></p>
  </div>
</section>

<style>
.vbor, .vbor *, .vbor *::before, .vbor *::after { box-sizing: border-box; }
.vbor {
  --acc: #2e6f9e; --ink: #16202b; --muted: #5c6a7a; --line: #e0e6ec; --soft: #f1f5f8;
  --f: var(--t-text-font, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Arial, sans-serif);
  --fh: var(--t-headline-font, var(--f));
  background: #f7f9fb; color: var(--ink); font-family: var(--f); padding: clamp(36px, 5vw, 80px) 20px;
}
.vbor-wrap { max-width: 1220px; margin: 0 auto; padding: 0 clamp(0px, 2vw, 40px); }
.vbor-head { display: grid; grid-template-columns: minmax(0, 1.25fr) minmax(0, .75fr); gap: clamp(22px, 3vw, 48px); align-items: end; margin-bottom: clamp(22px, 3vw, 34px); }
.vbor-h2 { font-family: var(--fh); font-size: clamp(26px, 3.4vw, 42px); line-height: 1.1; font-weight: 700; letter-spacing: -.02em; margin: 0 0 12px; }
.vbor-lead { font-size: 16px; line-height: 1.62; color: var(--muted); margin: 0; max-width: 38em; }
.vbor-sum { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); gap: 10px; }
.vbor-sum li { background: #fff; border: 1px solid var(--line); border-radius: 16px; padding: 16px 18px; }
.vbor-sum b { display: block; font-family: var(--fh); font-size: 24px; line-height: 1.05; font-weight: 700; color: var(--acc); letter-spacing: -.02em; }
.vbor-sum span { display: block; font-size: 12px; line-height: 1.45; color: var(--muted); margin-top: 5px; }

.vbor-filter { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 16px; }
.vbor-chip { border: 1px solid var(--line); margin: 0; padding: 11px 18px; -webkit-appearance: none; appearance: none; font: inherit; font-size: 14px;
  cursor: pointer; border-radius: 999px; background: #fff !important; color: var(--muted) !important; transition: .18s ease; }
.vbor-chip:hover { border-color: var(--acc); color: var(--ink) !important; }
.vbor-chip[aria-pressed="true"] { background: var(--acc) !important; color: #fff !important; border-color: var(--acc); }

.vbor-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 14px; }
.vbor-card { display: flex; flex-direction: column; background: #fff; border: 1px solid var(--line); border-radius: 20px; padding: clamp(20px, 2.2vw, 26px);
  opacity: 0; transform: translateY(10px); animation: vbor-in .35s ease forwards; }
.vbor-card__top { display: flex; align-items: center; gap: 12px; margin-bottom: 16px; }
.vbor-mark { flex: 0 0 44px; width: 44px; height: 44px; border-radius: 13px; background: var(--soft); display: flex; align-items: center; justify-content: center;
  font-family: var(--fh); font-size: 16px; font-weight: 700; color: var(--acc); }
.vbor-co { display: block; font-size: 15.5px; font-weight: 600; line-height: 1.3; }
.vbor-field { display: block; font-size: 12.5px; color: var(--muted); margin-top: 3px; }
.vbor-roles { list-style: none; margin: 0 0 16px; padding: 0; display: flex; flex-wrap: wrap; gap: 6px; }
.vbor-roles li { font-size: 12px; color: var(--muted); background: var(--soft); border-radius: 8px; padding: 6px 10px; }
.vbor-card p { margin: 0 0 18px; font-size: 14.5px; line-height: 1.65; color: var(--muted); }
.vbor-facts { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 8px; margin-top: auto; border-top: 1px solid var(--line); padding-top: 16px; }
.vbor-facts b { display: block; font-family: var(--fh); font-size: 17px; font-weight: 700; line-height: 1.15; }
.vbor-facts span { display: block; font-size: 11.5px; color: var(--muted); margin-top: 4px; }
.vbor-who { font-size: 12.5px; color: var(--muted); margin-top: 14px; }
.vbor-count { font-size: 13px; color: var(--muted); margin: 18px 0 0; }

@keyframes vbor-in { to { opacity: 1; transform: none; } }
@media (max-width: 900px) { .vbor-head { grid-template-columns: minmax(0, 1fr); align-items: start; } }
@media (prefers-reduced-motion: reduce) { .vbor * { transition: none !important; animation: none !important; opacity: 1 !important; transform: none !important; } }
</style>

<script>
(function () {
  /* ── ФОРМАТЫ ДЛЯ ФИЛЬТРА ── */
  var TABS = [
    { k: 'all', n: 'Все заявки' },
    { k: 'mass', n: 'Массовый подбор' },
    { k: 'spot', n: 'Специалисты' },
    { k: 'exec', n: 'Руководители' },
    { k: 'staff', n: 'Аутстаффинг' }
  ];

  /* ── ОТЗЫВЫ РАБОТОДАТЕЛЕЙ ── */
  var REVIEWS = [
    { co: 'Сеть пекарен «Тёплый угол»', kind: 'mass', field: 'Общественное питание, 24 точки',
      roles: ['Пекарь', 'Продавец', 'Помощник кондитера'], people: 34, days: 26, who: 'Ирина Соколова, руководитель отдела персонала',
      txt: 'Открывали шесть точек за квартал и своими силами не успевали. Агентство вело поток кандидатов и присылало сводку каждый вечер, поэтому мы понимали, где просядем по срокам. Из 34 вышедших к концу испытательного срока остались 30.' },
    { co: 'Завод «Промлит»', kind: 'spot', field: 'Металлообработка, 340 сотрудников',
      roles: ['Инженер-технолог', 'Мастер участка'], people: 3, days: 21, who: 'Андрей Лапшин, директор по производству',
      txt: 'Технолога с опытом литья под давлением искали своими силами полгода. Здесь показали пятерых за три недели, двое подошли по задачам сразу. Понравилось, что рекрутер приезжал на завод и разбирался в участке до старта поиска.' },
    { co: 'Логистический оператор «Стрелец»', kind: 'staff', field: 'Склады класса А, три площадки',
      roles: ['Комплектовщик', 'Водитель погрузчика'], people: 62, days: 4, who: 'Марина Гущина, управляющая складом',
      txt: 'Перед высоким сезоном нужна была дополнительная смена. Люди вышли на четвёртый день после заявки, оформление и выплаты остались на стороне агентства. Когда двое заболели, замену прислали в тот же день.' },
    { co: 'Торговый дом «Ветка»', kind: 'exec', field: 'Оптовые продажи продуктов питания',
      roles: ['Коммерческий директор'], people: 1, days: 38, who: 'Сергей Мальцев, собственник',
      txt: 'Позиция закрытая, объявление размещать было нельзя. Агентство собрало карту рынка по нашей нише и вышло на действующих руководителей. Кандидат работает второй год, оборот направления за это время вырос на 41 процент.' },
    { co: 'Клиника «Сфера здоровья»', kind: 'spot', field: 'Медицинские услуги, две клиники',
      roles: ['Администратор', 'Врач-терапевт', 'Медицинская сестра'], people: 7, days: 19, who: 'Ольга Тимченко, главный врач',
      txt: 'Ценю, что кандидатов проверяли по документам и сертификатам до собеседования: мы не тратили время на тех, у кого не в порядке аккредитация. Отдельный плюс за обратную связь тем, кто нам не подошёл.' },
    { co: 'Строительная компания «Вектор-С»', kind: 'mass', field: 'Малоэтажное строительство',
      roles: ['Монтажник', 'Разнорабочий', 'Прораб'], people: 18, days: 31, who: 'Дмитрий Крылов, руководитель проектов',
      txt: 'Работали по трём объектам одновременно, люди нужны были в разных районах области. Агентство разделило поиск по площадкам и закрыло бригады к началу сезона. Двоих меняли по гарантии, вопросов по срокам замены не было.' }
  ];

  var roots = document.querySelectorAll('.vbor');
  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('#vbor-grid'), bar = root.querySelector('#vbor-filter');
    var cnt = root.querySelector('#vbor-count'), sum = root.querySelector('#vbor-sum');
    var cur = 'all';

    function plural(n, a) { var m = n % 100; if (m > 4 && m < 21) return a[2]; m = n % 10; return m === 1 ? a[0] : (m > 1 && m < 5 ? a[1] : a[2]); }

    var people = 0, days = 0, j;
    for (j = 0; j < REVIEWS.length; j++) { people += REVIEWS[j].people; days += REVIEWS[j].days; }
    sum.innerHTML = '<li><b>' + people + '<\/b><span>человек вышло на работу по этим заявкам<\/span><\/li>' +
      '<li><b>' + Math.round(days / REVIEWS.length) + '<\/b><span>дней средний срок закрытия заявки<\/span><\/li>';

    bar.innerHTML = TABS.map(function (t) {
      return '<button type="button" class="vbor-chip" data-k="' + t.k + '" aria-pressed="' + (t.k === 'all') + '">' + t.n + '<\/button>';
    }).join('');

    function draw() {
      var list = [];
      for (var i = 0; i < REVIEWS.length; i++) if (cur === 'all' || REVIEWS[i].kind === cur) list.push(REVIEWS[i]);
      grid.innerHTML = list.map(function (r, i) {
        return '<article class="vbor-card" style="animation-delay:' + (i * 60) + 'ms"><div class="vbor-card__top">' +
          '<span class="vbor-mark">' + r.co.replace(/[^А-Яа-яA-Za-z]/g, '').charAt(0) + '<\/span>' +
          '<span><b class="vbor-co">' + r.co + '<\/b><span class="vbor-field">' + r.field + '<\/span><\/span><\/div>' +
          '<ul class="vbor-roles">' + r.roles.map(function (x) { return '<li>' + x + '<\/li>'; }).join('') + '<\/ul>' +
          '<p>' + r.txt + '<\/p>' +
          '<div class="vbor-facts"><div><b>' + r.roles.length + '<\/b><span>' + plural(r.roles.length, ['позиция', 'позиции', 'позиций']) + ' в заявке<\/span><\/div>' +
          '<div><b>' + r.people + '<\/b><span>' + plural(r.people, ['человек вышел', 'человека вышло', 'человек вышло']) + '<\/span><\/div>' +
          '<div><b>' + r.days + '<\/b><span>' + plural(r.days, ['день на подбор', 'дня на подбор', 'дней на подбор']) + '<\/span><\/div><\/div>' +
          '<span class="vbor-who">' + r.who + '<\/span><\/article>';
      }).join('');
      cnt.textContent = 'Показано ' + list.length + ' ' + plural(list.length, ['отзыв', 'отзыва', 'отзывов']) + ' из ' + REVIEWS.length;
    }

    bar.addEventListener('click', function (e) {
      var b = e.target.closest ? e.target.closest('.vbor-chip') : null;
      if (!b) return;
      cur = b.getAttribute('data-k');
      var bs = bar.querySelectorAll('.vbor-chip');
      for (var i = 0; i < bs.length; i++) bs[i].setAttribute('aria-pressed', bs[i].getAttribute('data-k') === cur ? 'true' : 'false');
      draw();
    });

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

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

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

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

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

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

Где в кодеЗа что отвечает
REVIEWS в начале скриптакомпания, сфера, позиции, число людей, срок и текст отзыва
TABS в начале скриптаназвания форматов услуги для фильтра над лентой
поле who в каждом отзывеимя и должность человека, который дал отзыв
--acc в начале стилейцвет активного чипа, сводных цифр и буквы вместо логотипа

Отзывы работодателей: зачем он нужен

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

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

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

  • Допишите объект в массив REVIEWS с полями co, kind, field, roles, people, days, who и txt. Ключ kind должен совпадать с одним из ключей в массиве TABS, иначе отзыв покажется только в общем списке.

  • Оставьте в массиве TABS один объект с ключом all, тогда над лентой будет одна кнопка. Можно скрыть контейнер фильтра стилем display none, логика вывода отзывов при этом сохранится.

  • Скрипт складывает поля people всех отзывов и усредняет поля days. Если нужны другие показатели, поправьте сборку строки sum в начале функции инициализации.

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

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

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

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

Ещё блоки для подбора персонала

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

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