Раскрываем возможности CSS-контейнерных запросов: уроки команды Netflix

Контейнерные запросы произвели революцию в подходе разработчиков к адаптивному дизайну, и команда Netflix на собственном опыте убедилась в том, какое глубокое влияние они могут оказать на оптимизацию разработки, повышение гибкости и производительности. В этом посте рассматриваются ключевые преимущества использования контейнерных запросов, сравнивая их со старыми методами, особенно с теми, которые используют JavaScript для управления макетом. Он включает примеры кода, иллюстрирующие каждый пункт и показывающие, как контейнерные запросы могут значительно облегчить вашу жизнь как разработчика.

1. Упрощенное проектирование компонентов «снизу вверх» и «сверху вниз».

Одним из наиболее значительных изменений, с которыми столкнулась команда Netflix, стал переход от подхода к проектированию «сверху вниз» к подходу «снизу вверх». Перед запросами к контейнерам родительские контейнеры должны были полностью знать требования к макету своих дочерних элементов. При использовании контейнерных запросов эта логика меняется на противоположную, что позволяет дочерним компонентам управлять своим макетом в зависимости от размера их собственного контейнера. Это упрощает роль родительского элемента и уменьшает объем логики макета в коде.

Пример: контейнерные запросы в сравнении с медиа-запросами и JavaScript.

До (необходим JavaScript):

/* Layout with media queries */
.card {
    width: 100%;
}

@media (min-width: 600px) {
    .card {
        width: 50%;
    }
}

@media (min-width: 900px) {
    .card {
        width: 33.33%;
    }
}
// JavaScript to detect parent container size
const container = document.querySelector('.container');
const card = document.querySelector('.card');

function adjustLayout() {
    if (window.innerWidth >= 900) {
        card.style.width = '33.33%';
    } else if (window.innerWidth >= 600) {
        card.style.width = '50%';
    } else {
        card.style.width = '100%';
    }
}

window.addEventListener('resize', adjustLayout);
adjustLayout();

После:

/* Container Query */
.container {
    container-type: inline-size;
}

.card {
    width: 100%;
}

@container (min-width: 600px) {
    .card {
        width: 50%;
    }
}

@container (min-width: 900px) {
    .card {
        width: 33.33%;
    }
}

В этом примере показано, как родительскому контейнеру больше не нужно управлять дочерним макетом. Правило @container позволяет .card реагировать на непосредственный размер контейнера, упрощая логику макета и полностью устраняя необходимость в JavaScript.

2. Отзывчивость без сложных медиазапросов

Команда Netflix обнаружила, как контейнерные запросы упрощают реагирование, особенно при проектировании, ориентированном на мобильные устройства. Вместо написания сложных медиа-запросов вы можете создавать повторно используемые компоненты, которые настраиваются в зависимости от размера их контейнера, что позволяет создавать динамические макеты для экранов различных размеров и устройств. Это особенно полезно для таких приложений, как Netflix, где доминирует мобильный трафик.

Пример. Отзыв компонента на запросы контейнера.

До:

/* Desktop versus Mobile
this only works if.sidebar is directly contained by a viewport-width element */
.sidebar {
    width: 300px;
}

@media (max-width: 768px) {
    .sidebar {
        width: 100%;
    }
}

После:

/* Responsive sidebar based on container,
.sidebar can be placed in any element of any width */
.container {
    container-type: inline-size;
}

.sidebar {
    width: 100%;
}

@container (min-width: 768px) {
    .sidebar {
        width: 300px;
    }
}

Вместо того, чтобы зависеть от медиа-запросов на основе области просмотра, .sidebar теперь реагирует на размер контейнера, что позволяет ей более естественно адаптироваться к динамическим макетам без необходимости знать размер области просмотра или родительского контейнера.

3. Уменьшена зависимость от JavaScript для управления макетом.

До появления запросов к контейнерам многим командам, включая Netflix, приходилось полагаться на JavaScript для создания динамических макетов. Запрашивая размер окна, JavaScript инициирует изменения макета, увеличивая как сложность, так и вероятность ошибок. Контейнерные запросы устраняют эту необходимость, позволяя CSS обрабатывать реагирование макета в зависимости от размера контейнера.

Пример. Удаление логики макета на основе JavaScript.

До:

const cardContainer = document.querySelector('.card-container');
const cards = cardContainer.children;

function adjustLayout() {
    if (cardContainer.offsetWidth > 900) {
        cards.forEach(card => card.style.width = '33.33%');
    } else if (cardContainer.offsetWidth > 600) {
        cards.forEach(card => card.style.width = '50%');
    } else {
        cards.forEach(card => card.style.width = '100%');
    }
}

window.addEventListener('resize', adjustLayout);
adjustLayout();

После:

.card-container {
    container-type: inline-size;
}

.card {
    width: 100%;
}

@container (min-width: 600px) {
    .card {
        width: 50%;
    }
}

@container (min-width: 900px) {
    .card {
        width: 33.33%;
    }
}

Этот подход не только уменьшает объем необходимого JavaScript, но и повышает производительность за счет исключения вычислений во время выполнения.

4. Меньше кода, меньше ошибок

Команда Netflix обнаружила, что использование контейнерных запросов приводит к уменьшению количества строк кода и ошибок, связанных с макетом. Перемещая логику макета из JavaScript в CSS и устраняя необходимость в сложных медиа-запросах, разработчики могут писать более удобный в сопровождении код.

Пример: сокращение кода макета

Команда Netflix заметила, что после внедрения контейнерных запросов они заметили значительное сокращение кода CSS — до 30 % для некоторых компонентов. В то же время команде удалось упростить многие сложные и иногда конфликтные медиа-запросы, исключив логику, которая управляла дочерними компонентами, добившись более высокой степени разделения задач. Такое сокращение не только ускоряет разработку, но и сводит к минимуму потенциальные точки сбоя, что приводит к меньшему количеству ошибок.

До:

/* Before with complex media queries */
.card {
    width: 100%;
}

@media (min-width: 600px) {
    .card {
        width: 50%;
    }
}

@media (min-width: 900px) {
    .card {
        width: 33.33%;
    }
}

После

.container {
    container-type: inline-size;
}

.card {
    width: 100%;
}

@container (min-width: 600px) {
    .card {
        width: 50%;
    }
}

@container (min-width: 900px) {
    .card {
        width: 33.33%;
    }
}

5. Улучшенный опыт разработчика.

бк. «это сделало мою жизнь в сто раз проще»

Возможно, одним из наиболее недооцененных преимуществ контейнерных запросов является улучшение условий для разработчиков. Сделав CSS более интуитивным и ориентированным на компоненты, разработчики могут сосредоточиться на создании многоразовых, гибких компонентов, не беспокоясь о том, как они будут вести себя в каждом возможном сценарии компоновки.

Как сказал один из членов команды Netflix: «Вот как CSS должен был работать с самого начала».

6. Резервный вариант Polyfill

Хотя контейнерные запросы сейчас используются во всех основных браузерах, существуют опасения, что более ранние версии браузеров все еще используются. Резервный вариант очень важен: команда Netflix использует этот полифил JavaScript, созданный участниками веб-сообщества. Реализация проста с обнаружением функций:

if (! CSS.supports("container-type:size")) {
  /*use polyfill from
  https://www.npmjs.com/package/container-query-polyfill */
 }

Заключение

Контейнерные запросы представляют собой огромный шаг вперед в CSS, упрощая разработчикам создание гибких, адаптивных компонентов, которые можно повторно использовать в разных частях сайта. Уменьшая зависимость от JavaScript для макетирования, устраняя сложные медиа-запросы и ускоряя разработку, они предлагают значительные преимущества как в производительности, так и в удобстве сопровождения. В настоящее время большинство вариантов использования находятся на страницах Netflix Tudum , и есть потенциальные планы по использованию контейнерных запросов в других частях Netflix. Команда Netflix считает контейнерные запросы первоклассным инструментом в наборе инструментов разработчика, и их использование будет только расширяться по мере того, как все больше разработчиков осознают гибкость и мощь, которые они приносят. Будь то модернизация существующих компонентов или разработка совершенно новых, контейнерные запросы предлагают более простой и понятный путь к адаптивному дизайну.

Если вы еще этого не сделали, попробуйте контейнерные запросы — вы, скорее всего, обнаружите, что это упрощает ваш рабочий процесс так, как вы не ожидали.