Продолжение: Дополнительные компоненты
17. Аккордеон (Accordion)
HTML структура:
<!-- Аккордеон -->
<div class="accordion" id="faqAccordion">
<div class="accordion-item">
<button class="accordion-header" data-accordion-target="accordion1">
<span class="accordion-title">Сколько времени занимает разработка лендинга?</span>
<span class="accordion-icon">
<i class="fas fa-chevron-down"></i>
</span>
</button>
<div class="accordion-content" id="accordion1">
<div class="accordion-body">
<p>Стандартный лендинг разрабатывается за 7-14 дней в зависимости от сложности проекта. Это включает:</p>
<ul>
<li>Разработку дизайна (2-3 дня)</li>
<li>Верстку и программирование (3-5 дней)</li>
<li>Тестирование и правки (2-3 дня)</li>
<li>Запуск и настройку (1 день)</li>
</ul>
<p>Срочная разработка возможна за 3-5 дней с доплатой 50%.</p>
</div>
</div>
</div>
<div class="accordion-item">
<button class="accordion-header" data-accordion-target="accordion2">
<span class="accordion-title">Какая стоимость разработки лендинга?</span>
<span class="accordion-icon">
<i class="fas fa-chevron-down"></i>
</span>
</button>
<div class="accordion-content" id="accordion2">
<div class="accordion-body">
<p>Стоимость зависит от сложности проекта:</p>
<ul>
<li><strong>Базовый лендинг</strong> — от 25 000 ₽</li>
<li><strong>Стандартный лендинг</strong> — от 45 000 ₽</li>
<li><strong>Премиум лендинг</strong> — от 75 000 ₽</li>
</ul>
<p>Точная цена рассчитывается после обсуждения требований проекта.</p>
</div>
</div>
</div>
<div class="accordion-item">
<button class="accordion-header" data-accordion-target="accordion3">
<span class="accordion-title">Что входит в стоимость разработки?</span>
<span class="accordion-icon">
<i class="fas fa-chevron-down"></i>
</span>
</button>
<div class="accordion-content" id="accordion3">
<div class="accordion-body">
<p>В базовую стоимость входит:</p>
<ul>
<li>Разработка уникального дизайна</li>
<li>Адаптивная верстка под все устройства</li>
<li>Интеграция форм обратной связи</li>
<li>Базовая SEO-оптимизация</li>
<li>Подключение аналитики (Google Analytics, Яндекс.Метрика)</li>
<li>Техническая поддержка 1 месяц</li>
</ul>
</div>
</div>
</div>
<div class="accordion-item">
<button class="accordion-header" data-accordion-target="accordion4">
<span class="accordion-title">Нужен ли мне хостинг и домен?</span>
<span class="accordion-icon">
<i class="fas fa-chevron-down"></i>
</span>
</button>
<div class="accordion-content" id="accordion4">
<div class="accordion-body">
<p>Да, для размещения лендинга необходимы:</p>
<ul>
<li><strong>Домен</strong> — адрес вашего сайта (например, site.ru). Стоимость от 200 ₽/год</li>
<li><strong>Хостинг</strong> — место для хранения файлов сайта. Стоимость от 150 ₽/месяц</li>
</ul>
<p>Мы поможем подобрать и настроить хостинг, либо можем предоставить готовое решение.</p>
</div>
</div>
</div>
<div class="accordion-item">
<button class="accordion-header" data-accordion-target="accordion5">
<span class="accordion-title">Предоставляете ли вы гарантию?</span>
<span class="accordion-icon">
<i class="fas fa-chevron-down"></i>
</span>
</button>
<div class="accordion-content" id="accordion5">
<div class="accordion-body">
<p>Да, мы предоставляем гарантию на все наши работы:</p>
<ul>
<li>Бесплатные исправления в течение 1 месяца после сдачи проекта</li>
<li>Техническая поддержка 1 месяц</li>
<li>Гарантия работоспособности всех функций</li>
</ul>
<p>Также предлагаем платные пакеты расширенной поддержки.</p>
</div>
</div>
</div>
</div>
CSS стили для аккордеона:
/* Аккордеон */
.accordion {
display: flex;
flex-direction: column;
gap: 15px;
}
.accordion-item {
background: white;
border-radius: 12px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.05);
overflow: hidden;
transition: all 0.3s ease;
}
.accordion-item:hover {
box-shadow: 0 5px 20px rgba(0, 0, 0, 0.1);
}
.accordion-item.active {
box-shadow: 0 5px 20px rgba(255, 107, 107, 0.15);
}
/* Заголовок аккордеона */
.accordion-header {
width: 100%;
padding: 20px 25px;
background: transparent;
border: none;
display: flex;
align-items: center;
justify-content: space-between;
gap: 20px;
cursor: pointer;
text-align: left;
transition: all 0.3s ease;
}
.accordion-header:hover {
background: rgba(255, 107, 107, 0.03);
}
.accordion-item.active .accordion-header {
background: rgba(255, 107, 107, 0.05);
}
.accordion-title {
font-size: 16px;
font-weight: 600;
color: var(--text-color);
line-height: 1.5;
flex: 1;
}
.accordion-icon {
width: 32px;
height: 32px;
flex-shrink: 0;
display: flex;
align-items: center;
justify-content: center;
background: rgba(255, 107, 107, 0.1);
border-radius: 50%;
color: var(--primary-color);
transition: all 0.3s ease;
}
.accordion-item.active .accordion-icon {
background: var(--primary-color);
color: white;
transform: rotate(180deg);
}
/* Контент аккордеона */
.accordion-content {
max-height: 0;
overflow: hidden;
transition: max-height 0.4s cubic-bezier(0.4, 0, 0.2, 1);
}
.accordion-item.active .accordion-content {
max-height: 1000px;
}
.accordion-body {
padding: 0 25px 25px;
color: #6c757d;
line-height: 1.8;
}
.accordion-body p {
margin-bottom: 15px;
}
.accordion-body p:last-child {
margin-bottom: 0;
}
.accordion-body ul {
margin: 15px 0;
padding-left: 20px;
}
.accordion-body li {
margin-bottom: 10px;
position: relative;
}
.accordion-body li::marker {
color: var(--primary-color);
}
.accordion-body strong {
color: var(--text-color);
font-weight: 600;
}
/* Адаптивность */
@media (max-width: 768px) {
.accordion-header {
padding: 15px 20px;
}
.accordion-title {
font-size: 15px;
}
.accordion-icon {
width: 28px;
height: 28px;
}
.accordion-body {
padding: 0 20px 20px;
font-size: 14px;
}
}
—
18. Табы (Tabs)
HTML структура:
<!-- Табы для услуг -->
<div class="tabs-container">
<div class="tabs-nav">
<button class="tab-button active" data-tab="landing">
<i class="fas fa-laptop-code"></i>
<span>Лендинги</span>
</button>
<button class="tab-button" data-tab="corporate">
<i class="fas fa-building"></i>
<span>Корпоративные сайты</span>
</button>
<button class="tab-button" data-tab="ecommerce">
<i class="fas fa-shopping-cart"></i>
<span>Интернет-магазины</span>
</button>
<button class="tab-button" data-tab="support">
<i class="fas fa-headset"></i>
<span>Поддержка</span>
</button>
</div>
<div class="tabs-content">
<!-- Таб: Лендинги -->
<div class="tab-pane active" id="landing">
<div class="tab-content-wrapper">
<div class="tab-image">
<img src="images/service-landing.jpg" alt="Разработка лендингов">
</div>
<div class="tab-info">
<h3>Разработка продающих лендингов</h3>
<p>Создаем эффективные одностраничные сайты, которые конвертируют посетителей в клиентов. Каждый лендинг разрабатывается с учетом психологии продаж и современных маркетинговых практик.</p>
<div class="tab-features">
<div class="feature-item">
<i class="fas fa-check-circle"></i>
<span>Уникальный дизайн</span>
</div>
<div class="feature-item">
<i class="fas fa-check-circle"></i>
<span>Адаптивная верстка</span>
</div>
<div class="feature-item">
<i class="fas fa-check-circle"></i>
<span>SEO-оптимизация</span>
</div>
<div class="feature-item">
<i class="fas fa-check-circle"></i>
<span>Интеграция с CRM</span>
</div>
</div>
<div class="tab-pricing">
<div class="price-tag">
<span class="price-label">от</span>
<span class="price-value">25 000 ₽</span>
</div>
<button class="btn btn-primary">Заказать</button>
</div>
</div>
</div>
</div>
<!-- Таб: Корпоративные сайты -->
<div class="tab-pane" id="corporate">
<div class="tab-content-wrapper">
<div class="tab-image">
<img src="images/service-corporate.jpg" alt="Корпоративные сайты">
</div>
<div class="tab-info">
<h3>Корпоративные сайты</h3>
<p>Разрабатываем многостраничные сайты для компаний, которые хотят представить свой бизнес в интернете. Создаем удобную структуру, профессиональный дизайн и функциональные решения.</p>
<div class="tab-features">
<div class="feature-item">
<i class="fas fa-check-circle"></i>
<span>Многостраничная структура</span>
</div>
<div class="feature-item">
<i class="fas fa-check-circle"></i>
<span>Система управления</span>
</div>
<div class="feature-item">
<i class="fas fa-check-circle"></i>
<span>Блог и новости</span>
</div>
<div class="feature-item">
<i class="fas fa-check-circle"></i>
<span>Мультиязычность</span>
</div>
</div>
<div class="tab-pricing">
<div class="price-tag">
<span class="price-label">от</span>
<span class="price-value">60 000 ₽</span>
</div>
<button class="btn btn-primary">Заказать</button>
</div>
</div>
</div>
</div>
<!-- Таб: Интернет-магазины -->
<div class="tab-pane" id="ecommerce">
<div class="tab-content-wrapper">
<div class="tab-image">
<img src="images/service-ecommerce.jpg" alt="Интернет-магазины">
</div>
<div class="tab-info">
<h3>Интернет-магазины</h3>
<p>Создаем полнофункциональные интернет-магазины с удобным каталогом, корзиной, системой оплаты и доставки. Интегрируем с популярными платежными системами и службами доставки.</p>
<div class="tab-features">
<div class="feature-item">
<i class="fas fa-check-circle"></i>
<span>Каталог товаров</span>
</div>
<div class="feature-item">
<i class="fas fa-check-circle"></i>
<span>Корзина и оплата</span>
</div>
<div class="feature-item">
<i class="fas fa-check-circle"></i>
<span>Личный кабинет</span>
</div>
<div class="feature-item">
<i class="fas fa-check-circle"></i>
<span>Интеграция с 1С</span>
</div>
</div>
<div class="tab-pricing">
<div class="price-tag">
<span class="price-label">от</span>
<span class="price-value">120 000 ₽</span>
</div>
<button class="btn btn-primary">Заказать</button>
</div>
</div>
</div>
</div>
<!-- Таб: Поддержка -->
<div class="tab-pane" id="support">
<div class="tab-content-wrapper">
<div class="tab-image">
<img src="images/service-support.jpg" alt="Техническая поддержка">
</div>
<div class="tab-info">
<h3>Техническая поддержка</h3>
<p>Обеспечиваем стабильную работу вашего сайта. Регулярные обновления, резервное копирование, мониторинг безопасности и быстрое решение любых технических проблем.</p>
<div class="tab-features">
<div class="feature-item">
<i class="fas fa-check-circle"></i>
<span>Обновление контента</span>
</div>
<div class="feature-item">
<i class="fas fa-check-circle"></i>
<span>Резервное копирование</span>
</div>
<div class="feature-item">
<i class="fas fa-check-circle"></i>
<span>Мониторинг 24/7</span>
</div>
<div class="feature-item">
<i class="fas fa-check-circle"></i>
<span>Приоритетная поддержка</span>
</div>
</div>
<div class="tab-pricing">
<div class="price-tag">
<span class="price-label">от</span>
<span class="price-value">5 000 ₽/мес</span>
</div>
<button class="btn btn-primary">Подключить</button>
</div>
</div>
</div>
</div>
</div>
</div>
CSS стили для табов:
/* Контейнер табов */
.tabs-container {
background: white;
border-radius: 15px;
box-shadow: 0 5px 30px rgba(0, 0, 0, 0.08);
overflow: hidden;
}
/* Навигация табов */
.tabs-nav {
display: flex;
background: #f8f9fa;
border-bottom: 2px solid #e9ecef;
overflow-x: auto;
scrollbar-width: thin;
}
.tabs-nav::-webkit-scrollbar {
height: 4px;
}
.tabs-nav::-webkit-scrollbar-track {
background: #f1f1f1;
}
.tabs-nav::-webkit-scrollbar-thumb {
background: var(--primary-color);
border-radius: 2px;
}
.tab-button {
flex: 1;
min-width: 150px;
padding: 20px 25px;
background: transparent;
border: none;
display: flex;
flex-direction: column;
align-items: center;
gap: 8px;
cursor: pointer;
color: #6c757d;
font-size: 14px;
font-weight: 500;
transition: all 0.3s ease;
position: relative;
}
.tab-button::after {
content: '';
position: absolute;
bottom: -2px;
left: 0;
width: 100%;
height: 2px;
background: var(--primary-color);
transform: scaleX(0);
transition: transform 0.3s ease;
}
.tab-button:hover {
color: var(--primary-color);
background: rgba(255, 107, 107, 0.05);
}
.tab-button.active {
color: var(--primary-color);
background: white;
}
.tab-button.active::after {
transform: scaleX(1);
}
.tab-button i {
font-size: 24px;
}
/* Контент табов */
.tabs-content {
padding: 40px;
}
.tab-pane {
display: none;
animation: fadeIn 0.5s ease;
}
.tab-pane.active {
display: block;
}
.tab-content-wrapper {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 40px;
align-items: center;
}
/* Изображение таба */
.tab-image {
border-radius: 12px;
overflow: hidden;
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.1);
}
.tab-image img {
width: 100%;
height: auto;
display: block;
transition: transform 0.5s ease;
}
.tab-image:hover img {
transform: scale(1.05);
}
/* Информация таба */
.tab-info h3 {
font-size: 28px;
font-weight: 700;
color: var(--text-color);
margin-bottom: 20px;
}
.tab-info > p {
font-size: 16px;
line-height: 1.8;
color: #6c757d;
margin-bottom: 30px;
}
/* Особенности */
.tab-features {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 15px;
margin-bottom: 30px;
}
.feature-item {
display: flex;
align-items: center;
gap: 10px;
font-size: 15px;
color: var(--text-color);
}
.feature-item i {
color: var(--primary-color);
font-size: 18px;
}
/* Цены */
.tab-pricing {
display: flex;
align-items: center;
gap: 20px;
padding-top: 30px;
border-top: 1px solid #e9ecef;
}
.price-tag {
display: flex;
align-items: baseline;
gap: 5px;
}
.price-label {
font-size: 14px;
color: #6c757d;
}
.price-value {
font-size: 32px;
font-weight: 700;
color: var(--primary-color);
}
/* Анимация появления */
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
/* Адаптивность */
@media (max-width: 992px) {
.tab-content-wrapper {
grid-template-columns: 1fr;
gap: 30px;
}
.tab-image {
order: -1;
}
}
@media (max-width: 768px) {
.tabs-content {
padding: 30px 20px;
}
.tab-button {
min-width: 120px;
padding: 15px 20px;
font-size: 13px;
}
.tab-button i {
font-size: 20px;
}
.tab-info h3 {
font-size: 24px;
}
.tab-features {
grid-template-columns: 1fr;
}
.tab-pricing {
flex-direction: column;
align-items: flex-start;
}
.price-value {
font-size: 28px;
}
}
—
19. Карусель отзывов (Reviews Carousel)
HTML структура:
<!-- Карусель отзывов -->
<div class="reviews-carousel">
<div class="carousel-container">
<button class="carousel-btn carousel-prev" aria-label="Предыдущий отзыв">
<i class="fas fa-chevron-left"></i>
</button>
<div class="carousel-track-container">
<div class="carousel-track">
<!-- Отзыв 1 -->
<div class="carousel-slide">
<div class="review-card">
<div class="review-header">
<div class="review-avatar">
<img src="images/avatar1.jpg" alt="Иван Петров">
</div>
<div class="review-author">
<h4>Иван Петров</h4>
<p>Владелец интернет-магазина</p>
</div>
<div class="review-rating">
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
</div>
</div>
<div class="review-body">
<p>Отличная работа! Лендинг получился именно таким, как я хотел. Все сделано качественно и в срок. Конверсия выросла на 40% за первый месяц. Особенно порадовала оперативность в общении и готовность вносить правки.</p>
</div>
<div class="review-footer">
<div class="review-project">
<i class="fas fa-briefcase"></i>
<span>Лендинг для интернет-магазина</span>
</div>
<div class="review-date">
<i class="far fa-calendar"></i>
<span>15 января 2024</span>
</div>
</div>
</div>
</div>
<!-- Отзыв 2 -->
<div class="carousel-slide">
<div class="review-card">
<div class="review-header">
<div class="review-avatar">
<img src="images/avatar2.jpg" alt="Мария Сидорова">
</div>
<div class="review-author">
<h4>Мария Сидорова</h4>
<p>Директор маркетингового агентства</p>
</div>
<div class="review-rating">
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
</div>
</div>
<div class="review-body">
<p>Профессиональный подход к делу! Ребята сразу поняли, что нам нужно, и предложили отличные решения. Дизайн современный, все работает быстро и без багов. Клиенты довольны, мы тоже. Рекомендую!</p>
</div>
<div class="review-footer">
<div class="review-project">
<i class="fas fa-briefcase"></i>
<span>Корпоративный сайт</span>
</div>
<div class="review-date">
<i class="far fa-calendar"></i>
<span>22 декабря 2023</span>
</div>
</div>
</div>
</div>
<!-- Отзыв 3 -->
<div class="carousel-slide">
<div class="review-card">
<div class="review-header">
<div class="review-avatar">
<img src="images/avatar3.jpg" alt="Алексей Смирнов">
</div>
<div class="review-author">
<h4>Алексей Смирнов</h4>
<p>Основатель стартапа</p>
</div>
<div class="review-rating">
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
</div>
</div>
<div class="review-body">
<p>Работали над сложным проектом с множеством интеграций. Команда справилась на отлично! Все технические задачи решены, сайт работает стабильно. Отдельное спасибо за консультации по продвижению.</p>
</div>
<div class="review-footer">
<div class="review-project">
<i class="fas fa-briefcase"></i>
<span>Веб-приложение</span>
</div>
<div class="review-date">
<i class="far fa-calendar"></i>
<span>10 ноября 2023</span>
</div>
</div>
</div>
</div>
<!-- Отзыв 4 -->
<div class="carousel-slide">
<div class="review-card">
<div class="review-header">
<div class="review-avatar">
<img src="images/avatar4.jpg" alt="Елена Волкова">
</div>
<div class="review-author">
<h4>Елена Волкова</h4>
<p>Владелец салона красоты</p>
</div>
<div class="review-rating">
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
</div>
</div>
<div class="review-body">
<p>Очень довольна результатом! Сайт получился красивым и удобным. Клиенты легко находят нужную информацию и записываются онлайн. Количество записей увеличилось в 2 раза. Спасибо за отличную работу!</p>
</div>
<div class="review-footer">
<div class="review-project">
<i class="fas fa-briefcase"></i>
<span>Сайт для салона красоты</span>
</div>
<div class="review-date">
<i class="far fa-calendar"></i>
<span>5 октября 2023</span>
</div>
</div>
</div>
</div>
<!-- Отзыв 5 -->
<div class="carousel-slide">
<div class="review-card">
<div class="review-header">
<div class="review-avatar">
<img src="images/avatar5.jpg" alt="Дмитрий Козлов">
</div>
<div class="review-author">
<h4>Дмитрий Козлов</h4>
<p>Руководитель IT-компании</p>
</div>
<div class="review-rating">
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
</div>
</div>
<div class="review-body">
<p>Высокий уровень профессионализма! Код чистый, структура продуманная, все оптимизировано. Сайт загружается моментально. Работать с такими специалистами - одно удовольствие. Обязательно обратимся еще!</p>
</div>
<div class="review-footer">
<div class="review-project">
<i class="fas fa-briefcase"></i>
<span>Корпоративный портал</span>
</div>
<div class="review-date">
<i class="far fa-calendar"></i>
<span>18 сентября 2023</span>
</div>
</div>
</div>
</div>
</div>
</div>
<button class="carousel-btn carousel-next" aria-label="Следующий отзыв">
<i class="fas fa-chevron-right"></i>
</button>
</div>
<!-- Индикаторы -->
<div class="carousel-indicators">
<button class="indicator active" data-slide="0"></button>
<button class="indicator" data-slide="1"></button>
<button class="indicator" data-slide="2"></button>
<button class="indicator" data-slide="3"></button>
<button class="indicator" data-slide="4"></button>
</div>
</div>
CSS стили для карусели:
/* Карусель отзывов */
.reviews-carousel {
position: relative;
padding: 40px 0;
}
.carousel-container {
position: relative;
display: flex;
align-items: center;
gap: 20px;
}
/* Трек карусели */
.carousel-track-container {
overflow: hidden;
width: 100%;
}
.carousel-track {
display: flex;
transition: transform 0.5s cubic-bezier(0.4, 0, 0.2, 1);
}
.carousel-slide {
min-width: 100%;
padding: 0 10px;
}
/* Карточка отзыва */
.review-card {
background: white;
border-radius: 15px;
padding: 30px;
box-shadow: 0 5px 30px rgba(0, 0, 0, 0.08);
transition: all 0.3s ease;
}
.review-card:hover {
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.12);
transform: translateY(-5px);
}
/* Заголовок отзыва */
.review-header {
display: flex;
align-items: center;
gap: 15px;
margin-bottom: 20px;
flex-wrap: wrap;
}
.review-avatar {
width: 60px;
height: 60px;
border-radius: 50%;
overflow: hidden;
flex-shrink: 0;
border: 3px solid rgba(255, 107, 107, 0.2);
}
.review-avatar img {
width: 100%;
height: 100%;
object-fit: cover;
}
.review-author {
flex: 1;
min-width: 150px;
}
.review-author h4 {
font-size: 18px;
font-weight: 600;
color: var(--text-color);
margin-bottom: 5px;
}
.review-author p {
font-size: 14px;
color: #6c757d;
margin: 0;
}
.review-rating {
display: flex;
gap: 5px;
color: #ffc107;
font-size: 16px;
}
/* Тело отзыва */
.review-body {
margin-bottom: 20px;
}
.review-body p {
font-size: 16px;
line-height: 1.8;
color: #6c757d;
margin: 0;
}
/* Футер отзыва */
.review-footer {
display: flex;
justify-content: space-between;
align-items: center;
padding-top: 20px;
border-top: 1px solid #e9ecef;
flex-wrap: wrap;
gap: 15px;
}
.review-project,
.review-date {
display: flex;
align-items: center;
gap: 8px;
font-size: 14px;
color: #6c757d;
}
.review-project i,
.review-date i {
color: var(--primary-color);
}
/* Кнопки навигации */
.carousel-btn {
width: 50px;
height: 50px;
border-radius: 50%;
background: white;
border: 2px solid #e9ecef;
color: var(--text-color);
font-size: 18px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.3s ease;
flex-shrink: 0;
z-index: 10;
}
.carousel-btn:hover {
background: var(--primary-color);
border-color: var(--primary-color);
color: white;
transform: scale(1.1);
}
.carousel-btn:active {
transform: scale(0.95);
}
.carousel-btn:disabled {
opacity: 0.3;
cursor: not-allowed;
}
.carousel-btn:disabled:hover {
background: white;
border-color: #e9ecef;
color: var(--text-color);
transform: scale(1);
}
/* Индикаторы */
.carousel-indicators {
display: flex;
justify-content: center;
gap: 10px;
margin-top: 30px;
}
.indicator {
width: 12px;
height: 12px;
border-radius: 50%;
background: #e9ecef;
border: none;
cursor: pointer;
transition: all 0.3s ease;
padding: 0;
}
.indicator:hover {
background: rgba(255, 107, 107, 0.5);
transform: scale(1.2);
}
.indicator.active {
background: var(--primary-color);
width: 30px;
border-radius: 6px;
}
/* Адаптивность */
@media (max-width: 992px) {
.carousel-btn {
width: 45px;
height: 45px;
font-size: 16px;
}
}
@media (max-width: 768px) {
.reviews-carousel {
padding: 20px 0;
}
.carousel-container {
gap: 10px;
}
.carousel-btn {
width: 40px;
height: 40px;
font-size: 14px;
}
.review-card {
padding: 20px;
}
.review-header {
gap: 12px;
}
.review-avatar {
width: 50px;
height: 50px;
}
.review-author h4 {
font-size: 16px;
}
.review-author p {
font-size: 13px;
}
.review-body p {
font-size: 15px;
}
.review-footer {
flex-direction: column;
align-items: flex-start;
gap: 10px;
}
}
@media (max-width: 576px) {
.carousel-btn {
display: none;
}
.carousel-slide {
padding: 0;
}
}
—
20. JavaScript для компонентов
JavaScript код:
// ============================================
// АККОРДЕОН
// ============================================
class Accordion {
constructor(element) {
this.accordion = element;
this.items = this.accordion.querySelectorAll('.accordion-item');
this.allowMultiple = this.accordion.hasAttribute('data-allow-multiple');
this.init();
}
init() {
this.items.forEach((item, index) => {
const header = item.querySelector('.accordion-header');
const content = item.querySelector('.accordion-content');
// Установка ID если его нет
if (!content.id) {
content.id = `accordion-content-${index}`;
}
// Установка aria-атрибутов
header.setAttribute('aria-expanded', 'false');
header.setAttribute('aria-controls', content.id);
content.setAttribute('role', 'region');
// Обработчик клика
header.addEventListener('click', () => {
this.toggle(item);
});
});
}
toggle(item) {
const isActive = item.classList.contains('active');
// Закрыть все, если не разрешено множественное открытие
if (!this.allowMultiple) {
this.items.forEach(otherItem => {
if (otherItem !== item) {
this.close(otherItem);
}
});
}
// Переключить текущий элемент
if (isActive) {
this.close(item);
} else {
this.open(item);
}
}
open(item) {
const header = item.querySelector('.accordion-header');
const content = item.querySelector('.accordion-content');
item.classList.add('active');
header.setAttribute('aria-expanded', 'true');
// Установка max-height для плавной анимации
content.style.maxHeight = content.scrollHeight + 'px';
}
close(item) {
const header = item.querySelector('.accordion-header');
const content = item.querySelector('.accordion-content');
item.classList.remove('active');
header.setAttribute('aria-expanded', 'false');
content.style.maxHeight = null;
}
openAll() {
this.items.forEach(item => this.open(item));
}
closeAll() {
this.items.forEach(item => this.close(item));
}
}
// Инициализация всех аккордеонов
document.querySelectorAll('.accordion').forEach(accordion => {
new Accordion(accordion);
});
// ============================================
// ТАБЫ
// ============================================
class Tabs {
constructor(element) {
this.container = element;
this.buttons = this.container.querySelectorAll('.tab-button');
this.panes = this.container.querySelectorAll('.tab-pane');
this.init();
}
init() {
this.buttons.forEach(button => {
button.addEventListener('click', () => {
const targetId = button.getAttribute('data-tab');
this.switchTab(targetId);
});
});
// Поддержка клавиатурной навигации
this.buttons.forEach((button, index) => {
button.addEventListener('keydown', (e) => {
let newIndex = index;
if (e.key === 'ArrowLeft') {
newIndex = index > 0 ? index - 1 : this.buttons.length - 1;
} else if (e.key === 'ArrowRight') {
newIndex = index < this.buttons.length - 1 ? index + 1 : 0;
} else {
return;
}
e.preventDefault();
this.buttons[newIndex].click();
this.buttons[newIndex].focus();
});
});
}
switchTab(targetId) {
// Деактивировать все кнопки и панели
this.buttons.forEach(btn => {
btn.classList.remove('active');
btn.setAttribute('aria-selected', 'false');
});
this.panes.forEach(pane => {
pane.classList.remove('active');
});
// Активировать выбранные
const activeButton = this.container.querySelector(`[data-tab="${targetId}"]`);
const activePane = document.getElementById(targetId);
if (activeButton && activePane) {
activeButton.classList.add('active');
activeButton.setAttribute('aria-selected', 'true');
activePane.classList.add('active');
// Сохранить состояние в URL
if (history.pushState) {
const url = new URL(window.location);
url.searchParams.set('tab', targetId);
history.pushState({}, '', url);
}
}
}
}
// Инициализация всех табов
document.querySelectorAll('.tabs-container').forEach(tabs => {
const tabsInstance = new Tabs(tabs);
// Восстановление состояния из URL
const urlParams = new URLSearchParams(window.location.search);
const activeTab = urlParams.get('tab');
if (activeTab) {
tabsInstance.switchTab(activeTab);
}
});
// ============================================
// КАРУСЕЛЬ ОТЗЫВОВ
// ============================================
class ReviewsCarousel {
constructor(element) {
this.carousel = element;
this.track = this.carousel.querySelector('.carousel-track');
this.slides = Array.from(this.track.children);
this.prevButton = this.carousel.querySelector('.carousel-prev');
this.nextButton = this.carousel.querySelector('.carousel-next');
this.indicators = Array.from(this.carousel.querySelectorAll('.indicator'));
this.currentIndex = 0;
this.slideWidth = this.slides[0].getBoundingClientRect().width;
this.autoplayInterval = null;
this.autoplayDelay = 5000;
this.init();
}
init() {
// Расположение слайдов
this.setSlidePosition();
// Обработчики кнопок
this.prevButton?.addEventListener('click', () => this.prevSlide());
this.nextButton?.addEventListener('click', () => this.nextSlide());
// Обработчики индикаторов
this.indicators.forEach((indicator, index) => {
indicator.addEventListener('click', () => this.goToSlide(index));
});
// Свайп на мобильных
this.initSwipe();
// Автопрокрутка
this.startAutoplay();
// Пауза при наведении
this.carousel.addEventListener('mouseenter', () => this.stopAutoplay());
this.carousel.addEventListener('mouseleave', () => this.startAutoplay());
// Обновление при изменении размера окна
window.addEventListener('resize', () => {
this.slideWidth = this.slides[0].getBoundingClientRect().width;
this.setSlidePosition();
this.updateCarousel();
});
// Обновление состояния
this.updateCarousel();
}
setSlidePosition() {
this.slides.forEach((slide, index) => {
slide.style.left = this.slideWidth * index + 'px';
});
}
updateCarousel() {
// Перемещение трека
this.track.style.transform = `translateX(-${this.currentIndex * this.slideWidth}px)`;
// Обновление индикаторов
this.indicators.forEach((indicator, index) => {
indicator.classList.toggle('active', index === this.currentIndex);
});
// Обновление кнопок
if (this.prevButton) {
this.prevButton.disabled = this.currentIndex === 0;
}
if (this.nextButton) {
this.nextButton.disabled = this.currentIndex === this.slides.length - 1;
}
}
goToSlide(index) {
this.currentIndex = Math.max(0, Math.min(index, this.slides.length - 1));
this.updateCarousel();
this.resetAutoplay();
}
nextSlide() {
if (this.currentIndex < this.slides.length - 1) {
this.currentIndex++;
} else {
this.currentIndex = 0; // Зацикливание
}
this.updateCarousel();
this.resetAutoplay();
}
prevSlide() {
if (this.currentIndex > 0) {
this.currentIndex--;
} else {
this.currentIndex = this.slides.length - 1; // Зацикливание
}
this.updateCarousel();
this.resetAutoplay();
}
startAutoplay() {
if (this.autoplayInterval) return;
this.autoplayInterval = setInterval(() => {
this.nextSlide();
}, this.autoplayDelay);
}
stopAutoplay() {
if (this.autoplayInterval) {
clearInterval(this.autoplayInterval);
this.autoplayInterval = null;
}
}
resetAutoplay() {
this.stopAutoplay();
this.startAutoplay();
}
initSwipe() {
let startX = 0;
let currentX = 0;
let isDragging = false;
this.track.addEventListener('touchstart', (e) => {
startX = e.touches[0].clientX;
isDragging = true;
this.stopAutoplay();
});
this.track.addEventListener('touchmove', (e) => {
if (!isDragging) return;
currentX = e.touches[0].clientX;
const diff = startX - currentX;
// Предпросмотр перемещения
this.track.style.transform = `translateX(-${this.currentIndex * this.slideWidth + diff}px)`;
});
this.track.addEventListener('touchend', () => {
if (!isDragging) return;
isDragging = false;
const diff = startX - currentX;
const threshold = this.slideWidth / 4;
if (Math.abs(diff) > threshold) {
if (diff > 0) {
this.nextSlide();
} else {
this.prevSlide();
}
} else {
this.updateCarousel();
}
this.startAutoplay();
});
}
}
// Инициализация всех каруселей
document.querySelectorAll('.reviews-carousel').forEach(carousel => {
new ReviewsCarousel(carousel);
});
// ============================================
// ДОПОЛНИТЕЛЬНЫЕ УТИЛИТЫ ДЛЯ КОМПОНЕНТОВ
// ============================================
// Автоматическое обновление высоты textarea
document.querySelectorAll('textarea[data-auto-resize]').forEach(textarea => {
textarea.style.overflow = 'hidden';
const resize = () => {
textarea.style.height = 'auto';
textarea.style.height = textarea.scrollHeight + 'px';
};
textarea.addEventListener('input', resize);
resize();
});
// Счетчик символов для textarea
document.querySelectorAll('textarea[data-max-length]').forEach(textarea => {
const maxLength = parseInt(textarea.getAttribute('data-max-length'));
const counter = document.createElement('div');
counter.className = 'char-counter';
counter.textContent = `0 / ${maxLength}`;
textarea.parentNode.appendChild(counter);
textarea.addEventListener('input', () => {
const length = textarea.value.length;
counter.textContent = `${length} / ${maxLength}`;
counter.classList.toggle('limit-reached', length >= maxLength);
});
});
// Подтверждение перед уходом со страницы (если есть несохраненные изменения)
let hasUnsavedChanges = false;
document.querySelectorAll('input, textarea, select').forEach(field => {
field.addEventListener('change', () => {
hasUnsavedChanges = true;
});
});
window.addEventListener('beforeunload', (e) => {
if (hasUnsavedChanges) {
e.preventDefault();
e.returnValue = '';
return '';
}
});
// Сброс флага при отправке формы
document.querySelectorAll('form').forEach(form => {
form.addEventListener('submit', () => {
hasUnsavedChanges = false;
});
});
// Печать страницы
function printPage() {
window.print();
}
// Экспорт в PDF (требует библиотеку html2pdf.js)
async function exportToPDF(element, filename = 'document.pdf') {
if (typeof html2pdf === 'undefined') {
console.error('html2pdf library is not loaded');
return;
}
const opt = {
margin: 10,
filename: filename,
image: { type: 'jpeg', quality: 0.98 },
html2canvas: { scale: 2 },
jsPDF: { unit: 'mm', format: 'a4', orientation: 'portrait' }
};
await html2pdf().set(opt).from(element).save();
}
// Полноэкранный режим
function toggleFullscreen(element = document.documentElement) {
if (!document.fullscreenElement) {
element.requestFullscreen().catch(err => {
console.error('Error attempting to enable fullscreen:', err);
});
} else {
document.exitFullscreen();
}
}
// Копирование HTML элемента
function copyElement(element) {
const range = document.createRange();
range.selectNode(element);
window.getSelection().removeAllRanges();
window.getSelection().addRange(range);
try {
document.execCommand('copy');
window.getSelection().removeAllRanges();
if (window.toast) {
window.toast.success('Скопировано', 'Содержимое скопировано в буфер обмена');
}
} catch (err) {
console.error('Failed to copy:', err);
}
}
// Скачивание файла
function downloadFile(url, filename) {
const link = document.createElement('a');
link.href = url;
link.download = filename;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}
// Создание и скачивание текстового файла
function downloadText(text, filename = 'file.txt') {
const blob = new Blob([text], { type: 'text/plain' });
const url = URL.createObjectURL(blob);
downloadFile(url, filename);
URL.revokeObjectURL(url);
}
// Создание и скачивание JSON файла
function downloadJSON(data, filename = 'data.json') {
const json = JSON.stringify(data, null, 2);
const blob = new Blob([json], { type: 'application/json' });
const url = URL.createObjectURL(blob);
downloadFile(url, filename);
URL.revokeObjectURL(url);
}
// Экспорт утилит
window.componentUtils = {
printPage,
exportToPDF,
toggleFullscreen,
copyElement,
downloadFile,
downloadText,
downloadJSON
};




