Адаптивный дизайн и пользовательские свойства CSS: определение переменных и контрольных точек

Никаких медиа-запросов! Верстка адаптивных макетов с CSS Grid

От автора: пользовательские свойства CSS (или переменные CSS) становятся все более и более популярными. Они наконец достигли достойной поддержки браузерами и постепенно пробиваются в различные производственные среды. Популярность пользовательских свойств не должна вызывать удивления, поскольку они могут быть очень полезны во многих случаях, включая управление цветовой палитрой, настройку компонентов и создание тем. Но переменные также могут быть очень полезны, когда речь идет о том, как реализовать с помощью CSS адаптивный дизайн.

Давайте рассмотрим элемент article с заголовком и абзацем внутри:

<article class="post"> <h2 class="heading">Post's heading</h2> <p class="paragraph"> Lorem ipsum dolor sit amet, consectetur adipisicing elit. Laudantium numquam adipisci recusandae officiis dolore tenetur, nisi, beatae praesentium, soluta ullam suscipit quas? </p>
</article>

В таком случае это распространенный сценарий изменения некоторых размеров в зависимости от ширины области просмотра. Один из способов сделать это — использовать медиа-запросы:

.post { padding: 0.5rem 1rem; margin: 0.5rem auto 1rem;
} .heading { font-size: 2rem;
} @media (min-width: 576px) { .post { padding: 1rem 2rem; margin: 1rem auto 2rem; } .heading { font-size: 3rem; }
}

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

Повторяющиеся селекторы: Помимо раздувания кода из-за повторяющихся объявлений, это также усложняет будущий рефакторинг, например, каждый раз, когда имя класса изменяется, требуется помнить об обновлении его в нескольких местах.

Повторяющиеся свойства: обратите внимание, что при перезаписи правил CSS в медиа-запросах требуется повторение всего объявления (например font-size: 3rem;), даже если здесь изменяется только значение 3rem.

Повторяющиеся медиа-запросы. Чтобы адаптивные стили оставались контекстуальными, обычной практикой является включение одних и тех же медиа-запросов в нескольких местах, близких к стилям, которые они переопределяют. К сожалению, это не только делает код объемнее, но и может значительно усложнить поддержку контрольных точек. С другой стороны, хранение всех адаптивных стилей в одном месте, далеко от их первоначальных объявлений, может быть очень запутанным: в результате мы получаем множество ссылок на одни и те же элементы, которые находятся в совершенно разных местах.

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

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

Использование переменных CSS для значений свойств

Наиболее распространенный способ использования пользовательских свойств CSS в адаптивном дизайне — это использование переменных для хранения значений, которые изменяются внутри медиа-запросов. Для этого объявите переменную, содержащую значение, которое должно измениться, а затем переназначьте ее значение внутри медиа-запроса:

:root { --responsive-padding: 1rem;
} @media (min-width: 576px) { :root { --responsive-padding: 2rem; }
} .foo { padding: var(--responsive-padding);
}

Назначение переменных для селектора :root не всегда хорошая идея. Как и в JavaScript, наличие множества глобальных переменных считается плохой практикой. В реальной жизни попробуйте объявить пользовательские свойства в области, в которой они будут фактически использоваться.

Таким образом, мы смогли обойтись без нескольких правил класса .foo. Мы также отделяем логику (изменение значений) от реальных конструкций (декларации CSS). Адаптация этого подхода в нашем примере сверху дает нам следующий CSS:

.post { --post-vertical-padding: 0.5rem; --post-horizontal-padding: 1rem; --post-top-margin: 0.5rem; --post-bottom-margin: 1rem; --heading-font-size: 2rem;
} @media (min-width: 576px) { .post { --post-vertical-padding: 1rem; --post-horizontal-padding: 2rem; --post-top-margin: 1rem; --post-bottom-margin: 2rem; --heading-font-size: 3rem; }
} .post { padding: var(--post-vertical-padding) var(--post-horizontal-padding); margin: var(--post-top-margin) auto var(--post-bottom-margin);
} .heading { font-size: var(--heading-font-size);
}

Обратите внимание, что использование переменных в сокращенных свойствах (например padding, margin или font) допускает некоторые очень интересные последствия. Поскольку пользовательские свойства могут содержать практически любое значение (подробнее об этом позже), даже пустую строку, неясно, как значение сокращенного свойства будет разделено на производные свойства, которые будут использоваться в каскаде позже. Например, auto используемое в приведенном выше свойстве margin может оказаться верхним и нижним полями, левым и правым полем, верхним полем, правым полем, нижним полем или левым полем — все зависит от значения пользовательских свойств вокруг.

Сомнительно, что код выглядит чище, чем в предыдущем примере, но в более широком масштабе он определенно более удобен в обслуживании. Попробуем немного упростить этот код.

Обратите внимание, что некоторые значения здесь повторяются. Что если мы попытаемся объединить повторяющиеся переменные? Давайте рассмотрим следующее изменение:

:root { --small-spacing: 0.5rem; --large-spacing: 1rem; --large-font-size: 2rem;
} @media (min-width: 576px) { :root { --small-spacing: 1rem; --large-spacing: 2rem; --large-font-size: 3rem; }
} .post { padding: var(--small-spacing) var(--large-spacing); margin: var(--small-spacing) auto var(--large-spacing);
} .heading { font-size: var(--large-font-size);
}

Это выглядит чище, но на самом ли деле это лучше? Не обязательно. При достижении гибкости и читаемости это все равно может быть неправильным решением для отдельных случаев. Нам определенно не следует объединять некоторые переменные только потому, что они случайно оказались одинаковыми. Иногда, если мы делаем это как часть хорошо продуманной системы, это может помочь нам упростить вещи и сохранить согласованность всего проекта. Однако в других случаях такой способ может быстро сделать все запутанным и проблематичным. Теперь давайте рассмотрим еще один подход к коду.

Использование переменных CSS в качестве множителей

Пользовательские свойства CSS — довольно новая функция для современного Интернета. Еще одна удивительная особенность, появившаяся в последние годы, — это функция calc(). Она позволяет нам выполнять реальные математические операции в живом CSS. С точки зрения поддержки, она поддерживается во всех браузерах, которые поддерживают пользовательские свойства CSS.

calc() имеет тенденцию очень хорошо работать с переменными CSS, делая их еще более мощными. Это означает, что мы можем использовать как calc() в пользовательских свойствах, так и пользовательские свойства внутри calc()!
Например, следующий CSS-код является абсолютно допустимым:

:root { --size: 2;
} .foo { --padding: calc(var(--size) * 1rem); /* 2 × 1rem = 2rem */ padding: calc(var(--padding) * 2); /* 2rem × 2 = 4rem */
}

Почему это важно для нас и наших адаптивных проектов? Это означает, что мы можем использовать функцию calc() для изменения пользовательских свойств CSS внутри медиа-запросов. Допустим, у нас есть отступ, который должен иметь значение 5px на мобильном устройстве и 10px на настольном. Вместо того, чтобы объявлять это свойство два раза, мы можем присвоить ему переменную и умножить ее на два для больших экранов:

:root { --padding: 1rem; --foo-padding: var(--padding);
} @media (min-width: 576px) { :root { --foo-padding: calc(var(--padding) * 2); }
} .foo { padding: var(--foo-padding);
}

Выглядит хорошо, однако все значения ( —padding, calc(—padding * 2)) находятся вне их объявления (padding). Синтаксис также может быть довольно запутанным с двумя различными переменными padding ( —padding и —foo-padding) и неясными отношениями между ними.

Чтобы прояснить ситуацию, давайте попробуем закодировать это наоборот:

:root { --multiplier: 1;
} @media (min-width: 576px) { :root { --multiplier: 2; }
} .foo { padding: calc(1rem * var(--multiplier));
}

Таким образом, мы получили тот же вычисленный результат с намного более чистым кодом! Вместо использования переменной для начального значения свойства (1rem), переменная использовалась для хранения множителя (1на маленьких экранах и 2 на больших экранах). Это также позволяет нам использовать переменную —multiplier в других объявлениях. Давайте применим эту технику к отступам и полям в нашем предыдущем фрагменте:

:root { --multiplier: 1;
} @media (min-width: 576px) { :root { --multiplier: 2; }
} .post { padding: calc(.5rem * var(--multiplier)) calc(1rem * var(--multiplier)); margin: calc(.5rem * var(--multiplier)) auto calc(1rem * var(--multiplier));
}

Теперь давайте попробуем реализовать тот же подход с типографикой. Сначала мы добавим к нашим проектам еще один заголовок:

<h1 class="heading-large">My Blog</h1>
<article class="post"> <h2 class="heading-medium">Post's heading</h2> <p class="paragraph"> Lorem ipsum dolor sit amet, consectetur adipisicing elit. Laudantium numquam adipisci recusandae officiis dolore tenetur, nisi, beatae praesentium, soluta ullam suscipit quas? </p>
</article>

Имея несколько стилей текста, мы можем использовать переменную для управления их размерами:

:root { --headings-multiplier: 1;
} @media (min-width: 576px) { :root { --headings-multiplier: 3 / 2; }
} .heading-medium { font-size: calc(2rem * var(--headings-multiplier))
} .heading-large { font-size: calc(3rem * var(--headings-multiplier))
}

Вы, возможно, заметили, что 3 / 2 это вообще недопустимое значение CSS. Почему это не вызывает ошибку? Причина в том, что синтаксис для переменных CSS чрезвычайно прост, что означает, что переменной можно присвоить практически все, даже если это недопустимое значение CSS для любого существующего свойства CSS. Объявленные пользовательские свойства CSS практически не оцениваются до тех пор, пока они не будут вычислены пользовательским агентом в определенных объявлениях. Таким образом, если переменная используется в значении какого-либо свойства, это значение станет действительным или недействительным во время вычисления значения.

Да, и еще одно примечание: если вам интересно, я использовал значение 3 / 2 просто, чтобы создать точку. В реальной жизни было бы лучше писать вместо этого 1.5 , чтобы сделать код более читабельным. Теперь давайте посмотрим на готовый реальный пример, объединяющий все, что мы рассмотрели выше:

Опять же, я бы не рекомендовал объединять calc() с пользовательскими свойствами, чтобы сделать код более кратким, как общее правило. Но я определенно могу представить сценарии, в которых это помогает сделать код более организованным и обслуживаемым. Этот подход также позволяет значительно снизить объем CSS, если использовать его с умом.

С точки зрения читабельности, мы можем считать его более читаемым, когда основное правило будет понято. Это помогает объяснить логику и отношения между значениями. С другой стороны, некоторые могут считать его менее читаемым, потому что трудно сразу прочитать, что свойство содержит в качестве значения, без предварительной математической обработки. Кроме того, одновременное использование слишком большого количества переменных и функций calc() может излишне усложнить понимание кода, особенно для младших и внешних разработчиков, которые не ориентированы на CSS.

Заключение

Подводя итог, можно найти множество способов использования пользовательских свойств CSS в адаптивном дизайне, определенно не ограничиваясь примерами, показанными выше. Переменные CSS могут использоваться просто для отделения значений от дизайна. Их также можно объединить с какой-нибудь математикой. Ни один из представленных подходов не лучше и не хуже других. Целесообразность их использования зависит от ситуации и контекста.

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

Автор: Mikolaj Dobrucki

Источник: https://css-tricks.com/

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