Abr@X@bra.ru
Что может поломать макет на гридах (CSS Grid Layout)
Что может поломать макет на гридах (CSS Grid Layout)

Что может поломать макет на гридах (CSS Grid Layout)

08.09.2017
93

Перевод статьи BREAKING THE GRID с сайта daverupert.com, автор — Дейв Руперт

Два способа поломать CSS-гриды, и как их починить.

Как фронтенд-разработчика, меня ничто так не раздражает, как неожиданное появление горизонтального скроллбара на сайте. Однако, выстраивая раскладку страницы заказа с помощью CSS-гридов, я на удивление обнаружил что-то мистическое, что поломало мой контейнер. Я полагал, что с размерами у гридов всё решается автоматически.

В итоге я нашёл два способа поломать CSS-гриды. Как это бывает, я умудрился в одной раскладке сделать и то, и другое. ‍

№1: Элементы с overflow-x поломают грид

Для нашего магазина использовался паттерн подменю, которое могло бы прокручиваться с помощью overflow-x, чтобы показать больше вариантов оплаты. Даже с примененным overflow-x грид-элемент растягивался до scrollWidth подменю. Оказывается, любой элемент с overflow-x, будь то блок <code> или адаптивная таблица, поломали бы грид. Вот наглядный пример.


Обычно с решением проблемы горизонтального переполнения справляется overflow-x: auto. Но не в этот раз. Ещё идея — обернуть элемент, попробовав всякие трюки с overflow: hidden, чтобы заставить раскладку подчиняться. Тоже нет.

Дело в том, что по умолчанию у грид-элементов стоит min-width: auto и их размер автоматически устанавливается по контенту в них. Оно устанавливает их min-width по ширине переполняющего блока. Поэтому для исправления глубоко вложенного переполняющего дочернего элемента вы, вопреки интуиции, не обращаете внимания на родительский элемент, а идете вверх по DOM-дереву до самого грид-элемента и обнуляете ему min-width.

.grid > * { min-width: 0; }

Благодаря этому размеры грид-элемента с переполнением контента установятся правильно. Но не всегда.

№2: Контролы формы поломают грид

В нашей раскладке тоже использовался грид для размещения полей формы бок о бок. Я создал тестовый пример на CodePen с различными типами полей, чтобы изолировать эту проблему. При уменьшении окна браузера до момента появления горизонтального скролла (~380px) можно видеть, как поля выходят за границы грид-полосы.


В итоге, чтобы пофиксить подобные поля, придётся применить max-width: 100%, и хотя это в основном исправляет такое поведение в Chrome и Safari, некоторые элементы в этих браузерах и все подобные элементы в Firefox и Edge по-прежнему вываливаются за грид-полосу.

Потому что элементы <input> и им подобные (<img><progress><select><video>) обладают потенциальной способностью быть чем-то, называемым «Замещаемыми элементами»

Что ещё, чёрт возьми, за «Замещаемые элементы»?

Согласно MDN, замещаемый элемент — элемент, представление которого выходит за рамки CSS. Это ситуация, когда браузер получает разметку и внедряет элемент с чем-то вроде Shadow DOM. <video> — подходящий пример, но большинство контролов формы также попадают под это описание.

Замещаемые элементы существуют на какой-то спецификационной ничейной территории. Правила их оформления и отображения четко не определены, только лишь то, что у них «часто есть внутренние размеры». Поэтому каждый браузер по своему трактует внешний вид по умолчанию определённых элементов и использование замещаемых элементов в целом. На скриншоте ниже видно, как в зависимости от браузера выглядят замещаемые элементы и их размеры.


В нашей ситуации у некоторых элементов input есть фантомные «внутренние размеры», применяемые браузером. Они не отображаются в качестве браузерного стиля, ни во вкладке «Стили», ни в «Вычисленные стили», но их min-width около ~180px для стандартного <input type="text">.

Чтобы кроссбраузерно пофиксить «вываливание» элементов за границы грид-полосы, придётся переопределить этот внутренний размер замещаемых элементов.

/* Применяем max-width для замещаемых элементов и контролов формы. */
img, video, audio, canvas, input,
select, button, progress { max-width: 100%; }

/* Заставляем поля с типом file и submit переносить текст */
input[type="file"],
input[type="submit"] { white-space: pre-wrap; }

/* Чиним прогрессбар и поле-ползунок */
progress,
input[type="range"] { width: 100%; }

/* Фиксим поля с типом Number в Firefox */
@supports (--moz-appearance: none) {
  input[type="number"] { width: 100%; }
}

Здесь есть побочный эффект, когда прогрессбар, и поля range и number теперь всегда занимают 100% грид-полосы. Это можно переопределить и настроить.

Можно ли назвать это багами гридов? Не совсем.

Такое поведение, как указал некто на StackOverflow, соответствует спецификации. Гриды перенимают поведение флексбоксов относительно min-width: auto, а у замещаемых элементов их min-width зависит от внутренних размеров. Так что само по себе это не баг.

Тем не менее, почти для каждой из моих раскладок мне приходилось применять вспомогательный класс к капризным грид-элементам. Чтобы у него было запоминающееся фирменное название, я назвал его «Fit Grid» (по-русски можно передать как «Влит-в-грид» — прим. перев.).

/*
 _______  ___   _______    _______  ______    ___   ______  
|       ||   | |       |  |       ||    _ |  |   | |      | 
|    ___||   | |_     _|  |    ___||   | ||  |   | |  _    |
|   |___ |   |   |   |    |   | __ |   |_||_ |   | | | |   |
|    ___||   |   |   |    |   ||  ||    __  ||   | | |_|   |
|   |    |   |   |   |    |   |_| ||   |  | ||   | |       |
|___|    |___|   |___|    |_______||___|  |_||___| |______| 
Дейв Руперт
Читайте ещё: https://daverupert.com/2017/09/breaking-the-grid/
*/

/* 
 * Удаляем `min-width: auto` из элементов грида.
 * Чиним элементы с overflow-x.
*/
.fit-grid > * { min-width: 0; }

/* Применяем max-width к замещаемым элементам и контролам формы */
.fit-grid img,
.fit-grid video,
.fit-grid audio,
.fit-grid canvas,
.fit-grid input,
.fit-grid select,
.fit-grid button,
.fit-grid progress { max-width: 100%; }

/* Заставляем поля с типом file и submit переносить текст */
.fit-grid input[type="file"],
.fit-grid input[type="submit"] { white-space: pre-wrap; }

/* Чиним прогрессбар и поле-ползунок */
.fit-grid progress,
.fit-grid input[type="range"] { width: 100%; }

/* Фиксим поля с типом Number в Firefox */
@supports (--moz-appearance: none) {
  .fit-grid input[type="number"] { width: 100%; }
}

Гриды для тяжелых раскладок не должны задумываться, что в них за контент, и должны выдерживать самые разные сценарии. Было бы неплохо внедрить это в каждый проект, чтобы сделать грид-элементы максимально устойчивыми. Пусть это и не баг, но я отношу это к территории «Clearfix 2.0».

Как по мне, так замещаемые элементы всегда должны подчиняться и вмещаться в грид-полосы. Я был бы рад, если бы браузеры сошлись на этом. Единственный вопрос, который крутится в моей голове, это должны ли контролы формы, такие как прогрессбар, ползунок и числовые занимать всю грид-полосу. Решение — inputselect { width: 100% }, но нутром чую, что не все этого захотят.

Если у вас есть улучшения, дайте мне знать, и я опубликую их на GitHub, но, пожалуйста, перед требованием исправить что-то, всё же проверяйте это в каждом браузере. Спасибо, d00dz.

Спасибо Грегу Уитворту из Microsoft Edge за помощь в отладке и лучшем понимании замещаемых элементов.




CSS, CSS Grid
Читайте также:
Canvas – HTML5. Подробное руководство

Canvas – HTML5. Подробное руководство

Canvas с его обманчиво простым API может революционно преобразовать создание веб-приложений для всех устройств, а не тол...
Читать
CSS Grid, подробное руководство по гридам с примерами

CSS Grid, подробное руководство по гридам с примерами

Это руководство было создано как ресурс, который поможет вам лучше понять и изучить Grid CSS, и было организовано таким ...
Читать
3 новых CSS-функции для изучения в 2017 году

3 новых CSS-функции для изучения в 2017 году

В этом посте, поговорить хочу про CSS, а если быть точнее про новые его фишки которые стоить знать.
Читать
Источник: css-live