Учебное пособие по Flexbox и Sass Grid: как упростить адаптивный дизайн
Опубликовано: 2022-03-11Недавно передо мной встала задача создать свою собственную систему сетки, и, поскольку изобретение колеса всегда полезно в качестве учебного опыта, я решился на это. Я знал, что это будет интересная задача, но был удивлен тем, насколько легко она оказалась!
В этом эксперименте мы рассмотрим макеты Flexbox и то, как они позволяют изящно реализовать макеты без каких-либо сумасшедших хаков. Кроме того, если вы не знакомы с Sass, мы посмотрим, как он работает, и воспользуемся некоторыми удобными утилитами Sass. Возможно, вы даже узнаете что-то новое о сетках CSS, таких как та, что является частью Bootstrap.
Очень краткое введение в Sass и Flexbox
Sass — это, по сути, инструмент, который позволяет вам избежать некоторых недостатков CSS, это язык сценариев, который интерпретируется в CSS. Синтаксис выглядит очень знакомым, если вы уже пишете стили CSS, но его набор инструментов включает в себя переменные, примеси для повторного использования и директивы if, for, each и while среди прочих. Одна из самых удобных особенностей Sass заключается в том, что любой допустимый код CSS является действительным Sass, поэтому вы можете постепенно трансформировать свою кодовую базу.
Простой пример цикла for:
@for $i from 1 through 3 { .a-numbered-class-#{$i} { width: (20 * $i) * 1px; } }
Этот простой цикл повторяется от 1 до 3 и создает классы. Индекс итерации будет удобно храниться в $i
. Мы также можем заняться математикой и напечатать .a-numbered-class-X
три раза с разной шириной каждый раз. Этот код выводит:
.a-numbered-class-1 { width: 20px; } .a-numbered-class-2 { width: 40px; } .a-numbered-class-3 { width: 60px; }
Как мы видим, мы можем абстрагироваться от большей части работы, которую вам придется выполнять в CSS. В CSS вам придется копировать, вставлять и изменять вручную, что, очевидно, более подвержено ошибкам и менее элегантно. Если вы еще не пробовали, не теряйте больше времени!
Flexbox расшифровывается как Flexible Box, система компоновки CSS3, которая динамически позиционирует и распределяет элементы. Это очень мощный инструмент, который позволяет создавать гибкие макеты с минимальными усилиями. Для получения дополнительной информации о том, как изучить Flexbox, ознакомьтесь с Полным руководством Криса Койера по Flexbox.
Сетки
Переходя к самой сетке, начнем с ее основных элементов. Они будут вдохновлены элементами сетки Bootstrap: контейнерами, строками и столбцами, каждый из которых содержится внутри первого.
Мы будем использовать соглашения об именах БЭМ для имен классов. Соглашения БЭМ довольно просты в использовании и добавляют много информации об элементе и его контексте. Короче говоря, у вас есть:
- Блоки , которые «инкапсулируют автономную сущность, имеющую смысл сам по себе»:
.block
. - Элементы , которые являются «частями блока и не имеют отдельного значения», обозначаются именем блока, двумя символами подчеркивания и элементом:
.block__elem
. - Модификаторы , такие как «Флаги на блоках или элементах», которые представлены двумя дефисами:
.block .block--mod
.
Контейнеры
Это самый внешний элемент сетки, он будет содержать наши элементы строки. Существует два типа контейнеров: .container
и .container--fluid
.
Поведение .container
определяется тем, что он находится на 100% ширины ниже определенной точки, имеет максимальную фиксированную ширину над ней и имеет равные поля слева и справа:
$grid__bp-md: 768; .container { max-width: $grid__bp-md * 1px; margin: 0 auto; }
Поиграйте с этим здесь, расширяя и сжимая окно «вывода».
Для контейнера с жидкостью, который всегда имеет ширину 100%, мы просто переопределяем эти свойства с помощью модификатора:
&--fluid { margin: 0; max-width: 100%; }
Поиграй с этим здесь.
Это было легко! Теперь у нас реализованы оба контейнера. Переходим к следующему элементу.
Ряды
Строки будут горизонтальными организаторами нашего контента.
Мы будем использовать Flexbox для позиционирования дочерних элементов строки, заставляя их обтекать, чтобы они не переполнялись, и придавая им 100% ширину внутри строки (чтобы мы могли вложить их позже).
&__row { display: flex; flex-wrap: wrap; width: 100%; }
Это расположит дочерние элементы рядом и перенесет их в новые строки, если сумма их ширины больше, чем она сама. Теперь нам просто нужно добавить несколько div, и это будет выглядеть так:
Поиграйте с этим здесь, расширяя и сжимая окно «вывода».
Все начинает обретать форму, но это еще не сетка CSS. Это отсутствует…
Столбцы
Колонки — это место, где живет контент сайта. Они определяют, на сколько частей разделен ряд и сколько они занимают. Мы собираемся сделать макет из двенадцати столбцов. Это означает, что мы можем разделить строку на одну или до двенадцати частей.
Для начала немного элементарной математики. Когда мы хотим иметь один столбец, его ширина должна быть 100%. Если мы хотим двенадцать столбцов. Тогда каждый должен занимать 8,333…% или 100/12 ширины.
С Flexbox для распространения контента таким образом мы можем использовать flex-basis
.
Чтобы разделить на четыре столбца, мы теперь добавим что-то вроде:
flex-basis: (100 / 4 ) * 1%;
Таким образом, мы можем заставить каждый элемент занимать 25% ширины или любой другой процент, который мы хотим.
Поиграй с этим здесь.
Давайте сделаем это более динамичным. Поскольку мы хотим, чтобы это отражало наши возможные классы, давайте назовем .col-1
класс для div-столбца, который будет иметь 8,333% ширины, поскольку двенадцать из них должны поместиться, прежде чем им придется переноситься на новую строку. Процент будет увеличиваться до .col-12
, который будет занимать 100%.
$grid__cols: 12; @for $i from 1 through $grid__cols { .col-#{$i} { flex-basis: (100 / ($grid__cols / $i) ) * 1%; } }
Просто чтобы прояснить, что происходит, скажем, мы хотим разделить ширину на четыре равные части. Нам понадобится .col-3
, так как он подходит 4 раза из 12, а это значит, что .col-3
должен иметь 25% flex-базиса:
100 / ($grid__cols / $i) 100 / (12 / 3) = 25
Это уже начинает походить на сетку!
Поиграй с этим здесь.
Колонки, зависящие от ширины экрана
Теперь мы хотим иметь элемент, который имеет определенную ширину на мобильных устройствах, но другую ширину на планшетах и так далее. Мы будем использовать определенные точки останова в зависимости от ширины окна. Наш пользовательский интерфейс будет реагировать на эти контрольные точки и адаптироваться к идеальному макету, адаптированному к размерам экрана различных устройств. Назовем точки останова по размеру: small (sm), medium (md) и так далее, .col-sm-12
будет элементом, занимающим 12 столбцов как минимум до точки останова sm
.

Давайте переименуем класс .col-*
в .col-sm-*
. Поскольку наша сетка будет в первую очередь мобильной, мы будем применять ее свойства ко всем размерам экрана. Для тех, которые нам нужно вести себя по-другому с большими экранами, мы добавим класс: .col-md-*
.
Представьте элемент с .col-sm-12
и .col-md-4
. Ожидаемое поведение будет заключаться в том, что ниже точки останова «md» (средний) он будет иметь ширину 100 %, а выше — 33,333 % — очень распространенное явление, поскольку на мобильных устройствах вам может потребоваться размещать элементы сверху, а не рядом с ними. друг друга, когда ваша ширина гораздо более ограничена.
Для этого нам нужно добавить медиа-запрос (выражение, содержащее код, который будет выполняться только выше или ниже определенной ширины или на определенном устройстве) в точке останова и создать наши столбцы md
, как мы делали раньше для sm
:
@media screen and (min-width: $grid__bp-md * 1px) { @for $i from 1 through $grid__cols { &__col-md-#{$i} { flex-basis: (100 / ($grid__cols / $i) ) * 1%; } } }
Поиграй с этим здесь.
Это уже приближается к чему-то полезному. Это немного ВЛАЖНО (Понятно? Это не СУХО…), так что давайте сделаем это более абстрактным.
Как мы видели, нам понадобится медиа-запрос для каждой точки останова, поэтому давайте создадим миксин, который получает точку останова, которая динамически создает медиа-запросы. Это может выглядеть примерно так:
@mixin create-mq($breakpoint) { @if($breakpoint == 0) { @content; } @else { @media screen and (min-width: $breakpoint *1px) { @content; } } }
Теперь давайте просто обернем то, что у нас было для создания классов __col
, в миксин с именем create-col-classes
и воспользуемся миксином create-mq
.
@mixin create-col-classes($modifier, $grid__cols, $breakpoint) { @include create-mq($breakpoint) { @for $i from 1 through $grid__cols { &__col#{$modifier}-#{$i} { flex-basis: (100 / ($grid__cols / $i) ) * 1%; } } } }
Вот и все. Чтобы использовать его, мы теперь определяем наши точки останова на карте Sass и повторяем их.
$map-grid-props: ('-sm': 0, '-md': $grid__bp-md, '-lg': $grid__bp-lg); @each $modifier , $breakpoint in $map-grid-props { @include create-col-classes($modifier, $grid__cols, $breakpoint); }
Наша система сетки в основном готова! Мы определили класс .container__col-sm-*
, который будет использоваться по умолчанию, и мы можем изменить его поведение на больших экранах с помощью container__col-md-*
и container__col-lg-*
.
Мы можем даже вкладывать ряды! Поиграй с этим здесь.
Самое приятное в этом то, что если бы мы теперь хотели, чтобы у него были все те же точки останова, что и у Bootstrap v4, нам просто нужно было бы сделать:
$grid__bp-sm: 576; $grid__bp-md: 768; $grid__bp-lg: 992; $grid__bp-xl: 1200; $map-grid-props: ( '': 0, '-sm': $grid__bp-sm, '-md': $grid__bp-md, '-lg': $grid__bp-lg, '-xl': $grid__bp-xl );
Вот и все! Поиграй с этим здесь.
Обратите внимание, что Bootstrap использует более полный подход, ориентированный на мобильные устройства, чем мы изначально обсуждали. Наименьшие размеры окна не имеют суффикса, такого как sm
или md
, по причине того, что класс, эквивалентный .container__col-X
, будет применяться не только при ширине окна от 0 до 576 пикселей; если мы не перезапишем его явно, это будет такое количество столбцов для каждого размера окна. В противном случае мы могли бы добавить класс .container__col-sm-Y
, чтобы сделать его шириной Y столбцов между точками останова sm
.
Смещения
Смещения добавят отступ, оставленный относительно предыдущего столбца. .container__col-offset-4
добавит margin-left: 33.333%
для всех размеров экрана. .container__col-md-offset-4
сделает то же самое, но выше точки останова md
.
Реализация теперь тривиальна; мы добавляем свойство -offset
в тот же цикл, в котором создаем классы, но вместо flex-bases
мы пишем свойство margin-left
. Мы также должны сделать дополнительный для -offset-0
, так как мы можем захотеть очистить поля на больших экранах:
@mixin create-col-classes($modifier, $grid-cols, $breakpoint) { @include create-mq($breakpoint) { &__col#{$modifier}-offset-0 { margin-left: 0; } @for $i from 1 through $grid-cols { &__col#{$modifier}-#{$i} { flex-basis: (100 / ($grid-cols / $i) ) * 1%; } &__col#{$modifier}-offset-#{$i} { margin-left: (100 / ($grid-cols / $i) ) * 1%; } } } }
Теперь у нас есть полнофункциональные оффсеты! Поиграй с этим здесь.
Отображаемость
Иногда нам нужно отобразить/скрыть элемент ниже или выше определенной точки. Для этого мы можем сделать классы доступными, как в Bootstrap v4.
Например, класс .hidden-md-up
скроет любой элемент этого класса, начиная с точки останова md
и выше; и наоборот, .hidden-md-down
скроет его от точки останова вниз.
Код для этого снова прост: мы просто повторяем наши точки останова и создаем класс .hidden-*
с a for each
точки останова. Тем не менее, мы изменили класс create-mq
его более абстрактным:
@each $modifier , $breakpoint in $map-grid-props { @if($modifier == '') { $modifier: '-xs'; } @include create-mq($breakpoint - 1, 'max') { .hidden#{$modifier}-down { display: none !important; } } @include create-mq($breakpoint, 'min') { .hidden#{$modifier}-up { display: none !important; } } }
В качестве примечания, разве это не одно из немногих хороших применений !important
? Обратите внимание, что элемент может иметь сколь угодно большую специфичность с правилом display: block
, но мы все равно хотели бы скрыть его ниже или выше точки останова. Если вы не согласны с таким подходом, дайте мне знать в комментариях!
Вот и все: теперь у нас есть система отображаемости.
Поиграй с этим здесь.
Заключение
Хотя эта «инфраструктура» еще не готова к работе, она показывает, насколько мощными могут быть макеты Flexbox и насколько удобным является Sass. С помощью всего нескольких строк кода мы реализовали основные функции CSS-фреймворка/сетки.
Пусть это также послужит уроком, что базовая версия практически любого программного обеспечения может быть реализована очень легко. Именно конкретные проблемы реального мира начинают накапливаться и усложнять жизнь.
Я создал репозиторий GitHub, где вы можете отправлять запросы или запросы на извлечение.
Какие функции вы хотели бы видеть реализованными? Можно ли упростить реализацию или сделать ее более элегантной?
Не стесняйтесь, дайте мне знать ваше мнение о комментариях ниже.