В прошлой части мы вынесли десктопные стили в отдельный файл с атрибутом media, чтобы телефон их не ждал. У этого приёма есть две тонкости, о которые мы споткнулись уже после внедрения: одна возвращает блокировку отрисовки, вторая ломает вёрстку на определённой ширине.
Это тридцать первая часть цикла «PageSpeed без мифов».
1. Две строки сделали файл блокирующим
Атрибут media у отдельного файла — это самый низкий порог среди всех правил в нём. Если там только min-width: 768px и выше, телефон шириной 390 px файл не ждёт. Но достаточно одного правила с маленьким порогом — и весь файл становится блокирующим для телефонов.
У нас так случилось из-за двух правил на 48 байт: @media (min-width: 381px) и (min-width: 441px) — подгонка какой-то мелочи под чуть более широкие телефоны. Порог файла опустился до 381 px, и современные телефоны (390–430 px) снова ждали весь десктопный CSS. В отчёте — 450 мс из 850 на блокирующие стили.
Лечение: перевернуть такие правила на max-width — сделать «широкий» вариант базой, а узкий — исключением. Тогда они остаются в мобильном файле, и порог десктопного возвращается наверх.
# какие пороги есть в десктопном файле — самый маленький и станет media
grep -o 'min-width: *[0-9.]*px' main.mq.css | sort -u -k2 -n | head
2. Каскад: правило из десктопного файла побеждает
Файл с правилами min-width подключается после базового. Значит, при равной специфичности побеждает правило из него — даже если в исходном SCSS оно было написано раньше базового. До разделения порядок определял исходник, после — порядок файлов.
Что мы получали на практике, каждый раз ровно с ширины порога:
- лишний отступ слева в 10 px у блока, который должен был от него избавиться;
object-fit: containвместоcoverу видео — и некрасиво скруглённые углы;- размер шрифта 70 px вместо 50;
- скрытый слот, который снова становился видимым.
Признак этой проблемы: расхождение с макетом начинается ровно с какой-то ширины (у нас — с 561 px, порога разделения) и сохраняется на всех экранах шире, а ниже всё правильно. Если видите такое — смотрите в десктопный файл.
Лечение — специфичность, а не перестановка. Двигать правила в исходнике бесполезно: порядок теперь задают файлы. Нужно сделать «правильное» правило специфичнее — добавить класс-родитель или модификатор.
/* было: .card .media в базе проигрывает .card .media из @media в отдельном файле */
.card .media { object-fit: cover; }
/* стало: правило-исключение специфичнее */
.card.isVideo .media { object-fit: cover; }
Как не попадаться
- После включения разделения проверьте вёрстку на ширинах порогов и на 1–2 px выше каждого.
- Держите в мобильном файле всё, что меньше вашего главного порога: подгонки под «чуть шире» пишите через
max-width. - После каждой сборки смотрите, какой порог получил десктопный файл (команда выше).
Вёрстка «едет» с определённой ширины?
Пришлите адрес и ширину — найду правило, которое побеждает не то.
// загружаю комментарии…