
Всем здравствуйте, дорогие читатели!
Меня зовут Рамазан, я Frontend-разработчик, энтузиаст и я люблю исследовать грани возможностей современных Web-технологий.
В этой статье мы поговорим о том, нормально ли применять в рамках Atomic CSS подхода рукописный CSS. Бытуют разные мнения по этому поводу. Кто-то считает, что в атомарном подходе нужно использовать только утилиты, а кто-то допускает и произвольные сочетания рукописных CSS классов с ними. Я постараюсь разобраться в этом, аргументируя свою позицию.
В качестве примера Atomic CSS фреймворка я буду использовать привычный для меня mlut - в нём есть все необходимые для разработчика возможности. Так что с ним мы сможем увидеть все аспекты взаимодействия утилит и рукописного CSS в атомарном подходе.
Чтобы полностью понять эту статью, вам нужно знать только HTML и CSS. Конечно, вам будет легче её читать, если у вас уже есть какое-то представление об Atomic CSS. Но в любом случае все примеры, написанные на mlut, будут объяснены также с помощью CSS, так что вам необязательно знать этот фреймворк.
Ну что ж, мне не ��ерпится начать!
Страшная тайна Atomic CSS
до 10% всех стилей в проекте вам придется написать руками, даже при использовании атомарного подхода.
Нередко возникают случаи, в которых использовать утилиты просто неудобно: они очень сильно загромождают разметку, делают её нечитаемой и неподдерживаемой. Далее мы разберёмся в каких случаях всё же удобнее применять рукописный CSS.
Задание палитры сайта и кастомных переменных
Палитра сайта может оказаться довольно богатой, и хорошей практикой является задание её с помощью кастомных CSS свойств. Кастомных свойств вообще может быть очень много: различные размеры, та же цветовая палитра, стандартные отступы и т.д. Если задать все эти свойства с помощью утилит, то разметка станет просто нечитаемой. А если у сайта есть разные темы, то пиши пропало. Давайте покажу пример, как выглядело задание палитры, когда я решил определить всё одними утилитами:
<article class="...
@:pfcs-d_-FastLeftBgc-rgba(30,58,138,0.4) @:pfcs-d_-FastRightBgc-rgba(30,64,175,0.6)
@:pfcs-d_-FastLeftBgc-rgba(25,60,184,0.5)_h @:pfcs-d_-FastRightBgc-rgba(20,71,230,0.7)_h
@:pfcs-d_-FastHeadC-rgb(190,219,255) @:pfcs-d_-FastTextC-rgb(142,197,255)
@:pfcs-l_-FastLeftBgc-rgba(239,246,255,1) @:pfcs-l_-FastRightBgc-rgba(219,234,254,0.5)
... и ещё 16 вот таких строчек с разными цветами ...
"> ... </article>
Можете прочитать это и разобраться? Вот и я уже не могу так сходу это сделать, хотя это я писал. Поэтому, чтобы облегчить жизнь и себе, и другим, лучше задать палитру и некоторые важные глобальные переменные в отдельном CSS или в input-файле, в котором находится конфиг mlut. Конечно, нет необходимости переносить все кастомные свойства в этот файл - тогда перегружен будет он. CSS переменные, которые используются только в рамках какого-либо компонента или виджета, лучше определять в соответствующей разметке посредством утилит.
Анимации
Если вы используете какие-то сложные анимации в своём проекте, то их описание вам тоже придётся задавать с помощью рукописного CSS. Конечно, есть возможности создавать базовые анимации, как в других фреймворках - там может оказаться несколько утилит для этого. Но если вы хотите создать что-то действительно сложное и интересное, то вам понадобится рукописный CSS, где бы вы его ни писали: в конфиге Tailwind или input-файле mlut.
В mlut я использовал следующую технику работы:
описывал в
input-файле шаги анимации с помощью@keyframes;затем уже в разметке пользовался утилитами
An*которые отвечают CSS свойствамanimation-*.
Вот пример анимации:
<p class="And4s Ann-changeСolor">Animated paragraph</p>
В input-файл мы впишем следующий код:
@keyframes changeColor {
0%,
100% {
color:red;
}
33% {
color: blue;
}
66% {
color: black;
}
}
А такими будут сгенерированные из разметки стили:
.And4s {
animation-duration: 4s;
}
.Ann-change-color {
animation-name: changeColor;
}
Но у нас же в синтаксисе есть множество at-состояний, так почему же просто не включить ещё и @keyframes? Да, это было бы классно, особенно, чтобы быстро делать не очень сложные анимации. Но даже если такая функциональность появится, сложные анимации всё равно стоит писать в input-файле или в отдельном CSS, чтобы не перегружать разметку.
Перенастройка хелперов
В mlut есть такие в��троенные сущности, которые называются хелперами. По сути, это устоявшиеся утилитарные CSS классы, которые нужны почти в каждом проекте. Они опциональны для подключения, но в дефолтном конфиге они есть. btn - это пример такого хелпера, который позволяет задать базовые свойства для кнопки. Данный класс содержит вот такие стили:
.btn {
display: inline-block;
margin: 0;
padding: 0;
line-height: inherit;
text-align: center;
text-decoration: none;
vertical-align: middle;
background: none;
border: 0;
cursor: pointer;
}
Если вы хотите что-то поменять в этом хелпере, то можете переписать его в input-файле с помощью обычного рукописного CSS. Тогда стили этого хелпера просто дополнятся (или перепишутся) в соответствии с правилами каскада CSS.
Стили от JS библиотек
Есть целое множество библиотек, которые помогают стилизовать разметку средствами JS. Например, библиотека highlight.js для подсветки кода. Чтобы она раскрасила нужным образом синтаксис, необходимо на блок кода повесить нужный класс. И если мы хотим как-то ещё модифицировать эти блоки кода, мы можем в том же input-файле определить дополнительные свойства для классов этой библиотеки, например для .hljs или .language-html. Таким образом, например, можно убрать скроллбары у блоков кода.
Можно ли как-то связать утилиты с рукописным CSS?
Да! Обсудим мы это на примере возможностей фреймворка mlut. Говоря простыми словами, мы можем использовать значения утилит в рукописном CSS. В mlut за это отвечают миксин apply и функция uv - о них мы и будем говорить дальше.
Миксин apply
В стандартном сценарии его использоавния в него можно вставить набор желаемых утилит, а mlut уже впишет в итоговый CSS файл соответствующие стили. Примерно так это выглядит на практике:
// input.scss
@use 'mlut' as ml with (
// mlut config
);
.box {
@include ml.apply("-Sz50vh Bgc-red M-a");
}
Тогда в итоговом CSS файле окажутся следующие стили:
.box {
width: 50vh;
height: 50vh;
}
.box {
background-color: red;
}
.box {
margin: auto;
}
Как видно из примера, если мы примени apply внутри селектора, то в итоговом CSS сгенерированные стили будут относиться к этому селектору. Если же apply работает вне какого-либо селектора, то будут просто сгенерированы переданные утилиты.
Ещё можно заметить, что для каждой применённой в apply утилиты mlut создаёт отдельное правильно с нашим селектором .box. Это нормально. Обычно в продакшн идут уже минифицированные стили, и тут уже минификатор автоматически схлопывает все эти правила в одно общее. У CLI mlut есть встроенный минификатор, и включить его можно в конфиге, либо в аргументах запуска.
Для чего нужен apply
Главная задача, которую выполняет этот миксин - это согласование рукописных CSS-стилей и утилит в разметке. Например, мы задали отступы у некоторых блоков в разметке, а сейчас хотим согласовать их с отступами тех элементов, стилизация которых задаётся рукописным CSS из-за особенностей используемой библиотеки. У mlut есть некоторые встроенные единицы измерения, как например u и gg, которые часто используются для удобства разработки. Но их неудобно вручную однозначно конвертировать в стандартные единицы измерения.
Кроме того, у mlut есть стандартные брейкпойнты для медиавыражений. Например md, который соответствует размеру 768px и может быть настроен по желанию. И, если мы хотим в рукописном CSS быстро писать медиа-выражения, не заглядывая постоянно в определения брейкпойнтов, то миксин apply здесь будет очень уместен. К слову, у mlut есть специальный миксин bp, который также позволяет более удобно создавать медиа-выражения с брейкпойнтами, но apply в этом смысле более универсален - с ним можно задавать любые at-правила.
Вот пример использования миксина apply в данном кейсе:
.input-caption {
@include mlut.apply("@s:acnc-a@:pfcs_C-red");
}
CSS:
@supports (accent-color: auto) {
@media (prefers-color-scheme: dark) {
.input-caption{
color: red;
}
}
}
Можно ли переиспользовать стили с помощью apply?
Теоретически - да, такое безусловно возможно, но в реальности это плохое решение. Сейчас любой серьёзный проект в том или ином виде использует генерацию разметки. Более подходящим решением здесь будет использовать так называемые алиасы. В этой технике мы создаем глобальный словарь, ключи в котором - алиасы, а значения - повторяющиеся наборы утилит:
module.exports = {
externalLink: "Txd-n Fns6u C-$accent900 C-$accent850_h",
navLink: "Txd-n Fns5u C-$accent600 C-$accent550_h"
}
А затем используем этот словарь в разметке:
<a href="#" class="<%= it.css.navLink %>"> Home </a>
В чём плюсы такого подхода:
Алиасы существуют только в build-time — реиспользуются имеющиеся утилиты;
Их меньше чем обычных классов — (почти) нет проблем с неймингом;
Алиасы можно редактировать in-place через замену подстрок.
Поэтому для переиспользуемости стилей хорошей практикой является применение алиасов. Но эта тема довольно обширна и достойна более подробного рассмотрения в другой статье.
Функция uv
Хотя эта функция несколько проще, чем apply, она тоже очень важна. То, что она делает, можно увидеть в значении её названия: uv- "utility value", то есть "значение утилиты". В аргумент этой функции передаётся утилита, из которой выделяется значение, а оно потом должно быть присвоено некоторому свойству в рукописном CSS классе. Это выглядит вот так:
.spoiler {
margin: ml.uv('M3gg;a;6u');
}
CSS:
.spoiler {
margin: calc(var(--ml-gg) * 3) auto 1.5rem;
}
Область применения у этой функции чуть уже, чем у миксина apply. Её следует использовать только для согласования значений рукописных CSS свойств и утилит в разметке, особенно если значения сложные.
Заключение
Вот моя статья и подошла к концу. В итоге мы пришли к выводу, что использовать рукописный CSS в атомарном подходе нормально в случаях, когда мы:
настраиваем палитру и кастомные свойства;
создаём сложные анимации и декоративные эффекты;
настраиваем стили разных библиотек и фреймворков.
Также мы выяснили, что можем использовать утилиты даже в рукописных стилях, чтобы сделать значения свойств согласованными во всей кодовой базе. Надеюсь, вам было интересно и полезно!
В будущем я планирую написать ещё несколько статей со своими размышениями на тему Atomic CSS и Frontend разработки вообще. Поэтому оставайтесь на связи!
Успехов вам в увлекательном пути Frontend-разработки!
