Модули аккордеона и табов: производительность и доступность на сайте

Аккордеон и табы: как не провалить Core Web Vitals и доступность

Разработка и обслуживание любых видов сайтов:

Информационные сайты или веб-приложения
Сайты визитки, landing page, корпоративные сайты, онлайн каталоги, квиз, промо-сайты, блоги, новостные ресурсы, информационные порталы, форумы, агрегаторы
Сайты или веб-приложения электронной коммерции
Интернет-магазины, B2B-порталы, маркетплейсы, онлайн-обменники, кэшбэк-сайты, биржи, дропшиппинг-платформы, парсеры товаров
Веб-приложения для управления бизнес-процессами
CRM-системы, ERP-системы, корпоративные порталы, системы управления производством, парсеры информации
Сайты или веб-приложения электронных услуг
Доски объявлений, онлайн-школы, онлайн-кинотеатры, конструкторы сайтов, порталы предоставления электронных услуг, видеохостинги, тематические порталы

Это лишь некоторые из технических типов сайтов, с которыми мы работаем, и каждый из них может иметь свои специфические особенности и функциональность, а также быть адаптированным под конкретные потребности и цели клиента

Услуги, которые мы предлагаем
Показано 1 из 1Все 2062 услуг
Модули аккордеона и табов: производительность и доступность на сайте
Простой
~1 день

Наши компетенции:

Часто задаваемые вопросы

Последние работы

  • image_website-b2b-advance_0.webp
    Разработка сайта компании B2B ADVANCE
    1414
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1285
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    982
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1241
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    982
  • image_bitrix-bitrix-24-1c_fixper_448_0.webp
    Разработка веб-сайта для компании ФИКСПЕР
    995

Аккордеон и табы: как не провалить Core Web Vitals и доступность

Мы часто сталкиваемся с проектами, где аккордеоны и табы реализованы без учёта производительности и доступности. Типичные проблемы: анимация высоты на height: auto вызывает Layout Shift, контент скрытых панелей не индексируется, а отсутствие ARIA-атрибутов делает компоненты недоступными для скринридеров. Результат — низкий LCP, проваленный аудит Lighthouse, потеря до 30% трафика с мобильных устройств. Наш опыт показывает, что правильная реализация этих компонентов «под ключ» занимает от 2 до 3 дней и включает: серверный рендер, URL-синхронизацию, Schema.org разметку и полную поддержку WCAG 2.1 AA. Ниже — конкретные решения, которые мы применяем в продакшене.

Проблемы, которые решаем

Как избежать CLS при анимации аккордеона?

Использование height: auto в CSS transitions приводит к перерасчёту layout. Решение — JavaScript с измерением scrollHeight и установкой фиксированного значения. Мы применяем этот подход уже в 30+ проектах. Альтернатива — CSS grid-template-rows: 0fr / 1fr с overflow: hidden. Но JS-версия обеспечивает плавную анимацию во всех браузерах, включая старые Safari.

Почему контент в табах может не индексироваться?

Google индексирует содержимое с display: none, но придаёт ему меньший вес. Для SEO-критичного контента используем visibility: hidden + position: absolute и серверный рендер всех панелей. Сравните: при клиентском скрытии контент получает вес 0.3 от открытого, а при SSR — полный вес. В одном из проектов это увеличило видимость страницы на 25% по данным Яндекс.Вебмастера.

Как сделать табы доступными для клавиатуры?

Реализуем паттерн role="tablist" с ArrowLeft/Right, Home/End. Управление фокусом происходит через tabIndex. Без этого компонент не проходит WCAG 2.1 AA.

Как мы это делаем: стек и кейс

Используем React 18, Next.js 14, TypeScript, Tailwind. Для анимации — CSS transitions с JS-управлением высотой. Пример аккордеона (сокращённый):

import { useState, useRef, useEffect } from 'react' interface AccordionItem { id: string title: string content: React.ReactNode } function AccordionItem({ item, isOpen, onToggle }: { item: AccordionItem isOpen: boolean onToggle: () => void }) { const contentRef = useRef<HTMLDivElement>(null) const [height, setHeight] = useState(0) useEffect(() => { if (contentRef.current) { setHeight(isOpen ? contentRef.current.scrollHeight : 0) } }, [isOpen]) return ( <div className="accordion-item"> <button className="accordion-item__trigger" onClick={onToggle} aria-expanded={isOpen} aria-controls={`accordion-content-${item.id}`} id={`accordion-btn-${item.id}`} > {item.title} </button> <div id={`accordion-content-${item.id}`} role="region" aria-labelledby={`accordion-btn-${item.id}`} style={{ height, overflow: 'hidden', transition: 'height 0.3s ease' }} > <div ref={contentRef} className="accordion-item__body"> {item.content} </div> </div> </div> ) } 

Для табов обязательно добавляем URL-синхронизацию через useSearchParams и Schema.org разметку для FAQ. Пример табов с keyboard navigation:

import { useState, useRef } from 'react' interface Tab { id: string label: string content: React.ReactNode } export function Tabs({ tabs, defaultTab }: { tabs: Tab[]; defaultTab?: string }) { const [activeId, setActiveId] = useState(defaultTab ?? tabs[0]?.id) const tablistRef = useRef<HTMLDivElement>(null) function handleKeyDown(e: React.KeyboardEvent, currentIndex: number) { let newIndex = currentIndex if (e.key === 'ArrowRight') newIndex = (currentIndex + 1) % tabs.length else if (e.key === 'ArrowLeft') newIndex = (currentIndex - 1 + tabs.length) % tabs.length else if (e.key === 'Home') newIndex = 0 else if (e.key === 'End') newIndex = tabs.length - 1 else return e.preventDefault() setActiveId(tabs[newIndex].id) const tabEls = tablistRef.current?.querySelectorAll('[role="tab"]') ;(tabEls?.[newIndex] as HTMLElement)?.focus() } return ( <div className="tabs"> <div role="tablist" ref={tablistRef} className="tabs__list"> {tabs.map((tab, i) => ( <button key={tab.id} role="tab" id={`tab-${tab.id}`} aria-selected={activeId === tab.id} aria-controls={`tabpanel-${tab.id}`} tabIndex={activeId === tab.id ? 0 : -1} onClick={() => setActiveId(tab.id)} onKeyDown={e => handleKeyDown(e, i)} className={`tabs__tab ${activeId === tab.id ? 'tabs__tab--active' : ''}`} > {tab.label} </button> ))} </div> {tabs.map(tab => ( <div key={tab.id} role="tabpanel" id={`tabpanel-${tab.id}`} aria-labelledby={`tab-${tab.id}`} hidden={activeId !== tab.id} tabIndex={0} className="tabs__panel" > {tab.content} </div> ))} </div> ) } 

Серверный рендер всех панелей решает проблему SEO. Для этого в Next.js используем force-dynamic на странице с табами или рендерим все панели в HTML, скрывая их через CSS (не через display:none, а через position:absolute; visibility:hidden; height:0; overflow:hidden).

Дополнительный пример: аккордеон с анимацией на CSS Grid
.accordion__panel { display: grid; grid-template-rows: 0fr; transition: grid-template-rows 0.3s ease; } .accordion__panel--open { grid-template-rows: 1fr; } 

Этот метод избавляет от JS, но не даёт плавности в старых браузерах.

Процесс работы

Этап Длительность Результат
Аудит текущей реализации 1-2 часа Отчёт по Lighthouse, WAVE, CWV
Проектирование компонентов 2-4 часа Схема ARIA, дизайн анимаций
Разработка (HTML+CSS+JS) 4-6 часов Нативные компоненты
Разработка React обёртки 6-8 часов Компоненты с SSR, URL, Schema
Тестирование 2-4 часа Keyboard, скринридер, perftest
Деплой и документация 2-4 часа Git merge, readme, обучение

Сроки и что входит

Компонент Срок
Простой аккордеон (нативный) 2-3 часа
React аккордеон с анимацией 4-6 часов
Система табов с URL и Schema 1-1.5 дня
Полный комплект (аккордеон+табы) 2-3 дня

Отметим: Что входит в работу:

  • Исходный код компонентов (TypeScript, CSS modules или Tailwind)
  • Документация по API и ARIA-атрибутам
  • Интеграция в существующий проект (Git)
  • Обучение команды (1 час)
  • Поддержка в течение 2 недель после деплоя

Типичные ошибки

  • Использование overflow: hidden для анимации высоты — вызывает CLS. Лучше JS.
  • Забыть про prefers-reduced-motion — отключаем анимацию.
  • Не проверять состояние загрузки — гидратированный компонент может "моргнуть".

Сравнение подходов

Критерий Нативный HTML (<details>) React-компонент (JS-анимация)
Время разработки 1 час 4-6 часов
Анимация Только CSS (без interpolate-size не работает) Плавная, любая
Доступность Встроенная семантика Требуется ARIA вручную
SEO Индексируется полностью Требует SSR для скрытого контента

Нативный HTML дешевле и доступнее, но React-решение даёт полный контроль над анимацией и URL-синхронизацией.

Пошаговая инструкция по реализации аккордеона

  1. Создайте разметку с заголовками и контентом.
  2. Добавьте ARIA-атрибуты: aria-expanded, aria-controls.
  3. Напишите скрипт для анимации высоты через scrollHeight.
  4. Обработайте состояние prefers-reduced-motion.
  5. Проверьте клавиатурную навигацию (Tab, Enter).
  6. Протестируйте в скринридерах (NVDA, VoiceOver).

Заключение

Правильно реализованные аккордеоны и табы — это не только удобство пользователей, но и улучшение метрик производительности. Наши инженеры сертифицированы по WCAG 2.1 AA, поэтому гарантируем правильную реализацию ARIA. 5+ лет опыта в веб-разработке, 30+ проектов с аккордеонами и табами — мы знаем все подводные камни. Если вы хотите получить аналогичный результат — свяжитесь с нами для оценки вашего проекта. Для получения консультации и оценки — пишите нам. Мы оценим задачу и предложим оптимальное решение. В среднем экономия на поддержке составляет около 3000 BYN в год, а окупаемость — 3 месяца.