
Очень нужный дисклеймер: Теперь в CSS мы (вроде как) можем использовать функции! Я знаю, это не самое приятное чувство - закончить читать о новой фиче только для того, чтобы автор сказал: "И, надеюсь, мы увидим её через пару лет". К счастью, прямо сейчас вы можете попробовать (неполную) версию функций CSS в Chrome Canary с экспериментальным флагом, хотя кто знает, когда мы сможем использовать на рабочем сайте.
Аргументы, значения по умолчанию и возвращаемые значения!
Я пил кофе, когда прочитал новость о том, что Chrome создаёт прототипы функций в CSS, и… я не выронил чашку из рук. Я был взволнован, но думал, что "функции" в CSS будут похожи на миксы в Sass - ну, знаете, шаблоны для создания повторно используемых шаблонов. Это, конечно, круто, но на самом деле это просто синтаксический сахар для написания меньшего количества кода CSS.
Но я внимательнее присмотрелся к примеру, и тут уж чашка чуть не выпала у меня из рук.
Аргументы?! Возвращаемые значения?! Ради этого стоит на время отложить свои дела! Мне нужно было узнать обо всем этом больше, и, к счастью, спецификация написана очень понятно. Что ещё более удивительно, вы можете использовать функции уже прямо сейчас в браузере Chrome Canary! Итак, после изучения спецификации и многочисленных экспериментов вот мои основные выводы о функциях CSS.
Что же такое функции в CSS?
Мне нравится это определение из спецификации:
Пользовательские функции предоставляют авторам те же возможности, что и пользовательские свойства, но с параметрами
Они используются в тех же местах, где вы бы использовали пользовательские свойства, но функции возвращают разные значения в зависимости от переданного аргумента. Синтаксис самой простой функции - это правило @function
, за которым следует имя функции в виде <штриховой-идентификатор> + ()
:
@function --dashed-border() {
/* ... */
}
Функции без аргументов похожи на пользовательское свойство, так что… Чтобы сделать их функциональными, мы можем передавать аргументы внутри скобок, и также в виде <штрихового-идентификатора>
:
@function --dashed-border(--color) {
/* ... */
}
Мы можем использовать дескриптор result
, чтобы вернуть некий результат, основанный на переданном аргументе:
@function --dashed-border(--color) {
result: 2px dashed var(--color);
}
div {
border: --dashed-border(blue); /* 2px dashed blue */
}
Мы можем даже использовать значения по умолчанию! Просто напишите после аргумента двоеточие (:), а затем его значение по умолчанию.
@function --dashed-border(--color: red) {
result: 2px dashed var(--color);
}
div {
border: --dashed-border(); /* 2px dashed red */
}
У функций может быть проверка типов
Функции могут проверять тип аргументов и возвращаемых значений, что будет полезно, когда мы захотим интерполировать значение, как мы делаем с переменными, созданными с помощью @property
, а также когда у нас есть встроенные условные конструкции для выполнения различных вычислений в зависимости от типа аргумента.
Чтобы добавить типы аргументов, мы передаём синтаксический компонент. Это тип, заключённый в угловые скобки, например, цвет - <color>
, длина - <length>
. Существуют также синтаксические множители, такие как плюс (+), для приёма списка этого типа, разделённого пробелами.
@function --custom-spacing(--a ) { /* ... */ } /* e.g. 10px */
@function --custom-background(--b ) { /* ... */ } /* e.g. hsl(50%, 30% 50%) */
@function --custom-margin(--c +) { /* ... */ } /* e.g. 10px 2rem 20px */
Если вместо этого мы хотим определить тип возвращаемого значения, мы можем написать ключевое слово returns
с последующим синтаксическим компонентом:
@function --progression(--current, --total) returns {
result: calc(var(--current) / var(--total) * 100%);
}
Единственное исключение для типов: если мы хотим принять более одного типа с помощью синтаксического комбинатора (|), нам нужно заключить типы в функцию-оболочку type()
:
@function --wideness(--d type( | )) { /* ... */ }
У функций могут быть аргументы-списки
Хотя в Canary это, похоже, в настоящее время не работает, в будущем мы сможем передавать в функции CSS в качестве аргументов списки, заключая их в фигурные скобки. Так, в следующем примере из спецификации передается список значений - {1px, 7px, 2px} - и возвращается сумма дополнительного аргумента с максимальным значением из этого списка.
@function --max-plus-x(--list, --x) {
result: calc(max(var(--list)) + var(--x));
}
div {
width: --max-plus-x({ 1px, 7px, 2px }, 3px); /* 10px */
}
Интересно, можно ли будет тогда выбирать конкретный элемент из списка? А также определять, какой длины должен быть список? Допустим, мы хотим принимать только списки, содержащие четыре элемента, затем выбирать каждый по отдельности для выполнения некоторого вычисления и возвращать его. Здесь много вопросов!
Досрочный возврат значения невозможен
Все верно, досрочный возврат значения невозможен. Это не то, что определено в спецификации, но не было опробовано, а то, что просто не будет разрешено. Таким образом, если у нас есть два варианта возвращаемого значения, один из которых заключён в правило @media
или @supports
, а другой находится вне этого правила в конце функции, то всегда будет возвращаться последний:
@function --suitable-font-size() {
@media (width > 1000px) {
result: 20px;
}
result: 16px; /* Всегда возвращается 16px */
}
Мы должны изменить порядок возвращаемых значений, поставив условный result
последним. Это не имеет большого смысла в других языках программирования, где функция завершается после возврата чего-либо, но здесь есть своя причина, так как C в CSS расшифровывается как Cascade (Каскад): этот порядок позволяет условному результату переопределять последний результат, что очень по CSS-шному:
@function --suitable-font-size() {
result: 16px;
@media (width > 1000px) {
result: 20px;
}
}
Чего еще не хватает?
Но во всем этом есть и своя ложка дегтя.
Согласно заметке Chrome о функциях CSS, мы находимся еще на очень ранней стадии, поскольку не можем:
- ...использовать локальные переменные. Хотя я их использовал, и они похоже работали.
- ...использовать рекурсивные вызовы функций (они все ломают!),
- ...перечислять аргументы,
- ...обновлять функции и проводить соответствующие изменения стилей,
- ...использовать правило
@function
в каскадных слоях или в Объектной модели CSS (CSSOM), - ...использовать "функции скобок Айверсона … поэтому любые запросы
@media
или подобные им нужно делать при помощи вспомогательных пользовательских свойств (в :root или похожих областях)."
Прочитав, что такое скобки Айверсона, я понял, что в настоящее время мы не можем использовать возвращаемое значение за правилами @media
или @support
. Например, следующий фрагмент кода из спецификации не должен работать:
@function --suitable-font-size() {
result: 16px;
@media (width > 1000px) {
result: 20px;
}
}
Хотя, судя по результатам тестирования, теперь это поддерживается. Тем не менее, мы можем использовать промежуточное пользовательское свойство и вернуть его в конце:
@function --suitable-font-size() {
--size: 16px;
@media (width > 600px) {
--size: 20px;
}
result: var(--size);
}
А как насчет миксинов? Скоро и они будут здесь. Как говорит спецификация:
На данный момент эта спецификация определяет только пользовательские функции, которые работают на уровне значений CSS. Ожидается, что позже будут определены "миксины" - функции, которые работают на уровне стилевых правил.
Ну, что ж. Будем ждать и следить за процессом.