
Задача
Сделать одностраничный лендинг для кофейни: показать атмосферу, меню, галерею и собрать заявки на бронирование столика. Чистая вёрстка без фреймворков и сборщиков — HTML, CSS, JS.
Как работает
Свёрстано с нуля на чистой троице. Только HTML5, CSS3 и vanilla JavaScript.
Навигация: фиксированная шапка с blur-эффектом. При скролле меняет прозрачность и добавляет тень. На мобилке — гамбургер-меню, выезжает справа.
Hero: фоновое фото с оверлеем, заголовок с анимацией fadeInDown, подзаголовок — fadeInUp. Параллакс на скролле — фоновое изображение двигается медленнее контента.
Меню: фильтр по категориям (кофе, чай, десерты, еда) + карусель с навигацией. Карусель на чистом JS: пересчёт ширины карточек под контейнер, стрелки, точки-индикаторы, свайп на тач-устройствах. Используется scrollend для синхронизации с прокруткой, с фолбэком на таймаут.
Галерея: CSS Grid с нестандартной раскладкой — первая картинка занимает 2×2 ячейки. Hover-эффект: зум + градиентный оверлей.
Форма бронирования: валидация полей, маска для телефона +7 (___) ___-__-__, модалка с подтверждением. Всё на чистом JS — без сторонних библиотек.
phoneInput.addEventListener('input', (e) => {
let value = e.target.value.replace(/\D/g, '');
if (value.length > 0) {
if (value[0] === '7' || value[0] === '8') value = value.substring(1);
let formatted = '+7 (';
if (value.length > 0) formatted += value.substring(0, 3);
if (value.length >= 3) formatted += ') ' + value.substring(3, 6);
if (value.length >= 6) formatted += '-' + value.substring(6, 8);
if (value.length >= 8) formatted += '-' + value.substring(8, 10);
e.target.value = formatted;
}
});
Анимации: Intersection Observer для появления блоков при скролле. Атрибуты data-aos — своя реализация, без библиотеки AOS.
Адаптив: 4 брейкпоинта — 1024px, 992px, 840px, 480px. Сетки перестраиваются, карусель пересчитывает видимое количество карточек.
Итог
Готовый лендинг кофейни: адаптивный, с анимациями, каруселью и формой бронирования.