Слоистый CSS со взбитыми
сливками
Никита Дубко, HR Tech Яндекса
Никита Дубко
- 16 лет в профессии
- верстаю на CSS 5
- делаю дизайн-систему
- подкастер
- беларус
Уютный проект
.block {
color: red;
}
.block__element {
width: 100500px;
}
.block--modifier {
color: tomato;
}
БЭМ
Специфичность
ID
:pseudo, .class, [attr]
::pseudo, element
#header
:not(#id)
:hover
.card
[hidden]
::before
span
.card {
background-color: #eee;
}
.card__title {
color: #000;
}
.card--brand {
background-color: tomato;
}
.card--brand .card__title {
color: #fff;
}
.card { /* (0, 1, 0) */
background-color: #eee;
}
.card__title { /* (0, 1, 0) */
color: #000;
}
.card--brand { /* (0, 1, 0) */
background-color: tomato;
}
.card--brand .card__title { /* (0, 2, 0) */
color: #fff;
}
@import "global.css";
@import "blocks/card.css";
@import "blocks/article.css";
@import "blocks/input.css";
А давайте добавим модный
CSS-фреймворк!
Кто-то в команде
.input-group >
:not(:first-child):not(.dropdown-menu):not(.valid-tooltip):not(.valid-feedback):not(.invalid-tooltip):not(.invalid-feedback) {
margin-left: calc(var(--bs-border-width) * -1);
border-top-left-radius: 0;
border-bottom-left-radius: 0;
}
Я никому ничего не должен!
Автор библиотеки
!important
.block.block.block.block.block {
--my-specificity: is-ok;
}
.block[class] {
--my-specificity: is-ok;
}
.block:not(#wtf) {
--my-specificity: is-ok;
}
Origin
- 🧑💻 разработчик
- 👶 пользователь
- 💻 браузер
a:-webkit-any-link {
color: -webkit-link;
cursor: pointer;
text-decoration: underline;
}
В случае возникновения конфликтов последнее слово должно быть за пользователем, <...>.
Håkon Lie
Origin
!important
💻 браузер
!important
👶 пользователь
!important
🧑💻 разработчик
- - - - - -
- 🧑💻 разработчик
- 👶 пользователь
- 💻 браузер
⚠️ !important
не увеличивает специфичность
✅ !important
изменяет «слой»
Context
!important
🥷 shadow
!important
📃 host
- - - - - -
- 📃 host
- 🥷 shadow
.navbar-inverse .navbar-nav > li > a {
color: #9d9d9d;
}
.link { color: red; }
.nav-link { color: lightsteelblue; }
a:-webkit-any-link { /* Browser style */
color: -webkit-link;
cursor: pointer;
text-decoration: underline;
}
⬇️ Сверху вниз
Селектор |
Свойство |
Значение |
.navbar-inverse .navbar-nav > li > a |
color |
#9d9d9d |
.link |
color |
red |
.nav-link |
color |
lightsteelblue |
a:-webkit-any-link |
color |
-webkit-link |
a:-webkit-any-link |
cursor |
pointer |
a:-webkit-any-link |
text-decoration |
underline |
|
ID |
:pseudo, .class, [attr] |
::pseudo, element |
Origin |
.navbar-inverse .navbar-nav > li > a |
0 |
2 |
2 |
Dev |
.link |
0 |
1 |
0 |
Dev |
.nav-link |
0 |
1 |
0 |
Dev |
a:-webkit-any-link |
0 |
1 |
1 |
Browser |
⬇️ Сверху вниз
Селектор |
Свойство |
Значение |
Вес |
.navbar-inverse .navbar-nav > li > a |
color |
#9d9d9d |
0,2,2 |
.link |
color |
red |
0,1,0 |
.nav-link |
color |
lightsteelblue |
0,1,0 |
a:-webkit-any-link |
color |
-webkit-link |
0,1,1 |
a:-webkit-any-link |
cursor |
pointer |
0,1,1 |
a:-webkit-any-link |
text-decoration |
underline |
0,1,1 |
⬆️ Снизу вверх
Селектор |
Свойство |
Значение |
Вес |
.nav-link |
color |
lightsteelblue |
0,1,0 |
.link |
color |
red |
0,1,0 |
.navbar-inverse .navbar-nav > li > a |
color |
#9d9d9d |
0,2,2 |
a:-webkit-any-link |
color |
-webkit-link |
0,1,1 |
a:-webkit-any-link |
cursor |
pointer |
0,1,1 |
a:-webkit-any-link |
text-decoration |
underline |
0,1,1 |
🔀 Сортируем
Селектор |
Свойство |
Значение |
Вес |
.navbar-inverse .navbar-nav > li > a |
color |
#9d9d9d |
0,2,2 |
.nav-link |
color |
lightsteelblue |
0,1,0 |
.link |
color |
red |
0,1,0 |
a:-webkit-any-link |
color |
-webkit-link |
0,1,1 |
a:-webkit-any-link |
cursor |
pointer |
0,1,1 |
a:-webkit-any-link |
text-decoration |
underline |
0,1,1 |
<nav class="navbar-inverse">
<ul class="navbar-nav">
<li><a href="#" class="nav-link">Link 1</a></li>
<li><a href="#" class="nav-link">Link 2</a></li>
</ul>
</nav>
.navbar-inverse .navbar-nav > li > a ✅ |
color |
#9d9d9d |
.nav-link ❌ |
color |
lightsteelblue |
.link ❌ |
color |
red |
a:-webkit-any-link ❌ |
color |
-webkit-link |
a:-webkit-any-link ✅ |
cursor |
pointer |
a:-webkit-any-link ✅ |
text-decoration |
underline |
@layer reset, bootstrap, utilities;
@import url('bootstrap.css') layer(bootstrap);
@layer utilities {
.sr-only { /* ... */ }
}
@layer reset {
*, *::before, *::after {
box-sizing: border-box;
}
}
p { font-size: 18px; }
Reset
Bootstrap
Utilities
< Undefined >
@layer second {
.sr-only { /* ... */ }
}
@layer first {
*, *::before, *::after {
box-sizing: border-box;
}
}
p {
font-size: 18px;
}
Second
First
< Undefined >
@layer
reset,
bootstrap,
utilities;
Reset
Bootstrap
Utilities
< Undefined >
@import url('bootstrap.css') layer(bootstrap);
/* Undefined layer */
p {
font-size: 18px;
}
@layer reset {
*, *::before, *::after { box-sizing: border-box; }
}
@layer utilities {
.sr-only { /* ... */ }
}
@import url('bootstrap.css')
layer(bootstrap);
p { font-size: 18px; }
Reset
Utilities
Bootstrap
< Undefined >
@layer reset {
*, *::before, *::after { box-sizing: border-box; }
}
@layer utilities {
.sr-only { /* ... */ }
}
@layer bootstrap {
.some { /* ... */ }
}
p { font-size: 18px; }
Reset
Utilities
Bootstrap
< Undefined >
<p id="id" class="class">Text</p>
@layer first, second;
@layer second {
p { color: yellow; }
#id { color: red; }
}
@layer first {
.class { color: blue; }
}
p { color: green; }
⬇️ Сверху вниз
Слой |
Селектор |
Свойство |
Вес |
second |
p |
color: yellow |
0,0,1 |
second |
#id |
color: red |
1,0,0 |
first |
.class |
color: blue |
0,1,0 |
undefined |
p |
color: green |
0,0,1 |
🔀 Сортируем слои
Слой |
Селектор |
Свойство |
Вес |
undefined |
p |
color: green |
0,0,1 |
first |
.class |
color: blue |
0,1,0 |
second |
p |
color: yellow |
0,0,1 |
second |
#id |
color: red |
1,0,0 |
🔀 Сортируем внутри слоёв
Слой |
Селектор |
Свойство |
Вес |
undefined |
p |
color: green |
0,0,1 |
first |
.class |
color: blue |
0,1,0 |
second |
#id |
color: red |
1,0,0 |
second |
p |
color: yellow |
0,0,1 |
⏺️ Ищем победителя
Слой |
Селектор |
Свойство |
Вес |
undefined |
p |
color: green |
0,0,1 |
first |
.class |
color: blue |
0,1,0 |
second |
#id |
color: red |
1,0,0 |
second |
p |
color: yellow |
0,0,1 |
!important
Слои
- < Undefined >
- Utilities
- Components
- Reset
@layer
reset,
components,
utilities;
Слои
!important
Reset
!important
Components
!important
Utilities
!important
< Undefined >
- - - - - -
- < Undefined >
- Utilities
- Components
- Reset
@layer
reset,
components,
utilities;
@layer a {
@layer common, important;
@layer important { /* ... */ }
@layer common { /* ... */ }
@layer { /* ... */ }
}
@layer a.common {
/* ... */
}
a.common
a.important
a.< anonimous >
a.< undefined >
< undefined >
@layer utilities {
@layer important {
/* ... */
}
}
@layer utilities.common {
/* ... */
}
a.important
a.common
a.< undefined >
< undefined >
@layer default {
a { color: maroon; }
}
@layer theme {
a {
color: var(--brand-primary, purple);
}
.no-theme {
color: revert-layer;
}
}
Вы уже используете @layers
😉
Пора сделать ещё один шаг 🚶🏻♂️
@import url('bootstrap.css');
button {
font-size: 18px;
}
@import url('bootstrap.css') layer(bootstrap);
button {
font-size: 18px;
}
@import url('reset.css') layer(reset);
@layer reset {
*,
*::before,
*::after {
/* Some custom reset styles */
}
}
p { font-size: 18px; }
@layer reset, components, debug;
@import url('debug.css') layer(debug);
@import url('reset.css') layer(reset);
@layer components {
/* Some styles */
}
reset.css
- фреймворки
- глобальные стили
- стили компонентов
- темизация
- классы-утилиты
@layer
reset,
frameworks.bootstrap,
framework.material,
global,
components.design-system,
components.custom,
theme.light,
theme.dark,
utilities;