Анатомия React компонента
Моё видение того, как должен выглядеть React компонент
421
7 мин.
Стандарт SST-A определяет требования к семантике, доступности и логике работы табов.
overflow-x: auto. Горизонтально прокручивается только полоса, а не вся страница; содержимое открытой панели должно адаптироваться к ширине экрана.10–20px. Это визуально показывает продолжение списка и может избавить от декоративных стрелок-индикаторов. Нельзя обрезать подпись или индикатор фокуса активной вкладки; само визуальное указание не заменяет клавиатурную навигацию.-webkit-overflow-scrolling: touch не требуется считать базовым условием плавной прокрутки. В современных версиях iOS и iPadOS ускоренная прокрутка областей с overflow работает без этого свойства; на iPhone оно также может создавать контекст наложения. Свойство добавляется только для явно поддерживаемых старых версий после проверки на устройствах.Активная вкладка имеет заметное визуальное состояние, например акцентную линию или изменение фона. Различие не должно передаваться только цветом.
При выборе вкладки, перемещении клавиатурного фокуса или открытии страницы по прямой ссылке полоса прокручивается так, чтобы выбранная вкладка оказалась в области видимости, по возможности ближе к центру.
Центрирование ограничивается допустимым диапазоном прокрутки: первая и последняя вкладки у края контейнера не всегда могут располагаться точно по центру.
Для направления LTR целевое значение можно рассчитать относительно начала прокручиваемого содержимого:
targetScrollLeft = clamp(tabStart + tabWidth / 2 - viewportWidth / 2, 0, scrollWidth - viewportWidth)
Здесь tabStart — позиция вкладки относительно начала полосы прокрутки. Для RTL-разметки учитываются направление письма и различия поведения горизонтальной прокрутки; при возможности используется логическая прокрутка элемента.
role="tablist" и доступное имя через видимый заголовок с aria-labelledby или через aria-label.role="tab", уникальный стабильный id и aria-controls="panel-id", указывающий на связанную панель.role="tabpanel", уникальный id и aria-labelledby="tab-id", связывающий её с управляющей вкладкой.aria-selected="true", остальные — aria-selected="false". Неактивные панели скрываются, например атрибутом hidden.tabindex="0", остальные — tabindex="-1". Для панели без фокусируемого содержимого может задаваться tabindex="0", чтобы она была достижима в последовательности клавиатурной навигации.#tab-id. Идентификаторы задаются в URL-safe формате либо кодируются и декодируются одинаково при чтении хеша.history.replaceState обновляет текущую запись и не добавляет отдельный шаг «Назад» для каждого переключения. Приложение должно обновлять состояние вкладки в том же обработчике, а не полагаться на событие hashchange.history.pushState; состояние вкладки синхронизируется при событии popstate.tablist, tab и tabpanel применяются только для набора вкладок, переключающих панели на той же странице. Для навигации между страницами используются ссылки, даже если визуально они оформлены как вкладки.Tab переводит фокус на активную вкладку внутри полосы и затем выводит его из списка табов к содержимому панели или следующему элементу страницы.← и → перемещают фокус между вкладками; при достижении края переходят к противоположному концу списка. В интерфейсах RTL направление клавиш согласуется с порядком и направлением чтения.Home и End могут переводить фокус к первой и последней вкладке. При ручной активации выбранная вкладка активируется клавишей Enter или Space; при автоматической активации выбор следует за фокусом.aria-selected, связи aria-controls и aria-labelledby, а также видимость панелей синхронизированы.replaceState, переключение обновляет адрес, не добавляя запись истории для каждой вкладки. Если применяется pushState, переходы «Назад» и «Вперёд» синхронизируют URL, активную вкладку и панель.prefers-reduced-motion: reduce.Полоса вкладок с панелями и связями ARIA:
<div class="tabs__viewport">
<div class="tabs__list" role="tablist" aria-label="Разделы документа">
<button
id="tab-overview"
type="button"
role="tab"
aria-controls="panel-overview"
aria-selected="true"
tabindex="0"
>
Обзор
</button>
<button
id="tab-details"
type="button"
role="tab"
aria-controls="panel-details"
aria-selected="false"
tabindex="-1"
>
Подробности
</button>
</div>
</div>
<section
id="panel-overview"
role="tabpanel"
aria-labelledby="tab-overview"
tabindex="0"
>
Содержимое раздела «Обзор».
</section>
<section
id="panel-details"
role="tabpanel"
aria-labelledby="tab-details"
tabindex="0"
hidden
>
Содержимое раздела «Подробности».
</section>CSS для однострочной прокручиваемой полосы:
.tabs__viewport {
overflow-x: auto;
padding: 2px;
scroll-behavior: smooth;
}
.tabs__list {
display: flex;
flex-wrap: nowrap;
width: max-content;
min-width: 100%;
}
.tabs__list [role="tab"] {
flex: 0 0 auto;
white-space: nowrap;
}
.tabs__list [role="tab"][aria-selected="true"] {
border-block-end: 2px solid currentColor;
}
.tabs__list [role="tab"]:focus-visible {
outline: 2px solid currentColor;
outline-offset: 2px;
}
@media (max-width: 767px) {
.tabs__viewport {
scrollbar-width: none;
}
.tabs__viewport::-webkit-scrollbar {
display: none;
}
}
@media (prefers-reduced-motion: reduce) {
.tabs__viewport {
scroll-behavior: auto;
}
}На мобильной ширине скрытая полоса прокрутки компенсируется частично видимой следующей вкладкой. Конкретная ширина такого фрагмента зависит от контейнера и подписей; активная вкладка и её фокусный индикатор остаются полностью видимыми.
Ширина 767px в медиазапросе — примерная точка переключения, а не требование SST-A. Она заменяется на breakpoint, принятый в адаптивной системе проекта.
Центрирование вкладки и синхронизация URL:
const centerTab = (tab) => {
tab.scrollIntoView({
block: 'nearest',
inline: 'center'
})
}
const findTabFromUrl = (tabs) => {
const tabId = window.location.hash.slice(1)
return tabs.find((tab) => tab.id === tabId) || null
}
const updateTabUrl = (tab) => {
window.history.replaceState(null, '', '#' + tab.id)
}При инициализации найденная через findTabFromUrl вкладка активируется вместе со своей панелью и передаётся в centerTab. При пользовательском выборе сначала обновляются aria-selected, roving tabindex и видимость панелей, затем вызываются centerTab и updateTabUrl. Этот фрагмент показывает синхронизацию состояния, URL и прокрутки; обработчики клавиатуры реализуются по правилам выше.
Моё видение того, как должен выглядеть React компонент
421
7 мин.
Стандарт SSF-U определяет требования к семантике, доступности и логике работы фуллскрина
248
2 мин.
Стандарт SSA определяет требования к семантике, доступности и логике работы аккордеона
258
2 мин.
Разбор критических ошибок веб-дизайна. Почему слайдеры, автоплей и тяжёлые страницы снижают конверсию и позиции в Google и Yandex
232
1 мин.
Стандарт SSP определяет требования к семантике, доступности и логике работы пагинации
240
1 мин.
Стандарт SSPS определяет требования к структуре и наименованию файлов и папок в проекте
345
2 мин.