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

Калькулятор окрашивания волос для сайта салона

Считает вилку стоимости окрашивания и время визита по четырём параметрам: длина волос, тип окрашивания, тонирование и уход после. Все коэффициенты собраны в одном объекте.

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

Код блока

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

    <div class="vbkoq-grid">
      <div class="vbkoq-panel" id="vbkoq-panel"></div>

      <aside class="vbkoq-out">
        <div class="vbkoq-sum">
          <span>Стоимость</span>
          <b id="vbkoq-price">0 ₽</b>
        </div>
        <div class="vbkoq-time">
          <span>Времени в кресле</span>
          <b id="vbkoq-time">0 мин</b>
        </div>
        <ul class="vbkoq-rows" id="vbkoq-rows"></ul>
        <p class="vbkoq-note">Вилка учитывает густоту и исходный цвет: на тёмных волосах осветление идёт дольше и требует больше состава. Точную цену мастер называет на консультации перед работой.</p>
        <a class="vbkoq-cta" href="#zapis">Записаться на консультацию</a>
      </aside>
    </div>
  </div>
</section>

<style>
.vbkoq, .vbkoq *, .vbkoq *::before, .vbkoq *::after { box-sizing: border-box; }
.vbkoq {
  --acc: #a8547a; --acc-dark: #8c4263; --ink: #2c2329; --muted: #7b6c73; --line: #ece0e4; --soft: #faf5f7; --bg: #f7f2f4;
  --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, 80px) 20px;
}
.vbkoq-wrap { max-width: 1140px; margin: 0 auto; padding: 0 clamp(0px, 2vw, 40px); }
.vbkoq-head { max-width: 44em; margin-bottom: clamp(22px, 3vw, 38px); }
.vbkoq-h2 { font-family: var(--fh); font-size: clamp(26px, 3.4vw, 40px); line-height: 1.12; font-weight: 700; letter-spacing: -.02em; margin: 0 0 12px; }
.vbkoq-lead { font-size: 16px; line-height: 1.65; color: var(--muted); margin: 0; }

.vbkoq-grid { display: grid; grid-template-columns: minmax(0, 1.3fr) minmax(0, .85fr); gap: 18px; align-items: start; }
.vbkoq-panel { background: #fff; border: 1px solid var(--line); border-radius: 20px; padding: clamp(20px, 2.4vw, 30px); display: grid; gap: 24px; }
.vbkoq-group > span { display: block; font-size: 13px; color: var(--muted); margin-bottom: 11px; }
.vbkoq-opts { display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: 9px; }
.vbkoq-opt { -webkit-appearance: none; appearance: none; margin: 0; cursor: pointer; font: inherit; font-size: 16px; line-height: 1.35;
  text-align: left; padding: 14px 16px; border: 1.5px solid var(--line); border-radius: 12px;
  background: #fff !important; color: var(--ink) !important; transition: border-color .18s ease, background .18s ease; }
.vbkoq-opt:hover { border-color: var(--acc); }
.vbkoq-opt[aria-pressed="true"] { border-color: var(--acc); background: var(--soft) !important; font-weight: 600; }
.vbkoq-opt i { display: block; font-style: normal; font-size: 12.5px; font-weight: 400; color: var(--muted); margin-top: 4px; }

.vbkoq-out { background: #fff; border: 1px solid var(--line); border-radius: 20px; padding: clamp(20px, 2.4vw, 28px); position: sticky; top: 20px; }
.vbkoq-sum { padding-bottom: 16px; border-bottom: 1px solid var(--line); }
.vbkoq-sum span, .vbkoq-time span { display: block; font-size: 12.5px; color: var(--muted); margin-bottom: 6px; }
.vbkoq-sum b { display: block; font-family: var(--fh); font-size: clamp(25px, 2.7vw, 32px); line-height: 1.1; font-weight: 700; color: var(--acc); letter-spacing: -.02em; }
.vbkoq-time { padding: 16px 0; border-bottom: 1px solid var(--line); }
.vbkoq-time b { display: block; font-family: var(--fh); font-size: 20px; font-weight: 600; }
.vbkoq-rows { list-style: none; margin: 16px 0 0; padding: 0; display: grid; gap: 9px; }
.vbkoq-rows li { display: flex; justify-content: space-between; gap: 14px; font-size: 13.5px; line-height: 1.45; color: var(--muted); }
.vbkoq-rows li b { color: var(--ink); font-weight: 600; white-space: nowrap; }
.vbkoq-note { margin: 18px 0 0; font-size: 12.5px; line-height: 1.6; color: var(--muted); }
.vbkoq-cta, .vbkoq-cta:link, .vbkoq-cta:visited, .vbkoq-cta:active, .vbkoq-cta:focus { display: flex; align-items: center; justify-content: center;
  min-height: 54px; margin-top: 18px; border-radius: 12px; font-size: 15.5px; font-weight: 600;
  background: var(--acc); color: #fff !important; text-decoration: none !important; transition: background .18s ease; }
.vbkoq-cta:hover { background: var(--acc-dark); color: #fff !important; }

@media (max-width: 900px) {
  .vbkoq-grid { grid-template-columns: minmax(0, 1fr); }
  .vbkoq-out { position: static; }
}
@media (max-width: 480px) { .vbkoq-opts { grid-template-columns: minmax(0, 1fr); } }
@media (prefers-reduced-motion: reduce) { .vbkoq * { transition: none !important; } }
</style>

<script>
(function () {
  /* ВСЕ КОЭФФИЦИЕНТЫ БЛОКА: цены в рублях, время в минутах */
  var CFG = {
    spreadDown: 0.94,   /* нижняя граница вилки цены */
    spreadUp: 1.18,     /* верхняя граница вилки цены */
    timeUp: 1.22,       /* верхняя граница вилки времени */
    groups: [
      { id: 'len', title: 'Длина волос', opts: [
        { n: 'Выше плеч', hint: 'каре и короче', k: 1, min: 0 },
        { n: 'До лопаток', hint: 'средняя длина', k: 1.35, min: 20 },
        { n: 'Ниже лопаток', hint: 'длинные', k: 1.75, min: 45 },
        { n: 'Ниже пояса', hint: 'очень длинные', k: 2.15, min: 80 }
      ] },
      { id: 'color', title: 'Тип окрашивания', opts: [
        { n: 'Корни', hint: 'отросшая часть до 3 см', price: 3200, min: 70 },
        { n: 'Один тон', hint: 'ровный цвет по всей длине', price: 4400, min: 110 },
        { n: 'Мелирование', hint: 'осветление прядями', price: 6200, min: 165 },
        { n: 'Растяжка цвета', hint: 'мягкий переход с осветлением', price: 8600, min: 215 }
      ] },
      { id: 'tone', title: 'Тонирование после осветления', opts: [
        { n: 'Не нужно', hint: 'оставляем базовый цвет', price: 0, min: 0 },
        { n: 'В один тон', hint: 'убрать желтизну, закрыть чешуйки', price: 1800, min: 30 },
        { n: 'Сложное', hint: 'смешанный оттенок по зонам', price: 3100, min: 50 }
      ] },
      { id: 'care', title: 'Уход после окрашивания', opts: [
        { n: 'Без ухода', hint: 'только сушка и укладка', price: 900, min: 25 },
        { n: 'Экспресс', hint: 'маска в четыре руки', price: 2100, min: 20 },
        { n: 'Восстановление', hint: 'два шага, для осветлённых волос', price: 4300, min: 45 }
      ] }
    ]
  };

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

  function init(root) {
    if (root.__vb) return; root.__vb = 1;
    var panel = root.querySelector('#vbkoq-panel');
    var priceEl = root.querySelector('#vbkoq-price'), timeEl = root.querySelector('#vbkoq-time'), rowsEl = root.querySelector('#vbkoq-rows');
    var state = { len: 1, color: 1, tone: 1, care: 0 };

    function money(n) { return String(Math.round(n / 100) * 100).replace(/\B(?=(\d{3})+(?!\d))/g, ' '); }
    function clock(m) {
      var mm = Math.round(m / 5) * 5, h = Math.floor(mm / 60), r = mm % 60;
      if (h === 0) return r + ' мин';
      return r === 0 ? h + ' ч' : h + ' ч ' + r + ' мин';
    }

    panel.innerHTML = CFG.groups.map(function (g) {
      return '<div class="vbkoq-group"><span>' + g.title + '<\/span><div class="vbkoq-opts" data-g="' + g.id + '">' +
        g.opts.map(function (o, i) {
          return '<button type="button" class="vbkoq-opt" data-i="' + i + '" aria-pressed="' + (state[g.id] === i ? 'true' : 'false') + '">' +
            o.n + '<i>' + o.hint + '<\/i><\/button>';
        }).join('') + '<\/div><\/div>';
    }).join('');

    function pick(id) {
      var g = null;
      for (var j = 0; j < CFG.groups.length; j++) if (CFG.groups[j].id === id) g = CFG.groups[j];
      return g.opts[state[id]];
    }

    function render() {
      var len = pick('len'), color = pick('color'), tone = pick('tone'), care = pick('care');
      var colorPrice = color.price * len.k;
      var tonePrice = tone.price ? tone.price * (len.k > 1.5 ? 1.3 : 1) : 0;
      var total = colorPrice + tonePrice + care.price;
      var mins = color.min + len.min + tone.min + care.min;

      priceEl.textContent = 'от ' + money(total * CFG.spreadDown) + ' до ' + money(total * CFG.spreadUp) + ' ₽';
      timeEl.textContent = 'от ' + clock(mins) + ' до ' + clock(mins * CFG.timeUp);

      var rows = [
        ['Окрашивание, ' + color.n.toLowerCase(), money(colorPrice) + ' ₽'],
        ['Тонирование', tonePrice ? money(tonePrice) + ' ₽' : 'не выбрано'],
        ['Уход и укладка', care.price ? money(care.price) + ' ₽' : 'входит в цену'],
        ['Длина', len.n.toLowerCase()]
      ];
      rowsEl.innerHTML = rows.map(function (r) {
        return '<li><span>' + r[0] + '<\/span><b>' + r[1] + '<\/b><\/li>';
      }).join('');
    }

    panel.addEventListener('click', function (e) {
      var btn = e.target.closest ? e.target.closest('.vbkoq-opt') : null;
      if (!btn) return;
      var box = btn.parentNode, id = box.getAttribute('data-g');
      state[id] = +btn.getAttribute('data-i');
      var all = box.querySelectorAll('.vbkoq-opt');
      for (var k = 0; k < all.length; k++) all[k].setAttribute('aria-pressed', all[k] === btn ? 'true' : 'false');
      render();
    });

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

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

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

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

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

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

Где в кодеЗа что отвечает
объект CFG в начале скриптацены, множители длины и время всех вариантов
spreadDown и spreadUpширина вилки цены снизу и сверху
массив groups внутри CFGсами группы вопросов и варианты ответов
текст в .vbkoq-noteпояснение, от чего зависит итоговая цена
href у кнопкиякорь перехода к записи на странице

Калькулятор окрашивания: зачем он нужен

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

Все числа блока лежат в объекте CFG в первых строках скрипта: цены услуг, множители длины, время каждой процедуры и границы вилки. Интерфейс собирается из этого же объекта, поэтому добавленный вариант сам появляется кнопкой в нужной группе. Кнопки крупные и работают на телефоне, размер текста в них не вызывает подзумливание на iOS. Результат считается в браузере, запросов наружу блок не делает.

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

  • Откройте объект CFG в начале скрипта: у каждого варианта есть поле price в рублях и поле min в минутах. Поменяйте эти числа, разметку и стили трогать не нужно.

  • Допишите объект в массив groups: идентификатор, заголовок и варианты. Кнопки отрисуются сами, а значение попадёт в расчёт после добавления слагаемого в функцию render.

  • Итог зависит от густоты волос и исходного цвета, которые видно только на консультации. Границы вилки задаются двумя коэффициентами и при желании сводятся к одной цифре.

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

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

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

Другие калькуляторы

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

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

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