Каждый разработчик современных веб-интерфейсов сталкивался с проблемой плавающих элементов. Декоративные бейджи, всплывающие подсказки, кнопки быстрых действий и плавающие заголовки отлично смотрятся на просторном экране. Но стоит пользователю прокрутить страницу или открыть сайт на смартфоне, как плавающий элемент неизбежно наползает на текст статьи или перекрывает важную кнопку.
Исторически определение таких коллизий (collision detection) требовало тяжелой артиллерии на JavaScript. Разработчики вешали слушатели событий скролла, подключали ResizeObserver или запускали постоянный опрос геометрических координат через getBoundingClientRect(). Это приводило к принудительным пересчетам геометрии страницы (layout thrashing), дерганой анимации и повышенному расходу батареи мобильных устройств.
Фронтенд-архитектор Ахмад Шадид представил нестандартную методику, позволяющую отслеживать сближение и взаимное перекрытие элементов исключительно средствами нативного декларативного CSS.
Архитектурная идея: коллизии в потоке рендеринга
Идея строится на объединении четырех современных веб-спецификаций: CSS Anchor Positioning, Scroll-driven Animations, Container Style Queries и свойства timeline-scope. Браузер отслеживает сближение блоков непосредственно в конвейере отрисовки страницы на уровне графического процессора, не дергая JavaScript-поток.
Концепция напоминает установку физического датчика расстояния между двумя движущимися объектами.
Измеритель с переполнением: как работает скрытая распорка
В разметку помещается служебный невидимый элемент-измеритель .measure. С помощью якорного позиционирования CSS Anchor Positioning его границы жестко привязываются к краям двух интересующих нас блоков: левый край измерителя цепляется за правый край первого элемента, а правый — за левый край второго.
Внутри измерителя размещается псевдоэлемент фиксированной ширины (распорка размером, например, 30 пикселей). Пока расстояние между блоками велико, распорка свободно помещается внутри. Но как только элементы сближаются на дистанцию менее 30 пикселей, внутри элемента-измерителя возникает горизонтальная прокрутка (overflow-x: auto):
/* Назначение якорных имен для отслеживаемых элементов */
.decorative-badge {
anchor-name: --badge;
}
.content-section {
anchor-name: --section;
}
/* Служебный измеритель растягивается точно в зазоре между элементами */
.measure {
position: absolute;
top: anchor(--badge top);
bottom: anchor(--badge bottom);
left: anchor(--badge right);
right: anchor(--section left);
overflow-x: auto;
pointer-events: none;
opacity: 0;
}
/* Распорка: провоцирует переполнение при сближении ближе 30 пикселей */
.measure::after {
content: "";
display: block;
width: 30px;
height: 100%;
}
Таймлайн прокрутки как триггер переменной
Появление полосы прокрутки внутри измерителя само по себе ничего не меняет визуально. Но современный CSS позволяет привязать прогресс анимации к прокрутке через scroll-timeline.
Создается крошечная анимация нулевой длительности. В момент, когда прокрутка сдвигается хоть на один пиксель (то есть распорка не поместилась), анимация переключает значение пользовательской переменной --touching с 0 на 1. А декоративный элемент реагирует на это изменение через контейнерный запрос стилей @container style():
:root {
--touching: 0;
/* Поднимаем область видимости таймлайна на уровень документа */
timeline-scope: --touch-timeline;
animation: detect-overlap 1ms linear;
animation-timeline: --touch-timeline;
}
.measure {
/* Связываем ось X с именованным таймлайном */
scroll-timeline: --touch-timeline x;
}
@keyframes detect-overlap {
from, to {
--touching: 1;
}
}
/* Плавающий элемент плавно растворяется при опасном перекрытии */
.decorative-badge {
transition: opacity 0.25s ease-out;
@container style(--touching: 1) {
opacity: 0;
pointer-events: none;
}
}
Браузерная поддержка и прогрессивное улучшение
В настоящее время связка Anchor Positioning и Style Queries полноценно поддерживается в браузерах на движке Chromium (Google Chrome, Microsoft Edge).
Для браузеров Safari и Firefox подход легко оформляется по принципу прогрессивного улучшения: базовые стили позиционируют элементы так, чтобы они не перекрывали контент даже при отсутствии поддержки якорных правил, а в современных браузерах включается адаптивная плавная реакция. Метод Ахмада Шадида наглядно демонстрирует, как возможности современного CSS постепенно вытесняют сложный клиентский скриптинг из задач верстки.
