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

Блок абонементов для сайта фитнес-клуба

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

  • ТипТарифы
  • НишаФитнес и спорт
  • Вес кода12.2 КБ
  • Заголовок H1нет, только H2
  • Форма Tildaнет
  • Интерактив
  • Считает цену
Живое демо — блок работает прямо здесь

Код блока

<section class="vbafk">
  <div class="vbafk-wrap">
    <div class="vbafk-head">
      <div class="vbafk-head__txt">
        <h2 class="vbafk-h2">Клубные карты</h2>
        <p class="vbafk-lead">Четыре варианта доступа в зал: от одного визита до года. Утренний тариф работает до 16:00 в будни, полный день снимает ограничения по времени и добавляет выходные.</p>
      </div>

      <div class="vbafk-sw" id="vbafk-sw" role="group" aria-label="Время посещения">
        <i class="vbafk-sw__thumb" id="vbafk-thumb" aria-hidden="true"></i>
        <button type="button" class="vbafk-sw__b" data-m="am" aria-pressed="true">Утро до 16:00</button>
        <button type="button" class="vbafk-sw__b" data-m="all" aria-pressed="false">Полный день</button>
      </div>
    </div>

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

    <p class="vbafk-note" id="vbafk-note"></p>
  </div>
</section>

<style>
.vbafk, .vbafk *, .vbafk *::before, .vbafk *::after { box-sizing: border-box; }
.vbafk {
  --acc: #d6f24b; --acc-dark: #bfdb35; --bg: #0e1013; --panel: #171a1f; --panel2: #1e222a;
  --ink: #f1f3f5; --muted: #99a1ab; --line: #262b32;
  --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;
}
.vbafk-wrap { max-width: 1200px; margin: 0 auto; padding: 0 clamp(0px, 2vw, 40px); }
.vbafk-head { display: flex; flex-wrap: wrap; align-items: flex-end; justify-content: space-between; gap: 20px clamp(20px, 3vw, 48px); margin-bottom: clamp(22px, 3vw, 36px); }
.vbafk-head__txt { max-width: 44em; }
.vbafk-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; }
.vbafk-lead { font-size: 16px; line-height: 1.65; color: var(--muted); margin: 0; }

.vbafk-sw { position: relative; display: inline-flex; padding: 5px; background: var(--panel); border: 1px solid var(--line); border-radius: 999px; }
.vbafk-sw__thumb { position: absolute; top: 5px; left: 5px; height: calc(100% - 10px); width: 0; border-radius: 999px; background: var(--acc);
  transition: transform .28s cubic-bezier(.22,1,.36,1), width .28s cubic-bezier(.22,1,.36,1); }
.vbafk-sw__b { position: relative; z-index: 1; -webkit-appearance: none; appearance: none; border: 0; margin: 0; cursor: pointer; font: inherit;
  font-size: 14.5px; line-height: 1.2; padding: 12px 22px; border-radius: 999px; white-space: nowrap;
  background: none !important; color: var(--muted) !important; transition: color .2s ease; }
.vbafk-sw__b:hover { color: var(--ink) !important; }
.vbafk-sw__b[aria-pressed="true"] { color: #14170f !important; font-weight: 700; }

.vbafk-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(236px, 1fr)); gap: clamp(12px, 1.3vw, 16px); align-items: stretch; }
.vbafk-card { position: relative; display: flex; flex-direction: column; background: var(--panel); border: 1px solid var(--line);
  border-radius: 20px; padding: clamp(20px, 2.2vw, 28px); }
.vbafk-card--top { border-color: var(--acc); background: var(--panel2); }
.vbafk-tag { position: absolute; top: -11px; left: clamp(20px, 2.2vw, 28px); background: var(--acc); color: #14170f;
  font-size: 10.5px; font-weight: 800; letter-spacing: .06em; text-transform: uppercase; border-radius: 999px; padding: 5px 12px; }
.vbafk-name { font-family: var(--fh); font-size: 18px; font-weight: 700; letter-spacing: -.01em; margin: 0 0 6px; }
.vbafk-for { font-size: 13px; line-height: 1.55; color: var(--muted); margin: 0 0 18px; min-height: 4.3em; }

.vbafk-price { min-height: 78px; margin-bottom: 16px; transition: opacity .16s ease; }
.vbafk-price.is-swap { opacity: 0; }
.vbafk-price b { display: block; font-family: var(--fh); font-size: clamp(26px, 2.7vw, 33px); line-height: 1.05; font-weight: 800; letter-spacing: -.02em; color: var(--acc); }
.vbafk-price span { display: block; font-size: 12px; line-height: 1.5; color: var(--muted); margin-top: 8px; }
.vbafk-price u { text-decoration: none; color: var(--ink); font-weight: 600; }

.vbafk-freeze { display: flex; align-items: center; gap: 9px; padding: 11px 13px; border-radius: 12px; background: var(--panel2); margin-bottom: 16px; }
.vbafk-card--top .vbafk-freeze { background: #262b33; }
.vbafk-freeze svg { flex: 0 0 16px; width: 16px; height: 16px; color: var(--acc); }
.vbafk-freeze span { font-size: 12.5px; line-height: 1.4; color: var(--muted); }
.vbafk-freeze b { color: var(--ink); font-weight: 600; }

.vbafk-inc { list-style: none; margin: 0 0 20px; padding: 0; flex: 1; display: grid; gap: 9px; align-content: start; }
.vbafk-inc li { display: flex; align-items: flex-start; gap: 9px; font-size: 13.5px; line-height: 1.5; color: var(--muted); }
.vbafk-inc li::before { content: ''; flex: 0 0 7px; width: 7px; height: 7px; margin-top: 7px; border-radius: 2px; background: var(--acc); }

.vbafk-cta, .vbafk-cta:link, .vbafk-cta:visited, .vbafk-cta:active, .vbafk-cta:focus { display: flex; align-items: center; justify-content: center;
  margin-top: auto; min-height: 50px; border-radius: 12px; font-size: 15px; font-weight: 700;
  background: var(--panel2); color: var(--ink) !important; text-decoration: none !important; transition: .18s ease; }
.vbafk-cta:hover { background: #2c323b; color: var(--ink) !important; }
.vbafk-card--top .vbafk-cta { background: var(--acc); color: #14170f !important; }
.vbafk-card--top .vbafk-cta:hover { background: var(--acc-dark); color: #14170f !important; }

.vbafk-note { margin: clamp(20px, 2.4vw, 30px) 0 0; font-size: 13.5px; line-height: 1.65; color: var(--muted); max-width: 60em; }

@media (prefers-reduced-motion: reduce) { .vbafk * { transition: none !important; } }
</style>

<script>
(function () {
  var CTA_HREF = '#zapis';

  /* ── КАРТЫ: am - утренняя цена, all - полный день ── */
  var CARDS = [
    { name: 'Разовый визит', forWho: 'Посмотреть зал и попробовать оборудование без обязательств.',
      am: 700, all: 1100, unit: 'за визит', visits: 1, freeze: 0, top: false,
      inc: ['Тренажёрный зал и кардиозона', 'Шкафчик и душ', 'Вводный инструктаж по технике'] },
    { name: 'Месяц', forWho: 'Проверить, встроится ли зал в вашу неделю, и не платить вперёд за год.',
      am: 2900, all: 3900, unit: 'в месяц', visits: 30, freeze: 7, top: false,
      inc: ['Всё из разового визита', 'Групповые занятия по расписанию', 'Один разбор техники с тренером', 'Заморозка на 7 дней'] },
    { name: 'Полгода', forWho: 'Основной вариант тех, кто ходит два или три раза в неделю круглый год.',
      am: 13900, all: 18900, unit: 'за 6 месяцев', visits: 180, freeze: 30, top: false,
      inc: ['Всё из месячной карты', 'Замеры состава тела раз в квартал', 'План занятий на первый месяц', 'Заморозка на 30 дней'] },
    { name: 'Год', forWho: 'Самая низкая цена за месяц и запас дней заморозки на отпуск и болезни.',
      am: 22900, all: 31900, unit: 'за 12 месяцев', visits: 365, freeze: 60, top: true,
      inc: ['Всё из полугодовой карты', 'Четыре персональные тренировки', 'Гостевые визиты, 6 штук за год', 'Заморозка на 60 дней', 'Перенос карты на другого человека один раз'] }
  ];

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

  function fmt(n) { return String(Math.round(n)).replace(/\B(?=(\d{3})+(?!\d))/g, ' ') + ' ₽'; }

  function init(root) {
    if (root.__vb) return; root.__vb = 1;
    var grid = root.querySelector('#vbafk-grid'), sw = root.querySelector('#vbafk-sw');
    var thumb = root.querySelector('#vbafk-thumb'), note = root.querySelector('#vbafk-note');
    var mode = 'am';
    var snow = '<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true" focusable="false">' +
      '<path d="M8 1.5v13M2.4 4.7l11.2 6.6M13.6 4.7L2.4 11.3"/><\/svg>';
    var month = CARDS[1];

    grid.innerHTML = CARDS.map(function (c, n) {
      return '<article class="vbafk-card' + (c.top ? ' vbafk-card--top' : '') + '">' +
        (c.top ? '<span class="vbafk-tag">Выгоднее всего за месяц<\/span>' : '') +
        '<h3 class="vbafk-name">' + c.name + '<\/h3>' +
        '<p class="vbafk-for">' + c.forWho + '<\/p>' +
        '<div class="vbafk-price" data-n="' + n + '"><\/div>' +
        '<p class="vbafk-freeze">' + snow + '<span>' + (c.freeze ? 'Заморозка: <b>' + c.freeze + ' дней<\/b> за срок карты' : 'Заморозка не предусмотрена') + '<\/span><\/p>' +
        '<ul class="vbafk-inc">' + c.inc.map(function (x) { return '<li>' + x + '<\/li>'; }).join('') + '<\/ul>' +
        '<a class="vbafk-cta" href="' + CTA_HREF + '">Оформить карту<\/a><\/article>';
    }).join('');

    var boxes = grid.querySelectorAll('.vbafk-price');

    function priceHTML(c) {
      var p = c[mode], extra = '';
      if (c.visits === 1) {
        extra = 'оплата на стойке при входе';
      } else if (c.visits === 30) {
        extra = 'при посещении дважды в неделю выходит <u>' + fmt(p / 8) + '<\/u> за занятие';
      } else {
        var months = c.visits === 180 ? 6 : 12;
        var save = month[mode] * months - p;
        extra = '<u>' + fmt(p / months) + '<\/u> в месяц, это на ' + fmt(save) + ' меньше, чем оплачивать помесячно';
      }
      return '<b>' + fmt(p) + '<\/b><span>' + c.unit + ', ' + extra + '<\/span>';
    }

    function draw(animate) {
      for (var n = 0; n < boxes.length; n++) {
        (function (box) {
          var c = CARDS[+box.getAttribute('data-n')];
          if (!animate) { box.innerHTML = priceHTML(c); return; }
          box.classList.add('is-swap');
          setTimeout(function () { box.innerHTML = priceHTML(c); box.classList.remove('is-swap'); }, 150);
        })(boxes[n]);
      }
      note.innerHTML = mode === 'am'
        ? 'Утренний тариф действует с открытия до 16:00 в будние дни. В выходные вход по нему закрыт, доплата за разовый визит в выходной составляет ' + fmt(500) + '. Карта именная, оформляется на стойке администратора за пять минут.'
        : 'Полный день открывает зал в любые часы работы клуба, включая выходные и праздники. Разница с утренним тарифом на годовой карте составляет ' + fmt(CARDS[3].all - CARDS[3].am) + ', это около ' + fmt((CARDS[3].all - CARDS[3].am) / 12) + ' в месяц.';
    }

    function place() {
      var on = sw.querySelector('.vbafk-sw__b[aria-pressed="true"]');
      if (!on) return;
      thumb.style.width = on.offsetWidth + 'px';
      thumb.style.transform = 'translateX(' + (on.offsetLeft - 5) + 'px)';
    }

    sw.addEventListener('click', function (e) {
      var btn = e.target.closest ? e.target.closest('.vbafk-sw__b') : null;
      if (!btn || btn.getAttribute('aria-pressed') === 'true') return;
      var all = sw.querySelectorAll('.vbafk-sw__b');
      for (var n = 0; n < all.length; n++) all[n].setAttribute('aria-pressed', String(all[n] === btn));
      mode = btn.getAttribute('data-m');
      place(); draw(true);
    });

    window.addEventListener('resize', place);
    draw(false);
    place();
    setTimeout(place, 400);
  }
})();
</script>

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

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

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

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

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

Где в кодеЗа что отвечает
массив CARDS в начале скриптаназвания карт, цены утром и днём, состав
поле freeze у картычисло дней заморозки в строке под ценой
поле topкакая карта получает бейдж и подсветку рамкой
CTA_HREFякорь, куда ведёт кнопка оформления

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

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

Цены лежат в массиве CARDS: два числа на каждую карту и список того, что входит. Пересчёт в месяц и разница с помесячной оплатой считаются скриптом от этих же чисел, сводить их вручную не нужно. Бейдж выгоды включается полем top у любой карты. Сетка построена на auto-fit, поэтому три карты или пять встанут так же ровно.

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

  • Удалите разметку переключателя и оставьте в скрипте режим am. Цены возьмутся из одного поля, остальные карточки продолжат работать.

  • Скрипт делит цену карты на её срок и сравнивает результат с двенадцатью месячными платежами. Достаточно поправить цены в CARDS, формулы пересчитают сами.

  • Да, допишите объект в CARDS. Сетка на auto-fit перестроится, а на узком экране карты встанут в одну колонку.

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

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

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

Другие блоки тарифов

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

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

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