Меня зовут София. Я - CSS инженер. Это мой блог.

Если вам очень больно без стилей, вы можете их.

CSS VALUE PROCESSING

Дисклеймер: статья представляет собой неполный перевод одного из разделов спецификации c некоторыми дополнениями.

После того, как user agent проанализировал документ и построил DOM, он должен назначить каждому элементу в дереве и, соответственно, каждому блоку в структуре форматирования значение для каждого свойства.

Итоговым значением каждого css-свойства является результат последовательного вычисления шести других значений:

  1. Declared value - сбор всех деклараций свойства, применимых к элементу.
  2. Cascaded value - выбор из списка одного подходящего значения по заданным правилам.
  3. Specified value - этап для определения единственного значения свойства.
  4. Computed value - проведение всех возможных операций над значением, которые не нарушают корректность его наследования.
  5. Used value - проведение всех оставшихся операций.
  6. Actual value - преобразование значение на основе ограничений среды отображения.

Declared value

Сбор всех деклараций свойства, применимых к элементу. При отборе значений учитывается:

  1. подходят ли они по типу данных для этого свойства
  2. находятся ли они внутри подходящего css condition (@media, @supports ,…)

Например:

@media (max-width: 756px) {
  .class {
    width: 100%;
  }
}

.class {
  width: 520px;
}

.class {
  width: 520ms;
}

.class2 {
  width: 500em;
}

Declared value для такого кода для свойства width для элемента <div class="class"></div> для планшетов будет выглядеть примерно как:

  1. декларация в селекторе .class, строчка такая-то, документ такой-то (или атрибут style, или инлайновые стили, в общем место объявления)
  2. декларация в селекторе .class, строчка такая-то, документ такой-то

Объявление ширины в микросекундах не будет включено в Declared value, потому что не валидно по типу, здесь должен быть distance unit. Объявление из селектора .class2 не пройдет потому что селектор не подходит блоку.

А на широком экране будет просто:

  1. декларация в селекторе .class, строчка такая-то, документ такой-то

... потому что @media-выражение не будет выполняться.

Cascaded value

Задача каскада - из всех потенциально возможных значений выбрать одно. Он принимает неупорядоченный список объявленных свойств, сортирует их как определено ниже, и выводит одно.

Уровни объявления

Существует восемь возможных уровней, где можно объявить значение свойства. Их приоритет в порядке убывания:

  1. Декларации из Transition
  2. !important декларации user-агента
  3. !important декларации пользователя
  4. !important декларации автора (то есть разработчика)
  5. Animation декларации
  6. Декларации автора (то есть разработчика)
  7. Декларации пользователя
  8. Декларации user-агента

Например:

<div class="blue" id="red"> CSS </div>
<style>
  .blue {
      color: blue !important;
  }

  .blue {
      color: red;
  }
</style>

Текст в блоке будет синего цвета, потому что уровень объявления !important декларации автора выше чем просто декларации автора.

Еще пример:

<div class="blue"> CSS </div>
<style>
  .blue {
    color: blue !important;
    animation: anim 3s infinite;
  }
  .@keyframes anim {
     50% {color: green;}
   }
</style>

Не смотря на наличие анимации цвета, ее не будет видно, потому что декларация цвета внутри директивы @keyframe находятся на уровне определения Animation декларации, который ниже чем !important декларации автора.

Специфичность селекторов

Каждая декларация имеет ту же специфичность, что и правило стиля, в котором она объявлена. На этом шаге декларации, которые не принадлежат правилу стиля (например, содержимое атрибута стиля), считаются более специфичными, чем любой селектор. Декларация с самой высокой специфичностью побеждает.

Как считается коэффициент специфичности?

Селекторы делятся на три уровня:

  1. по идентификатору
  2. по классу, по псевдоклассу и по атрибуту
  3. по псевдоэлементу и по тегу

Каждый из этих уровней при расчете специфичности конкретного селектора можно рассматривать образом, похожим на семантическое версионирование. Старшая, мажорная версия - количество селекторов первого уровня, минорная - селекторов второго уровня, патч - третьего.

selectors major minor patch
#id + 1 0 0
.class, :hover, [name="value"] 0 + 1 0
::before, div 0 0 + 1

Говорят что один селектор специфичнее другого, когда "версия" первого больше "версии" второго.

Например:

selector major minor patch total
span 0 0 1 0.0.1
.class #id 1 1 0 1.1.0
.class #id:hover::before 1 2 1 1.2.1
#id [name="value"] 1 1 0 1.1.0

Третий селектор побеждает в каскаде, так его аналог в семвере был бы самой старшей версией из имеющихся.

Дополнительно следует сказать что в такой системе гарантируется, что .a.b.c.d.e.f.e.r.g.t.h = 0.11.0 не будет иметь больший коэффициент специфичности чем #id = 1.0.0.

На практике все сказанное выше означает, что в такой ситуации текст будет красным даже при наведении мыши:

<div class="class" id="id" attr="attr"> CSS </div>
<style>
  div.class[attr="attr"]:hover {
    color: green; /* 0.3.1 */
  }

  #id {
    color: red; /* 1.0.0 */
  }
</style>

Порядок объявления

Последняя декларация в порядке документа выигрывает. Для этого:

Specified value

Промежуточный итог после каскада: если список на входе был не пустой - то отдается Cascaded value, а если нет, то для наследуемых свойств - Computed value родителя (если есть), или Initial value по спеке в остальных случаях.

Computed value

Как правило, Computed value разрешает значение, насколько это возможно, без раскладки документа или выполнения других дорогостоящих или трудно распараллеливаемых операций, таких как сетевые запросы или получение значений, отличных от элемента и его родителя. Computed value - значение, которое наследуют вложенные блоки как inherit. Computed value существует, даже если свойство не применяется непосредственно в элементу. Однако некоторые свойства могут изменить способ определения вычисленного значения в зависимости от того, применяется ли свойство к элементу.

Примеры вычисления Computed value:

Общий пример:

height: 10wh; /* CV = 100px */
  height: 50%; /* CV = 50px */
    height: inherit; /* CV = 25px */
/* три вложенных блока */

Used value

Used value является результатом взятия Computed value и завершения любых оставшихся вычислений, чтобы сделать его абсолютным теоретическим значением, используемым в макете документа. Например вычисление calc или вычисление процентов. Если свойство не применяется к этому элементу, то элемент не имеет used value для этого свойства.

Actual value

Значение преобразуется на основе ограничений среды отображения. Например:

Источники

Post Scriptum

Вопросы и предложения пишите мне в телеграм @ariarzer :)

За помощь в написании и всём остальном спасибо @SelenIT2 ❤️