От декларации до отображения
Sofi Valitova, CSS-engineer
Валитова София
От декларации
до отображения

:root { color: yellow;}div { color: 40px;}.class { color: gold !important;}#id { color: 1ms;}@keyframes color {from { color: fuchsia; }to { color: tomato; }}:has(.class, #id) {color: purple !important;}* { animation: color 2s infinite; }

Поплыли!
Value processing
- Declared value
- Cascaded value
- Specified value
- Computed value
- Used Value
- Actual Value
Declared value
Список всех значений, подходящих этому элементу.
- Селектор
- Условия
- Проверка типа
Declared value
Список всех значений, подходящих этому элементу.
- Селектор
- Условия
- Проверка типа
Селектор
<div class="a">CSS</div><style>#id { /* ... */ }.a { /* ... */ }div { /* ... */ }</style>
Declared value
Сбор всех деклараций свойства, применимых к элементу
- Селектор
- Условия
- Проверка типа
Условия
<div class="a">Safari IOs</div><style>@media (max-width: 600px) {}@supports (display: flex) {}@media (min-width: 1200px) {}</style>
Declared value
Сбор всех деклараций свойства, применимых к элементу
- Селектор
- Условия
- Проверка типа
Проверка типа
<div class="a">CSS</div><style>.a {height: 12ms;height: 12em;}</style>


Короткие свойства (shorthand) 🦖

Короткие свойства (shorthand) 🦖
background: url('image.svg');то же самое, что:
background-image: url('image.svg');background-color: initial;background-origin: initial;....

Выбрали декларации
| свойство | значение | селектор | место |
| border-left-color | red | #abc | cssWritingModes.html:12 |
| border-left-style | solid | #id | style.css:14 |
| height | 10px | - | style attribute |
| color | green | [attr='value'] | style.css:6 |
| border-left-color | red | .a | style.css:123 |
Cascading
Style
Sheets
Что такое каскад?
Cascaded value
Каскад принимает неупорядоченный список объявленных значений, сортирует их по приоритету их объявления, и выводит одно значение.
Cascaded value
- Уровни объявления
- Слои
@layer - Специфичность селекторов
- Порядок объявления
Уровни объявления
| Декларации из Transition |
| !important декларации user-агента |
| !important декларации пользователя |
| !important декларации автора (то есть разработчика) |
| Animation декларации |
| Декларации автора (то есть разработчика) |
| Декларации пользователя |
| Декларации user-агента |
Уровни объявления
| Декларации из Transition | transition |
| !important декларации user-агента | ! user-agent |
| !important декларации пользователя | ! user |
| !important декларации автора (то есть разработчика) | ! author |
| Animation декларации | animation |
| Декларации автора (то есть разработчика) | author |
| Декларации пользователя | user |
| Декларации user-агента | user-agent |
Декларации user-агента
- transition
- ! user-agent
- ! user
- ! author
- animation
- author
- user
- user-agent

Декларации user-агента
- transition
- ! user-agent
- ! user
- ! author
- animation
- author
- user
- user-agent

Декларации автора
- transition
- ! user-agent
- ! user
- ! author
- animation
- author
- user
- user-agent
.blue { color: blue;}.red { color: red;}Декларации автора
- transition
- ! user-agent
- ! user
- ! author
- animation
- author
- user
- user-agent
.blue { color: blue;}.red { color: red !important;}Декларации анимаций
- transition
- ! user-agent
- ! user
- ! author
- animation
- author
- user
- user-agent
.blue { color: red; animation: anim 3s infinite;}@keyframes anim { 50% {color: green;}}Декларации анимаций
- transition
- ! user-agent
- ! user
- ! author
- animation
- author
- user
- user-agent
.blue { color: red !important; animation: anim 3s infinite;}@keyframes anim { 50% {color: green;}}Декларации переходов
- transition
- ! user-agent
- ! user
- ! author
- animation
- author
- user
- user-agent
.red { color: red; transition: color 1s;}.green color: green;}Декларации переходов
- transition
- ! user-agent
- ! user
- ! author
- animation
- author
- user
- user-agent
.red { color: red; transition: color 1s;}.green color: green !important;}Декларации пользователя

Уровни объявления
| Декларации из Transition |
| !important декларации user-агента |
| !important декларации пользователя |
| !important декларации автора (то есть разработчика) |
| Animation декларации |
| Декларации автора (то есть разработчика) |
| Декларации пользователя |
| Декларации user-агента |
История
Декларации анимаций в 2020
.blue { color: red !important; animation: anim 3s infinite;}@keyframes anim { 50% {color: green;}}



Выводы из истории:
Не молчите
о проблеме
Cascaded value
- Уровни объявления
- Слои
@layer - Специфичность селекторов
- Порядок объявления
Слои @layer
Способ создать уровни объявления внутри стилей автора.
@layer default, components, theme;@layer default {button { color: red; }}@import url('theme.css') layer(theme); }@layer components {button { color: green; }}
А что, если у нас две декларации на одном уровне?
Две декларации на одном уровне?
<div class="red" id="blue">CSS</div><style>#blue { color: blue; }.red { color: red; }</style>
Две декларации на одном уровне?
<div class="red" id="blue">CSS</div><style>#blue { color: blue !important; }.red { color: red !important; }</style>
Здесь нет конфликта селекторов
- transition
- ! user-agent
- ! user
- ! author
- animation
- author
- user
- user-agent
<div class="red" id="blue"> CSS</div><style> .red { color: red !important; } #id { color: blue; }</style>Коэффициент специфичности
Набор чисел, позволяющий определить приоритет селектора.
- как разряды в числе
- как семантическое версионирование
Коэффициент специфичности
Селектора делятся на 3 уровня:
#id | +1 | 0 | 0 |
.class, :hover,[name="value"] | 0 | +1 | 0 |
::before, div | 0 | 0 | +1 |
Коэффициент специфичности
Селектора делятся на 3 уровня:
#id | 1 * 100 | 0 * 10 | 0 * 1 | 100 |
.class, :hover,[name="value"] | 0 * 100 | 1 * 10 | 0 * 1 | 010 |
::before, div | 0 * 100 | 0 * 10 | 1 * 1 | 001 |
Коэффициент специфичности
| span | 0 * 100 | 0 * 10 | 1 * 1 | 001 |
| .class #id | 1 * 100 | 1 * 10 | 0 * 1 | 110 |
| .class #id::before:hover | 1 * 100 | 2 * 10 | 1 * 1 | 121 |
| #id [name="value"] | 1 * 100 | 1 * 10 | 0 * 1 | 110 |
Коэффициент специфичности

А минусы где?
Сравним 11 классов и один идентификатор:
#id = 1 * 10^2 + 0 * 10^1 + 0 * 10^0 = 100
.a.b.c.d.e.f.e.r.g.t.h =
= 0 * 10^2 + 11 * 10^1 + 0 * 10^0 = 110
⁕⁕⁕ ⁕⁕
⁕⁕⁕⁕⁕?????
11 классов больше идентификатора???
На самом деле чуть-чуть не так
Используется степень двойки.
1 * 10^2 + 1 * 10^1 + 1 * 10^0
1 * 2^24 + 1 * 2^16 + 1 * 2^8

Семантическое версионирование
1.0.2Коэффициент специфичности
| селектор | major | minor | patch | результат |
| span | 0 | 0 | 1 | 0.0.1 |
| .class #id | 1 | 1 | 1 | 1.1.0 |
| .class #id::before:hover | 1 | 2 | 1 | 1.2.1 |
| #id [name="value"] | 1 | 1 | 0 | 1.1.0 |
Коэффициент специфичности
| селектор | major | minor | patch | результат |
| #id | 1 | 0 | 0 | 1.0.0 |
| .a.b.c.d.e.f.e.r.g.t.h | 0 | 11 | 0 | 0.11.0 |
polypane.app/css-specificity-calculator/


Атрибут style
Считается специфичнее всего для целей этого этапа.
<div id="id" style="color:red">CSS</div><style>#id { color: blue; }<style>
Порядок объявления
А что, если у нас два селектора одной специфичности?
https://twitter.com/mxstbr/status/1038073603311448064<div class="red blue">CSS</div><style>.blue { color: blue; }.red { color: red; }</style>
Порядок объявления
Чем ближе к концу документа, тем приоритетнее
https://twitter.com/mxstbr/status/1038073603311448064<div class="red blue">CSS</div><style>.blue { color: blue; }.red { color: red; }</style>
Выбрали одну декларацию
| свойство | значение | селектор | место |
| height | 100px | #abc | index.html:12 |
| height | 10em | #id | style.css:14 |
| height | calc(50% - 25px) | - | style="" |
| height | 50% | [n='4'] | style.css:6 |
| height | 1vh | .a | style.css:123 |
Specified value
А что, если свойство не указали ни мы, ни юзер-агент?
Specified value
А что, если свойство не указали ни мы, ни юзер-агент?
Берем initial value

Теперь у свойства точно есть значение
Computed value
Значение, которое наследуется.
height: 100px; // => 100pxheight: inherit // => 100px font-size: 10px;height: 5em; // => 50px CV=50px font-size: 20px;height: inherit; // => 50px CV=50px Computed value
- Вычисляются зависимые величины.
height: 100px; // => 100pxheight: 50%; // => 50px CV = 50%height: inherit; // => 25px CV = 50% Computed value
- Вычисляются зависимые величины. Кроме процентов!
--h: 100px;height: var(--h); // => 100px CV=100px --h: 10px;height: inherit; // => 100px CV=100px Computed value
- Проценты не вычисляются. Но остальные зависимые величины вычисляются.
- Подставляются переменные.
После этого этапа у нас есть значение, в котором посчитано все, что можно посчитать без отрисовки документа.
Used value
Считаем проценты
height: 50%;/* CV = 50% *//* UV = 25px */
Считаем calc()
width: calc(100px - 50%);/* CV = calc(100px - 50%) *//* UV = 25px */
Немного про calc()
width: calc(-50px);width: calc(2s);width: calc(50% - 50px);
width: 100px; // => 100pxwidth: calc(50% - 25px); // => UV = 25pxwidth: inherit; // => -12.5px ??? Range Checking (Проверка диапазона)
Приводим к ближайшему допустимому.

width: 100px; // => 100pxwidth: calc(50% - 25px); // => UV = 25pxwidth: inherit; // => 0px Считаем calc-size()
height: calc-size(auto, size - 50%);/* CV = calc-size(auto, size - 50%) *//* UV = 25px */
Справка про calc-size()
calc-size( <calc-size-basis>, <calc-sum> )- Базис
- Выражение для вычисления
Справка про calc-size()
width: calc-size(max-content, size / 2);Actual value
Преобразования на основе ограничений среды.
- Округление пикселей до целых.
- Приведение цвета в тот, который может отобразить экран.
Один слайд, чтобы править всеми
- Отбираем декларации
- Выбираем одну
- Наследуем её
- Вычисляем
- Отрисовываем на устройстве
Источники
- Спецификация css-cascade-4
- Про Value Definition Syntax
- Про Value Processing
- Почтовая рассылка www-style
София Валитова из KiskoLabs
- ariarzer@gmail.com
- Twitter – @ariarzer
- Telegram – @ariarzer
- Мой канал про css – @css_mind
- Мой блог про css – ariarzer.dev
Презентация сделана с помощью Shower.