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

Блок «Мастера салона» для сайта салона красоты

Плитки мастеров с портретом, направлением и стажем. При наведении на портрет поднимается подпись о том, с чем мастер работает чаще всего, под именем стоит ближайшее свободное окно.

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

Код блока

<section class="vbmsk">
  <div class="vbmsk-wrap">
    <div class="vbmsk-head">
      <h2 class="vbmsk-h2">Мастера и ближайшие окна</h2>
      <p class="vbmsk-lead">Направление, стаж и время, на которое ещё можно попасть на этой неделе. Наведите курсор на портрет или коснитесь его, чтобы прочитать, с чем мастер работает чаще всего.</p>
    </div>

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

    <p class="vbmsk-foot">Свободные окна показаны для примера. Актуальное расписание держит виджет записи: <a class="vbmsk-link" href="#zapis">выбрать время</a></p>
  </div>
</section>

<style>
.vbmsk, .vbmsk *, .vbmsk *::before, .vbmsk *::after { box-sizing: border-box; }
.vbmsk {
  --acc: #a35f66; --acc-dark: #8a4d54; --ink: #2f2429; --muted: #776a6e; --line: #ece0e1; --soft: #faf5f5;
  --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;
}
.vbmsk-wrap { max-width: 1160px; margin: 0 auto; padding: 0 clamp(0px, 2vw, 40px); }
.vbmsk-head { max-width: 44em; margin-bottom: clamp(24px, 3vw, 40px); }
.vbmsk-h2 { font-family: var(--fh); font-size: clamp(26px, 3.4vw, 40px); line-height: 1.12; font-weight: 600; letter-spacing: -.02em; margin: 0 0 12px; }
.vbmsk-lead { font-size: 16px; line-height: 1.65; color: var(--muted); margin: 0; }

.vbmsk-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(215px, 1fr)); gap: clamp(14px, 1.6vw, 20px); }
.vbmsk-card { position: relative; background: var(--soft); border-radius: 18px; overflow: hidden; cursor: pointer;
  opacity: 0; transform: translateY(10px); transition: opacity .45s ease, transform .45s cubic-bezier(.22,1,.36,1); }
.vbmsk-card.is-in { opacity: 1; transform: none; }
.vbmsk-card:focus { outline: 2px solid var(--acc); outline-offset: 3px; }
.vbmsk-pic { position: relative; overflow: hidden; }
.vbmsk-svg { display: block; width: 100%; height: auto; }
.vbmsk-hint { position: absolute; left: 0; right: 0; bottom: 0; padding: 16px 18px; background: rgba(47, 36, 41, .88);
  transform: translateY(101%); transition: transform .32s cubic-bezier(.22,1,.36,1); }
.vbmsk-hint p { margin: 0; color: #fff; font-size: 13.5px; line-height: 1.5; }
.vbmsk-card:hover .vbmsk-hint, .vbmsk-card:focus .vbmsk-hint, .vbmsk-card.is-open .vbmsk-hint { transform: none; }
.vbmsk-body { padding: 16px 18px 18px; }
.vbmsk-name { font-family: var(--fh); font-size: 17px; font-weight: 600; line-height: 1.25; margin: 0 0 4px; }
.vbmsk-dir { font-size: 13px; color: var(--acc); font-weight: 500; margin: 0 0 12px; }
.vbmsk-dir i { font-style: normal; color: var(--muted); font-weight: 400; }
.vbmsk-slot { display: flex; align-items: center; gap: 8px; margin: 0; padding-top: 12px; border-top: 1px solid var(--line);
  font-size: 13px; color: var(--muted); }
.vbmsk-slot b { color: var(--ink); font-weight: 600; }
.vbmsk-dot { flex: 0 0 7px; width: 7px; height: 7px; border-radius: 50%; background: var(--acc); }
.vbmsk-foot { margin: clamp(22px, 2.6vw, 32px) 0 0; font-size: 14.5px; line-height: 1.6; color: var(--muted); }
.vbmsk-link, .vbmsk-link:link, .vbmsk-link:visited { color: var(--acc) !important; text-decoration: underline; }
.vbmsk-link:hover { color: var(--acc-dark) !important; }

@media (prefers-reduced-motion: reduce) {
  .vbmsk-card { opacity: 1 !important; transform: none !important; transition: none !important; }
  .vbmsk-hint { transition: none !important; }
}
</style>

<script>
(function () {
  /* ── МАСТЕРА: hair — форма причёски на заглушке: long, bun, wave, crop ── */
  var STAFF = [
    { name: 'Анна Ковалёва', dir: 'Колорист', years: 11, c: '#efdcd8', hair: 'long', slot: 'вторник, 10:00',
      spec: 'Сложное окрашивание на тёмную базу: растяжка, тонирование после смывки, возвращение своего цвета.' },
    { name: 'Ирина Сенина', dir: 'Мастер маникюра', years: 7, c: '#e6dee9', hair: 'bun', slot: 'сегодня, 18:30',
      spec: 'Аппаратный маникюр, укрепление ногтей после наращивания, тонкая работа с кутикулой.' },
    { name: 'Мария Гурьева', dir: 'Бровист', years: 5, c: '#e3e2d5', hair: 'wave', slot: 'среда, 12:15',
      spec: 'Форма бровей под черты лица, окрашивание краской и хной, ламинирование с укладкой.' },
    { name: 'Дарья Полякова', dir: 'Парикмахер-стилист', years: 9, c: '#e8dcd2', hair: 'long', slot: 'завтра, 15:00',
      spec: 'Стрижки на тонкие волосы, укладки без плойки, работа с отросшей длиной и сечением.' },
    { name: 'Ольга Титова', dir: 'Косметолог', years: 12, c: '#dae3e2', hair: 'bun', slot: 'пятница, 11:00',
      spec: 'Чистки, уходовые программы для чувствительной кожи, подбор домашнего ухода на сезон.' },
    { name: 'Егор Липатов', dir: 'Барбер', years: 6, c: '#dfe0e8', hair: 'crop', slot: 'суббота, 09:40',
      spec: 'Стрижки машинкой и ножницами, оформление бороды опасной бритвой, камуфляж седины.' }
  ];

  var roots = document.querySelectorAll('.vbmsk');
  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('#vbmsk-grid');
    var INK = 'rgba(58,42,47,.8)';

    function portrait(c, hair) {
      var s = '<svg class="vbmsk-svg" viewBox="0 0 200 210" aria-hidden="true" focusable="false">';
      s += '<rect width="200" height="210" fill="' + c + '"/>';
      if (hair === 'long') s += '<rect x="60" y="66" width="80" height="94" rx="30" fill="' + INK + '"/>';
      s += '<circle cx="100" cy="92" r="32" fill="' + INK + '"/>';
      if (hair === 'bun') s += '<circle cx="100" cy="50" r="13" fill="' + INK + '"/>';
      if (hair === 'wave') s += '<path d="M67 88c0-21 15-35 33-35s33 14 33 35c-9-13-19-18-33-18s-24 5-33 18z" fill="' + INK + '"/>';
      if (hair === 'crop') s += '<path d="M69 82c4-16 16-25 31-25s27 9 31 25z" fill="' + INK + '"/>';
      s += '<path d="M44 210c0-31 25-49 56-49s56 18 56 49z" fill="' + INK + '"/>';
      return s + '<\/svg>';
    }
    function yearWord(n) { var m = n % 100, k = n % 10; return m > 10 && m < 20 ? 'лет' : k === 1 ? 'год' : k > 1 && k < 5 ? 'года' : 'лет'; }

    grid.innerHTML = STAFF.map(function (m) {
      return '<article class="vbmsk-card" tabindex="0">' +
        '<div class="vbmsk-pic">' + portrait(m.c, m.hair) +
        '<div class="vbmsk-hint"><p>' + m.spec + '<\/p><\/div><\/div>' +
        '<div class="vbmsk-body">' +
        '<h3 class="vbmsk-name">' + m.name + '<\/h3>' +
        '<p class="vbmsk-dir">' + m.dir + ' <i>· стаж ' + m.years + ' ' + yearWord(m.years) + '<\/i><\/p>' +
        '<p class="vbmsk-slot"><span class="vbmsk-dot" aria-hidden="true"><\/span>Ближайшее окно: <b>' + m.slot + '<\/b><\/p>' +
        '<\/div><\/article>';
    }).join('');

    var cards = Array.prototype.slice.call(grid.querySelectorAll('.vbmsk-card'));
    cards.forEach(function (card) {
      card.addEventListener('click', function () {
        var open = card.classList.contains('is-open');
        cards.forEach(function (o) { o.classList.remove('is-open'); });
        if (!open) card.classList.add('is-open');
      });
      card.addEventListener('keydown', function (e) {
        if (e.key === 'Enter' || e.key === ' ' || e.keyCode === 13 || e.keyCode === 32) { e.preventDefault(); card.click(); }
      });
    });

    if (!('IntersectionObserver' in window)) { cards.forEach(function (c) { c.classList.add('is-in'); }); return; }
    var io = new IntersectionObserver(function (es) {
      es.forEach(function (e) {
        if (!e.isIntersecting) return;
        var i = cards.indexOf(e.target);
        e.target.style.transitionDelay = Math.min(i, 5) * 60 + 'ms';
        e.target.classList.add('is-in');
        io.unobserve(e.target);
      });
    }, { rootMargin: '0px 0px -8% 0px' });
    cards.forEach(function (c) { io.observe(c); });
    setTimeout(function () { cards.forEach(function (c) { c.classList.add('is-in'); }); }, 2500);
  }
})();
</script>

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

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

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

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

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

Где в кодеЗа что отвечает
массив STAFF в начале скриптасостав мастеров: имя, направление, стаж, окно записи
поле spec у мастераподпись, которая поднимается при наведении на портрет
поле c и hairцвет подложки и форма причёски на заглушке портрета
--acc в стиляхцвет направления, точки у окна записи и ссылки под блоком

Мастера салона красоты: зачем он нужен

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

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

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

  • В скрипте замените вызов portrait на тег изображения с адресом из библиотеки Tilda и оставьте класс vbmsk-svg. Снимки лучше готовить в пропорции примерно 1:1,05, тогда они встанут на место заглушки без обрезки.

  • Да, касание плитки поднимает подпись и опускает её у соседней. То же самое делает клавиша пробела или ввода, когда плитка выбрана с клавиатуры.

  • Это обычный текст в поле slot, он не связан с расписанием. Поставьте туда живые дни или уберите строку целиком, если расписание меняется каждый час, а ссылку внизу направьте на виджет записи.

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

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

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

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

Ещё блоки для салонов красоты

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

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