Изоляция CSS-стилей
27-11-2018Время чтения ~ 4 мин.CSS, HTML, LESS, SASS 14396
Интересное видео опубликовал Вадим Макеев «Прототип изоляции стилей для Shower на веб-компонентах», где затрагивается вопрос изоляции css-стилей при верстке. Само видео посвящено одной из попыток реализации, но сама по себе тема (изоляции) мне кажется интересна, поскольку с ней сталкиваются практически каждый верстальщик.
В чём суть проблемы? CSS-стили имеют глобальную обрасть видимости. Не важно где объявлен стиль, он будет применён сразу ко всей html-странице. Пока html-код небольшой, особых проблем не возникает, поскольку элементарно решается за счёт именования css-классов. Но, как только html становится побольше, то именование классов должно быть более осмысленным, иначе разобраться в коде будет проблематично.
Но это ещё не всё. Существуют блоки, которые довольно сложны по своей html-структуре. Например шапка сайта может содержать в себе flex-сетку со множеством вложенных функциональных блоков (меню, логотипы, ссылки, иконки и т.д.). Скорее всего такой блок будет оформлен в виде отдельного html/php-файла, который просто подключается на странице. Но, если html-разметку мы можем разделить по файлам и верстать индивидуально, то с css-стилями такой «финт не прокатит», поскольку, опять же css имеет глобальную область видимости.
Когда-то данную проблему пытались решить с помощью атрибута scoped для тэга STYLE, но после его поддержка была прекращена из-за «сложности кода». На текущий момент проблему хотят решить вообще на корню, за счёт внедрения веб-компонентов.
Web-компоненты по сути представляют собой не просто блоки, а некие элементы, которые можно многократно встраивать на страницу. В качестве примера можно привести тэги VIDEO или AUDIO, которые на странице отображаются в виде сложных функциональных блоков (кнопки, полоса прогресса и т.п.).
Но веб-компоненты это отдалённое будущее, а проблема глобальной видимости CSS актульна сейчас, поэтому есть несколько базовых подходов/методик к её решению (точнее эмуляции). Я сразу отбрасываю использование JS, поскольку это совсем уж сложные варианты. Остановимся только на голом HTML и CSS, поэтому единственным способом будет — это использование атрибута class (или id, но это не совсем «кошерно»).
На уровне HTML
Самое главное — это указать контейнер для блока. Всё, что оказывается внутри — и есть компонент (включая сам контейнер). В идеале такой блок можно было бы разместить в любой части страницы простым копированием.
<div class="block1"> <h1>Первый блок</h1> <div>Текст</div> </div> <div class="block2"> <h1>Второй блок</h1> <div>Текст</div> </div>
В даном примере два блока, которые можно идентифицировать через классы, хотя это не обязательно: у каждого есть свой DIV-контейнер.
CSS каскадность
Если применить
h1 { color: red; }
то оба заголовка станут красными. На выручку приходит каскадность:
.block1 h1 { color: red; } .block2 h1 { color: green; }
Очевидно, что данный подход наиболее простой и эффективный. При использовании препроцессора Sass получится вполне себе элегантный и красивый код:
.block1 { h1 { color: red; } // прочие блоки div { } } .block2 { h1 { color: green; } // прочие блоки div { } }
Уникальность именования классов
Данный подход используется в методологии БЭМ, где каждый элемент обязательно содержит класс и именуется от своего родителя.
<div class="block1"> <h1 class="block1_h1">Первый блок</h1> <div class="block1_div">Текст</div> </div> <div class="block2"> <h1 class="block2_h1">Второй блок</h1> <div class="block2_div">Текст</div> </div>
В css-стилях, при этом, происходит отказ от каскадности:
.block1_h1 { color: red; } .block1_div { } .block2_h1 { color: green; } .block2_div { }
В Sass это может выглядеть так:
.block1 { &_h1 { color: red; } // прочие блоки &_div { } } .block2 { &_h1 { color: green; } // прочие блоки &_div { } }
Здесь мы видим уже усложнение кода, но самое главное — это отказ от фундаментальной основы CSS — каскадности. То есть код усложнился, добавились запутанные правила именования, но при этом он не дал никакого выигрыша. Когда вложенных элементов окажется много, БЭМ классы превращаются в жуткое мессиво. При том, я отмечу, только за счёт идиотского отказа от каскадности. Но, в любом случае, такой подход всё-таки тоже решает задачу изоляции css-стилей.
Утилитарные классы
Поскольку я сторонник атомарного CSS, то покажу как это решается с помощью утилитарных классов. Например в Berry CSS есть классы для красного и зеленного цветов. В этом случае css-код вообще не правится, а сразу используются готовые классы:
<div> <h1 class="t-red">Первый блок</h1> <div>Текст</div> </div> <div> <h1 class="t-green">Второй блок</h1> <div>Текст</div> </div>
Обратите внимание, что у родителя я убрал идентифицирующий класс. В Atomic CSS классы просто так у тэгов не прописываются. Если корневой DIV не имеет особых стилей, то нет нужды указывать и какие-нибудь классы. Это же относится и к любым другим тэгам.
Данный пример достаточно простой, но он показывает, что проблему верстки можно решить без изоляции стилей. Конечно атомарный CSS может и не содержать нужных классов, в этом случае придётся создать класс для контейнера и использовать описанный выше вариант с каскадностью. Если же это какой-то простой стиль, то можно вообще использовать style у тэга.
Главный плюс здесь в том, что можно вообще обойтись правкой только HTML-кода, ну и читабельность здесь на порядок выше, чем любой другой вариант.