Прежде чем мы начнем говорить о Sass и коде для этого проекта, позвольте мне напомнить вам, что это всего лишь быстрый эксперимент, который ни в коем случае не следует использовать в реальном контексте. Итак, то, что следует, является лишь доказательством того, что мы могли бы сделать с парой переменных Sass и функцией calc()
из CSS.
Вероятно, есть еще несколько недостатков и крайних случаев, которые не рассматриваются в этом примере. Если у вас есть какие-либо рекомендации по улучшению этого, обязательно поделитесь. В противном случае имейте в виду, что это всего лишь эксперимент.
Что мы пытаемся сделать?
В последние пару дней я работал с функцией calc () в CSS. calc()
может быть благословением, когда дело доходит до вычислений между единицами, и становится еще лучше, когда у вас есть пара переменных Sass, чтобы сделать все легко настраиваемым.
Тогда кто-то в Твиттере спросил меня, как мне построить систему сетки, если бы мне пришлось. Во-первых, я бы не стал. Уже существует слишком много грид-систем и фреймворков на основе CSS, и создание другой будет изобретением колеса. На самом деле, я однажды написал статью под названием « Нам не нужна ваша CSS-сетка» , даже если мое мнение сейчас немного спокойнее, чем когда я впервые писал статью. Короче говоря: некоторые умные люди уже создали грид-системы, которые являются более мощными, чем любая грид-система, которую я когда-либо мог придумать (например, Breakpoint и Susy ).
Во всяком случае, я не хотел делать систему сетки, как другие, поэтому я подумал: «Эй, почему бы не повеселиться с calc
» ? Я хотел, чтобы все было как можно проще — три переменные, одна точка останова, один миксин. Это все. Три настраиваемые переменные:
- Количество столбцов в сетке (по умолчанию
$grid-columns: 12
) - Ширина желоба между столбцами (по умолчанию
$grid-gutter: 10px
) - Ширина экрана, под которой мы перемещаемся в один столбец (по умолчанию
$grid-breakpoint: 48em
)
Еще одна особенность, которую я хотел иметь (которая становится все менее и менее особенной), состоит в том, чтобы избегать использования имени класса в DOM. Что еще более важно, я хотел сделать все из таблицы стилей. Это также означает, что мне пришлось использовать расширенные селекторы CSS, такие как :nth-of-type
.
Mixin Core
Я всегда стараюсь держать свои функции и смешивать подписи как можно более скудными. Для этого мне понадобилось не более одного аргумента:
@mixin grid($cols...) { // Sass magic }
… argList
самом деле является переменным числом аргументов (AKA и argList
), как указано в многоточиях после переменной $cols
. Основная идея заключается в том, чтобы проходить через эти аргументы и обрабатывать столбцы на основе этого благодаря :nth-of-type
CSS-селектору :nth-of-type
. Например, вызов grid(6, 6)
в сетке с 12 столбцами создаст 2 столбца, разделенных желобом 10px
.
Но перед циклом добавим пару объявлений для построения макета:
@mixin grid($cols...) { overflow: hidden; > * { float: left; margin-right: $gutter; } // Loop }
Чтобы выбрать все дочерние элементы из контейнера, вызывающего mixin, мы используем селектор *
с дочерним комбинатором >
. Могу поспорить, некоторые из вас уже задыхаются. Ну да. Это 2014 год, что означает, что производительность CSS больше не является проблемой . Кроме того, поскольку мы используем calc()
, мы не будем поддерживать ничего, кроме Internet Explorer 9, поэтому мы используем > *
, хорошо!
В нашем блоке объявлений мы перемещаем все непосредственные дочерние элементы и добавляем правое поле. Оболочка имеет overflow: hidden
чтобы очистить внутренние поплавки. Если вы более любитель микро-ясности , обязательно измените его в соответствии с вашими потребностями. В случае, если это список, не забудьте добавить list-style: none
и padding-left: 0
, если ваш CSS-сброс еще не выполняется.
Теперь петля!
@for $i from 1 through length($cols) { > :nth-of-type(#{$i}n) { $multiplier: $i / $grid-columns; width: calc(100% * #{$multiplier} - #{$grid-gutter} * (1 - #{$multiplier})); } }
Ох, это выглядит сложно, как ад! Давайте разберемся с этой строкой за раз. Для полного объяснения, давайте предположим, что мы называем grid(3, 7, 2)
, которая будет довольно стандартной компоновкой с центральным контейнером, обведенным 2 боковыми панелями. И не забывайте, что у нас есть макет из 12 столбцов с желобами по 10 пикселей, как мы определили в наших переменных ранее.
Сначала селектор. Не обращайте внимания на n
, мы увидим, почему это там, в следующем разделе. На данный момент все, что вам нужно понять, это то, что мы выбираем дочерних элементов один за другим, чтобы применить к ним ширину. Кстати, пустое пространство перед :nth-of-type
является неявным *
.
Теперь функция calc()
. Расчет выглядит довольно интенсивно, не так ли? На самом деле это не так сложно понять, если представить это. Давайте разберемся с нашей первой колонкой ( 3
). Если мы пройдем наше уравнение шаг за шагом, вот что мы получаем:
- 100% * 3/12 — 10px * (1 — 3/12)
- 100% * 0,25 — 10 пикселей * 0,75
- 25% — 7,5 пикселей
Наш столбец будет занимать более 25% от общей ширины минус 7,5 пикселей (мы надеемся, что целевые браузеры будут иметь дело с субпиксельной визуализацией). Все еще не ясно? Давайте посмотрим другой пример с нашим основным контейнером ( 7
):
- 100% * 7/12 — 10px * (1 — 7/12)
- 100% * 0,5833333333333334 — 10 пикселей * 0,41666666666666663
- 58,33333333333334% — 4,1666666666666663px
И последнее, но не менее важное, наша правая боковая панель ( 2
):
- 100% * 2/12 — 10px * (1 — 2/12)
- 100% * 0,16666666666666666 — 10 пикселей * 0,8333333333333334
- 16,666666666666666% — 8,333333333333334px
Теперь, если мы добавим три результата, чтобы убедиться, что все правильно:
- (25% — 7,5%) + (58,33333333333334% — 4,1666666666666663px) + (16,666666666666666% — 8,333333333333334px)
- (25% + 58,33333333333334% + 16,666666666666666%) — (4,1666666666666663px + 8,333333333333334px + 7,5px)
- 100% — 20 пикселей
Что имеет смысл, поскольку у нас есть 2 желоба по 10 пикселей. Это все для расчетов. Это было не так сложно, правда?
Последнее важное: мы удаляем правое поле с последнего дочернего элемента вне цикла с помощью другого расширенного селектора CSS:
> :nth-of-type(#{length($cols)}n) { margin-right: 0; }
Если вам интересно, применить маржу ко всем дочерним элементам, а затем удалить маржу из последнего дочернего элемента, лучше, чем просто применять маржу для каждого дочернего элемента, кроме последнего. Я попробовал оба.
Примечание: при использовании переменных Sass в функции calc()
не забудьте интерполировать их, если хотите, чтобы это работало. Помните, что calc()
компилируется не Sass, а самим браузером, поэтому необходимо, чтобы все значения были правильно записаны в функции.
Неизвестное количество предметов
Полагаю, нет нужды говорить, что система сетки достаточно хорошо обрабатывает вложенные сетки. Одна вещь, которую я хотел, была способность иметь вложенные сетки с неизвестным числом детей. Для этого есть множество причин, будь то загрузка Ajax, lazyload или что-то еще.
Поскольку мы не знаем количество детей, мы не можем включить «соотношение» для всех детей во включении миксинов. Поэтому я пришел к решению, требующему только шаблон одной строки (например, grid(3, 3, 3, 3)
). Затем, если имеется более 4 дочерних элементов, они все равно должны вести себя так, как будто это сетка из 4 столбцов (новая строка и т. Д.).
Также вы, возможно, заметили, что мы не используем подпакеты для каждой строки, так как мы не вносим никаких изменений в DOM. Таким образом, мы должны убедиться, что последний дочерний элемент контейнера и последний дочерний элемент каждой строки не имеют полей.
Отсюда селекторы :nth-of-type()
, которые мы видели ранее. Это означает, например, что у детей 4
, 8
, 12
и т. Д. Не будет правильного поля.
Работа с маленькими экранами
Теперь, когда у нас все работает как шарм, мы должны убедиться, что сетка изящно ухудшается на маленьких экранах. Я держу это просто: ниже заданной точки останова все складывается как один столбец.
@mixin grid($cols...) { // Mixin core @media (max-width: $breakpoint) { float: none; width: 100%; margin-right: 0; } }
Ниже этой ширины экрана элементы ведут себя так же, как и без системы сетки. То есть элементы уровня блока растягиваются, чтобы соответствовать ширине их родителя, и располагаются под ним в исходном порядке. Простой, но эффективный подход.
Улучшение вывода CSS с помощью заполнителей
Пока что это делает работу очень хорошо. Все отлично работает, и мы очень счастливы, не так ли? Однако, если у нас на одной странице несколько сеток, есть много повторяющихся правил CSS, которые можно объединить, чтобы сделать вывод легче.
Мы могли бы сделать наш миксин расширенным заполнителем вместо прямого сброса правил CSS. Во-первых, давайте создадим наши заполнители.
%grid-parent { overflow: hidden; } %grid-child { float: left; margin-right: $grid-gutter; } %grid-last-child { margin-right: 0; } @for $i from 1 through $grid-columns { %grid-col-#{$i} { $multiplier: $i / $grid-columns; width: calc(100% * #{$multiplier} - #{$grid-gutter} * (1 - #{$multiplier})); } } @media (max-width: $grid-breakpoint) { %grid-fallback { float: none; width: 100%; margin-right: 0; } }
Первые три заполнителя говорят сами за себя. Для четвертого заполнителя, чтобы избежать вычисления ширины непосредственно внутри миксина, мы создаем столько заполнителей, сколько нам нужно для нашей сетки (например, 12 для 12 столбцов) с помощью цикла @for
.
Что касается заполнителя %grid-fallback
, мы должны создать его экземпляр в медиа-запросе, чтобы иметь возможность расширить его из эквивалентного медиа-запроса в другом месте таблицы стилей. Действительно, у Sass есть некоторые ограничения в отношении кросс-медиа @extend (то есть он не работает).
И вот как теперь выглядит миксин — не делая ничего, кроме расширения заполнителей:
@mixin grid($cols...) { @extend %grid-parent; > * { @extend %grid-child; @for $i from 1 through length($cols) { &:nth-of-type(#{$i}n) { @extend %grid-col-#{nth($cols, $i)}; } } &:nth-of-type(#{length($cols)}n) { @extend %grid-last-child; } } @media (max-width: $grid-breakpoint) { @extend %grid-fallback; } }
Последние мысли
Эй, в конце концов это было довольно напряженно, не так ли? Честно говоря, сначала я думал, что это будет легко, но мне пришло в голову, что мне нужно было сделать несколько продвинутых Sass, чтобы сохранить вывод CSS таким чистым, как если бы он был написан от руки. Хорошее практическое правило заключается в том, что выходные данные из ваших файлов Sass должны быть разумно похожи на те, которые вы написали бы сами.