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

Блок «Тренеры фитнес-клуба» для Tilda

Тёмная сетка карточек: портрет-заглушка, направление, стаж и полоса загрузки по числу клиентов. Кнопки сверху отбирают тренеров по направлению.

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

Код блока

<section class="vbtrk">
  <div class="vbtrk-wrap">
    <div class="vbtrk-head">
      <div class="vbtrk-head__txt">
        <h2 class="vbtrk-h2">Кто ведёт тренировки</h2>
        <p class="vbtrk-lead">Тренерский состав зала: направление, стаж, узкая специализация и сколько человек каждый ведёт прямо сейчас. Портреты нарисованы заглушками, поставьте вместо них фотографии своих тренеров.</p>
      </div>
      <p class="vbtrk-sum" id="vbtrk-sum"></p>
    </div>

    <div class="vbtrk-tabs" id="vbtrk-tabs" role="group" aria-label="Отбор тренеров по направлению"></div>

    <div class="vbtrk-grid" id="vbtrk-grid"></div>

    <p class="vbtrk-foot">Первое занятие идёт как знакомство: замеры, разбор запроса и план на месяц вперёд. <a class="vbtrk-link" href="#zapis">Выбрать время</a></p>
  </div>
</section>

<style>
.vbtrk, .vbtrk *, .vbtrk *::before, .vbtrk *::after { box-sizing: border-box; }
.vbtrk {
  --acc: #d6f24b; --acc-dark: #bfdb35; --bg: #101215; --panel: #181b20; --panel2: #1f232a;
  --ink: #f1f3f5; --muted: #99a1ab; --line: #272c33;
  --f: var(--t-text-font, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Arial, sans-serif);
  --fh: var(--t-headline-font, var(--f));
  background: var(--bg); color: var(--ink); font-family: var(--f); padding: clamp(36px, 5vw, 84px) 20px;
}
.vbtrk-wrap { max-width: 1220px; margin: 0 auto; padding: 0 clamp(0px, 2vw, 40px); }
.vbtrk-head { display: flex; flex-wrap: wrap; align-items: flex-end; justify-content: space-between; gap: 18px clamp(20px, 3vw, 46px); margin-bottom: clamp(20px, 2.6vw, 30px); }
.vbtrk-head__txt { max-width: 44em; }
.vbtrk-h2 { font-family: var(--fh); font-size: clamp(27px, 3.6vw, 44px); line-height: 1.06; font-weight: 800; letter-spacing: -.03em; text-transform: uppercase; margin: 0 0 12px; }
.vbtrk-lead { font-size: 16px; line-height: 1.65; color: var(--muted); margin: 0; }
.vbtrk-sum { margin: 0; font-size: 13px; line-height: 1.5; color: var(--muted); white-space: nowrap; }
.vbtrk-sum b { color: var(--acc); font-family: var(--fh); font-size: 15px; font-weight: 700; }

.vbtrk-tabs { display: flex; flex-wrap: wrap; gap: 7px; margin-bottom: clamp(16px, 2vw, 24px); }
.vbtrk-tab { -webkit-appearance: none; appearance: none; cursor: pointer; font: inherit; font-size: 13.5px; line-height: 1.2;
  padding: 10px 17px; border: 1px solid var(--line); border-radius: 999px; letter-spacing: .01em;
  background: var(--panel) !important; color: var(--muted) !important; transition: color .18s ease, border-color .18s ease, background .18s ease; }
.vbtrk-tab:hover { border-color: var(--acc); color: var(--ink) !important; }
.vbtrk-tab[aria-pressed="true"] { background: var(--acc) !important; border-color: var(--acc); color: #14170f !important; font-weight: 700; }

.vbtrk-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(248px, 1fr)); gap: clamp(12px, 1.4vw, 18px); }
.vbtrk-card { display: flex; flex-direction: column; background: var(--panel); border: 1px solid var(--line); border-radius: 18px; overflow: hidden;
  opacity: 0; transform: translateY(14px); transition: opacity .45s ease, transform .45s cubic-bezier(.22,1,.36,1), border-color .2s ease; }
.vbtrk-card.is-in { opacity: 1; transform: none; }
.vbtrk-card.is-off { display: none; }
.vbtrk-card:hover { border-color: #3a424c; }

.vbtrk-pic { position: relative; height: 188px; }
.vbtrk-svg { display: block; width: 100%; height: 100%; }
.vbtrk-dir { position: absolute; left: 12px; bottom: 12px; background: var(--acc); color: #14170f; border-radius: 8px;
  font-size: 10.5px; font-weight: 800; letter-spacing: .08em; text-transform: uppercase; padding: 6px 10px; }

.vbtrk-body { flex: 1; display: flex; flex-direction: column; padding: clamp(16px, 1.9vw, 22px); }
.vbtrk-name { font-family: var(--fh); font-size: 18px; font-weight: 700; line-height: 1.2; letter-spacing: -.01em; margin: 0 0 4px; }
.vbtrk-exp { margin: 0 0 14px; font-size: 12.5px; color: var(--muted); }
.vbtrk-spec { margin: 0 0 14px; background: var(--panel2); border-radius: 12px; padding: 12px 14px; }
.vbtrk-spec span { display: block; font-size: 10px; letter-spacing: .09em; text-transform: uppercase; color: #6f7883; margin-bottom: 5px; }
.vbtrk-spec b { display: block; font-size: 13.5px; line-height: 1.5; font-weight: 500; color: var(--ink); }
.vbtrk-load { margin-top: auto; padding-top: 14px; border-top: 1px solid var(--line); }
.vbtrk-load__top { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; margin-bottom: 8px; }
.vbtrk-load__top b { font-family: var(--fh); font-size: 20px; font-weight: 800; color: var(--acc); }
.vbtrk-load__top i { font-style: normal; font-size: 11.5px; color: var(--muted); text-align: right; line-height: 1.35; }
.vbtrk-bar { height: 5px; border-radius: 5px; background: #23272e; overflow: hidden; }
.vbtrk-bar i { display: block; height: 100%; width: 0; border-radius: 5px; background: var(--acc); transition: width .9s cubic-bezier(.22,1,.36,1); }

.vbtrk-foot { margin: clamp(22px, 2.6vw, 32px) 0 0; font-size: 15px; line-height: 1.6; color: var(--muted); }
.vbtrk-link, .vbtrk-link:link, .vbtrk-link:visited { color: var(--acc) !important; text-decoration: underline; }
.vbtrk-link:hover { color: var(--acc-dark) !important; }

@media (max-width: 560px) { .vbtrk-sum { white-space: normal; } }
@media (prefers-reduced-motion: reduce) {
  .vbtrk-card { opacity: 1 !important; transform: none !important; transition: none !important; }
  .vbtrk-bar i, .vbtrk-tab { transition: none !important; }
}
</style>

<script>
(function () {
  /* ── ТРЕНЕРЫ: photo пустой, значит рисуется заглушка цвета tint ── */
  var COACHES = [
    { name: 'Илья Барсуков', dir: 'Силовая', tint: '#2b3140', photo: '', years: 12, load: 34,
      exp: 'мастер спорта по пауэрлифтингу',
      spec: 'Возврат к штанге после травмы поясницы: техника, объём и контроль подхода' },
    { name: 'Марина Зотова', dir: 'Функциональная', tint: '#33402f', photo: '', years: 7, load: 41,
      exp: 'тренер групповых и персональных программ',
      spec: 'Круговые занятия для тех, кто вернулся к спорту после долгого перерыва' },
    { name: 'Артур Гайнуллин', dir: 'Бокс', tint: '#402f2f', photo: '', years: 15, load: 22,
      exp: 'кандидат в мастера спорта, судья первой категории',
      spec: 'Подготовка к любительским турнирам: спарринги, работа ног, сгонка веса' },
    { name: 'Оксана Ветрова', dir: 'Растяжка', tint: '#3a2f40', photo: '', years: 9, load: 38,
      exp: 'образование по адаптивной физкультуре',
      spec: 'Подвижность тазобедренных и плечевых суставов при сидячей работе' },
    { name: 'Денис Кораблёв', dir: 'Силовая', tint: '#2f3a40', photo: '', years: 6, load: 29,
      exp: 'бакалавр физической культуры',
      spec: 'Первый год в зале: базовые движения, страховка и понятный дневник' },
    { name: 'Юлия Санина', dir: 'Функциональная', tint: '#403a2f', photo: '', years: 11, load: 26,
      exp: 'повышение квалификации по восстановлению',
      spec: 'Работа после операций на колене вместе с врачом лечебной физкультуры' }
  ];

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

  /* склонение: plu(3, 'год', 'года', 'лет') */
  function plu(n, a, b, c) { var m = n % 100, k = n % 10; return m > 10 && m < 20 ? c : k === 1 ? a : k > 1 && k < 5 ? b : c; }

  function init(root) {
    if (root.__vb) return; root.__vb = 1;
    var grid = root.querySelector('#vbtrk-grid'), tabs = root.querySelector('#vbtrk-tabs'), sum = root.querySelector('#vbtrk-sum');

    var dirs = [], total = 0, peak = 0, k;
    for (k = 0; k < COACHES.length; k++) {
      if (dirs.indexOf(COACHES[k].dir) < 0) dirs.push(COACHES[k].dir);
      total += COACHES[k].load;
      if (COACHES[k].load > peak) peak = COACHES[k].load;
    }
    sum.innerHTML = '<b>' + COACHES.length + '<\/b> ' + plu(COACHES.length, 'тренер', 'тренера', 'тренеров') +
      ', <b>' + dirs.length + '<\/b> ' + plu(dirs.length, 'направление', 'направления', 'направлений') +
      ',<br><b>' + total + '<\/b> ' + plu(total, 'человек', 'человека', 'человек') + ' на сопровождении';

    tabs.innerHTML = ['Все направления'].concat(dirs).map(function (d, n) {
      return '<button type="button" class="vbtrk-tab" data-d="' + d + '" aria-pressed="' + (n === 0) + '">' + d + '<\/button>';
    }).join('');

    function portrait(c, ini) {
      return '<svg class="vbtrk-svg" viewBox="0 0 260 188" preserveAspectRatio="xMidYMid slice" aria-hidden="true" focusable="false">' +
        '<rect width="260" height="188" fill="' + c + '"/>' +
        '<path d="M0 188 L120 0 L196 0 L76 188z" fill="#ffffff" opacity=".05"/>' +
        '<circle cx="130" cy="74" r="33" fill="#ffffff" opacity=".13"/>' +
        '<path d="M62 188c0-37 30-57 68-57s68 20 68 57z" fill="#ffffff" opacity=".13"/>' +
        '<text x="130" y="83" text-anchor="middle" font-family="inherit" font-size="24" font-weight="700" fill="#ffffff" fill-opacity=".62">' + ini + '<\/text><\/svg>';
    }

    grid.innerHTML = COACHES.map(function (c) {
      var w = c.name.split(' ');
      var ini = w[0].charAt(0) + (w[1] ? w[1].charAt(0) : '');
      var pic = c.photo ? '<img class="vbtrk-svg" src="' + c.photo + '" alt="' + c.name + ', тренер направления ' + c.dir.toLowerCase() + '">' : portrait(c.tint, ini);
      return '<article class="vbtrk-card" data-d="' + c.dir + '">' +
        '<div class="vbtrk-pic">' + pic + '<span class="vbtrk-dir">' + c.dir + '<\/span><\/div>' +
        '<div class="vbtrk-body">' +
        '<h3 class="vbtrk-name">' + c.name + '<\/h3>' +
        '<p class="vbtrk-exp">' + c.years + ' ' + plu(c.years, 'год', 'года', 'лет') + ' стажа, ' + c.exp + '<\/p>' +
        '<p class="vbtrk-spec"><span>Узкая специализация<\/span><b>' + c.spec + '<\/b><\/p>' +
        '<div class="vbtrk-load"><div class="vbtrk-load__top"><b>' + c.load + '<\/b><i>' + plu(c.load, 'человек', 'человека', 'человек') + '<br>ведёт сейчас<\/i><\/div>' +
        '<div class="vbtrk-bar"><i data-w="' + Math.round(c.load / peak * 100) + '"><\/i><\/div><\/div>' +
        '<\/div><\/article>';
    }).join('');

    var cards = Array.prototype.slice.call(grid.querySelectorAll('.vbtrk-card'));

    tabs.addEventListener('click', function (e) {
      var btn = e.target.closest ? e.target.closest('.vbtrk-tab') : null;
      if (!btn) return;
      var pick = btn.getAttribute('data-d');
      var all = tabs.querySelectorAll('.vbtrk-tab');
      for (var n = 0; n < all.length; n++) all[n].setAttribute('aria-pressed', String(all[n] === btn));
      cards.forEach(function (card) {
        var on = pick === 'Все направления' || card.getAttribute('data-d') === pick;
        card.classList.toggle('is-off', !on);
      });
    });

    function show(card, n) {
      card.style.transitionDelay = Math.min(n, 5) * 60 + 'ms';
      card.classList.add('is-in');
      var bar = card.querySelector('.vbtrk-bar i');
      if (bar) setTimeout(function () { bar.style.width = bar.getAttribute('data-w') + '%'; }, 180 + Math.min(n, 5) * 60);
    }
    function showAll() { cards.forEach(show); }

    if (!('IntersectionObserver' in window)) { showAll(); return; }
    var io = new IntersectionObserver(function (es) {
      es.forEach(function (e) {
        if (!e.isIntersecting) return;
        show(e.target, cards.indexOf(e.target));
        io.unobserve(e.target);
      });
    }, { rootMargin: '0px 0px -8% 0px' });
    cards.forEach(function (c) { io.observe(c); });
    setTimeout(showAll, 2500);
  }
})();
</script>

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

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

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

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

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

Где в кодеЗа что отвечает
массив COACHES в начале скриптаимена, направления, стаж и число клиентов
поле photo у тренераадрес фотографии, пустое значение оставляет заглушку
--acc в начале стилейцвет плашек направления, цифр и полосы загрузки
ссылка в нижней строкекуда ведёт предложение выбрать время

Тренеры фитнес-клуба: зачем он нужен

В фитнесе выбирают человека, с которым предстоит заниматься по несколько часов в неделю. Посетителю важно понять, кто ведёт силовую, кто растяжку и к кому идти после травмы. Блок отвечает на это в одном экране: направление вынесено на портрет, специализация идёт отдельной строкой, а рядом видно, сколько людей тренер ведёт сейчас. Такая карточка снимает часть вопросов до звонка администратору.

Данные лежат в массиве COACHES в начале скрипта: имя, направление, стаж, специализация и число клиентов. Портреты рисуются заглушками, поле photo принимает адрес файла из медиабиблиотеки Tilda. Кнопки отбора собираются из списка направлений автоматически, прописывать их отдельно не нужно. Полоса загрузки считается от максимума по всем тренерам, поэтому пропорции остаются верными при любом наборе.

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

  • Загрузите снимки в медиабиблиотеку Tilda и вставьте адреса в поле photo у каждого тренера. Пропорции задаёт контейнер, поэтому снимки подрежутся по центру и сетка останется ровной.

  • Кнопки отбора соберутся сами из поля dir: блок берёт список направлений прямо из массива тренеров. Разметку и стили править не нужно.

  • Она показывает загрузку тренера относительно самого занятого коллеги. Посетитель видит, у кого есть свободные окна, и выбирает осознаннее.

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

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

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

Другие блоки команды

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

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

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