По старшинству. Когда порядок в CSS важен

Старший аспирант «Нетологии» Алёна Батицкая перевела статью Криса Койера Precedence in CSS (When Order of CSS Matters) о приоритетности в CSS.

В обычный день, когда вы пишете CSS, вы вряд ли задумываетесь о приоритетности. Это и не нужно! Вам придется вспомнить об этом ровно в тот момент, когда у вас случится совпадение селекторов с одинаковой специфичностью.

Специфичность подразумевает, что порядок имеет значение. Стили, расположенные ниже по каскаду, выигрывают.

В пределах одной таблицы стилей

Предположим, у нас есть HTML-разметка:

!DOCTYPE html> 
html lang="ru"> 
head> 
  meta charset="UTF-8"> 
  title>Документ/title> 
  link rel="stylesheet" href="styles.css"> 
/head> 
body> 
  div class="module module-foo module-bar"> 
    Содержимое 
  /div> 
/body> 
/html> 

Порядок атрибутов в HTML значения не имеет. Все дело в порядке внутри таблицы стилей. Давайте посмотрим внимательнее на background:

 
/* 
  У всех селекторов ниже одинаковая специфичность 
*/ 
 
.module { 
  background: #ccc; 
  padding: 20px; 
  max-width: 400px; 
  margin: 20px auto; 
} 
 
.module-foo { 
  background: orange; 
} 
 
/* Объявлен ПОСЛЕДНИМ, выигрывает это свойство/значение */ 
.module-bar { 
  background: lightblue; /* Я выйграл! */ 
} 

Живой пример

Намеренно запутанный пример

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

Куски расположились в link rel="stylesheet">, внутри тега style type="text/css"> или подключены при помощи @import

 
 
!DOCTYPE html> 
html lang="ru"> 
head> 
  meta charset="UTF-8"> 
  title>Документ/title> 
 
   
  link rel="stylesheet" href="1.css"> 
 
   
  style> 
    .module-baz { 
      background-color: pink; 
    } 
  /style> 
 
/head> 
body> 
  div class="module module-foo module-bar module-baz"> 
    Содержимое 
  /div> 
 
   
  style> 
    @import "2.css"; 
    /* 
      Содержит 
      .module-bar { background: #f06d06; } 
    */ 
  /style> 
 
/body> 
/html> 

Я подписал куски номерами 1, 2 и 3. Все они содержат CSS-селекторы с одинаковой специфичностью. №3 объявлен последним, потому стили, которые он содержит, применяться к странице.

Асинхронная загрузка CSS так же учитывает порядок документов

Возможно, вы загружаете CSS при помощи чудесного подгрузчика наподобие loadCSS. Что произойдет, если мы захотим загрузить четвертый CSS-файл? С настройками, которые совпадут с одним из подключенных файлов в нашем извилистом примере. loadCSS по умолчанию подключит таблицу стилей в самый конец шапки сайта (перед. Новый файл станет третьим по порядку, а @import — соответственно четвертым. И все равно выиграет!

 
 
DOCTYPE html> 
html lang="ru"> 
head> 
  meta charset="UTF-8"> 
  title>Документ/title> 
  link rel="stylesheet" href="1.css"> 
 
  script src="loadCSS.js">/script> 
  script> 
    loadCSS("2.css"); 
  /script> 
 
   
/head> 
body> 
  div class="module module-foo module-bar module-late"> 
    Содержимое 
  /div> 
/body> 
/html> 

Вообще-то код, в котором link> или style> расположены внутри , будет считаться невалидным. Хотя и будет работать. Поэтому стили, загруженные при помощи loadCSS чаще всего будут выигрывать.

После окончания верстки принято проверять ее при помощи валидатора. Это поможет быстро найти и устранить ошибки. Проверка производится согласно с текущей спецификацией HTML/CSS.

Помимо этого вы можете указать специальный ID для контроля порядка загрузки CSS:

 
 
script id="loadcss"> 
  // загрузить CSS-файл прямо перед элементом, содержащим следующий код 
  loadCSS("path/to/mystylesheet.css", document.getElementById("loadcss")); 
/script> 

Критичный CSS может создать проблему?

Одна из причин, по которой вы используете loadCSS — намеренная попытка отложить загрузку ваших таблиц стилей, поскольку вы используете критичный CSS внутри. Этот прием помогает подгрузить стили в первых пакетах и ускорить время отрисовки страницы.

 
 
DOCTYPE html> 
html lang="ru"> 
head> 
  meta charset="UTF-8"> 
  title>Документ/title> 
  style> 
    /* #1 
       Критичный CSS находится здесь */ 
  /style> 
  script> 
    /* #2 
       Загрузка остального CSS 
    */ 
  /script> 
/head> 
body> 
  div class="module module-foo module-bar"> 
    Содержимое 
  /div> 
/body> 
/html> 

Практика критичного CSS предполагает перемещение CSS-селекторов на более высокую (в коде) позицию. В кусок №1. В часть с более низким приоритетом и, как следствие, простую для переопределения. Поэтому, теоретически, да, могут возникнуть конфликты/изменения между этапами, когда на странице загрузился только критичный CSS и когда весь CSS загрузился и примерился к содержимому страницы. Но таблицы стилей загрузятся полностью и будут находится ниже по коду относительно критичного CSS, так что в конечном итоге вы получите то, чего ожидали.

Наследование доставит проблемы?

В препроцессорах да, может.

Предположим, вы хотите стилизовать элемент с вариациями:

 
 
div class="module module-variation">Содержимое/div> 

И код препроцессора будет следующим (максимально упрощен для наглядности:

 
 
%variation { 
  background: orange; 
} 
 
.module { 
  background: #ccc; 
  padding: 20px; 
  max-width: 400px; 
  margin: 20px auto; 
} 
 
.module-variation { 
  @extend %variation; 
} 

Вы думаете: ОК, .module-variation идет ПОСЛЕДНИМ в списке селекторов, поэтому он выигрывает и цвет фона должен быть оранжевым. Но это не так, поскольку наследование (extend) передвигает селектор туда, где это наследование объявляется. В нашем случае это место прямо перед тем кодом, который мы пытаемся переопределить. Скомпилированный CSS выглядит так:

 
 
.module-variation { 
  background: orange; 
} 
 
.module { 
  background: #ccc; 
  padding: 20px; 
  max-width: 400px; 
  margin: 20px auto; 
} 

И так background будет цвета #ccc, а не того, которого мы хотели. Вероятно, проще всего решить эту проблему при помощи специфичности, а не при помощи порядка селекторов.

Нативное наследование, когда оно наконец появится, скорее всего будет менее запутанным.

Глупо этим управлять

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

Полный текст статьи читайте на Нетология