Русский
Русский
English
Статистика
Реклама

Перевод О конфликтах Sass и сравнительно новых возможностей CSS

Сравнительно недавно в CSS появилось много интересных возможностей, таких, как CSS-переменные и новые функции. Хотя всё это и может сильно упростить жизнь веб-дизайнерам, эти возможности способны неожиданными способами взаимодействовать с CSS-препроцессорами вроде Sass.



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

Ошибки


Если вы экспериментировали с CSS-функциями min() и max(), то, используя разные единицы измерения, могли столкнуться с сообщениями об ошибках, наподобие такой: Incompatible units: vh and em.


Сообщение об ошибке, возникающее при использовании различных единиц измерения в функциях min() и max()

Это сообщение выводится из-за того, что в Sass есть собственная функция min(). CSS-функция min(), в результате, игнорируется. Кроме того, Sass не может выполнять вычисления, используя единицы измерения, между которыми нет чётко зафиксированной взаимосвязи.

Например, взаимосвязь единиц измерения cm и in определена чётко, поэтому Sass может найти результат функции min(20in, 50cm) и не выдаёт ошибку в том случае, если чем-то подобным воспользоваться в коде.

То же самое происходит и с другими единицами измерения. Например, все угловые единицы измерения взаимосвязаны: 1turn, 1rad или 1grad всегда приводятся к одним и тем же значениям, выраженным в единицах измерения deg. То же самое справедливо, например, и в случае, когда 1s всегда равно 1000ms. 1kHz всегда равно 1000Hz, 1dppx всегда равно 96dpi, 1in всегда равно 96px. Именно поэтому Sass может преобразовывать друг в друга значения, выраженные в этих единицах измерения, и смешивать их в вычислениях, используемых в различных функциях, вроде собственной функции min().

Но всё идёт не так, когда между единицами измерения нет чёткой взаимосвязи (как, например, выше, у em и vh).

И такое происходит не только при использовании значений, выраженных в разных единицах измерения. Попытка использования функции calc() внутри min() тоже приводит к появлению ошибки. Если попробовать, в min(), поместить что-то вроде calc(20em + 7px), то выводится такая ошибка: calc(20em + 7px) is not a number for min.


Сообщение об ошибке, возникающее при попытке использования calc() внутри min()

Ещё одна проблема появляется в ситуации, когда пытаются использовать CSS-переменную или результат работы математических CSS-функций (вроде calc(), min(), max()) в CSS-фильтрах наподобие invert().

Вот сообщение об ошибке, которую можно увидеть в подобных обстоятельствах: $color: 'var(--p, 0.85) is not a color for invert


Использование var() в filter: invert() приводит к ошибке

То же самое происходит и с grayscale(): $color: calc(.2 + var(--d, .3)) is not a color for grayscale.


Использование calc() в filter: grayscale() приводит к ошибке

Конструкция filter: opacity() тоже подвержена подобным проблемам: $color: var(--p, 0.8) is not a color for opacity.


Использование var() в filter: opacity() приводит к ошибке

Но другие функции, используемые в filter, включая sepia(), blur(), drop-shadow(), brightness(), contrast() и hue-rotate(), работают с CSS-переменными совершенно нормально!

Оказалось, что причина этой проблемы похожа на ту, которой подвержены функции min() и max(). В Sass нет встроенных функций sepia(), blur(), drop-shadow(), brightness(), contrast(), hue-rotate(). Но там есть собственные функции grayscale(), invert() и opacity(). Первым аргументом этих функций является значение $color. Ошибка появляется из-за того, что при использовании проблемных конструкций такого аргумента Sass не находит.

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


Ошибка при использовании var() в color: hsl()

С другой стороны, без использования Sass конструкция color: hsl(9, var(--sl, 95%, 65%)) это совершенно правильная и совершенно нормально работающая CSS-конструкция.

То же самое справедливо и для таких функций, как rgb() и rgba():


Ошибка при использовании var() в color: rgba()

Кроме того, если импортировать Compass и попытаться использовать CSS-переменную внутри linear-gradient() или radial-gradient(), можно столкнуться с ещё одной ошибкой. Но, в то же время, в conic-gradient() переменными можно пользоваться без всяких проблем (конечно, если браузер эту функцию поддерживает).


Ошибка при использовании var() в background: linear-gradient()

Причина проблемы кроется в том, что в Compass есть собственные функции linear-gradient() и radial-gradient(), а вот функции conic-gradient() там никогда не было.

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

Вот засада!

Решение проблемы


Эту проблему можно решить, если вспомнить о том, что Sass чувствителен к регистру, а CSS нет.

Это означает, что можно написать что-то вроде Min(20em, 50vh) и Sass не распознает в этой конструкции свою собственную функцию min(). Никаких ошибок при этом выдано не будет. Эта конструкция будет представлять собой правильно сформированный CSS-код, который работает именно так, как ожидается. Аналогично, избавиться от проблем с другими функциями можно, нестандартным образом записывая их имена: HSL(), HSLA(), RGB(), RGBA(), Invert().

Если говорить о градиентах, то тут я обычно использую такую форму: linear-Gradient() и radial-Gradient(). Делаю я это из-за того, что такая запись близка к именам, используемым в SVG, но в данной ситуации сработает и любое другое подобное имя, включающее в себя хотя бы одну заглавную букву.

Зачем все эти сложности?


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

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


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

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

Во-вторых поддержка переменных всегда была довольно-таки незначительной причиной среди тех причин, по которым я использую Sass. Когда я начала пользоваться Sass, а было это во второй половине 2012 года, я сделала это, в основном, ради циклов. Ради той возможности, которой до сих пор нет в CSS. Хотя я перенесла некоторую логику, связанную с циклами, в препроцессор HTML (так как это уменьшает объём сгенерированного кода и позволяет избежать необходимости модифицировать и HTML, и CSS), я всё ещё использую циклы Sass во множестве случаев. Среди них генерирование списков значений, создание значений для настройки градиентов, создание списков точек при работе с функцией polygon(), создание списков трансформаций и так далее.

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

- let n = 12;while n--.item

Затем я бы создала переменную $n в Sass (и в этой переменной должно было бы быть то же значение, что и в HTML) и запустила бы с её использованием цикл, в котором сгенерировала бы трансформации, используемые для позиционирования каждого из элементов:

$n: 12;$ba: 360deg/$n;$d: 2em;.item {position: absolute;top: 50%; left: 50%;margin: -.5*$d;width: $d; height: $d;/* аккуратно оформим стили */@for $i from 0 to $n {&:nth-child(#{$i + 1}) {transform: rotate($i*$ba) translate(2*$d) rotate(-$i*$ba);&::before { content: '#{$i}' }}}}

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


CSS-код, сгенерированный на основе вышеприведённого кода

Теперь я перешла к другому подходу. А именно, с помощью Pug я генерирую индексы в виде пользовательских свойств, а затем использую их при объявлении transform.

Вот код, который планируется обработать с помощью Pug:

- let n = 12;body(style=`--n: ${n}`)- for(let i = 0; i < n; i++).item(style=`--i: ${i}`)

Вот Sass-код:

$d: 2em;.item {position: absolute;top: 50%;left: 50%;margin: -.5*$d;width: $d;height: $d;/* аккуратно оформим стили */--az: calc(var(--i)*1turn/var(--n));transform: rotate(var(--az)) translate(2*$d) rotate(calc(-1*var(--az)));counter-reset: i var(--i);&::before { content: counter(i) }}

Здесь можно поэкспериментировать с этим кодом.


Элементы, сгенерированные и стилизованные с использованием циклов

Применение такого подхода значительно уменьшило объём CSS-кода, сгенерированного автоматически.


CSS-код, сгенерированный на основе вышеприведённого кода

Но если нужно создать что-то вроде радуги, без Sass-циклов не обойтись.

@function get-rainbow($n: 12, $sat: 90%, $lum: 65%) {$unit: 360/$n;$s-list: ();@for $i from 0 through $n {$s-list: $s-list, hsl($i*$unit, $sat, $lum)}@return $s-list}html { background: linear-gradient(90deg, get-rainbow()) }

Вот рабочий вариант этого примера.


Многоцветный фон

Конечно, это можно сгенерировать и средствами переменных Pug, но у такого подхода нет преимуществ перед динамической природой CSS-переменных, и он не позволит уменьшить объём кода, передаваемого браузеру. В результате мне нет смысла отказываться от того, к чему я привыкла.

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

И, конечно, я могу написать в Sass собственные функции такого рода. Я так делала в самом начале, до того, как в Compass появилась поддержка обратных тригонометрических функций. Мне эти функции очень нужны, поэтому я написала их сама, пользуясь рядами Тейлора. Но в наши дни эти функции есть в Compass. Они лучше и производительнее тех, которые я писала сама.

Математические функции очень важны для меня по той причине, что я программист, а не художник. Значения в моём CSS-коде обычно формируются на основе математических вычислений. Это не какие-то магические числа, или что-то такое, что играет чисто эстетическую роль. В качестве примера их использования можно привести генерирование списка правильных или квазиправильных многоугольников для clip-path. Подобное применяется, например, при создании чего-то вроде аватаров или стикеров, форма которых отличается от прямоугольной.

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


Фигура с тремя вершинами

Вот как выглядит соответствующий Sass-код:

@mixin reg-poly($n: 3) {$ba: 360deg/$n; // базовый угол$p: (); // список координат точек, изначально пустой@for $i from 0 to $n {$ca: $i*$ba; // текущий угол$x: 50%*(1 + cos($ca)); // координата x текущей точки$y: 50%*(1 + sin($ca)); // координата y текущей точки$p: $p, $x $y // добавление координат текущей точки к списку координат}clip-path: polygon($p) // установка списка точек в качестве значения clip-path}

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

Немного более продвинутая версия этого примера может включать в себя вращение многоугольника, реализованное путём добавления одного и того же смещения ($oa) к углу, соответствующему каждой вершине. Это можно видеть в следующем примере. Здесь генерируются звёзды, которые устроены похожим образом, но всегда имеют чётное количество вершин. При этом каждая вершина с нечётным индексом располагается на окружности, радиус которой меньше основной окружности ($f*50%).


Звезда

Можно наделать и таких интересных звёздочек.


Звёзды

Можно создавать стикеры с границами (border), созданными с использованием необычных шаблонов. В данном примере стикер создаётся из единственного HTML-элемента, а шаблон, используемый для настройки border, создаётся с применением clip-path, циклов и математических вычислений в Sass. На самом деле, вычислений тут довольно много.


Стикеры со сложными границами

Ещё один пример представлен созданием фона для карточек. Здесь, в цикле, с помощью оператора получения остатка от деления и экспоненциальных функций, создаётся фон с имитацией эффекта дизеринга.


Эффект дизеринга

Здесь тоже интенсивно используются CSS-переменные

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

К сожалению, в CSS, каким бы заманчивым это ни выглядело, нельзя поместить что-то вроде следующего кода:

input::-webkit-slider-runnable-track,input::-moz-range-track,input::-ms-track { /* общие стили */ }

Работать это не будет. Весь этот набор правил игнорируется в том случае, если хотя бы один селектор не будет распознан. И, так как ни один браузер не знает о существовании всех трёх селекторов из этого примера, эти стили не будут применены ни в одном браузере.

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

input::-webkit-slider-runnable-track { /* общие стили */ }input::-moz-range-track { /* общие стили */ }input::-ms-track { /* общие стили */ }

Но это может привести к тому, что одни и те же стили появятся в коде три раза. А если нужно, скажем, поменять у track свойство background, это будет значить, что придётся редактировать стили в ::-webkit-slider-runnable-track, в ::-moz-range-track и в ::-ms-track.

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

@mixin track() { /* общие стили */ }input {&::-webkit-slider-runnable-track { @include track }&::-moz-range-track { @include track }&::-ms-track { @include track }}

Итоги


Главный вывод, который я могу сделать, получился таким: Sass в это то, без чего нам пока не обойтись.

Пользуетесь ли вы Sass?

Источник: habr.com
К списку статей
Опубликовано: 22.07.2020 16:08:14
0

Сейчас читают

Комментариев (0)
Имя
Электронная почта

Блог компании ruvds.com

Css

Разработка веб-сайтов

Разработка

Категории

Последние комментарии

© 2006-2020, personeltest.ru