Калькулятор уборки квартиры для сайта на Tilda
Считает уборку по площади и виду работ, добавляет санузлы, окна и дополнительные услуги, показывает сумму, время работы и состав бригады.
<section class="vbkuk2">
<div class="vbkuk2-wrap">
<div class="vbkuk2-head">
<h2 class="vbkuk2-h2">Посчитайте уборку своей квартиры</h2>
<p class="vbkuk2-lead">Задайте площадь, вид уборки и дополнительные работы. Рядом сразу появятся сумма, время на объекте и число клинеров в бригаде.</p>
</div>
<div class="vbkuk2-grid">
<div class="vbkuk2-panel">
<div class="vbkuk2-ctl">
<div class="vbkuk2-row"><span>Площадь квартиры</span><b id="vbkuk2-area">54 м²</b></div>
<input type="range" id="vbkuk2-range" min="20" max="200" step="1" value="54" aria-label="Площадь квартиры в квадратных метрах">
<div class="vbkuk2-ticks"><i>20 м²</i><i>110 м²</i><i>200 м²</i></div>
</div>
<div class="vbkuk2-ctl">
<span class="vbkuk2-lbl">Вид уборки</span>
<div class="vbkuk2-seg" id="vbkuk2-kinds" role="group" aria-label="Вид уборки"></div>
</div>
<div class="vbkuk2-pair">
<div class="vbkuk2-step">
<span class="vbkuk2-lbl">Санузлы</span>
<div class="vbkuk2-cnt">
<button type="button" data-t="bath" data-d="-1" aria-label="Меньше санузлов">-</button>
<b id="vbkuk2-bath">1</b>
<button type="button" data-t="bath" data-d="1" aria-label="Больше санузлов">+</button>
</div>
</div>
<div class="vbkuk2-step">
<span class="vbkuk2-lbl">Окна с рамой</span>
<div class="vbkuk2-cnt">
<button type="button" data-t="win" data-d="-1" aria-label="Меньше окон">-</button>
<b id="vbkuk2-win">0</b>
<button type="button" data-t="win" data-d="1" aria-label="Больше окон">+</button>
</div>
</div>
</div>
<div class="vbkuk2-ctl">
<span class="vbkuk2-lbl">Дополнительно</span>
<div class="vbkuk2-extras" id="vbkuk2-extras"></div>
</div>
</div>
<aside class="vbkuk2-out">
<span class="vbkuk2-out__lbl">Стоимость уборки</span>
<strong class="vbkuk2-sum" id="vbkuk2-sum">0 ₽</strong>
<span class="vbkuk2-m2" id="vbkuk2-m2">0 ₽ за м²</span>
<div class="vbkuk2-two">
<div><span>Время работы</span><b id="vbkuk2-time">0</b></div>
<div><span>Бригада</span><b id="vbkuk2-crew">0</b></div>
</div>
<ul class="vbkuk2-rows" id="vbkuk2-rows"></ul>
<a class="vbkuk2-cta" href="#zayavka">Выбрать день и время</a>
<p class="vbkuk2-note">Расчёт предварительный. Итог закрепляем после короткого осмотра на месте: сильные загрязнения и высокие потолки добавляют время.</p>
</aside>
</div>
</div>
</section>
<style>
.vbkuk2, .vbkuk2 *, .vbkuk2 *::before, .vbkuk2 *::after { box-sizing: border-box; }
.vbkuk2 {
--acc: #0f8f7b; --acc-dark: #0b7263; --ink: #16211f; --muted: #5b6f6b; --line: #dde9e6; --soft: #f1f7f5; --deep: #12302b;
--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;
}
.vbkuk2-wrap { max-width: 1140px; margin: 0 auto; padding: 0 clamp(0px, 2vw, 40px); }
.vbkuk2-head { max-width: 42em; margin-bottom: clamp(22px, 3vw, 34px); }
.vbkuk2-h2 { font-family: var(--fh); font-size: clamp(26px, 3.4vw, 40px); line-height: 1.1; font-weight: 700; letter-spacing: -.02em; margin: 0 0 12px; }
.vbkuk2-lead { font-size: 16px; line-height: 1.6; color: var(--muted); margin: 0; }
.vbkuk2-grid { display: grid; grid-template-columns: minmax(0, 1.05fr) minmax(0, .95fr); gap: 16px; align-items: start; }
.vbkuk2-panel { background: var(--soft); border-radius: 22px; padding: clamp(20px, 2.5vw, 28px); display: grid; gap: 22px; }
.vbkuk2-row { display: flex; justify-content: space-between; align-items: baseline; margin-bottom: 10px; font-size: 14px; color: var(--muted); }
.vbkuk2-row b { font-family: var(--fh); font-size: 20px; color: var(--ink); }
.vbkuk2-lbl { display: block; font-size: 14px; color: var(--muted); margin-bottom: 10px; }
.vbkuk2 input[type=range] { -webkit-appearance: none; appearance: none; width: 100%; height: 4px; border-radius: 4px; background: #cfe0dc; outline: none; }
.vbkuk2 input[type=range]::-webkit-slider-thumb { -webkit-appearance: none; appearance: none; width: 24px; height: 24px; border-radius: 50%;
background: var(--acc); border: 4px solid #fff; box-shadow: 0 1px 6px rgba(0,0,0,.2); cursor: pointer; }
.vbkuk2 input[type=range]::-moz-range-thumb { width: 18px; height: 18px; border-radius: 50%; background: var(--acc); border: 3px solid #fff; cursor: pointer; }
.vbkuk2-ticks { display: flex; justify-content: space-between; margin-top: 7px; font-size: 11px; color: #93a8a3; }
.vbkuk2-ticks i { font-style: normal; }
.vbkuk2-seg { display: grid; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); gap: 6px; }
.vbkuk2-seg button { min-height: 48px; padding: 6px 12px; margin: 0; border: 1.5px solid transparent; -webkit-appearance: none; appearance: none;
font: inherit; font-size: 14.5px; font-weight: 600; cursor: pointer; border-radius: 12px; background: #fff !important; color: var(--muted) !important; transition: .18s ease; }
.vbkuk2-seg button:hover { border-color: var(--acc); color: var(--ink) !important; }
.vbkuk2-seg button.vbkuk2-on, .vbkuk2-seg button.vbkuk2-on:hover { background: var(--acc) !important; color: #fff !important; }
.vbkuk2-pair { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 14px; }
.vbkuk2-cnt { display: flex; align-items: center; gap: 8px; background: #fff; border-radius: 12px; padding: 6px; }
.vbkuk2-cnt button { width: 42px; height: 42px; border: 0; margin: 0; -webkit-appearance: none; appearance: none; font: inherit; font-size: 20px;
line-height: 1; cursor: pointer; border-radius: 10px; background: var(--soft) !important; color: var(--ink) !important; }
.vbkuk2-cnt button:hover { background: var(--acc) !important; color: #fff !important; }
.vbkuk2-cnt b { flex: 1; text-align: center; font-family: var(--fh); font-size: 18px; }
.vbkuk2-extras { display: grid; gap: 7px; }
.vbkuk2-ex { display: flex; align-items: center; gap: 11px; background: #fff; border-radius: 12px; padding: 13px 15px; cursor: pointer; font-size: 14.5px; }
.vbkuk2-ex input { position: absolute; opacity: 0; width: 0; height: 0; }
.vbkuk2-ex em { flex: 0 0 20px; height: 20px; border: 1.5px solid #c3d6d1; border-radius: 6px; position: relative; }
.vbkuk2-ex input:checked + em { background: var(--acc); border-color: var(--acc); }
.vbkuk2-ex input:checked + em::after { content: ''; position: absolute; left: 6px; top: 2px; width: 5px; height: 10px; border: solid #fff; border-width: 0 2px 2px 0; transform: rotate(45deg); }
.vbkuk2-ex i { margin-left: auto; font-style: normal; font-size: 13px; color: var(--muted); white-space: nowrap; }
.vbkuk2-out { background: var(--deep); color: #fff; border-radius: 22px; padding: clamp(22px, 2.6vw, 30px); }
.vbkuk2-out__lbl { display: block; font-size: 13px; color: #9db9b3; }
.vbkuk2-sum { display: block; font-family: var(--fh); font-size: clamp(34px, 4.4vw, 50px); line-height: 1.05; font-weight: 700; letter-spacing: -.025em; color: #fff; margin: 6px 0 4px; }
.vbkuk2-m2 { display: block; font-size: 13.5px; color: #9db9b3; margin-bottom: 20px; }
.vbkuk2-two { display: grid; grid-template-columns: repeat(auto-fit, minmax(130px, 1fr)); gap: 10px; margin-bottom: 20px; }
.vbkuk2-two div { background: rgba(255,255,255,.08); border-radius: 14px; padding: 14px 16px; }
.vbkuk2-two span { display: block; font-size: 12.5px; color: #9db9b3; margin-bottom: 5px; }
.vbkuk2-two b { font-family: var(--fh); font-size: 18px; font-weight: 700; }
.vbkuk2-rows { list-style: none; margin: 0 0 22px; padding: 0; }
.vbkuk2-rows li { display: flex; justify-content: space-between; gap: 14px; padding: 11px 0; font-size: 14px; border-bottom: 1px solid rgba(255,255,255,.12); }
.vbkuk2-rows li span { color: #c5d8d4; }
.vbkuk2-rows li b { font-weight: 600; white-space: nowrap; }
.vbkuk2-cta, .vbkuk2-cta:link, .vbkuk2-cta:visited, .vbkuk2-cta:active, .vbkuk2-cta:focus { display: flex; align-items: center; justify-content: center;
min-height: 56px; border-radius: 13px; font-size: 16px; font-weight: 600; background: var(--acc); color: #fff !important; text-decoration: none !important; }
.vbkuk2-cta:hover { background: var(--acc-dark); color: #fff !important; }
.vbkuk2-note { margin: 14px 0 0; font-size: 12.5px; line-height: 1.55; color: #8fa8a3; }
@media (max-width: 860px) { .vbkuk2-grid { grid-template-columns: minmax(0, 1fr); } }
@media (prefers-reduced-motion: reduce) { .vbkuk2 * { transition: none !important; } }
</style>
<script>
(function () {
var RATES = {
kinds: [
{ k: 'sup', n: 'Поддерживающая', m2: 68, h: 0.055, bath: 450, win: 320 },
{ k: 'gen', n: 'Генеральная', m2: 145, h: 0.115, bath: 900, win: 380 },
{ k: 'rep', n: 'После ремонта', m2: 205, h: 0.16, bath: 1200, win: 520 }
],
extras: [
{ k: 'fridge', n: 'Холодильник внутри', p: 700, h: 0.5 },
{ k: 'oven', n: 'Духовка и вытяжка', p: 900, h: 0.7 },
{ k: 'balcony', n: 'Балкон или лоджия', p: 1400, h: 1 },
{ k: 'sofa', n: 'Химчистка дивана', p: 2600, h: 1.2 }
],
minOrder: 2400,
crewHours: 6
};
var roots = document.querySelectorAll('.vbkuk2');
for (var i = 0; i < roots.length; i++) init(roots[i]);
function init(root) {
if (root.__vb) return; root.__vb = 1;
var st = { area: 54, kind: 0, bath: 1, win: 0, ex: {} };
var $ = function (s) { return root.querySelector(s); };
function money(n) { return String(Math.round(n / 10) * 10).replace(/\B(?=(\d{3})+(?!\d))/g, ' '); }
function hword(n) { var t = n % 10, d = n % 100;
if (t === 1 && d !== 11) return 'час'; if (t > 1 && t < 5 && (d < 10 || d > 20)) return 'часа'; return 'часов'; }
var kh = '';
for (var a = 0; a < RATES.kinds.length; a++) {
kh += '<button type="button" data-i="' + a + '"' + (a === 0 ? ' class="vbkuk2-on"' : '') + '>' + RATES.kinds[a].n + '<\/button>';
}
$('#vbkuk2-kinds').innerHTML = kh;
var eh = '';
for (var b = 0; b < RATES.extras.length; b++) {
eh += '<label class="vbkuk2-ex"><input type="checkbox" data-k="' + RATES.extras[b].k + '"><em><\/em>' +
RATES.extras[b].n + '<i>' + money(RATES.extras[b].p) + ' ₽<\/i><\/label>';
}
$('#vbkuk2-extras').innerHTML = eh;
var kbtn = $('#vbkuk2-kinds').querySelectorAll('button');
for (var c = 0; c < kbtn.length; c++) {
kbtn[c].addEventListener('click', function () {
st.kind = +this.getAttribute('data-i');
for (var q = 0; q < kbtn.length; q++) kbtn[q].className = q === st.kind ? 'vbkuk2-on' : '';
render();
});
}
$('#vbkuk2-range').addEventListener('input', function () { st.area = +this.value; render(); });
var steps = root.querySelectorAll('.vbkuk2-cnt button');
for (var d = 0; d < steps.length; d++) {
steps[d].addEventListener('click', function () {
var t = this.getAttribute('data-t'), dv = +this.getAttribute('data-d');
var lim = t === 'bath' ? [1, 5] : [0, 14];
st[t] = Math.max(lim[0], Math.min(lim[1], st[t] + dv));
render();
});
}
var boxes = root.querySelectorAll('.vbkuk2-ex input');
for (var e = 0; e < boxes.length; e++) {
boxes[e].addEventListener('change', function () { st.ex[this.getAttribute('data-k')] = this.checked; render(); });
}
function render() {
var K = RATES.kinds[st.kind];
var base = st.area * K.m2;
var bathSum = (st.bath - 1) * K.bath;
var winSum = st.win * K.win;
var hours = st.area * K.h + (st.bath - 1) * 0.6 + st.win * 0.35 + 1;
var rows = [['Уборка ' + st.area + ' м²', base]];
if (bathSum) rows.push(['Дополнительные санузлы, ' + (st.bath - 1), bathSum]);
if (winSum) rows.push(['Мытьё окон, ' + st.win, winSum]);
var exSum = 0;
for (var j = 0; j < RATES.extras.length; j++) {
var ex = RATES.extras[j];
if (st.ex[ex.k]) { exSum += ex.p; hours += ex.h; rows.push([ex.n, ex.p]); }
}
var total = Math.max(RATES.minOrder, base + bathSum + winSum + exSum);
var crew = hours > RATES.crewHours * 2 ? 3 : (hours > RATES.crewHours ? 2 : 1);
var onSite = Math.max(2, Math.round(hours / crew));
$('#vbkuk2-area').textContent = st.area + ' м²';
$('#vbkuk2-bath').textContent = st.bath;
$('#vbkuk2-win').textContent = st.win;
$('#vbkuk2-sum').textContent = money(total) + ' ₽';
$('#vbkuk2-m2').textContent = money(total / st.area) + ' ₽ за м²';
$('#vbkuk2-time').textContent = onSite + ' ' + hword(onSite);
$('#vbkuk2-crew').textContent = crew === 1 ? 'один клинер' : (crew === 2 ? 'два клинера' : 'три клинера');
var h = '';
for (var r = 0; r < rows.length; r++) h += '<li><span>' + rows[r][0] + '<\/span><b>' + money(rows[r][1]) + ' ₽<\/b><\/li>';
$('#vbkuk2-rows').innerHTML = h;
}
render();
}
})();
</script> Вставляется целиком в блок T123 «HTML-код». Внутри уже есть стили и скрипт, подключать ничего не нужно.
Как вставить блок в Tilda
- Скопируйте кодКнопка «Скопировать код» выше кладёт всё содержимое в буфер обмена.
- Добавьте блок T123В редакторе страницы: «Библиотека блоков» → «Другое» → T123 «HTML-код».
- Вставьте и сохранитеНажмите «Контент», вставьте код в поле, сохраните и обновите страницу.
- ОпубликуйтеВ превью скрипты Tilda работают не всегда — проверяйте на опубликованной странице.
Подробная инструкция с граблями — как вставить вайб-блок в Tilda.
Что менять под себя
| Где в коде | За что отвечает |
|---|---|
объект RATES в начале скрипта | ставки за м², нормы часов, цены санузлов и окон |
массив extras внутри RATES | дополнительные работы, их цена и добавка ко времени |
поля minOrder и crewHours | минимальный заказ и порог, после которого бригада растёт |
--acc и --deep в стилях | цвет кнопки расчёта и фон тёмной карточки с итогом |
Калькулятор уборки квартиры: зачем он нужен
Цена уборки складывается из площади и того, что нужно отмывать дополнительно, поэтому обычная цифра «от» на сайте почти ничего не объясняет. Калькулятор берёт площадь ползунком, вид уборки переключателем, а санузлы и окна счётчиками с кнопками. Дополнительные работы вроде холодильника, духовки, балкона и химчистки дивана добавляются флажками и попадают в разбивку отдельными строками.
Кроме суммы блок показывает две вещи, о которых обычно спрашивают по телефону: сколько часов бригада проведёт на объекте и сколько человек приедет. Число клинеров растёт, когда объём работ переваливает за границу из настроек, а время делится на бригаду. Все ставки, нормы часов и цены дополнительных работ собраны в объекте RATES в начале скрипта.
Вопросы по этому блоку
-
Поле minOrder в объекте RATES задаёт нижнюю границу суммы. Если расчёт по площади оказался меньше, в итог попадёт именно это число. Поставьте ноль, если ограничения по минимальной сумме у вас нет.
-
Скрипт складывает нормы часов по всем работам и делит на порог crewHours. Пока объём меньше порога, приезжает один человек, дальше двое, при двойном превышении трое. Порог меняется одним числом.
-
Удалите из разметки блок счётчика с идентификатором vbkuk2-win, а в скрипте уберите переменную winSum из суммы и соответствующую строку разбивки. Счётчик санузлов и остальные поля продолжат работать.
-
Да. Код на этой странице копируется целиком, без регистрации и подписки. Указывать авторство не нужно, использовать можно в коммерческих проектах.
-
Нет. Все классы внутри имеют собственный префикс vbkuk2, поэтому не пересекаются ни со стилями Tilda, ни с другими блоками. Скрипт привязан к своему элементу и запускается один раз, даже если блок вставлен дважды.
-
Раскладка перестраивается под узкие экраны, размеры заданы через clamp, поля ввода имеют шрифт 16 пикселей — iOS не будет зумить страницу при фокусе. Отдельной мобильной версии подключать не нужно.
Другие калькуляторы
Ещё блоки для клининговых компаний
Собрать сайт целиком, а не по блокам
Дровосеки делают сайты на Tilda под ключ: структура, тексты, дизайн, вёрстка и вайб-блоки под задачи бизнеса.