Макет без полей с использованием CSS Grid

Макет без полей с использованием CSS Grid

От автора: когда-то существовал золотой стандарт макета веб-сайта, который все стремились создать, но это было трудно сделать правильно: Макет Святого Грааля.

Макет без полей с использованием CSS Grid

Не похоже, чтобы это было так сложно, правда? Но это была эпоха до появления flexbox; нашими инструментами для работы были таблицы и float, и ни один из них не подходил для этой задачи. Технически это было возможно, но требовались некоторые уловки.

После того, как flexbox получил поддержку в основных браузерах, этот макет превратился из «святого Грааля» в общедоступный; он был везде, потому что предлагал отличный пользовательский интерфейс и был доступен для всех разработчиков.

По мере развития Интернета я обнаружил новый интересный макет. Он предлагает фантастический пользовательский интерфейс, особенно для длинного текстового контента, такого как новостные статьи или документация. Но, как и в случае его предшественника, этого обманчиво трудно достичь; большинство реализаций требуют непонятных хаков или нелогичных уловок.

Недавно я обнаружил элегантное решение этой проблемы с помощью CSS Grid. В этом посте мы узнаем, как это работает!

Проблема

Вы когда-нибудь пробовали читать Википедию на очень большом экране? Выглядит это так:

Макет без полей с использованием CSS Grid

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

В дополнение к проблеме переноса строк, обычно трудно читать такие широкие строки текста; это утомляет глаз. Исследования показали, что идеальная длина строки составляет около 65 символов. Где-то между 45 и 85 обычно считается приемлемым в контексте латинского алфавита. Чтение — это сложный процесс, и мы должны стремиться сделать его как можно проще.

Стандартное решение этой проблемы — создать один столбец фиксированной ширины в центре страницы. Вы видели этот макет повсюду: в онлайн-журналах, документации, новостных сайтах и блогах. Вы смотрите на него прямо сейчас, на этом сайте!

Макет без полей с использованием CSS Grid

Однако есть усложняющий фактор — не весь контент следует ограничивать. Мы должны позволить изображениям, видео и пользовательским виджетам заполнять доступную ширину.

Обычный термин для такого рода вещей — «без полей». Это термин, заимствованный из издательского мира; когда что-то печатается без полей, оно достигает самого края бумаги.

Это новое требование значительно усложняет задачу. Относительно легко ограничить все дочерние элементы, но на самом деле CSS не имеет механизма выборочного ограничения некоторых дочерних элементов.

Решение

Начнем с конца, с нашего решения:

.wrapper { display: grid; grid-template-columns: 1fr min(65ch, 100%) 1fr;
}
.wrapper > * { grid-column: 2;
}
.full-bleed { width: 100%; grid-column: 1 / 4;
}

Эти стили назначаются этой разметке:

<main class="wrapper"> <h1>Some Heading</h1> <p>Some content and stuff</p> <img class="full-bleed" alt="cute meerkat" src="/meerkat.jpg" />
</main>

Здесь есть что рассматривать, так что давайте сделаем это шаг за шагом.

Сетка

.wrapper { display: grid; grid-template-columns: 1fr min(65ch, 100%) 1fr;
}

Если вы не знакомы с CSS Grid, это может показаться вам набором случайных символов и ключевых слов. Не бойтесь! Все будет объяснено.

grid-template-columns — это свойство, которое позволяет нам определять форму нашей сетки. Предоставляя 3 дискретных значения, мы указываем, что нам нужно 3 колонки.

Значения определяют ширину каждой колонки. Первая колонка такая же, как и последняя — 1fr. Единица fr представляет собой гибкий блок, который заполняет свободное пространство. В принципе это похоже на flex-grow: это соотношение того, сколько свободного места должен занимать столбец.

Наша центральная колонка имеет фиксированную ширину. Мы используем min, чтобы выбрать меньшее значение. На больших экранах она будет занимать ширину 65ch. На экранах меньшего размера, где недостаточно места по горизонтали для 65 символов, она ограничивается 100% доступной ширины контейнера.

Вот как это выглядит на практике:

Макет без полей с использованием CSS Grid

Это зависит от символов

Как мы уже обсуждали, мы хотим ограничить длину нашего текста, чтобы каждая строка была шириной около 65 символов. Мы можем «оценить» ширину, используя пиксели, но в CSS есть непонятная единица, которая немного упрощает нашу жизнь: ch.

.wrapper { /* Guesstimated width */ width: 800px; /* Ideal width */ width: 65ch;
}

Ch — такая же единица, как px или rem. Она соответствует ширине символа 0 в текущем шрифте для указанного размера шрифта. Вместо обратного проектирования ширины в пикселях мы указываем ширину в символах.

На практике вам все равно придется немного поработать; например, если ваш шрифт имеет особенно узкий символ 0, вам нужно немного увеличить размер.

Назначенный дочерний столбец

Мы определили сетку из трех колонок, и теперь пора назначить ей дочерние элементы. По умолчанию дочерние элементы будут размещены в первой доступной ячейке сетки. Однако мы хотим изменить это поведение по умолчанию; все потомки должны размещаться в центральной колонке, оставив первую и третью пустыми.

.wrapper > * { grid-column: 2;
}

В CSS Grid колонки индексируются от 1, так что 2 — это ссылка на среднюю колонку. Звездочка ( * ) — это подстановочный знак; он будет соответствовать элементам всех типов. Мы говорим, что каждый потомок должен быть отнесен ко второй средней колонке. Каждый новый дочерний элемент будет создавать новый ряд, например:

Макет без полей с использованием CSS Grid

Производительность подстановочного знака

Возможно, вы слышали, что использование селектора с подстановочными знаками ( * ) — плохая практика. Некоторые скажут вам, что это медленно и может повлиять на общую производительность страницы.

К счастью, это неправда. Это было развенчано снова и снова. Даже в 2009 году, когда компьютеры были медленнее, а браузеры менее оптимизированы, это не было проблемой. Проблемы производительности, связанные с селектором с подстановочными знаками, являются особенно устойчивой городской легендой.

Потомки без полей

Мы видели, как наша сетка может ограничивать элементы всех типов, но что делать, если мы хотим, чтобы дочерний элемент освободился и заполнил доступную ширину? Вот тут и появляется этот парень:

.full-bleed { width: 100%; grid-column: 1 / 4;
}

Этот специальный класс .full-bleed позволяет определенному дочернему элементу выйти из этой колонки и охватить все 3 колонки. 1 / 4 — это синтаксис начала / конца; мы говорим, что элемент должен начинаться с колонки 1 (включительно) и охватывать все до колонки 4 (не включая ее).

Макет без полей с использованием CSS Grid

Колонки сетки

Вам может быть интересно; наша сетка состоит из 3 колонок, поэтому почему мы ссылаемся на 4 в 1 / 4? Фактически колонки сетки индексируются линиями, а не ячейками. Представьте себе сетку из трех колонок:

Макет без полей с использованием CSS Grid

Когда мы говорим, что наш класс без полей должен охватывать колонки с 1 по 4, мы говорим, что он должен начинаться сразу после первой линии и заканчиваться прямо перед четвертой.

Более аккуратный синтаксис!

Читатель заметил: мы можем «подготовить» код к будущему, изменив определение колонок:

.full-bleed { grid-column: 1 / -1;
}

При изменении 4 на -1, мы считаем с конца, а не с начала. Мы говорим, что наша сетка должна начинаться после первой линии и заканчиваться перед последней.

Если вы знакомы с оператором javascript .slice, он работает аналогичным образом; arr.slice(-1) захватывает последний элемент, поскольку отрицательные числа означают, что он должен считаться справа, а не слева.

Хитрость в том, что каждый дочерний элемент становится отдельным рядом сетки, и каждый дочерний элемент может заполнить столько рядов, сколько захочет. Большинство элементов будут занимать только эту центральную колонку, но некоторые вместо этого будут занимать все 3.

Конфликтующие стили

Возможно, вы заметили, что потомкам с классом full-bleed даются противоречивые значения grid-column:

/* from the wildcard (*) selector */
grid-column: 2;
/* from the .full-bleed class */
grid-column: 1 / 4;

Это работает из-за Специфичности CSS; наш класс full-bleed отменяет селектор подстановочных знаков (*), потому что он более конкретен. Мы намеренно создаем конфликт, потому что знаем, что наш класс full-bleed будет иметь приоритет.

Прелесть селектора с подстановочными знаками в том, что он всегда проигрывает эти битвы, поэтому это отличный способ установить значения по умолчанию.

Отступы

На экранах меньшего размера мы хотим добавить немного отступов, чтобы текст не располагался прямо на краю экрана. Мы можем сделать это разными способами, включая прямое использование padding! Но поскольку это статья о CSS Grid, давайте рассмотрим решение на основе сетки:

.wrapper { display: grid; grid-template-columns: 1fr min(60ch, calc(100% - 64px)) 1fr; grid-column-gap: 32px;
}

grid-column-gap — отличное свойство, которое позволяет нам добавлять промежутки между колонками. В данном случае я жестко закодировал значение 32 пикселя.

Однако это создает проблему; из-за этого лишнего места мы получим ужасную горизонтальную полосу прокрутки на мобильных устройствах.

Причина в том, что зазоры находятся вне колонок. Мы указываем колонке занимать 100% доступной ширины, и мы говорим, что у колонки должно быть 32 пикселя с каждой стороны. Если у устройства экран шириной 500 пикселей, мы закончим рендеринг на 564 пикселя (столбец 500 пикселей + 32 пикселя на каждую сторону).

Мы можем решить эту проблему с помощью совершенно волшебного свойства CSS calc. Calc позволяет нам смешивать единицы разных типов. В этом случае мы хотим, чтобы центральный столбец был на 64 пикселя короче полной ширины. Мы можем получить это значение с помощью calc(100% — 64px). Это станет нашей новой минимальной шириной для мобильных устройств.

CSS для разработчиков JavaScript

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

Я был в этой лодке, и мне это надоело. Итак, я углубился в изучение того, как браузер интерпретирует и отображает CSS. На это потребовались годы, но в результате я стал намного увереннее владеть языком.

Я использую эти знания в онлайн-курсе под названием CSS для разработчиков JavaScript. Это интерактивный курс, в котором используются те же методы, которые я использую в этом блоге для создания динамического интерактивного контента. Я настолько уверен в этой идее, что оставил свою работу старшим штатным инженером-программистом в Gatsby Inc., чтобы работать над ней на постоянной основе.

Заключение

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

Некоторые читатели предположили, что того же эффекта можно добиться с помощью flexbox или вообще без использования оболочки. К сожалению, есть некоторые компромиссы, которые делают эти альтернативы неработоспособными.

Историческое решение этой проблемы — использовать отрицательные поля. Оно работает отлично, но мне кажется немного хакерским.

Автор: Joshua Comeau

Источник: joshwcomeau.com

Редакция: Команда webformyself.