Стратегическое руководство по использованию пользовательских свойств CSS

Стратегическое руководство по использованию пользовательских свойств CSS

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

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

Чем они похожи на переменные в препроцессорах?

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

$smashing-red: #d33a2c;

В Less мы используем символ @

@smashing-red: #d33a2c;

Пользовательские свойства следуют аналогичным соглашениям и используют префикс:

:root { --smashing-red: #d33a2c; }
.smashing-text { color: var(--smashing-red);
}

Важным отличием пользовательских свойств и переменных в препроцессорах является то, что пользовательские свойства имеют другой синтаксис установки значения и получения этого значения. При извлечении значения пользовательского свойства мы используем функцию var().

Следующее наиболее очевидное различие заключается в названии. Они называются «пользовательскими свойствами», потому что они действительно являются свойствами CSS. В препроцессорах вы можете объявлять и использовать переменные практически в любом месте, включая внешние блоки объявлений, в медиа запросах или даже в составе селектора.

$breakpoint: 800px;
$smashing-red: #d33a2c;
$smashing-things: ".smashing-text, .cats"; @media screen and (min-width: $breakpoint) { #{$smashing-things} { color: $smashing-red; }
}

Большинство приведенных выше примеров недействительны с использованием пользовательских свойств.

Пользовательские свойства имеют одинаковые правила о том, где их можно использовать в качестве обычных свойств CSS. Гораздо лучше думать о них как о динамических свойствах, чем о переменных. Это означает, что они могут использоваться только внутри блока декларации, или, другими словами, пользовательские свойства привязаны к селектору. Это может быть :root селектор или любой другой допустимый селектор.

:root { --smashing-red: #d33a2c; } @media screen and (min-width: 800px) { .smashing-text, .cats { --margin-left: 1em; }
}

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

.smashing-text, .cats { color: var(--smashing-red); margin: 0 var(--margin-horizontal); padding: calc(var(--margin-horizontal) / 2)
}

Тем не менее, они не могут использоваться в медиа запросах или селекторах, включая :nth-child().

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

Динамические или статические

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

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

$background: blue;
.blue { background: $background;
}
$background: red;
.red { background: $background;
}

результат:

.blue { background: blue;
}
.red { background: red;
}

Когда это будет показано в CSS, переменные исчезнут. Это означает, что мы могли бы читать файл .scss и определять его вывод, не зная ничего о HTML, браузере или других входах. Это не относится к пользовательским свойствам.

Препроцессоры действительно имеют своего рода «область блока», где переменные могут временно изменяться внутри селектора, функции или миксина. Это изменяет значение переменной внутри блока, но оно все еще статично. Это привязано к блоку, а не к селектору. В приведенном ниже примере переменная $background изменяется внутри блока .example. Он возвращается обратно к исходному значению вне блока, даже если мы используем тот же селектор.

$background: red;
.example { $background: blue; background: $background;
} .example { background: $background;
}

Результат:

.example { background: blue;
}
.example { background: red;
}

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

Это замечательно, потому что вы можете изменить значение пользовательского свойства внутри медиа-запроса, используя псевдоселектор, такой как hover или даже с помощью JavaScript.

a { --link-color: black;
}
a:hover,
a:focus { --link-color: tomato;
}
@media screen and (min-width: 600px) { a { --link-color: blue; }
} a { color: var(--link-color);
}

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

Глобальный или локальный

В дополнение к статическому или динамическому, переменные также могут быть глобальными или локальными. Если вы знаете JavaScript, вы знакомы с этим. Переменные могут быть применены ко всему внутри приложения, или их область действия может быть ограничена конкретными функциями или блоками кода.

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

Это то, с чем мы знакомы в CSS. Мы разработали системы проектирования, соглашения об именах и библиотеки JavaScript, чтобы помочь изолировать локальные компоненты и элементы глобального дизайна. Пользовательские свойства предоставляют новые возможности для решения этой старой проблемы.

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

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

Глобальные переменные имеют тенденцию быть статическими

Есть несколько небольших исключений, но, вообще говоря, большинство глобальных вещей в CSS также статичны.

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

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

Локальные статические переменные это нормально (иногда)

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

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

Рассмотрим классический пример кнопочного компонента с несколькими вариантами размеров.

Стратегическое руководство по использованию пользовательских свойств CSS

Мой scss может выглядеть примерно так:

$button-sml: 1em;
$button-med: 1.5em;
$button-lrg: 2em; .btn { // Visual styles
} .btn-sml { font-size: $button-sml;
} .btn-med { font-size: $button-med;
} .btn-lrg { font-size: $button-lrg;
}

Очевидно, что этот пример имел бы большее значение, если бы я использовал переменные несколько раз или получал значения margin и padding из переменных размера. Тем не менее, способность быстро прототипировать разные размеры может быть достаточной причиной.

Поскольку большинство статических переменных являются глобальными, мне нравится различать статические переменные, которые используются только внутри компонента. Для этого вы можете добавить префикс в эти переменные с именем компонента, или вы можете использовать другой префикс, такой как c-variable-name для компонента или l-variable-name для локального. Вы можете использовать любой префикс, который хотите, или вы можете префикс глобальных переменных. Независимо от того, что вы выберете, полезно дифференцировать, особенно если конвертировать существующую базу кода для использования пользовательских свойств.

Когда использовать пользовательские свойства

Если использовать статические переменные внутри компонентов это нормально, когда мы должны использовать пользовательские свойства? Преобразование существующих переменных препроцессора в пользовательские свойства обычно имеет мало смысла. В конце концов, причина пользовательских свойств совершенно другая. Пользовательские свойства имеют смысл, когда у нас есть свойства CSS, которые изменяются относительно условия в DOM — особенно динамическое условие, такое как :focus :hover , media query или JavaScript.

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

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

Примечание. Знаете ли вы, что $var является допустимым значением для пользовательского свойства? Последние версии Sass распознают это, и поэтому нам нужно интерполировать переменные, назначенные пользовательским свойствам, например: #{$var} . Это говорит Sass, что вы хотите вывести значение переменной, а не просто $var в таблице стилей. Это необходимо только для ситуаций, таких как пользовательские свойства, где имена переменных также могут быть действительными CSS.

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

$button-sml: 1em;
$button-med: 1.5em;
$button-lrg: 2em; .btn { --button-size: #{$button-sml};
} @media screen and (min-width: 600px) { .btn-med { --button-size: #{$button-med}; } .btn-lrg { --button-size: #{$button-lrg}; }
} .btn { font-size: var(--button-size);
}

Здесь я создаю одно настраиваемое свойство: —button-size. Это настраиваемое свойство сначала привязывается ко всем элементам кнопки, используя класс btn. Затем я изменяю значение —button-size выше 600px для классов btn-med и btn-lrg. Наконец, я применяю это настраиваемое свойство ко всем элементам кнопки в одном месте.

Не будьте слишком умны

Динамический характер пользовательских свойств позволяет нам создавать некоторые умные и сложные компоненты.

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

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

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

Одним из примеров, который недавно показал это для меня, было следующее:

:root { --font-scale: 1.2; --font-size-1: calc(var(--font-scale) * var(--font-size-2)); --font-size-2: calc(var(--font-scale) * var(--font-size-3)); --font-size-3: calc(var(--font-scale) * var(--font-size-4)); --font-size-4: 1rem; }

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

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

Это означает, что коэффициенты вычисляются во время выполнения, и вы можете изменить их, обновив только значение —font-scale. Например:

@media screen and (min-width: 800px) { :root { --font-scale: 1.33; }
}

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

Хотя приведенный выше пример полезен для прототипирования, в производстве, я бы предпочел увидеть что-то вроде этого:

:root { --font-size-1: 1.728rem; --font-size-2: 1.44rem; --font-size-3: 1.2em; --font-size-4: 1em;
} @media screen and (min-width: 800px) { :root { --font-size-1: 2.369rem; --font-size-2: 1.777rem; --font-size-3: 1.333rem; --font-size-4: 1rem; }
}

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

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

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

Изменение значения, а не переменной

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

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

В этом примере у нас есть два настраиваемых свойства, которые используются в примерном компоненте. Я переключаюсь с использования значения —font-size-small на —font-size-large в зависимости от размера экрана.

:root { --font-size-small: 1.2em; --font-size-large: 2em; }
.example { font-size: var(--font-size-small);
}
@media screen and (min-width: 800px) { .example { font-size: var(--font-size-large); }
}

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

.example { --example-font-size: 1.2em;
}
@media screen and (min-width: 800px) { .example { --example-font-size: 2em; }
}

Наконец, в одном месте я использую значение этого пользовательского свойства:

.example { font-size: var(--example-font-size);
}

В этом примере и других перед ним медиа-запросы использовались только для изменения значения настраиваемых свойств. Вы также можете заметить, что существует только одно место, где используется оператор var(), и обновляются обычные свойства CSS.

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

Отзывчивый дизайн с пользовательскими свойствами

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

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

Если что-то меняется, это переменная

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

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

Отделите логику от дизайна

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

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

Логическая сгиб

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

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

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

Взгляните на действительно простой пример шестисегментной сетки gridbox:

.row { --row-display: block;
}
@media screen and (min-width: 600px) { .row { --row-display: flex; }
}

Свойство —row-display настраивается для block. Выше 600 пикселей режим отображения установлен на гибкий. Ниже складки могут выглядеть так:

.row { display: var(--row-display); flex-direction: row; flex-wrap: nowrap;
}
.col-1, .col-2, .col-3,
.col-4, .col-5, .col-6 { flex-grow: 0; flex-shrink: 0;
}
.col-1 { flex-basis: 16.66%; }
.col-2 { flex-basis: 33.33%; }
.col-3 { flex-basis: 50%; }
.col-4 { flex-basis: 66.66%; }
.col-5 { flex-basis: 83.33%; }
.col-6 { flex-basis: 100%; }

Мы сразу же знаем, что —row-display — это значение, которое изменяется. Первоначально он будет block, поэтому значения flex будут игнорироваться.

Этот пример довольно прост, но если мы расширили его, включив в него столбец гибкой ширины, заполняющий оставшееся пространство, вероятно, нужно будет преобразовать flex-grow, flex-shrink и flex-basis значения в пользовательские свойства. Вы можете попробовать это или взглянуть на более подробный пример здесь.

Пользовательские свойства для темизации

Я в основном возражал против использования пользовательских свойств для глобальных динамических переменных и, надеюсь, подразумевал, что добавление пользовательских свойств в селектор :root во многих случаях считается вредным. Но каждое правило имеет исключение, а для пользовательских свойств — темизация.

Ограниченное использование глобальных пользовательских свойств может сделать их намного проще.

Как правило, темизация означает, что пользователь может настроить пользовательский интерфейс каким-то образом. Это может быть что-то вроде изменения цветов на странице профиля. Или это может быть что-то более локализованное. Например, вы можете выбрать цвет заметки в приложении Google Keep.

Стратегическое руководство по использованию пользовательских свойств CSS

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

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

Использование глобальных динамических свойств

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

:root { --THEME-COLOR: var(--user-theme-color, #d33a2c); }

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

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

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

Пример выше задает значение —THEME-COLOR —user-theme-color в значение —user-theme-color если оно существует. Если —user-theme-color не установлен, будет использоваться значение #d33a2c. Таким образом, нам не нужно предоставлять —THEME-COLOR каждый раз, когда мы используем — —THEME-COLOR.

В следующем примере вы можете ожидать, что фон будет установлен на green. Однако значение —user-theme-color не было установлено в корневом элементе, поэтому значение —THEME-COLOR не изменилось.

:root { --THEME-COLOR: var(--user-theme-color, #d33a2c); }
body { --user-theme-color: green; background: var(--THEME-COLOR);
}

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

Если мы хотим выставить определенные свойства для наследования, мы можем заменить селектор :root на *:

* { --THEME-COLOR: var(--user-theme-color, #d33a2c); }
body { --user-theme-color: green; background: var(--THEME-COLOR);
} 

Теперь значение —THEME-COLOR пересчитывается для каждого элемента, и поэтому может использоваться локальное значение —user-theme-color. Другими словами, цвет фона в этом примере будет green.

Обновление пользовательских свойств с помощью JavaScript

Если вы хотите установить пользовательские свойства с использованием JavaScript, существует довольно простой API, и он выглядит так:

const elm = document.documentElement;
elm.style.setProperty('--USER-THEME-COLOR', 'tomato');

Здесь я устанавливаю значение —USER-THEME-COLOR в элементе документа, или, другими словами, :root элемент, где он будет наследоваться всеми элементами.

Это не новый API; это тот же метод JavaScript для обновления стилей на элементе. Это встроенные стили, поэтому они будут иметь более высокую специфичность, чем обычный CSS.

Это означает, что легко применять локальные настройки:

.note { --note-color: #eaeaea;
}
.note { background: var(--note-color);
}

Здесь я установил значение по умолчанию для —note-color и scope this для компонента .note . Я сохраняю объявление переменной отдельно от объявления свойства, даже в этом простом примере.

const elm = document.querySelector('#note-uid');
elm.style.setProperty('--note-color', 'yellow');

Затем я нацеливаю конкретный экземпляр элемента .note и изменяю значение —note-color для этого элемента. Теперь это будет иметь более высокую специфичность, чем значение по умолчанию.

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

Манипулирование цветом с пользовательскими свойствами

Помимо шестнадцатеричных значений и названных цветов, CSS имеет такие цветовые функции, как rgb() и hsl(). Это позволяет нам указывать отдельные компоненты цвета, такие как оттенок или легкость. Пользовательские свойства могут использоваться в сочетании с функциями цвета.

:root { --hue: 25;
}
body { background: hsl(var(--hue), 80%, 50%);
}

Это полезно, но некоторые из наиболее широко используемых функций препроцессоров — это расширенные функции цвета, которые позволяют нам манипулировать цветом, используя такие функции, как lighten, darken или desaturate:

darken($base-color, 10%);
lighten($base-color, 10%);
desaturate($base-color, 20%);

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

Мы видели, что пользовательские свойства могут использоваться внутри существующих цветовых функций, таких как rgb() и hsl() но они также могут использоваться в calc(). Это означает, что мы можем преобразовать действительное число в процент, умножив его, например, calc(50 * 1%) = 50%.

:root { --lightness: 50;
}
body { background: hsl(25, 80%, calc(var(--lightness) * 1%));
}

Причина, по которой мы хотим сохранить значение легкости как действительное число, заключается в том, что мы можем манипулировать им с помощью calc прежде чем преобразовать его в процент. Например, если я хочу затемнить цвет на 20%, я могу увеличить его легкость на 0.8 . Мы можем сделать это немного легче для чтения, разделив вычисление легкости на локальное пользовательское свойство:

:root { --lightness: 50;
}
body { --lightness: calc(var(--lightness * 0.8)); background: hsl(25, 80%, calc(var(--lightness) * 1%));
}

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

Упрощение темизации

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

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

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

Использование пользовательских свойств сегодня

Даже если вы поддерживаете IE10 и 11, вы можете начать использовать пользовательские свойства сегодня. Большинство примеров в этой статье связаны с тем, как мы пишем и структурируем CSS. Преимущества существенны с точки зрения ремонтопригодности, однако большинство примеров только уменьшают то, что в противном случае можно было бы сделать с помощью более сложного кода.

Я использую инструмент, называемый postcss-css-variables, для преобразования большинства функций пользовательских свойств в статическое представление одного и того же кода. Другие подобные инструменты игнорируют пользовательские свойства внутри медиа-запросов или сложных селекторов, обрабатывая настраиваемые свойства, подобные переменным препроцессора.

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

Загрузка правильной таблицы стилей

Существует много способов использования postCSS. Я использую процесс gulp для компиляции отдельных таблиц стилей для более старых и старых браузеров. Упрощенная версия моей задачи gulp выглядит так:

import gulp from "gulp";
import sass from "gulp-sass";
import postcss from "gulp-postcss";
import rename from "gulp-rename";
import cssvariables from "postcss-css-variables";
import autoprefixer from "autoprefixer";
import cssnano from "cssnano"; gulp.task("css-no-vars", () => gulp .src("./src/css/*.scss") .pipe(sass().on("error", sass.logError)) .pipe(postcss([cssvariables(), cssnano()])) .pipe(rename({ extname: ".no-vars.css" })) .pipe(gulp.dest("./dist/css"))
); gulp.task("css", () => gulp .src("./src/css/*.scss") .pipe(sass().on("error", sass.logError)) .pipe(postcss([cssnano()])) .pipe(rename({ extname: ".css" })) .pipe(gulp.dest("./dist/css"))
);

В результате получается два файла CSS: обычный с пользовательскими свойствами (styles.css) и один для старых браузеров (styles.no-vars.css). Я хочу, чтобы IE10 и 11 были styles.no-vars.css и другие браузеры, чтобы получить обычный файл CSS.

Обычно я выступал за использование запросов функций, но IE11 не поддерживает запросы к функциям, и мы использовали так называемые пользовательские свойства, так что в этом случае смысл использования другой таблицы стилей имеет смысл.

Интеллектуальная работа с другой таблицей стилей и предотвращение вспышки неравномерного содержимого — непростая задача. Если вам не нужны динамические функции пользовательских свойств, вы можете рассмотреть возможность использования всех styles.no-vars.css браузера.no styles.no-vars.css и использования пользовательских свойств просто как инструмент разработки.

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

<head> <style> /* inlined critical CSS */ </style> <script> loadCSS('non-critical.css'); </script>
</head>

Мы можем расширить это, чтобы загрузить либо styles.css, либо styles.no-vars.css в зависимости от того, поддерживает ли браузер пользовательские свойства. Мы можем обнаружить поддержку следующим образом:

if ( window.CSS && CSS.supports('color', 'var(--test)') ) { loadCSS('styles.css');
} else { loadCSS('styles.no-vars.css');
} 

Вывод

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

Это сводится к пониманию разницы между динамическими и статическими переменными в CSS, а также несколькими простыми правилами:

Отдельная логика от проектирования;

Если свойство CSS изменяется, рассмотрите использование пользовательского свойства;

Измените значение настраиваемых свойств, а не какое настраиваемое свойство;

Глобальные переменные обычно статичны.

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

Автор: Michael Riethmuller

Источник: https://www.smashingmagazine.com/

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