Кроссбраузерность и деградация
Сайт должен работать везде — но не обязан выглядеть одинаково. Разбираем прогрессивное улучшение, @supports и что делать с Safari.
Что ты научишься делать
- Построишь вёрстку так, чтобы новая фича была улучшением, а не условием работы
- Научишься читать caniuse и решать, брать ли фичу в проект
- Сделаешь раскладку с subgrid и рабочим запасным вариантом
#Главная мысль урока
Работать и выглядеть одинаково — разные требования, и путать их дорого.
Попытка добиться пиксель в пиксель во всех браузерах съедает недели и рождает костыли, которые ломаются на следующем обновлении. Требование «человек может сделать то, зачем пришёл» выполняется гораздо дешевле.
Отсюда подход: базовая версия работает у всех, современные возможности добавляют удобства тем, у кого они есть.
#Прогрессивное улучшение против изящной деградации
Два подхода, которые звучат похоже, а дают разный код.
Изящная деградация — сначала пишем современную версию, потом чиним для старых браузеров. Починка идёт вслед за поломками, и список костылей растёт.
Прогрессивное улучшение — сначала простая рабочая версия, потом улучшения поверх. Ничего не ломается, потому что база не зависит от новых возможностей.
Так не надо
/* Сначала современное… */
.cards {
display: grid;
grid-template-columns: subgrid;
}
/* …потом чиним, если не поддержано */
@supports not (grid-template-columns: subgrid) {
.cards { display: flex; flex-wrap: wrap; }
.card { flex: 1 1 240px; }
}Отрицательная проверка. Каждая новая фича требует своей ветки «а если нет», и они накапливаются. Легко забыть одну и не заметить.
Так надо
/* База: работает везде */
.cards {
display: flex;
flex-wrap: wrap;
gap: 16px;
}
.card { flex: 1 1 240px; }
/* Улучшение поверх */
@supports (grid-template-rows: subgrid) {
.cards { display: grid; }
}База не знает о новых возможностях и не может из-за них сломаться. Улучшение либо применяется целиком, либо не применяется вовсе.
#Когда @supports не нужен
Проверка нужна реже, чем кажется. CSS устроен так, что непонятное объявление просто отбрасывается — и этого часто достаточно.
.panel { /* Старый браузер применит это… */ background: #6544e0; /* …и отбросит это, если не поймёт функцию */ background: color-mix(in oklab, #6544e0 80%, white);}.card { /* Пиксели поймут все */ gap: 16px; /* Современные возьмут значение из шкалы */ gap: var(--space-4, 16px);}Порядок здесь принципиален: сначала запасное значение, потом современное. Браузер применит последнее из тех, что понял.
| Ситуация | Нужен ли @supports |
|---|---|
| Одно свойство с запасным значением выше | Нет — сработает естественный отбор |
| Незнакомый псевдокласс в селекторе | Нет — правило целиком не применится |
| Улучшение из нескольких свойств, которые нужны вместе | Да |
| База и улучшение конфликтуют между собой | Да |
Незнакомое @-правило вроде @container | Нет — блок игнорируется целиком |
#Что делать с Safari
Safari регулярно отстаёт по срокам и остаётся единственным движком на iOS: любой браузер там внутри — это WebKit. То есть «просто попросить поставить другой браузер» не работает.
Практические выводы.
- Проверяй на настоящем Safari. Chrome с эмуляцией iPhone показывает движок Chrome, а не WebKit. Это не проверка.
- Помни про
-webkit-. У части свойств —backdrop-filter,mask,text-size-adjust— префикс до сих пор нужен. - Проверяй
100vh. На мобильном Safari он долго считался без панели браузера, и блок вылезал за экран. Современная замена —100dvh. - Не полагайся на самые свежие фичи. Если возможность появилась в Chrome полгода назад, в Safari она может быть ещё в разработке.
.hero { /* Запасное значение для старых браузеров */ min-block-size: 100vh; /* Динамическая высота: учитывает исчезающую панель браузера */ min-block-size: 100dvh;}#Префиксы: что живо, что мертво
Эпоха, когда каждое свойство писали четыре раза, закончилась. Сегодня префиксы нужны в единичных случаях, а лишние — вредят: браузер может применить устаревшую реализацию вместо правильной.
| Свойство | Префикс |
|---|---|
border-radius, box-shadow, transition, transform | Не нужен уже много лет |
flex, grid | Не нужен |
backdrop-filter | Нужен -webkit- для части сборок Safari |
mask-image | Нужен -webkit- |
appearance | Иногда нужен -webkit- |
::-webkit-scrollbar | Собственное расширение WebKit, стандарта нет |
#Как читать caniuse
Общий процент поддержки — наименее полезное число на странице. Смотреть нужно на другое.
Сравни свою аудиторию с общей
Девяносто пять процентов в мире ничего не говорят, если половина твоих людей приходит с одного старого устройства. Загляни в аналитику своего сайта.
Прочитай примечания
Зелёная клетка с цифрой означает частичную поддержку. Внизу страницы описано, чего именно не хватает, — часто это важнее самого факта поддержки.
Посмотри на дату появления
Фича, появившаяся полгода назад, есть только у тех, кто обновляется. Фича трёхлетней давности — практически у всех.
Реши цену отказа
Если без фичи сайт выглядит чуть скромнее — берём смело. Если без неё нельзя оформить заказ — нужен полноценный запасной путь.
#Тестирование без парка устройств
Покупать десять телефонов не нужно. Достаточно нескольких приёмов, которые ловят большинство проблем.
- Настоящий Safari на любом Mac. Один компьютер закрывает весь iOS: движок тот же.
- Замедление процессора в DevTools. Четырёхкратное замедление приближает поведение среднего телефона.
- Отключение возможностей вручную. Временно переименуй
@supports (grid-template-rows: subgrid)в заведомо неподдерживаемое — увидишь запасную ветку своими глазами. - Проверка без скриптов. Выключи JavaScript и пройди основной сценарий. Это же ловит проблемы у людей с блокировщиками.
- Масштаб двести процентов. Ловит обрезанный текст и сломанную раскладку лучше, чем эмуляция маленьких экранов.
#Как это выглядит в реальном проекте
@layer components { /* База: флексы. Работают везде, начиная с очень старых браузеров */ .gallery { display: flex; flex-wrap: wrap; gap: 16px; } .gallery__item { flex: 1 1 240px; display: flex; flex-direction: column; } /* Кнопка прижата вниз — простое решение без сетки */ .gallery__btn { margin-block-start: auto; } /* Улучшение первого уровня: сетка вместо флексов */ @supports (display: grid) { .gallery { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); } } /* Улучшение второго уровня: строки выравниваются между карточками */ @supports (grid-template-rows: subgrid) { .gallery { grid-auto-rows: auto 1fr auto; } .gallery__item { display: grid; grid-row: span 3; grid-template-rows: subgrid; } .gallery__btn { margin-block-start: 0; } }}Три уровня, и каждый следующий необязателен. Человек со старым браузером увидит работающую галерею, просто с чуть менее ровными кнопками — и никогда об этом не узнает.
#Когда НЕ надо
- Поддерживать браузеры, которых нет у аудитории. Каждая ветка совместимости — код, который надо сопровождать. Проверь аналитику, прежде чем платить эту цену.
- Ставить префиксы «на всякий случай». Устаревшая префиксная реализация может перекрыть правильную и вести себя иначе.
- Оборачивать в
@supportsвсё подряд. Одиночное свойство и так безопасно отбрасывается. - Добиваться одинакового вида. Разные системы по-разному рисуют шрифты, поля и полосы прокрутки. Это нормально и не требует починки.
Где спотыкаются почти все
Современное значение написано выше запасного
Почему Браузер применяет последнее объявление, которое понял. Если современное идёт первым, запасное перекроет его у всех.
Как надо Сначала запасное значение, потом современное.
Проверка через <code>@supports not</code>
Почему Отрицательные ветки накапливаются: каждая новая фича требует своей. Одну обязательно забудут.
Как надо Делай базу рабочей, а улучшения добавляй положительной проверкой.
Тестирование iOS в режиме устройства Chrome
Почему Меняется только размер окна и строка User-Agent. Движок остаётся прежним, баги WebKit не проявятся.
Как надо Открой настоящий Safari — один Mac закрывает весь iOS.
Префикс после стандартного свойства
Почему Браузер, понимающий оба, применит устаревший префиксный вариант.
Как надо Префиксное объявление всегда идёт первым.
- Прогрессивное улучшение progressive enhancement
- Рабочая база плюс необязательные улучшения поверх.
- Изящная деградация graceful degradation
- Современная версия, дополненная починками для старых браузеров.
- Проверка поддержки @supports
- Правило, применяющее блок только если браузер понимает конструкцию.
- Префикс поставщика vendor prefix
- Приставка вроде
-webkit-для экспериментальных реализаций. - Динамическая высота экрана dvh
- Единица, учитывающая исчезающие панели мобильного браузера.
Проверь себя
Браузер применяет последнее объявление, которое понял. Современное должно идти последним.
Одиночное непонятное объявление и так отбрасывается. Проверка нужна там, где ветки мешают друг другу.
Все браузеры на iOS работают на WebKit. Его баги в Chrome не воспроизведутся.
Проверяется разбор, а не качество реализации. Тестирование в живом браузере это не отменяет.
Общий процент не говорит ничего о твоей аудитории, а примечания часто важнее самого факта поддержки.
Задание
Раскладка с рабочим запасным вариантом
Галерея свёрстана только на subgrid: там, где его нет, карточки просто ложатся в столбик без выравнивания.
Переверни подход. Сделай базой флексы: карточки в ряд с переносом, кнопка прижата вниз через margin-block-start: auto. А subgrid добавь сверху через положительный @supports. Отрицательную проверку убери.
Подсказка 1
База: .gallery { display: flex; flex-wrap: wrap; gap: 12px; } и .gallery__item { flex: 1 1 200px; display: flex; flex-direction: column; }
Подсказка 2
Кнопке в базовом варианте: .gallery__btn { margin-block-start: auto; }
Подсказка 3
Улучшение: @supports (grid-template-rows: subgrid) { … } — внутрь переносишь всё, что было про grid.
Показать решение
<div class="gallery"> <article class="gallery__item"> <h3>Эспрессо</h3> <p>Двойная порция.</p> <button class="gallery__btn">Заказать</button> </article> <article class="gallery__item"> <h3>Раф на кокосовом молоке</h3> <p>Мягкий сладковатый напиток для тех, кому латте кажется резким.</p> <button class="gallery__btn">Заказать</button> </article> <article class="gallery__item"> <h3>Фильтр</h3> <p>Зерно из Эфиопии.</p> <button class="gallery__btn">Заказать</button> </article></div>/* База: работает везде */.gallery { display: flex; flex-wrap: wrap; gap: 12px;}.gallery__item { flex: 1 1 200px; display: flex; flex-direction: column; gap: 6px; padding: 12px; background: #f5f5fa; border-radius: 10px;}.gallery__btn { margin-block-start: auto; }/* Улучшение: строки выравниваются между карточками */@supports (grid-template-rows: subgrid) { .gallery { display: grid; grid-template-columns: repeat(3, 1fr); grid-auto-rows: auto 1fr auto; } .gallery__item { display: grid; grid-row: span 3; grid-template-rows: subgrid; } .gallery__btn { margin-block-start: 0; }}.gallery__item h3 { margin: 0; font-size: 15px; }.gallery__item p { margin: 0; font-size: 13px; color: #5b6070; }.gallery__btn { padding: 8px; font: inherit; font-size: 13px; color: #fff; background: #6544e0; border: 0; border-radius: 8px;}