В этом уроке мы собираемся преобразовать дизайн на основе сетки в макет, вдохновленный тем, чем вы занимаетесь ? Страница Tumblr, где пользователь мог выбрать набор тем, чтобы адаптировать их рекомендуемый контент.
Выполняется только визуальный дизайн сетки, а не функциональность выбора, как показано в Pen, которую мы будем строить:
Основная цель — реализовать дизайн с помощью CSS Grid, но запасной макет с плавающей точкой описан в разделе « Поддержка » ниже.
наценка
По сути, содержимое страницы состоит из списка карточек:
<ul class="grid"> <li class="card header"> <h1>Which foods do you like?</h1> <p>Tell us what you crave for and we'll get you the tasty bits</p> </li> <li class="card"> <a href="..."> <h2>Pizza</h2> <img src="..." alt="A salami pizza"> </a> </li> <!-- ... --> </ul>
Карточка, которая представляет тему, предложенную пользователю (в нашем примере это еда), имеет заголовок и иллюстративное изображение, оба обернутые в элемент ссылки. Другие могут быть приняты; см., например, отличную статью о компоненте карты на Inclusive Components , где анализируются плюсы и минусы таких альтернатив.
Структурная планировка
В этом разделе будут реализованы основы проектирования сетки. Следующий раздел будет стилизовать карты. Это перо показывает макет с использованием заполнителей для элементов сетки. Запустите его в браузере, который поддерживает CSS Grid.
Прежде чем приступить к написанию кода, важно указать функции и адаптивное поведение сетки. Попробуем записать некоторые свойства, которым он должен удовлетворять.
Технические характеристики
В дизайне представлены два вида карточек: серия тематических карточек и вступительная обложка. Мы размещаем их на базовой сетке, состоящей из квадратных ячеек фиксированного размера. Каждая тематическая карта занимает только одну из этих ячеек, а обложка охватывает большую область смежных ячеек, размер которой зависит от ширины области просмотра. Кроме того, строки и столбцы разделены одним и тем же желобом фиксированного размера.
Сетка имеет столько столбцов (фиксированного размера), сколько они помещаются в область просмотра:
Но мы не хотим, чтобы миллионы столбцов на больших экранах, поэтому давайте ограничим максимальное количество столбцов:
Столбцы всегда центрированы по горизонтали в области просмотра:
Центрируются только столбцы, а не элементы сетки. Это означает, что карты в неполном ряду выровнены по левому краю сетки, а не по центру области просмотра:
Проверьте эти требования в вышеупомянутой ручке. Кроме того, полезно проверять макет с помощью инструментов CSS Grid, предоставляемых некоторыми браузерами, такими как инспектор сетки Firefox.
Учитывая этот контрольный список, мы можем запустить нашу любимую среду разработки и начать кодирование.
Реализация
Давайте введем пару глобальных переменных Sass для представления параметров макета, определенных в спецификациях, а именно:
-
$item-size
для размера стороны ячеек сетки -
$col-gutter
для желоба между дорожками сетки -
$vp-gutter
для безопасного пространства между элементами сетки и краями области просмотра -
$max-cols
для максимального количества столбцов, которые может иметь сетка
Мы могли бы использовать пользовательские свойства CSS для этих переменных, избегая необходимости препроцессора и позволяя нам редактировать их с помощью инструментов разработки в браузере и наблюдать, как изменения происходят мгновенно. Но мы собираемся использовать эти значения даже для запасного макета, подходящего для старых браузеров, где переменные CSS не поддерживаются. Более того, мы используем выражения с этими значениями даже в селекторах медиа-запросов, где пользовательские свойства и функция calc()
не полностью доступны даже в последних браузерах:
$item-size: 210px; $col-gutter: 10px; $vp-gutter: $col-gutter; $max-cols: 5;
Мы должны установить контекст форматирования сетки для элемента сетки:
.grid { display: grid; }
Свойство grid-gap
разделяет дорожки сетки на указанное количество места.
Но эти желоба вставляются только между дорожками, а не перед первой дорожкой и после последней. Горизонтальное заполнение контейнера сетки предотвращает соприкосновение столбцов с краями области просмотра:
.grid { grid-gap: $col-gutter; padding: 0 $vp-gutter; }
Столбцы сетки можно определить с помощью свойства grid-template-columns
и функции repeat
со значением auto-fill
в качестве номера повторения и переменной $item-size
для аргумента списка дорожек:
.grid { grid-template-columns: repeat(auto-fill, $item-size); }
Это говорит браузеру заполнить ширину контейнера сетки (элемент .grid
) как можно большим количеством столбцов фиксированного размера, учитывая вертикальные желоба.
Стоит отметить, что мы могли бы использовать режим автоподбора, и во многих комбинациях размеров области просмотра и количества элементов сетки мы не могли отличить auto-fill
. Но когда в сетке всего несколько элементов с неполной строкой, с auto-fit
элементы будут центрированы, вместо того, чтобы начинаться слева от сетки, как подробно описано в спецификации проекта. Это происходит потому, что, хотя при auto-fill
сетка всегда имеет как можно больше столбцов, при auto-fit
заполнении пустые столбцы удаляются, а центрирование оставшихся столбцов помещает элементы в центр области просмотра:
Если первая строка сетки завершена, столбцы не удаляются, и нет разницы между двумя режимами:
Вернуться к столбцам сетки. До этого момента количество столбцов не имело ограничений. Он может произвольно увеличиваться при увеличении ширины области просмотра. Но согласно спецификации, сетка должна иметь максимальное количество столбцов. Это можно исправить с помощью свойства max-width
:
.grid { max-width: grid-width($max-cols); }
grid-width
— это пользовательская функция Sass, которая возвращает ширину сетки с n столбцами:
@function grid-width($num-cols) { @return $num-cols * $item-size + ($num-cols - 1) * $col-gutter; }
Первое умножение учитывает размер, требуемый столбцами, а второе представляет пространство, требуемое желобами.
Согласно спецификации, сетка должна быть всегда горизонтально отцентрирована. Мы можем объединить старый трюк с автоматическими полями и justify-content
для выполнения этой задачи:
.grid { justify-content: center; margin: 40px auto; }
justify-content
центрирует столбцы, когда внутри контейнера сетки остается свободное место. Это происходит, когда контейнер кровоточит от одного края окна просмотра к другому. Боковые автоматические поля .grid
сам контейнер .grid
, когда он достиг максимальной ширины.
Теперь о строках. Они не указаны явно, как в случае со столбцами. Скорее, они неявно добавляются браузером по мере необходимости, и мы просто указываем их размер с помощью свойства grid-auto-rows
. Повторно используя переменную $item-size
, каждая ячейка сетки имеет форму квадрата, согласно спецификации:
.grid { grid-auto-rows: $item-size; }
Давайте перейдем к размеру карт. В маленьких окнах просмотра, когда сетка настраивается на один столбец, карта покрытия охватывает только ячейку сетки, в то время как при наличии двух или более столбцов она должна охватывать область сетки 4 × 4:
@include when-n-cols(2) { .grid .header { grid-row: span 2; grid-column: span 2; } }
when-n-cols()
— это миксин Sass для выражения медиазапроса, подходящего для сетки с заданным количеством столбцов:
@mixin when-n-cols($n) { @media screen and (min-width: #{grid-width($n) + 2 * $vp-gutter + $scrollbar-size}) { @content; } }
Правила CSS, представленные @content
, активны всякий раз, когда ширина области просмотра равна или превышает ширину сетки со столбцами $n
плюс два безопасных пространства для отделения элементов сетки от краев области просмотра. $scrollbar-size
— это просто верхняя граница размера вертикальной полосы прокрутки, чтобы учесть тот факт, что ширина, указанная в медиазапросе, является всей шириной области просмотра, включая возможную вертикальную полосу прокрутки.
Что касается тематических карточек, тут ничего не поделаешь, потому что поведение сетки по умолчанию делает их такими же размерами, как и назначенные им ячейки сетки.
Хорошо, мы получили это! Обратитесь к структурному Pen в начале этого раздела, чтобы увидеть все эти фрагменты кода, объединенные в полный код.
Карты
Здесь мы строим карты — или их внутреннее содержание, чтобы быть более точным.
Давайте сначала рассмотрим тематические карточки. Чтобы сделать их кликабельными, элемент ссылки расширяется, чтобы заполнить всю область карты:
.grid .card a { display: block; width: 100%; height: 100%; position: relative; }
Затем мы удостоверяемся, что изображение карты покрывает всю поверхность карты:
.grid .card img { display: block; width: 100%; height: 100%; object-fit: cover; }
В этом примере миниатюры имеют квадратное соотношение сторон, и поэтому они хорошо масштабируются внутри своих элементов сетки. Для обработки изображений произвольного размера, object-fit: cover
масштабирование object-fit: cover
(с сохранением соотношения сторон) и в конечном итоге обрезание изображения для размещения внутри контейнера.
Теперь настала очередь названия карты:
.grid .card h2 { margin: 0; position: absolute; top: 0; left: 0; right: 0; bottom: 0; padding: 10px; text-decoration: none; font-family: Raleway, sans-serif; font-size: 1em; letter-spacing: 1px; color: #fff; }
При абсолютном позиционировании элемент заголовка удаляется из потока и располагается над изображением в верхнем левом углу карты.
Чтобы улучшить контраст между меткой и произвольным нижележащим изображением карты, между этими двумя графическими элементами расположен частично прозрачный слой.
Здесь я использую ту же самую точную технику, которая использовалась на исходной странице Tumblr, где это наложение состоит из радиального градиента, который начинается полностью прозрачным в центре карты и заканчивается частично непрозрачным черным к границам, по кругу, придавая изображению своего рода тонкий эффект прожектора. Давайте визуализируем этот слой как фоновое изображение ссылки карты, которую он только что расширил, чтобы покрыть всю поверхность карты:
.grid .card h2 { background-image: radial-gradient(ellipse at center, transparent 0, rgba(0,0,0, .36) 100%); }
Что касается титульной карты, то здесь можно использовать множество техник, но давайте будем простыми. Карта содержит два центрированных блока текста. При боковом заполнении их горизонтальный размер ограничен, а при text-align
их содержимое центрируется. После этого блоки центрируются по вертикали, просто нажимая на них с небольшим количеством верхнего отступа, примененного к контейнеру карты:
.grid .header { box-sizing: border-box; text-align: center; padding-top: 23px; font-size: 1.6875em; line-height: 1.3; background: radial-gradient(ellipse at center, transparent 0, rgba(0,0,0, 0.48) 100%) hsla(0, 0%, 27%, 1); } .grid .header h1 { margin-top: 0; margin-bottom: 0.67em; font-family: 'Cherry Swash', cursive; text-transform: capitalize; font-size: 1em; font-weight: normal; padding-left: 28px; padding-right: 28px; } .grid .header p { margin: 0; font-family: 'Raleway', sans-serif; font-size: 0.52em; padding-left: 34px; padding-right: 34px; }
При использовании медиазапроса размер шрифта увеличивается, а отступы настраиваются, когда сетка отображает два или более столбца:
@include when-n-cols(2) { .grid .header { font-size: 2.5em; padding-top: 100px; } .grid .header h1 { padding-left: 80px; padding-right: 80px; } .grid .header p { padding-left: 120px; padding-right: 120px; } }
Пришло время добавить интерактивности к тематическим карточкам. Они должны уменьшить размер при наведении:
.grid .card:hover { transform: scale(0.95); } .grid .header:hover { transform: none; }
С переходом CSS это изменение визуального состояния сглаживается:
.grid .card { transition-property: transform; transition-duration: 0.3s; }
На картах можно перемещаться с помощью клавиатуры, так почему бы не настроить стиль их фокуса так, чтобы он соответствовал внешнему виду и ощущению мыши? Мы должны масштабировать контейнер .card
когда якорная ссылка внутри него получает фокус. Хммм … нам нужно стилизовать элемент только тогда, когда один из его дочерних элементов получает фокус — и у нас есть псевдокласс : focus- Within для этого:
.grid .card a:focus { outline: none; } .grid .card:focus-within { transform: scale(0.95); }
Сначала сбрасывается стиль фокуса ссылки по умолчанию, а затем используется то же преобразование, что и раньше, когда карта (ее ссылка) получает фокус.
Служба поддержки
CSS Grid
В настоящее время CSS Grid имеет широкую поддержку , но что мы можем сделать с другими браузерами? В демонстрационном перо есть запасной макет, реализованный с помощью поплавков, который ведет себя точно так же, как макет Grid. Давайте кратко рассмотрим, как это работает и взаимодействует с реализацией Grid.
Карты имеют размеры, смещены влево и имеют нижнее поле для горизонтальной сетки:
.card { float: left; width: $item-size; height: $item-size; margin: 0; margin-bottom: $col-gutter; }
На этом этапе мы можем просто установить max-width
контейнера .grid
чтобы ограничить количество столбцов на большом экране, использовать автоматические поля для .grid
сетки, и компоновка будет почти такой же, как и у сетки, но с тем важным отличием, что, когда контейнер не достигает своей максимальной ширины, карты выравниваются по левому краю области просмотра. Это резервный макет для браузеров, не поддерживающих CSS Grid, поэтому мы могли бы быть довольны этим. В противном случае мы могли бы пойти дальше и добавить еще немного кода, чтобы исправить эту разницу. Давайте попробуем.
Мы устанавливаем ширину контейнера .grid
когда есть только один столбец, .grid
его в области просмотра и отделяем от краев экрана:
.grid { width: grid-width(1); margin: 40px auto; padding-left: $vp-gutter; padding-right: $vp-gutter; }
Обратите внимание, как мы использовали функцию Sass для grid-width
представленную выше.
Теперь, используя .grid
медиа-запроса, мы определяем ширину контейнера .grid
когда есть два столбца:
@include when-n-cols(2) { width: grid-width(2); .card:nth-child(2n) { margin-right: $col-gutter; } .header { $header-size: grid-width(2); width: $header-size; height: $header-size; } }
Мы также удвоили размер карточки заголовка и установили правильное поле для горизонтального желоба сетки.
Этот шаблон повторяется для сетки из двух, трех,… $max-cols
столбцов, заботясь о сбросе и назначении margin-right
от соответствующих карт. Например, когда сетка имеет три столбца:
@include when-n-cols(3) { width: grid-width(3); .card { margin-right: $col-gutter; } .card:nth-child(2), .card:nth-child(3), .card:nth-child(3n + 6) { margin-right: 0; } }
Пожалуйста, обратитесь к ручке для остальной части кода.
Теперь, когда у нас есть два блока CSS, которые реализуют один и тот же макет на одной странице, мы должны быть уверены, что они не конфликтуют друг с другом. В частности, в браузерах, поддерживающих Grid, макет с плавающей точкой не должен нарушать макет Grid. Благодаря присущим CSS Grid возможностям переопределения , достаточно сбросить ширину контейнера сетки, установить его max-width
и сбросить поля карточек ( grid-gap
сетки решает теперь проблемы с решетками сетки):
@supports (display: grid) { .grid { width: auto; max-width: grid-width($max-cols); .card { margin: 0 !important; } } }
Эти переопределения должны происходить только тогда, когда поддерживается CSS Grid, так как в противном случае они могут нарушить макет с плавающей точкой Это условное переопределение выполняется с @supports
правила @supports
.
Код в ручке организован так, что эти два блока кода для макета являются независимыми. То есть один из них может быть удален, не влияя на другой. Для достижения этого правила CSS, общие для обеих реализаций макета, были сгруппированы вместе:
.grid { margin: 0; padding: 0; list-style: none; margin: 40px auto; padding-left: $vp-gutter; padding-right: $vp-gutter; }
Итак, если когда-нибудь мы захотим избавиться от макета с плавающей точкой, это легко сделать.
Чтобы завершить это обсуждение запасного макета, обратите внимание, что его код выглядит более сложным и не таким интуитивно понятным, как CSS Grid. Это подчеркивает силу техники CSS, специально разработанной для макета, в отличие от более старой функции CSS (float), чье первоначальное намерение (ab) использовалось в течение многих лет из-за отсутствия более специфических методов.
Другие преимущества
Что касается :focus-within
, он не поддерживается в браузерах Microsoft . С определенным выше CSS на этих пользовательских агентах пользователь пропускает визуальную обратную связь, когда ссылка на элемент сетки получает фокус. Вот способ исправить это:
.grid .card a:focus { outline: 2px solid #d11ee9; } .grid .card:focus-within { transform: scale(0.95); } .grid .card:focus-within a:focus { outline: none; }
Неподдерживающий браузер использует только первое правило, которое настраивает вид контура фокуса по умолчанию. Даже поддерживающий браузер использует это правило, но контур снова сбрасывается в третьей строке.
Другой альтернативой является использование псевдоклассов :hover
и :focus
элемента link, а не для корневого элемента карты. На самом деле, для большей общности, я предпочел масштабировать всю карту, но в этом конкретном случае, когда ссылка растягивается, чтобы охватить всю карту, мы могли бы просто сделать это:
.grid .card a:hover { transform: scale(0.95); } .grid .card a:focus { outline: none; transform: scale(0.95); }
Таким образом, нет необходимости в focus-within
, и карта в фокусе ведет себя так же, как при наведении, даже в старых браузерах.
В браузерах Microsoft также есть некоторые проблемы с подгонкой объектов , поэтому если изображения не имеют такого же соотношения сторон, как карты, они могут выглядеть растянутыми.
Наконец, в IE9 и ниже CSS-градиенты не реализованы . Как следствие, прозрачный слой, который улучшает контраст между названием карты и базовым изображением, не виден. В IE9 это можно исправить, добавив цвет фона, указанный с помощью rgba()
цвета rgba()
:
.grid .card h2 { background: rgba(0,0,0, 0.2); background: radial-gradient(ellipse at center, transparent 0, rgba(0,0,0, .36) 100%); }
Заключительные слова
Мы уже указали на мощь CSS Grid в предыдущем разделе. Кроме того, мы также можем заметить, как адаптивное поведение сетки, описанной в спецификации проекта, было достигнуто без медиазапросов. Фактически, только два запроса, использованных в предыдущих фрагментах, были оба для обложки — один для назначения карты ее области сетки, а другой для ее содержимого.
Итак, после некоторого кодирования и множества пицц и пирожных, мы подошли к концу. Но конец — это только начало чего-то другого, и мы могли бы продолжить дальнейшую работу, такую как добавление некоторых входных анимаций к карточкам, выполнение обзора доступности и попытка выполнить функцию выбора, аналогичную исходной странице Tumblr.
Спасибо Freepik за вкусные картинки. Перейдите по ссылкам карты для просмотра оригинальных изображений.