- Основы CSS
- Подходы к написанию CSS
- Динамический CSS
- Вёрстка под различные девайсы
- Препроцессоры и постпроцессоры CSS
- CSS Modules
Основы CSS
Cascading Style Sheets, CSS — каскадные таблицы стилей; язык таблиц стилей (stylesheet), используемый для представления внешнего вида HTML-документа.
Язык CSS описывает, как элемент должен отображаться на экране.
Документом (document) называют текстовый файл, структурированный при помощи языка разметки HTML, SVG или XML.
Утверждения
Утверждение, заявление (англ. statement) — структурный элемент CSS, который начинается с любых непробельных символов и заканчивается первой закрывающей фигурной скобкой или точкой с запятой.
Есть два типа утверждений:
- Набор правил, или проще: правило (англ.
ruleset,rule). - At-правило (англ.
at-rule).
Любое другое утверждение считается недопустимым и игнорируется.
Правило и его составляющие
Правило (англ. rule) — CSS-утверждение, состоящее из селектора (или группы селекторов) и блока объявлений.
selector1, selector2 /*, ...*/ {
property1: value1;
property2: value2;
/* ... */
}
Cелектор (англ. selector) используется для выбора элементов страницы. Ниже приведены примеры пяти селекторов:
#menu
.title
div
.parent .child
button:hover
Два и более селекторов отделяются друг от друга запятыми и образуют группу селекторов.
p, span, h1
.sister, .brother
Блоком объявлений (англ. declaration block) называется всё, что находится внутри фигурных скобок. Обычно там находятся объявления.
{
property1: value1;
property2: value2;
}
Например,
{
font-size: 14px;
color: red;
}
Объявлением (англ. declaration) называется пара свойство-значение (англ. property-value).
propetry: value;
Объявления отделяются друг от друга точками с запятой. Если пропущена точка с запятой, то стили могут примениться неправильно или не примениться вовсе.
{
width: 50% /* отсутствие ";" - синтаксическая ошибка */
height: 100%; /* данное правило и все последующие не будут применены, поскольку внутри этого блока объявлений выше имеется синтаксическая ошибка */
}
.article-title, .header-title {
color: #ccc;
font-size: 18px;
}
Блоки объявлений могут быть пустыми.
div {}
Блоки объявлений могут быть вложенными, если используются некоторые at-rules (например, @media() {}.
@media (/*...*/) {
div {
/*...*/
}
}
At-правило
At-правило (англ. at-rule) — CSS-утверждение, указывающее CSS, как себя вести.
Начинается с символа @ (at, at sign) и включает в себя весь следующий блок объявлений или весь код до ;.
@charset "utf-8"; /* определение набора символов */
@import 'custom.css'; /* импорт таблицы стилей (дополнительный запрос к серверу) */
/* медиазапросы, объявление CSS-правил в зависимости от девайса */
@media screen and (max-width: 1080px) {
/* ... */
}
/* объявление CSS-правил в зависимости от поддержки браузером */
@supports (display: grid) {
/* ... */
}
/* объявление и использование шрифта */
@font-face {
font-family: "Open Sans";
src: url("/* ... */") format("/* ... */");
}
.className {
font-family: "Open Sans";
}
/* объявление и использование анимации */
@keyframes fadeIn {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
.className {
animation-name: fadeIn;
animation-duration: 200ms;
}
At-правила могут быть вложенны друг в друга.
@supports (display: grid) {
@media screen { /* ... */ }
}
Типы элементов в CSS
Блочные и строчные элементы
Блочные элементы — элементы высшего уровня (визуально выглядят как блоки), располагающиеся на странице вертикально и задающие её структуру. Они создают разрыв строки перед элементом и после него, образуя прямоугольную область, по ширине занимающую всю ширину блока-родителя.
Блочные элементы могут содержать как строчные, так и другие блочные элементы.
Блочный элемент <p> является иключением не должен содержать внутри себя другие блочные элементы (в том числе и p).
Строчные (встроенные) элементы используются для форматирования текстовых фрагментов (кроме <area>, <img>). Они не формируют новые блоки контента, не создают разрыв строки вокруг себя. Многие строчные элементы не контролируют поля, отступы, ширину и высоту.
Строчные элементы могут содержать только данные и другие строчные элементы, они не могут содержать блочные элементы. (кроме a)
Блочные и строчные элементы являются основными элементами страницы. С них всё начиналось, и можно сделать практически что угодно, используя только их.
Также есть блочно-строчные элементы, которые обладают смешанной характеристикой: являются встроенным, но могут задавать поля, отступы, ширину и высоту.
Сделать элемент блочным, строчным или блочно-строчным в CSS можно с помощью:
.block { display: block; }
.inline { display: inline; }
.inline-block { display: inline-block; }
На данный момент существует много других типов элементов, все из которых отражает свойство display (table, flex, grid и прочие).
Замещаемые и незамещаемые элементы
Замещаемый (replaced) является элемент, представление которого выходит за рамки CSS. Стили CSS не могут влиять на содержимое замещаемых элементов — только на их позиционирование.
Замещаемые элементы.
<iframe>— содержит веб-страницу, не зависящую от родительской страницы и её стилей.<video><img><embed><input type="image">
В некоторых случаях являются замещаемыми.
<canvas><audio><option><applet><object>
Внешний вид и размеры замещаемых элементов могут определяться извне.
Например, img без заданных свойств width и height, занимает свой естественный размер.
При изменении одного из этих свойств, второе высчитается автоматически, с учётом пропорций.
Остальные элементы можно отнести к незамещаемым.
CSS Box Model
Каждому HTML-элементу соответствует прямоугольная область, называемая боксом (box).
Структура этой области называется блочной, боксовой моделью (box model).
- content — контент, содержимое; внутренняя область элемента, содержащая текст или другие элементы.
- padding — поля элемента, окружающие контент.
- border — рамки элемента, окружающие поля элемента.
- margin — внешние отступы (пустое пространство вокруг элемента), определяющие расстояние до соседних элементов.
Каждая из этих составляющих имеет какую-то область (area) и границу (edge).

Каждый бокс, являясь прямоугольником, должен иметь ширину и высоту.
Существует два способа задать бокс, от которых зависят его ширина и высота.
border-box—paddingиborderвходят в указанные в свойствахwidthиheightширину и высоту.content-box—widthиheightопределяют ширину и высоту только для контента (content width, content height),paddingиborderдополнительно увеличивают размер бокса./* высота бокса: 100px, высота контента: 100px - (16 * 2)px - (4 * 2)px = 60px */ .border-box { box-sizing: border-box; height: 100px; padding: 16px; border: 4px solid; }/* высота контента: 100px, высота бокса: 100px + (16 * 2)px + (4 * 2)px = 140px */ .content-box { box-sizing: content-box; height: 100px; padding: 16px; border: 4px solid; }
Величина внешних отступов может быть отрицательной, величина остальных составляющих блочной модели — не может.
.class {
margin-top: -16px;
}
Внешние отступы прозрачны, остальные составляющие могут быть закрашены в какой-то цвет (border-color для рамок, background-color для полей и контента.
Типы отношений элементов в CSS
Подраздел относится скорее к DOM (представлению документа в виде дерева). Просто краткое напоминание, что даже у элементов есть отношения:
-
Элемент-предок (ancestor) и элемент-потомок (descendant). Элемент-потомок находится в элементе-предке, но при этом предок может не быть его родителем (потомок может быть вложен в другой элемент, также являющийся потомком для рассматриваемого предка). В примере ниже такая связь наблюдается только между
divиspan,divиimg. -
Родительский (parent) и дочерний элементы (child). Дочерний элемент находится в родительском и других уровней вложенности между ними нет. Ниже такая связь только между
divиp,divиa,pиspan,aиimg. -
Элементы-братья (sibling). Должны иметь одного и того же родителя. Называются смежными (adjacent), если следуют прямо друг за другом. Ниже братьями (причём смежными) являются только
pиa. Элементaназывается следующим (following) за элементомp,p— предшествующий (preceding) элементуa(понятия следования и предшествия определены только для смежных элементов). -
Остальные элементы. Хоть они и являются дальними родственниками (как минимум один предок — корень дерева), считаются безотносительными друг к другу. Ниже такими являются
spanиimg,pиimg,spanиa.
<div>
<p>
<span></span>
</p>
<a>
<img />
</a>
</div>
Способы разметки
Способ разметки в CSS (CSS layout mode, layout) — алгоритм, определяющий позицию и размер блоков на основании того, как они взаимодействуют с их родственными блоками (ancestor, sibling).
Содержащий блок (containing block) — предок рассматриваемого элемента, который может влиять на его размер и позицию. Чаще всего содержащим блоком является элемент-родитель, но не всегда.
Начальный содержащий блок (initial) — <html>. Размеры начального блока зависят от viewport.
Типы разметки:
- Нормальная, обычная (normal flow) — блочная разметка для блочных элементов (
<div>,dispalay: block) и линейная разметка (<span>,display: inline) для строчных элементов. Используется по умолчанию. - Табличная (table) для построения таблиц (
<table>,<tr>,<th>,display: table). - Float-разметка для размещения элемента слева или справа при том, что весь остальной контент оборачивает этот элемент в порядке норамальной разметки. (
float: left,float: right). - Позиционированная.
Позиционированный элемент (positioned) — элемент, имеющий вычисленное значениеrelative,absolute,fixedи прочие (но неstatic) в свойствеposition.
Любой элемент по умолчанию имеетposition: static, не считается позиционированным и не реагирует на свойстваtop,right,bottom,left,z-index.
Относительно позиционированный элемент (relatively) — элемент, значения свойствtop,bottomиleft,rightкоторого определяют вертикальное и горизонтальное смещения (offset) от нормального позиционирования соответственно (position: relative).
Абсолютно позиционированный элемент (absolutely) — элемент, значения свойствtop,rightbottom,leftопределяют смещения от границ содержащего блока (приposition: absoluteэто ближайший позиционированный элемент-предок, приposition: fixed— начальный содержащий блок). Абсолютно позиционированный элемент удаляется из нормальной разметки документа и не занимает в ней никакого места. Margins добавляются к смещению. - Flexbox-разметка (flexible box).
- Сеточная (grid).
Селекторы в СSS
Селектор (selector) используется для выбора (select) элементов страницы, к которым мы хотим применить стили.
Селекторы подразделяются на простые и составные. В них также включаются псевдоклассы и псевдоэлементы.
Простые селекторы
Селектор по типу выбирает элементы по типу узла.
div, p, span, a { }
Селектор по классу выбирает элементы, соответствующие атрибуту class.
.classname {}
Селектор по id выбирает элементы, соответствующие атрибуту id.
#idname {}
Универсальные селекторы выбирают элементы всех типов.
* {}
Селекторы атрибутов выбирают элементы на основании наличия указанного атрибута, а также соответствия значения атрибута, если оно указано.
[attr operator value i] {}
a[href] {} /* <a> элементы с атрибутом href */
a[href="https://qq.com"] {} /* ...точно соответствующим "https://qq.com" */
a[class^="qq"] {} /* ...начинающимся с подстроки 'qq' */
a[href*="qq"] {} /* ...содержащим подстроку 'qq' */
a[href*="qq" i] {} /* ...содержащим подстроку 'qq' (case insensitive благодаря флагу 'i') */
a[href~="qq" i] {} /* ...содержащим целое слово 'qq' (значение атрибута состоит из слов и пробелов) */
a[href$=".com"] {} /* ...заканчивающимся подстрокой '.com' */
Составные селекторы и комбинаторы
Составной селектор состоит из двух или более простых селекторов, между которыми могут быть комбинирующие операторы — комбинаторы (combinators).
В комбинаторах приоритеты и правила группировки операторов при выборке элементов отсутствуют, поэтому составление выборки осуществляется строго справа налево, от одного простого селектора к другому.
Комбинатор потомков (descendant) A B выбирает элементы, соответствующие селектору B и имеющие предка, соответствующего селектору A.
Комбинатор потомков представлен одним или несколькими пробельными символами (возможно, с переходом на новую строку или даже с комментариями).
.grandfather .son {}
.father .son {}
Комбинатор потомков сработает лишь в том случае, если между селекторами нет других комбинаторов. В следующем примере он не применяется, поэтому строки эквивалентны.
.father>.son {}
.father > .son {}
Случай, когда селекторов больше двух (например, C A B), означает лишь наличие дополнительного условия. Помимо сказанного выше про A B, у искомых элементов должен также имееться элемент-предок, соответствующий селектору C.
.grandfather .father .son {}
Это точно так же работает как для большего числа селекторов (по индукции), так и для комбинаторов других типов.
Комбинатор детей (child) A > B является частным случаем комбинатора потомков. Он выбирает элементы, соответствующие селектору B и имеющие родительский элемент, соответствующий селектору A.
.grandfather > .father > .son {}
.father > .son {}
Комбинатор смежных братьев (adjacent sibling, next sibling) A + B выбирает элементы, соответствующие селектору B, являющиеся смежными братьями с элементами, соответсвующими селектору A, и следующие прямо за ними.
Комбинатор общих братьев (general sibling, subsequent sibling) A ~ B выбирает элементы, соответствующие селектору B, являющиеся братьями с элементами, соответсвующими селектору A, и следующие за ними (не обязательно прямо, между ними могут быть и другие братья).
Рассмотрим несколько примеров, чтобы понять, как работают комбинаторы братьев:
<div class="row">
<div class="cell white"></div>
<div class="cell"></div>
<div class="cell white"></div>
<div class="cell"></div>
</div>
<style>
.row { display: flex; width: fit-content; border: 1px solid; }
.cell { width: 50px; height: 50px; }
</style>
<style>
.white + .cell { background: black; } /* Рис. 1 */
.cell + .cell { background: black; } /* Рис. 2 */
.white + .white { background: black; } /* Рис. 3 */
.white ~ .cell { background: black; } /* Рис. 4 */
.cell ~ .cell { background: black; } /* Рис. 5 */
.white ~ .white { background: black; } /* Рис. 6 */
</style>

Рис. 1 Каждое поле, следующее за белым (.white), окрашивается в чёрный.
Рис. 2 Каждое поле, следующее за другим полем, окрашивается в чёрный (все, кроме первого).
Рис. 3 Не найдено белых полей, следующих за белыми.
Рис. 4 Каждое поле, если ранее встречалось хоть одно белое, окрашивается в чёрный.
Рис. 5 Каждое поле, если ранее встречалось хоть одно поле, окрашивается в чёрный.
Рис. 6 Каждое белое поле, если ранее встречалось хоть одно белое поле, окрашивается в чёрный.
Всё это работает так, потому что все поля имеют один класс cell и у них нет братьев другого класса.
Стоит отметить, что комбинаторы (не считая комбинатора потомков) используются крайне редко, так как могут стать причиной довольно непредсказуемого поведения кода, поскольку при их использовании добавление новых блоков будет сказываеться на стилях других блоков, что может быть особенно заметно, когда в ход вступает специфичность.
Если между селекторами ничего не стоит AB, то происходит их объединение: выбираются элементы, соответствующие обоим селекторам одновременно.
div.white {}
.white.cell {}
div#body {}
p.title {}
Если хотя бы одна часть селектора написана неправильно, то этот селектор полностью игнорируется, на другие селекторы это никак не влияет.
Если между селекторами стоит запятая A, B то они не связаны между собой выбором элементов (в этом случае выборка осуществляется для каждого селектора в отдельности), но ко всем найденным элементам применется один набор стилей. Такой тип связи называется группой селекторов.
Псевдоклассы и псевдоэлементы
Псевдоэлемент — ключевое слово, добавляемое к селектору, позволяющее стилизовать определённую часть выбранного элемента (например, первоя строка).
Псевдоэлементы также позволяют контролировать элементы, находящиеся за пределами документа; позволяют ссылаться на недоступную для получения иным путём информацию (например, полоса прокрутки). Они должны всегда начинаться с двух двоеточий, хоть в большинство браузеров их разрешено использовать и с одним для обратной совместимости:
.field::placeholder { color: #8ab7e5 } /* изменение цвета замещающего текста для input и textarea */
p::before, p::after { content: "♥"; } /* вставка сердечка перед контентом каждого блока p и после него */
p::first-line { color: #8ab7e5 } /* изменение первой строки блочного элемента (в данном случае p) */
p::first-letter { font-size: 24px; } /* изменение первой буквы блочного элемента (в данном случае p) */
*::selection { background: #8ab7e5 } /* изменение цвета выделения текста */
*::-webkit-scrollbar-thumb { background: #8ab7e5 } /* изменение цвета ползунка полосы прокрутки в браузерах на движке webkit */
Псевдокласс — ключевое слово, добавленное к селектору и определяющее его состояние.
Псевдоклассы всегда начинаются с двоеточия. Некоторые из них могут принимать параметры.
input:focus {} /* любое поле ввода, находящееся в фокусе */
option:checked {} /* каждая отмеченная опция */
button:disabled {} /* каждая недоступная кнопка */
link:not(:visited) {} /* каждая непосещённая ссылка */
*:hover {} /* любой элемент при наведении на него */
Если псевдокласс или псевдоэлемент применяются ко всем элементам, то не рекомендуется пропускать универсальный селектор *, иначе может возникнуть путаница.
Селекторы ниже похожи, но выбирают совершенно разные элементы:
div:hover {} /* любой блок div, на который наведён курсор */
div :hover {} /* ~ div *:hover (любой элемент, на который наведён курсор, располагающийся в блоке div) */
С помощью псевдоклассов также можно работать с родительскими и дочерними элементами:
div:empty {} /* каждый блок без дочерних элементов */
div:not(:empty) {} /* каждый блок, имеющий дочерние элементы */
div:first-child {} /* каждый блок, являющийся первым ребёнком */
div:nth-child(3) {} /* каждый блок, являющийся третьим по счёту ребёнком */
div:last-child {} /* каждый блок, являющийся последним ребёнком */
div:only-child {} /* каждый блок, являющийся единственным ребёнком */
Псевдоклассы могут идти последовательно друг за другом:
div:first-child:last-child /* эквивалентно :only-child */
Стоит также отметить, что некоторые псевдоклассы совпадают с атрибутами элементов, поэтому их можно заменить селектором атрибутов. Следующие строки эквивалентны:
button:disabled {}
button[disabled] {}
Применение CSS-селекторов в JavaScript (Selectors API)
Selectors API предоставляет методы, с помощью которых можно быстро и просто получить список узлов документа путем сопоставления с группой селекторов.
// возвращает первый найденный Element или null, если совпадений не найдено
const element = parentNode.querySelector('selectors');
// возвращает NodeList, содержащий все найденные элементы, или пустой NodeList
const elementList = parentNode.querySelectorAll('selectors');
const root = document.querySelector('#root');
const articleTitles = root.querySelectorAll('.article > .title');
Параметр selectors всегда должен быть валидной строкой CSS-селекторов, в противном случае выдаётся исключение SyntaxError.
Специфичность селекторов
Специфичность селекторов (selector’s specificity) определяет приоритетность селекторов в таблице стилей. Чем специфичнее селектор, тем выше его приоритет.
Eсли в выборку несколькими селекторами попадают одни и те же элементы и затрагиваются одни и те же свойства в блоках объявлений, в конечном счёте применяются значения свойств из объявлений более специфичного селектора.
Специфичность представлена четырьмя числами, записанными через запятую A,B,C,D.
Правила специфичности
- Атрибут style (inline-стиль). Специфичность:
1,0,0,0. - Атбрибут id. Специфичность:
0,1,0,0. - Атбрибут class, все остальные атрибуты (кроме
id) и псевдоклассы (кроме:not). Специфичность:0,0,1,0. - Типы элементов и псевдоэлементы. Специфичность:
0,0,0,1. - Универсальные селекторы и псевдокласс
:not. Специфичность:0,0,0,0(нулевая специфичность).
В составных селекторах каждое из четырёх чисел специфичности суммируется отдельно.
Комбинирующие операторы не влияют на специфичность.
* body #root .container ul > li:last-child::after { content: "."; }
/* 0,1,2,4 = 0,0,0,0 + 0,0,0,1 + 0,1,0,0 + 0,0,1,0 + 0,0,0,1 + 0,0,0,1 + 0,0,1,0 + 0,0,0,1 */
В группе селекторов специфичность для каждого селектора считается отдельно.
.cell.white, /* 0,0,2,0 = 0,0,1,0 + 0,0,1,0 */
div /* 0,0,0,1 */
{ background: #000; }
Стоит отметить, что порядок расположения стилей в class-атрибуте не важен. В примере ниже результат одинаковый:
<p class="title text"></p>
<p class="text title"></p>
Почему специфичность нельзя представить одним числом
В примере ниже элемент div выбирается двумя селекторами, первых из которых представлен одним атрибутом id, а второй — 11 атрибутами class.
<div id="identifier" class="a b c d e f g h i j k"></div>
div {
width: 100px;
height: 100px;
}
#id {
background: red; /* 0, 1, 0, 0 (применится это правило) */
}
.a.b.c.d.e.f.g.h.i.j.k {
background: green; /* 0, 0, 11, 0 */
}
Если бы специфичность суммировалась в одно число, то 11 атрибутов class переопределяли бы стили одного id (11 * 10 > 100), 11 атбитуров id переопределяли бы inline-стиль (11 * 100 > 1000), 11 элементов переопределяли бы один class, но на самом деле этого не происходит и не должно происходить. Поэтому числа специфичности должны суммируются отдельно.
Наследование
Наследование (Inheritance) — автоматическая передача некоторых свойств (объявлений) элемента-предка его потомкам. В этом случае потомок наследует некоторые свойства предка.
Механизм наследования позволяет разработчикам писать меньше кода. Наследуются не все свойства, поскольку иначе наследование бы только усложнило написание CSS.
Наследуются свойства, задающие параметры отображения текста (все font-*, большинство text-*), произношения текста вслух (speak-*) и отображения списка (list-*), также наследуются color, visibility, cursor, letter-spacing, line-height, white-space, border-collapse, border-spacing и другие, менее популярные свойства.
Почти все остальные свойства не наследуются. Среди них: свойста блочной модели (border-*, margin-*, padding-*, *-width, *-height), позиционирования (position, top, left,right, bottom, float), background-*, transform, display, z-index, text-decoration и другие.
<div>
<p>Notes</p>
</div>
div {
font-size: 24px;
color: red;
}
Текст в примере выше, располагающийся в элементе <p>, будет красным, поскольку свойства font-size и color наследуются от div.
Наследуемое объявление не имеет специфичности (specificity) и важности (importance), о которой будет рассказываться дальше, поэтому его может перекрыть даже правило с универсальным селектором *, имеющим нулевую специфичность, или любое объявление, по умолчанию указанное в стилях браузера (user-agent declaration).
Если добавить следующее правило в примере выше, то текст станет чёрным несмотря на то, что специфичность селектора * — 0,0,0,0, а селектора div — 0,0,0,1.
* {
color: #000;
}
Наследуются не объявленные значения (declared values), а вычисленные значения (computed values).
.parent {
font-size: 24px;
line-height: 2em; /* 48px (считается от font-size текущего элемента) */
}
.child {
font-size: 36px;
/* в line-height унаследуется не объявленное значение 2em,
а вычисленное 2em от 24px = 48px */
}
Наследуемое свойство от ближнего предка перекрывает наследуемое от дальнего. Если бы мы вместо * использовали body, то цвет текста остался бы красным, поскольку div находится ближе к p, чем body, и тоже определяет свойство color.
body {
color: #000;
}
Наследование тесно связано с древовидной структурой объектной модели CSS.
Наследования обусловлено внутренней реализацией языка. Оно заложено не конкретными свойствами, заданными по умолчанию в стилях, а самим поведением наследуемых свойств.
Чтобы явно унаследовать значение родительского свойства, можно использовать значение inherit.
p {
color: inherit;
}
Также есть значения initial и unset. Первое устанавливает значение, по умолчанию заданное браузером, а в случае его отсутствия работает как inherit, если свойство наследуемое (naturally inherited). Второе — наоборот: наследует, если свойство наследуемое, в противном случае устанавливает значение браузера.
Использование unset в примере выше установит красный цвет, initial — чёрный.
Каскад
Cascading Style Sheets (CSS) переводится как каскадные таблицы стилей. Ключевым словом выступает каскад.
Если несколько селекторов ссылаются на один и тот же DOM-элемент, а в CSS-правилах, привязанных к этим селекторам, затрагивается одно и то же свойство (например, width, color), то возникает конфликт: браузеру нужно решить, какое из правил следует применить к элементу, какое из них важнее (приоритетнее). Для этого существует специальный алгоритм, с которым мы познакомимся далее.
Пример конфликта свойства color:
<div id="foo" class="bar" />
.bar {
color: white;
}
#foo {
color: red;
}
div {
color: white;
}
Конкретно в данном примере после всех рассчётов стилей будет применён цвет red. Будем разбираться, почему так.
Каскадом (англ. cascade) или каскадированием (англ. cascading) называют процесс группировки всех *таблиц стилей, полученных из разных источников и применяемых к элементу, и разрешения конфликтов между CSS-правилами (как в примере выше.)
Все объявления трёх селекторов ниже будут применены к блоку с классом element.
<div class="element"></div>
<div class="another-element"></div>
div.element {
width: 200px;
background: #000;
}
.element {
width: 100px;
}
.another-element, .element {
color: #fff;
}
Объявления width: 100px и width: 200px конфликтуют друг с другом. Решение о том, какое из них применится в конечном счёте, зависит от приоритета, об определении которого рассказывается ниже.
Возможные источники стилей (браузер, автор и пользователь)
Таблицы стилей (англ. style sheets) могут иметь 3 разных источника (англ. origins): стили браузера, автора и пользователя.
Стили браузера
Стили браузера, объявления браузера (англ. user-agent declarations, browser declarations) — это стили, которые задаются по умолчанию браузером.
Вы можете воспринимать стили браузера как стили по умолчанию, поскольку они работают даже тогда, когда ваш HTML-документ не содержит CSS-объявлений.

Эти стили невозможно отключить, но их очень просто перезаписать.
Некоторые стили браузера можно найти в папке с файлами браузера.

Пример стилей браузера: ссылки <a> имеют синий цвет и подчёркнуты (англ. underlined).
Как добиться консистентности в разных браузерах
Каждый браузер задаёт свои стандартные стили, поэтому страница может отображаться в браузерах по-разному и разработчики часто подключают файлы вроде reset.css или normalize.css, чтобы сбросить стили браузера.
Стили автора
Стили автора, объявления автора (англ. author declarations) — стили, подключенные к HTML-документу через тэг <style> и указанные в этом тэге либо напрямую, либо по ссылке на CSS-файл.
Очевидно, что в данном случае под автором подразумевается разработчик (англ. developer) данного сайта, который писал код HTML-документа.
Самописные стили в Chrome Dev Tools
Стили, которые вы самостоятельно добавляете в Chrome Dev Tools, также относятся к стилям автора и имеют одинаковый приортитет с теми стилями, что написал разработчик HTML-документа.
Ваши рукописные стили добавляются в новый файл inspector-stylesheet, который создаёт сам Chrome.

Стили, добавленные в блок element.style {} в Chrome Dev Tools, также считаются стилями автора и задаются напрямую в атрибуте style выбранного HTML-элемента.

Стили пользователя
Стили пользователя, объявления пользователя (англ. user declarations, user styles) — стили, которые пользователь указывает в настройках браузера.
Ранее пользовательские стили можно было объявить в файле Custom.css, который можно было найти в папке с файлами браузера, но разработчики решили отключить эту возможность.

В наше время в качетве альтернативы файлу Custom.css сейчас могут использоваться различные расширения Chrome (англ. Chrome extensions). Например, расширение User CSS.
Раньше же пользовательские стили можно было объявить следующим образом:
1) Заходим сюда: chrome://version/
2) Копируем Путь к профилю и открываем в проводнике.
3) Находим папку User StyleSheets и в ней файл Custom.css (сейчас вы его уже не найдёте, а если добавите, то он учитываться не будет).
4) Изменяем или добавляем новые CSS-объявления в файл.
Пример файла Custom.css
Алгоритм (порядок) каскада
Будем рассматривать каскад относительно одного HTML-элемента.
1) Сперва находятся все CSS-объявления (англ. declarations), принадлежащие рассматриваемому элементу.
2) Если несколько правил конфликтуют друг с другом, то сравнивается их источник (важность). Применяется объявление из более приоритетного источника.
3) Если важность совпадает, то сравнивается специфичность. Применяется объявление с более специфичным селектором.
4) Если важность и специфичность совпадают, то сравнивается порядок источника. Применяется объявление, которое объявлено позже в коде.
1) Важность (Importance)
Производится упорядочивание объявлений по важности (англ. importance), зависящей от источника (origin).
Чем ниже по списку ниже, тем выше приоритет.
1) Объявления браузера (user-agent declarations).
2) Объявления пользователя (user normal declarations).
3) Объявления автора (author normal declarations).
4) CSS Animations
5) Объявления автора с добавлением ключевого слова !important (author important declarations).
6) Объявления пользователя с добавлением ключевого слова !important (user important declarations).
7) Объявления браузера с добавлением ключевого слова !important (user-agent important declarations).
8) CSS Transitions
Важность повышается за счёт ключевого слова !important.
* {
box-sizing: border-box !important;
}
Если два конфликтующих правила содержат !important, то применится более специфичное из них.
#article { padding: 24px !important; } /* применится это правило: id специфичнее класса */
.article { padding: 16px !important; }
Если к одному правилу ключевое слово !important применен дважды (!important!important), то важность данного правила не только не изменится, но произойдёт синтаксическая ошибка и правило просто не применится.
#article { padding: 24px !important; }
#article { padding: 24px !important!important; } // синтаксическая ошибка "semi-colon expected" (ожидается ";")
2) Специфичность (Specificity)
Если важность и источник совпадают, то сравнивается специфичность селекторов (selector’s specificity), которым принадлежат объявления.
В примере ниже применится объявление width: 200px, поскольку его селектор специфичнее (подробнее об этом будет дальше).
div.element { /* специфичность: 0,0,1,1 */
width: 200px; /* применится это объявление */
background: #000;
}
.element { /* специфичность: 0,0,1,0 */
width: 100px;
}
3) Порядок источника (Source Order)
Если важность, источник и специфичность совпадают, приоритет объявлений зависит от их расположения в коде, порядка источника (source order). Объявленный позже блок кода (правило, объявление) имеет больший приоритет. Это работает так же, если правила находятся в разных CSS-файлах: приортитетнее считается то правило, чей файл подключен позже.
/* два правила */
p {
color: green;
}
p {
color: red; /* применится это объявление (важность, источник, специфичность совпадают и объявлено позже) */
}
/* два объявления */
.article {
color: green;
color: red; /* применится это объявление */
}
<!-- два файла -->
<link href="styles.css" rel="stylesheet" />
<!-- правила (и их объявления) в файле ниже приоритетнее -->
<link href="styles2.css" rel="stylesheet />
Значение, полученное в результате каскада, называется каскадным значением (англ. cascaded value).
Хороший пример проверки знаний каскада
При вычислении каскада сохраняется не только последнее значение, но вся цепочка значений по их приоритету. Таким образом, если самое приоритетное правило будет отключено (например, при помощи JavaScript или Dev Tools в браузере), то вместо него применяется второе по значимости правило. Сейчас разберём большой пример и отсортируем правила в порядке их приоритетности для браузера.
<div id="a" class="a" style="color: pink">
Notes
</div>
/* файл со стилями автора */
#a {
color: red;
}
.a {
color: orange;
}
#a#a#a {
color: yellow;
}
#a#a#a {
color: green !important!important;
}
#a {
color: lightblue !important;
}
.a {
color: blue !important;
}
#a {
color: purple !important;
}
div {
color: blue;
}
html {
color: yellow;
}
Начнём с атрибута style, далее пойдём по порядку. Будем записывать селекторы в порядке их приоритетов в массив и сортировать его по убыванию приоритета правил.
Для начала предположим, что атрибут style и весь css в примере располагаются в коде программы, то есть они написаны программистом (автором) и имеют одинаковый источник, если к правилам не применяется ключевое слово !important.
1) Атрибут style имеет специфичность 1, 0, 0, 0. Пока не с чем его сравнивать, просто добавляем в массив: [style].
2) Селектор #a имеет тот же источник, что и style. Переходим к рассмотрению (вычислению) специфичности (второй этап алгоритма): 0, 1, 0, 0. Специфичность ниже, чем у style. Значит массив приоритетов: [#a, style].
3) Селектор .a имеет тот же источник, а его специфичность: 0, 0, 1, 0, что ещё ниже, значит имеем массив: [style, #a, .a].
4) Селектор #a#a#a имеет тот же источник, а его специфичность: 0, 0, 3, 0, что ещё выше #a и ниже style: [style, #a#a#a, #a, .a].
5) Селектор #a#a#a!important!important имеет неправильный синтаксис для правила (поскольку нельзя ключевое слово !important применить дважды к одному правилу), поэтому правило просто не применится и в массив его добавлять нет смысла.
6) Селектор #a!important имеет источник author + important, что выше всех предыдуших правил, его специфичность: 0, 1, 0, 0. Имеем: [#a!important, style, #a#a#a, #a, .a].
7) Селектор .a!important имеет источник author + important, поэтому он приоритетнее, чем 1)-5). При этом его специфичность: 0, 0, 1, 0, что меньше, чем у 6), а значит массив примет вид: [#a!important, .a!important, style, #a#a#a, #a, .a].
8) Селектор #a!important-2 совпадает с селектором 6), поэтому имеет тот же источник и ту же специфичность, то при этом позже объявлен в коде, а значит считается более приоритетным. Тогда имеем: [#a!important-2, #a!important, .a!important, style, #a#a#a, #a, .a].
9) Селектор div имеет источник автора и его специфичность 0, 0, 0, 1, значит он ниже по приоритету, чем все предыдущие селекторы: [#a!important-2, #a!important, .a!important, style, #a#a#a, #a, .a, div]
10) Селектор html является родителем всех элементов, а значит и элемента, который мы рассматриваем, при этом свойство color относится к свойствам текстовых полей, а значит является наследуемым. Наследование не имеет специфичности, поэтому любой из селекторов выше его перебивает по приоритету. Унаследование свойства происходит лишь в том случае, если нет ниодного правила, которое задаёт это свойство напрямую элементу. Таким образом, наследование можно добавить в самый конец нашего массива: [#a!important-2, #a!important, .a!important, style, #a#a#a, #a, .a, div, html].
Как можно увидеть, к элементу применится правило #a!important-2, то есть фиолетовый цвет (color: purple).
Скорость выполнения селекторов браузерами
Обработка некоторых селекторов занимает в несколько раз больше (в 5-6 раз) времени, чем других селекторов.
Тем не менее, сейчас в браузерах настолько всё оптимизировано, что данная проблема является одной из наименьших из существующих.
В интернете можно найти множество тестов, которые показывают, что при тысячах элементов на странице даже достаточно сложные селекторы обрабатываются считанные миллисекунды.
Подходы к написанию CSS
Проблемы CSS
CSS изначально создавался для стилизации элементов HTML-разметки и ни для чего более. Он обладает огромной гибкостью, позволяя решить любую задачу множеством способов.
- Обращение к элементам происходит при помощи селекторов. Существует множество способов обратиться к одному и тому же элементу. ```html
```css
#header-title {}
.title {}
[class="title"]
p {}
* {}
- Многие из этих способов можно комбинировать.
.title#header-title p[class="title"] p.title {} .header > * {} .header #header-title {} - Один селектор может выбирать несколько различных элементов на странице. ```css
</div>
```css
.title {} /* выберет оба элемента span и p */
div {} /* выберет и header, и article */
-
CSS-правила могут конфликтовать друг с другом и эти конфликты решаются каскадом, но разработчик всё время должен знать важность, специфичность и место в коде для каждого объявления.
-
Всё это усугубляется отсутствием модульности кода. Имеется только глобальная область видимости.
Учитывая всё сказанное выше, возможны случаи, когда стили нового элемента страницы влияют на стили уже существующего элемента, или, наоборот, новый элемент зависит от стилей существующего.
Такой код трудно поддерживать, расширять и над ним практически невозможно работать в команде.
Всё это стало основной предпосылкой к появлению методологий в CSS. Каждая из них по-своему накладывает некоторые ограничения, уменьшая допустимую сложность селекторов, вносит подобие модульности или разделения ответственности (Separation of Concerns).
Некоторые рекомендации
Правила специфичности не так сложны при конкретных селекторах. Но когда селекторов сотни в одном файле, трудно слёту рассчитать (а потом держать это в уме), какой стиль будет применён в итоге или как отельное правило (rule) повлияет на элементы страницы.
Есть некоторые рекомендации, соблюдение которых поможет не только значительно улучшить читабильность кода, но иногда тем или иным образом оптимизировать его:
- Стараться использовать только самые распространённые простые селекторы, покрывающие практически любые случаи - селекторы по классу.
.content p {} /* не очень хорошо */ .content #title {} /* лучше, но есть недочёты (будет сказано дальше) */ .content .title {} /* хорошо */ - То же самое касается и комбинаторов: в большинстве случаев лучше ограничиться комбинатором потомков.
.content ~ .title {} /* результат не очень то предсказуем */ .content > .title {} /* можно иногда использовать, но не все о нём помнят, что сказывается на читаемости кода */ .content .title {} /* хорошо */ - Малая вложенность селекторов. Желательно, не более двух селекторов по классу (
.parent .child). Это упрощает разбор селектора и ускоряет поиск соответствующих ему элементов как для разработчика, так и для браузера..page .content .text-block .title {} /* плохо */ .text-block .title {} /* хорошо */ .text-block .title:hover {} /* тоже хорошо */ - Как можно меньше использовать inline-стили (ведь их практически невозможно переопределить). Вместо этого лучше выносить CSS-код в классы. ```css
Text
/* плохо */
Test
/* хорошо */
* Стараться *избегать* использования *ключевого слова* `!important`, предпочитая ему решение проблемы перекрытия стилей при помощи *специфичности*. Лишь иногда (но очень редко) `!important` является единственным решением проблемы (например, если перекрываются чрезвычайно специфичные стили сторонней библиотеки).
Одной из интересных особенностей CSS является то, что простой селектор можно использовать дважды и специфичность увеличится, а выборка элементов не изменится. Это может помочь, если стили сторонней библиотеки по какой-то причине импортируются позже, чем стили проекта.
```html
<div class="article"></div>
/* перекрыть стили сторонней библиотеки ниже можно специфичностью дублированием селектора: */
.article.article {/* ... */} /* специфичность: 0,0,2,0 */
/* можно и так: */
div.article {/* ... */} /* специфичность: 0,0,1,1 */
/* но лучше не делать вот так: */
div.article {/* ... */ !important} /* важность: author important declaration */
/* стили из сторонней библиотеки */
.article {/* ... */} /* специфичность: 0,0,1,0 */
- При переопределении CSS-правил в своём коде следует больше полагаться на специфичность и меньше на порядок в коде. Тогда приоритет стилей не изменится, если блоки кода в одном файле поменяются местами или CSS-файлы подключатся не в том порядке.
В примере ниже специфичность двух селекторов одинакова и цвет текста зависит от порядка объявления правил в коде. Если поменять правила местами, то применится другой цвет.
<div class="page">
<div class="header">
<p class="title">Notes</p>
</div>
</div>
.page .title {
color: red;
}
.header .title {
color: green; /* применится этот цвет */
}
- При использовании сторонних библиотек стоит обращать внимание именно на порядок подключения CSS-файлов: нужно подключать свою таблицу стилей последней. ```html
<link rel=”stylesheet” href=”external-lib.min.css> <link rel=”stylesheet” href=”styles.css>
* Удалять неиспользуемые стили и не подключать большие библиотеки стилей (по типу bootstrap) целиком, если планируется использование лишь малой части (меньше 20%) их функциональности. Это уменьшит время загрузки CSS-файла, его парсинга и построения CSSOM.
### Почему лучше избегать использования селекторов по id и использовать селекторы по классу
* Один *элемент не может иметь два идентификатора*, но *может иметь сколь угодно классов*.
```html
<!-- это не будет работать! -->
<p id="title header-title"></p>
<!-- это тем более! игнорирование или ошибка -->
<p id="title" id="header-title"></p>
<!-- а это работает -->
<p class="title header-title"></p>
- Селектор по id выбирает лишь первый элемент на странице. ```html
* Если есть *только селекторы по классу*, *специфичность вычисляется проще*.
```css
.page {} /* 0,0,1,0 */
.content .title {} /* 0,0,2,0 */
.content .title:hover {} /* 0,0,3,0 */
В случае же использования атрибутов id и class, количество возможных комбинаций возрастает. Больше комбинаций — больше сложность.
#page #title {} /* 0,2,0,0 (может перезаписать все правила ниже) */
.page #title {} /* 0,1,1,0 */
#page .title {} /* 0,1,1,0 */
.page .title {} /* 0,0,2,0 */
БЭМ
Блок-Элемент-Модификатор (БЭМ, BEM) — самая популярная методология CSS на данный момент.
- Блок (Block) — переиспользуемый элемент сайта.
- Элемент (Element) — некоторая часть блока, не имеющая функционального смысла вне блока.
- Модификатор (Modifier) — свойство блока или элемента, меняющие его внешний вид или поведение.
Возможный синтаксис БЭМ
.block_element-modifier {}
/* или */
.block__element--modifier {}
БЭМ в CSS
.page {} /* блок */
.page__content {} /* элемент */
.content {} /* блок */
.articles {} /* блок */
.article {} /* блок */
.article__title {} /* элемент */
.article__title--bold {} /* модификатор */
.article__title--red {} /* модификатор */
.article__image {} /* элемент */
.article__image--small {} /* модификатор */
.article__image--large {} /* модификатор */
БЭМ в SCSS
.page {
/* any css for .page */
&__content {
/* any css for .page__content */
}
}
.content {}
.articles {}
.article {
/* any css for .article */
&__title {
/* any css for .article__title */
&--bold {
/* any css for .article__title--bold */
}
&--red {
/* any css for .article__title--red */
}
}
}
Стоит отметить, что любой DOM-элемент в рамках БЭМ может быть и блоком, и элементом одновременно.
В примере ниже div является блоком article и элементом item блока content
<div class="content">
<div class="content__item article">
<p class="article__title"></p>
</div>
</div>
Преимущества и недостатки БЭМ
Преимущества БЭМ
- Модульность кода и изолированность модулей друг от друга.
- Простая специфичность.
Недостатки БЭМ
- Слишком длинные названия классов.
Пример использования БЭМ с React
import React from 'react';
const articles = [{ title: 'BEM' }, { title: 'React' }];
const renderArticle = (item, index) => (
<div className="article" key={index}>
<p className="article__title">
<span className="article__title--red">{`Article #${index}:`}</span>
{item.title}
</p>
<img className="article__image" />
</div>
);
const Articles = () => (
<div className="articles">
{articles.map(renderArticle)}
</div>
);
const render = () => (
<div className="page">
<div className="page__content content">
<Articles />
</div>
</div>
);
OOCSS
Объектно-Ориентированный CSS (Object Oriented CSS, OOCSS) — подход, использующий преимущества ООП в CSS.
Основная идея заключается в переиспользуемости кода: принцип Don’t Repeat Yourself (DRY).
Объектом в этом подходе выступает любой визуально повторяющийся шаблон, который можно выделить как фрагмент кода.
Элементы страницы задаются объектными классами, являющиеся отдельными объектами в таблицах стилей.
В отличие от многих, подход OOCSS не устанавливает правил наименования классов; не запрещает использовать тэги, id и прочее, что позволяет сочетать его с другими подходами.
Первое правило подхода OOCSS: разделение Структуры и Оформления
Структура (Structure) — совокупность невидимых для пользователя свойств элемента.
Пример структурных свойств: height, width, margin, padding, overflow (размер, позиционирование и прочее).
Оформление (Skin) — совокупность видимых свойств элемента.
Пример свойств оформления: color, font, shadow, gradient.
Другими словами: структура состоит из инструкций о том, как все расположено, а оформление определяет, как выглядит макет.
Такое разделение позволяет размещать копию объекта в любое место сайта, не переопределяя при этом существующие стили.
Эта копия расширяется дополнительными стилями через class-атрибут (часто сразу несколькими классами).
Второе правило подхода OOCSS: разделение Контейнера и Контента
Контентом является любой элемент, расположенный в каком-то другом элементе — контейнере.
.container .content
Суть заключается в том, чтобы использовать комбинатор потомков как можно реже.
Если контент не зависит от конкретного контейнера, мы можем переиспользовать код чаще.
Например, вместо
.sidebar {}
.sidebar .menu {}
.sidebar .menu .menu-item {}
можно разделить контейнер и контент следующим образом
.sidebar {}
.menu {}
.menu-item {}
что даёт возможность использовать меню где-нибудь ещё.
OOCSS в CSS
Ищем повторяющиеся блоки кода:
.button {
width: 100px;
height: 40px;
padding: 4px 8px;
background-color: #000;
border-radius: 4px;
color: #fff;
}
.button-wide {
width: 200px;
height: 40px;
padding: 4px 8px;
background-color: #000;
border-radius: 4px;
color: #fff;
}
<div class="button"></div>
<div class="button-wide"></div>
Выносим их в классы:
.skin-button {
background-color: #000;
border-radius: 4px;
color: #fff;
}
.structure-button {
height: 40px;
padding: 4px 8px;
}
.button {
width: 100px;
}
.button-wide {
width: 200px;
}
<div class="button skin-button structure-button"></div>
<div class="button-wide skin-button structure-button"></div>
OOCSS в SCSS
При использовании OOCSS с чистым CSS, атрибут class у DOM-элементов сильно разрастается, но что, если…
@mixin skin-button {
background-color: #000;
border-radius: 4px;
color: #fff;
}
@mixin structure-button {
height: 40px;
padding: 4px 8px;
}
.button {
@include skin-button;
@include structure-button;
width: 100px;
}
.button-wide {
@include skin-button;
@include structure-button;
width: 200px;
}
<div class="button"></div>
<div class="button-wide"></div>
Хотя, имея в арсенале миксины, можно сделать ещё лучше (не совсем OOCSS) в этом примере:
@mixin button-defaults {
background-color: #000;
border-radius: 4px;
color: #fff;
height: 40px;
padding: 4px 8px;
}
.button {
@include button-defaults;
width: 100px;
}
.button {
@include button-defaults;
width: 200px;
}
Преимущества и недостатки OOCSS
Преимущества OOCSS
- Хорошая переиспользуемость кода.
Недостатки OOCSS
- Сильная связанность кода ухудшает его поддержку: классы достаточно общие, могут использоваться повсеместно (нельзя просто изменить их, скорее всего придётся менять разметку).
SMACSS
SMACSS — масштабируемая и модульная архитектура для CSS (Scalable and Modular Architecture for CSS).
Идея заключается в разбиении стилей на слои и тесно связана с принципом разделения ответственности (SoC). Каждый слой выполняет только свои обязательства. Это позволяет улучшить поддержку кода.
Стили разбиваются на 5 слоёв:
- Базовые стили (base rules) — стили основных элементов страницы. Обычно тэги (body, div, input, …), псевдоклассы и псевдоэлементы, атрибуты кроме class и id (изредка class: например, стилизация custom select).
/* базовые стили */ input {} input:focus {} *:hover {} *::selection {} - Стили макета (layout rules) — стили глобальных элементов страницы (header, footer, sidebar, …), разбивающих страницу на блоки с контентом, состоящие из модулей. Автор подхода предлагает использовать id, чтобы подчеркнуть уникальность элементов, но можно этого не делать. Префикс:
layout-,l-илиgrid-. ```css #main { width: 40%; } /* глобальный элемент / #header { width: 100%; } / глобальный элемент */
/* стили макета ниже навешивается на предков глобальных элементов (например, на body) */ .l-fixed #main { width: 320px; } .l-fixed #header { width: 320px; }
* **Стили модулей** (modules rules) — стили *переиспользуемых блоков* страницы. Автор подхода советует *избегать* здесь *тэгов* и *id*. *Префикс*: `module-`, где *module* — *название модуля*.
```css
/* стили модуля */
.article {}
.article-title {}
.article-img
<div class="article">
<p class="article-title"></p>
<img class="article-img" />
<div></div>
</div>
- Стили состояния (state rules) — различные состояния модулей и структуры сайта. Автор допускает использование
!importantтолько в этом разделе. Префикс:is-./* стили состояния */ .button.is-disabled {} .tab.is-active {} #sidebar.is-mobile {} .is-hidden {} - Стили темы (theme rules) — некоторые дополнительные стили, описывающие, как модули и макет могут выглядеть. Обычно меняются время от времени. Необязательная категория (стили могут быть уже учтены в других категориях). ```css /* article.css */ .article-title { color: #000; }
/* theme.css / .article-title { color: #ff0000; } / меняем цвет с чёрного на красный в честь какого-то события */
Обычно на *каждый слой* отводится *отдельный файл*: `base.scss`, `layout.scss`, `module-name.scss`, `state.scss`, `theme.scss`.
## Atomic CSS
**Атом** является *мельчайшей частицей вещества*.
**Атомный CSS** (Atomic CSS) — подход, похожий на *OOCSS*, где в качестве объекта выступает *одно объявление* (свойство: значение), *отражающееся в названии класса*.
```css
.Mt-4 { margin-top: 8px; }
.Fs-16 { font-size: 16px; }
.W-320 { width: 320px; }
<p class="mt-4 fs-16 w-320"></p>
Атомный CSS хорошо подходит для тех, кто хочет писать макет и стили в одном месте.
Тем не менее, вручную писать код с таким подходом не очень удобно, поэтому существует инструмент Atomizer, который рекурсивно обходит html файлы и генерирует весь необходимый css.
Классы: reference
Псевдоклассы: a — :active, c — :checked, f — :focus, h — :hover.
Псевдоэлементы: a — ::after, b — ::before, fl — ::first-letter, fli — ::first-line, ph — ::placeholder.
Комбинаторы: _ — комбинатор потомков, > — комбинатор детей, + — комбинатор братьев.
Atomic CSS и CSS
Синтаксис html в случае использования Atomizer следующий
<div class="D(f) Jc(c) Op(0.8):h"></div>
<div class="article">
<p class="article_C(red)"></p>
</div>
Atomizer при запуске просмотрит html и автоматически сгенерирует css
.D\(f\) {
display: flex;
}
.Jc\(c\) {
justify-content: center;
}
.Op\(0\.8\)\:h:hover {
opacity: 0.8;
}
.article .C\(red\) {
color: red;
}
Преимущества и недостатки Atomic CSS
Преимущества OOCSS
- Относительно хорошая переиспользуемость кода (изменил значение в одном месте, изменилось везде).
- Специфичность: лучше, чем использование inline стилей, поскольку здесь стили хранятся в сгенерированных css файлах.
- Можно настроить пользовательские переменные в Atomizer:
{ "1": "1px solid #000", "foo": "2px dotted #f00", }
Недостатки OOCSS
- Названия классов содержат описание объявления, а не суть элемента, что усложняет разработку.
- К пункту выше можно добавить, что атрибуты class могут достигать невероятных размеров с увеличением количества объявлений.
- Управление отображением элемента находится в HTML (отвечающим за разметку), а должно оставаться в CSS.
- Нет встроенной поддержки некоторых вещей, в том числе и grid (нужно либо подключать дополнение или писать самому).
AMCSS
Модули атрибутов для CSS (Attribute Modules for CSS, AMCSS) — подход, использующий атрибуты и их значения вместо классов.
Как и в многих других методологиях, идея AMCSS заключается в логической группировке CSS кода.
- Модули (Modules) — замена классам; описываются атрибутами; похожи на блоки и элементы в БЭМ.
- Вариации (Variations) — различные состояния модулей; представлены значениями атрибутов; похожи на модификаторы в БЭМ.
- Черты (Traits) — коллекция значений, имеющих одну цель; похожи на SUIT utils.
Одной из целей подхода является исправление проблемы БЭМ со слишком длинными названиями классов.
<!-- /* БЭМ * / -->
<div class="button button--large button--primary"></div>
<!-- /* AMCSS * / -->
<div am-Button="large primary"></div>
Префикс am- добавляется для того, чтобы не было конфликтов с другими атрибутами.
Синтаксис AMCSS
Черты пишутся в camelCase, модули — в PascalCase.
Отношение родитель-ребёнок обозначаются дефисом.
В качестве селектора используется селектор по атрибуту ~=, выбирающий элементы, содержащие необходимый атрибут и указанные слова (разделённые пробелами) в значении атрибута.
Это позволяет создать поведение, аналогичное классам.
Значения атрибутов, подобно классам, разделяются пробелами, но при этом имеют более широкий спектр допустимых символов.
<div am-traitName="name name2 mobile:name3"></div>
<div am-ModuleName></div>
<div am-ModuleName-ChildElement></div>
<div am-ModuleName="variation"></div>
[am-traitName~="name"] {}
[am-traitName~="name2"] {}
[am-traitName~="name3"], .breakpoint-mobile [am-traitName~="mobile:name3"] {}
[am-ModuleName] {
/* Стили модуля (блока) */
}
[am-ModuleName~="variation"] {
/* Стили вариации модуля ModuleName */
}
[am-ModuleName-ChildElement] {
/* Стили дочернего элемента модуля ModuleName */
}
Стоит обратить внимание, что вариация не может существовать без базовых стилей модуля.
Любой элемент, удовлетворающий второму селектору в примере ниже, удовлетворяет и первому тоже.
[am-ModuleName] {} /* базовый атрибут и его стили */
[am-ModuleName~="variation"] {}
Черта же не имеет стилей в базовом атрибуте, но зато черты можно смешивать и сочетать в любом месте кода.
[am-traitName~="name"] {}
Существует так же другой синтаксис AMCSS, более близкий к БЭМ, но теряющий некоторые преимущества:
<div am-ModuleName am-ModuleName-variation></div>
[am-ModuleName] {}
[am-ModuleName-variation] {}
AMCSS и CSS
Модули и вариации
<div am-Article>
<p am-Article-Title="red translucent"></p>
</div>
[am-Article] {
display: flex;
}
[am-Article-Title] {
margin: 0;
}
[am-Article-Title~="red"] {
color: #ff0000;
}
[am-Article-Title~="translucent"] {
opacity: 0.5;
}
Черты
<p am-font="primary" am-color="red"></p>
[am-font~="primary"] {
font-size: 18px;
font-weight: bold;
}
[am-color~="red"] {
color: #c20606;
}
Преимущества и недостатки AMCSS
Подход похож на БЭМ, поэтому обладает теми же преимуществами:
Преимущества AMCSS
- Модульность кода и изолированность модулей друг от друга.
- Простая специфичность. (специфичность у всех атрибутов, в том числе и классов, одинаковая: 0,0,1,0).
- Решена проблема с длинными названиями классов, свойственная БЭМ. ```html
``` Встроенные стили в React (атрибут style). ```jsx /* isHovered, activeColor - переменные */ <p style= /> <p style= /> ``` Встроенные стили во Vue (атрибут style с привязкой данных): ```jsx /* isHovered, activeColor - переменные */
/* или короче */ <p :style="{ color: isHovered ? '#f00' : '#000' }" /> <p :style="{ color: activeColor }" /> ``` ### Преимущества и недостатки CSS-in-JS Изначально в JavaScript и в CSS не было модулей. Со временем в них появилась необходимость, тогда в JavaScript появилась первая модульная система CommonJS, а вслед за ней и стандартизированная версия ECMAScript Modules. CSS изначально создан для стилизации документов, поэтому в нём так и не появились встроенные модули. Одна глоабльная область видимости (scope): любой класс на сайте может быть применён к любому элементу. По мере роста приложения это приводит к проблемам, поэтому и появились методологии. С помощью этой функциональности решается проблема с названиями. *Преимущества CSS-in-JS*: * Модульность кода. * Внедрение области видимости в CSS (нужные стили импортируются). * Явные зависимости. ### Как функции css и styled работают внутри Замечание: в JavaScript есть возможность *вызвать функцию* следующим образом. ```js const css = (strings, ...vars) => `{ ${strings} }`; css`magic` // вернёт '{ magic }' ``` Это называется **тэговым шаблоном** и работает *только с шаблонными строками* \`\`. *Первым параметром* приходит *массив из подстрок*, который получается в результате *разбиения* шаблонной строки её *переменными*, *остальные параметры* — сами *переменные*. ```js `magic` // 0 переменных, массив подстрок: ['magic'], массив переменных: [] `${2*2} > ${+true}` // 2 переменные, массив подстрок: ['', ' > ', ''], массов переменных: [4, 1] ``` Сделано это для того, чтобы можно было *валидировать* и *заменять переменные*, а затем собрать и *вернуть новую строку*. Как работает функция `css`: ```jsx // произвольная функция генерации хэша const generateRandomHash = () => Math.random().toString(36).substring(7).slice(0, 5); // функция создания правила const createRuleset = (className = '', styles = '') => ` .${className} { ${styles} } `; // получаем строку стилей из параметров тэгового шаблона const getStyles = (strings = [], vars = []) => strings.map((item, index) => `${item}${vars[index] || ''}`).join(''); const css = (strings, ...vars) => { // название класса создаётся на основании хэша, обеспечивая уникальность правила const className = `css-${generateRandomHash()}`; // объединяем параметры тэгового шаблона в строку без изменений (здесь могла быть валидация и правка значений) const styles = getStyles(strings, vars); // генерируется правило const ruleset = createRuleset(className, styles); // создаётся