Этапы обработки значений свойств в CSS

Краткий пересказ раздела "Value Processing" CSS модуля каскада.

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

Отбор всех объявлений значений для свойств, который происходит по правилам фильтрации:

  • стили должны принадлежать текущему документу
  • стили должны проходить через условия @media и @supports
  • стили должны принадлежать блоку правил, селектор которого совпадает с элементом
  • декларации должны быть валидными: имя свойства должно быть известно браузеру и значение должно совпадать с синтаксисом данного свойства. Так, например, объявление color: 20px; будет проигнорировано.

Также на этом этапе происходит конвертирование свойств их старого синтаксиса в новый (value aliasing), например, замена значений с префиксами на стандартные.

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

На этом этапе из списка объявленных значений должно остаться лишь одно. Формируется это значение на основе главной особенности CSS – каскада. При отборе значения во внимание принимаются:

  • происхождение (браузерные, пользовательские, авторские стили) и важность (наличие !important)
  • контекст инкапсуляции (речь о Shadow DOM)
  • инлайн-стили (стили, приклеплённые к элементу)
  • каскадные слои @layer
  • cпецифичность селекторов
  • область близости (видимости) @scope
  • порядок появления в коде

Если этап каскада не дал победителя (такое может быть, если не было и объявленных значений), то происходит процесс получения значений по умолчанию defaulting:

  • ненаследумые свойства приобретают значение initial
  • наследумые свойства берут значения свойства от родительского элемента

Разработчики могут повлиять на значение с помощью глобальных ключевых слов inherit, initial, unset, revert и revert-layer.

Значения могут прийти в относительном виде, например, font-size: 1em, font-size: small, font-weight: bolder, color: tomato. Здесь происходит их преобразование в абсолютный вид согласно таблице определения свойства (для каждого свойства нужно смотреть раздел "Computed Value" в спецификациях или на MDN).

Этот этап расчётов происходит ещё до создания раскладки страницы, поэтому не все значения будут преобразованы, например, width: 50% так и останется 50%.

Примеры преобразований:

  • значения с относительными едниницами измерения (vw, vh, em, ex)
  • значения в виде ключевых слов smaller, bolder и др.
  • процентные значения, то только для некоторых свойств, например, font-size: 125%, но width: 50% и line-height: 1.5 не будут преобразованы
  • относительные url, например, background-image: url('../image.png')

Важная заметка: вычисленное значение у свойства есть даже тогда, когда оно не применяется к этому элементу. Например, border-spacing можно задать не только таблице, а так как оно наследуется, то может повлиять на все вложенные таблицы. То есть вычисленные значения передаются по наследству.

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

  • width: 80%в width: 1200px
  • line-height: 1.5 в line-height: 24px

Также на этом этапе отбрасываются те значения, которые не имеют смысла для данного элемента, например, свойство flex не будет иметь used value на элементе, который не является дочерним элементом flex-контейнера.

Для заметки: метод в JavaScript window.getComputedStyle невсегда возвращает computed value, это может быть и used value. В общем случае, такое значение называют resolved value.

Как правило, Used value уже готово к использованию, но тут вступают ограничения среды отображения и браузеру может потребоваться, например, округление дробных чисел там, где нужны целые (border-width: 2.2px -> border-width: 2px) или нужно подкорректировать размер текста на основе font-size-adjust.