CSS-live.ru

Никто не знает CSS: специфичность — не каскад

Пролог (в котором едва обошлось без драки)

Прошедшие выходные ознаменовались небольшой драмой в веб-сообществе. Началась она с безобидного «теста» по CSS в твиттере Макса Штойбера, разработчика styled-components и react-boilerplate:

Насколько хорошо вы знаете CSS? (эмодзи: ученик у доски)

При таких классах:

.red { color: red; }
.blue { color: blue; }

какого цвета будут эти дивы?

<div class="red blue">
<div class="blue red">

44% из свыше 14 тысяч ответивших выбрали вариант «первый див синий, второй красный». Правильный ответ, конечно же — оба дива синие: из правил с одинаковой специфичностью применяется последнее в CSS-коде, порядок классов в HTML-разметке на это никак не влияет.

Реакция на опрос, а особенно на его результаты, была довольно бурной. Кто-то искал в вопросе дальнейший подвох и придирался к незакрытым тегам или отсутствию контента (без которого цвету, а не фону, не к чему примениться, и формально оба дива будут прозрачными:). Но во многих ответах сквозила мысль, что ни к чему разработчикам тратить время на эту путаницу, и как хорошо, мол, что нынешние инструменты типа CSS-in-JS позволяют не забивать голову такой ерундой и «сосредоточиться на главном». А такие «каверзные вопросы», мол, годятся лишь для того, чтоб «срезать» неугодных кандидатов на собеседованиях.

Многих евангелистов CSS эта мысль просто возмутила. Эрик Мейер, автор легендарной «книги с рыбами», в запале написал:

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

Потом он пояснял, что не хотел никого обидеть и действительно лишь предлагал разработчикам обратить внимание на области, основы которых не кажутся им настолько чуждыми. Но пожар уже разгорелся. Тут закономерно возмутились уже JS-разработчики: что это за язык такой, для входа в который непременно нужно показать знание каких-то странных и нелогичных шаманских ритуалов, почему-то называемых «самыми основами», хотя без них можно прекрасно обходиться? Эхо той битвы прокатывается по твиттеру до сих пор, почти не утихая.

Что сильнее всего удивило в этой драме меня — то, как много спорщиков с обеих сторон баррикад не знали, что такое каскад.

Никто не знает CSS

Это сильное утверждение — не фигура речи, я готов за него ответить. Никто не знает CSS. Ни вы. Ни я. Ни даже Эрик Мейер или Таб Аткинс. И это абсолютно нормально.

«Знать CSS» невозможно хотя бы потому, что само определение CSS как языка постоянно меняется. Модули спецификации появляются и отмирают. В них вносятся исправления, подчас незаметно переворачивающие основы. Некоторые модули существуют в двух-трех вариантах (разного уровня) одновременно, и иногда один и тот же код считается ошибкой по одному из них и валидным по второму. Поддержка браузерами — тоже не критерий: веб-стандарты — дело добровольное, поэтому браузеры поддерживают то, что им удобно и так, как им удобно. Включая такое, чего ни в одном стандарте не было.

Так что не знать, какая сейчас специфичность у какого-нибудь :matches(), не стыдно. На таких вопросах можно «засыпать» даже редакторов спецификации (они не всегда успевают за правками друг друга:). Но некоторые вещи, мне кажется, достаточно фундаментальны для того, чтобы знать их в принципе. Откуда вообще берутся стили. Как CSS обрабатывает ошибки. Общие принципы построения раскладки. Опять же, не нужно помнить это всё наизусть — даже эти знания в CSS в любой момент могут устареть и потерять актуальность! Но как минимум помнить, где найти: где лежат первоисточники (спецификации), какие первоисточники наиболее актуальны (текущий редакторский черновик, отмеченный в списке как «Current work») и т.п.

Понятие каскада, на мой взгляд, вполне заслуживает места в списке таких основ. Не зря первая буква в аббревиатуре CSS обозначает именно его, и никакие «CSS-in-JS» эту букву отменить не в силах! Тем более, что у нас есть замечательный повод обновить знания: совсем недавно спецификация для него обновилась, и в ней, как водится, появилось немало сюрпризов. С конца прошлого месяца основной спецификацией для CSS-каскада стал…

Модуль каскада и наследования 4 уровня

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

Модуль немаленький, так что начнем знакомство с ним с раздела 8, «Изменения»: что новенького (по сравнению с предыдущим, 3-м уровнем)? Основных новинок три:

  • Новое ключевое слово revert, чтобы «откатить каскад» (!);
  • Условное подключение стилей через @import с помощью директивы @supports;
  • Добавлено определение того, как каскадируются стили с ограниченной областью видимости («scoped styles» в оригинале).

Если вы внимательно следили за недавней эволюцией HTML, последний пункт может вас удивить. Казалось бы, «scoped styles» выпилили из спецификации еще два года назад, браузеры тоже отказались от их поддержки, что ж CSS-ники только сейчас «выспались» с этой новинкой?

Добавлено 10.02.2021: при очередном обновлении этой спецификации «scoped styles» из нее тоже убрали, одной загадкой стало меньше. Но их место не будет пустовать, его заняла другая новинка — «контексты инкапсуляции». Еще один наглядный пример стремительных перемен в CSS!

Но сначала надо наконец разобраться как следует, что же такое этот самый каскад?

Главная часть (в которой без драки не обошлось:)

Как часто бывает в CSS-спецификациях, ключевые понятия даются либо вообще без определения, либо определяются косвенно, через то, что они делают. Каскад — не исключение:

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

Так что каскад — никакие не «вложенные селекторы» и прочие домыслы, что часто ошибочно ассоциируют с этим словом. На самом деле это своего рода турнир на выбывание, этакий «мортал комбат» для CSS-объявлений разного вида и происхождения, которые должны сразиться друг с другом, чтобы в итоге остался только один — сильнейший. Этот победитель получит всё, именно его значение будет определять вид элемента, а проигравших ждет полное забвение. Оставшаяся часть 6-го раздела спецификации разъясняет правила этого турнира.


Рис. 1. CSS-каскад в представлении художника XIX века: водопад, на котором значения свойств борются друг с другом до последнего выжившего (фрагмент изображения с Wikimedia Commons)

Первое, на что обращают внимание для CSS-объявлений — их происхождение и важность (эх, совсем как в жизни:). Затем — новинка! — контекст инкапсуляции. Специфичность, вокруг которой вечно ломают столько копий, идет лишь третьей (так что те, кто не считал ее «фундаментальным знанием», не так уж неправы:). Наконец, последним идет порядок в коде — но именно он решает исход поединка, когда прочие факторы равны. Свежий боец всегда побеждает равного по силе, но уставшего, следующее правило того же происхождения, важности, области видимости и специфичности перекрывает предыдущее. Кратко рассмотрим все эти факторы.

Происхождение (источник) и важность

Небольшой лайфхак: если вам что-то очень не нравится в CSS-спецификациях — непонятно, громоздко, просто не нравится — можно пойти на гитхаб рабочей группы CSS и попросить это убрать. Лично мне в предыдущих редакциях CSS-каскада не нравились какие-то «перекрывающие стили»: я их не понимал (на чем однажды попался), они загромождали алгоритм, были плохо документированы и — главное — никогда (?) не работали ни в одном браузере. В общем, их больше нет:). И порядок приоритетности стилей по источнику и важности отныне такой (по убыванию):

  1. Стили во время CSS-переходов (transition)
  2. Браузерные стили с !important
  3. Пользовательские стили с !important
  4. Авторские (т.е. наши, разработческие) стили с !important
  5. Стили во время анимаций
  6. Обычные (т.е. без !important) авторские стили
  7. Обычные пользовательские стили
  8. Обычные браузерные стили

У этого порядка есть логика. Браузерные стили — последний резерв, если ничего лучше не нашлось. Если пользователю они неудобны, он вправе переопределить их. Но дизайнер и разработчик сайта/приложения, скорее всего, могут сделать еще лучше и удобнее (они же профессионалы!), так что есть смысл положиться на их выбор. Далее, от анимации не было бы видимого проку, если бы значения, которые она меняет, не перекрывали статичные стили — поэтому эти изменения должны быть приоритетнее. Кроме действительно важных стилей. Которые, в свою очередь, в совсем уж крайнем случае тоже может переопределить пользователь (да, обычно дизайнер знает лучше, но всегда найдутся исключения). Наконец, у браузеров могут быть свои «секреты», которые нельзя переопределять никому (например, невыделяемость текста в поле типа password в WebKit). Ну а плавный переход от одного значения к другому возможен лишь тогда, когда это «переходное» значение приоритетнее начального и конечного — откуда бы те ни пришли (иначе вместо перехода будет резкий «перескок»).

Со стилями при анимациях есть два неочевидных, но важных нюанса. Во-первых, к ним относятся не только обычные @keyframe-анимации, но и анимации через Web Animations API: для CSS-каскада это одно и то же. А во-вторых, как и три года назад, правильно по стандарту им назначает приоритет один лишь Firefox (добавлено 26.03.2020: теперь и Chrome!). В остальных браузерах анимации перекрывают даже стиль с !important. В некоторых зарубежных статьях встречается утверждение, будто именно это — стандартное поведение: не верьте им, они врут (как минимум, заблуждаются).

И все те, кто гордо бьют себя пяткой в грудь «Я не использую каскад!» и при этом не довольствуются браузерными стилями, а перекрывают их своими — без разницы, «-in-JS» или просто так — получается, тоже врут:). Не шутите с каскадом!

Область видимости Контекст инкапсуляции (обновлено 10.02.2021)

(Добавлено 10.02.2021: раньше, когда спецификация еще упоминала «Scoped styles», этот раздел рассказывал о путанице между ними и новым механизмом изоляции стилей, основанным на теневой DOM. Всё это удалено ввиду неактуальности:)

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

Общий принцип здесь такой: для обычных объявлений, без !important, самыми приоритетными будут стили самого внешнего компонента, а для !important-объявлений — наоборот, приоритет будет у самого внутреннего. То есть, как правило, вложенные компоненты будут стараться максимально вписаться в своё окружение, по возможности подхватывая стили того места, куда их вставили. Их собственные стили служат фолбэком. Но если какой-то стиль для компонента настолько исключительно важен, что для него оправдано задать !important (может быть, что-то важное для доступности, типа минимального размера активной области?), то такое важное исключение будет максимально защищено от случайного «перезатирания».

Специфичность

А вот про нее мы много говорить не будем. Про нее вы и так знаете. Или нет… но это и впрямь не так уж важно. Специфичность — не первое дело в каскаде. И даже не второе.

И правила специфичности можно не зубрить. Классические шпаргалки, помогающие в них ориентироваться — по рыбам или с помощью темной стороны Силы — еще актуальны. Исключения (добавлено 4.02.2022: пожалуй, уже пора их запоминать) — только функциональные псевдоклассы :where() (специфичность всегда равна 0), :is(), :not(), :has() (берут специфичность самого специфичного аргумента) и :nth-child(... of <что-то>) (специфичность этого «что-то» плюс один псевдокласс).

Порядок в коде

Здесь тоже вроде бы всё ясно: чем позже в коде, тем важнее. Ясно, что классы-модификаторы должны идти после немодифицированных классов (и позже подключаться, если они в отдельном файле). Но не забывайте, что стили анимаций — даже из того же самого файла — считаются другим источником, причем более приоритетным. И даже если объявить их в самом начале, они всё равно перекроют по каскаду вашу идеально плоскую структуру классов. И даже стили, бережно расставленные скриптом по атрибутам style! А в старых «вебкитятах» — даже если у них будет !important (это уже не по стандарту, но учитывать это пока еще надо).

Дальнейшие приключения CSS-значений

Но сражение и победа в смертельной битве каскада — еще не конец истории CSS-значения. Это всего лишь переход с первой стадии их жизненного цикла на вторую. А всего этих стадий целых шесть! Давайте быстренько пробежимся по ним: это тоже важные понятия для того, чтобы претендовать на знание CSS.

Объявленные значения (declared values)

Сначала CSS отбирает «претендентов» на участие в турнире: выбирает правила, в принципе относящиеся к данному элементу — из стилей, присоединенных к документу, внутри сработавших условий @media, @supports и т.д., с совпадающими селекторами (а может, и не селекторами — взять тот же атрибут style). Из браузерных стилей, из стилей сайта/приложения — откуда угодно. Может случиться и такое, что претендентов на какое-то свойство для какого-то элемента не окажется вообще (список объявленных значений может быть пустым).

Каскадное значение (cascaded value)

Этот «титул» достается единственному победителю в каскаде. Если претендентов не нашлось и турнир не состоялся — нет и победителя (каскадного значения).

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

Указанное значение (specified value)

Право считаться указанным значением свойства для элемента — тот «приз», за который значения «бились» в каскаде. Но если каскадного значения нет (турнир не состоялся), указанное значение свойству всё равно назначается. Это значение по умолчанию. То, что помечено в спецификации для этого свойства как «начальное» (initial).

Вот почему, например, все неизвестные HTML-элементы, для которых заведомо нет никаких браузерных стилей, отображаются как display:inline — это начальное значение для display.

Вычисленное значение (computed value)

Чтобы как-то работать со значением дальше, указанное значение надо преобразовать во что-то конкретное. Например, практически всё с размерностью длины переводится в пиксели: font-size: 0.6em при дефолтных 16px у предка превратятся в 9.6px. И потомки элемента унаследуют именно этот результат вычисления — вычисленное значение.

Для разных свойств бывают свои нюансы: скажем, для line-height 1.5em и 150% пересчитаются в пиксели (и это значение в пикселях достанется потомку, независимо от его собственного font-size), а вот безразмерный множитель 1.5 так и унаследуется безразмерным (и для каждого потомка будет переводиться в пиксели заново, с его font-size). Напротив, для width проценты наследуются именно как проценты, без перевода в пиксели (так что width: inherit в родителе с width: 80% будет соответствовать тем же 80% от родителя, или 64% от его контейнера). Обычно такие нюансы оговариваются в спецификации для каждого свойства, в отдельной одноименной графе, так что присматривайтесь к ней повнимательнее.


Рис. 2. Пример, где искать нюансы про вычисленные значения в спецификациях

А вот метод getComputedStyle, несмотря на название, далеко не всегда возвращает именно вычисленное значение (о чем спецификация CSS Cascade честно предупреждает).

И еще важный нюанс: вычисленное значение у свойства есть даже тогда, когда оно к этому элементу не применяется. Например, вычисленные значения flex могут быть не только у флекс-элементов. Или border-spacing — не только у таблиц. А поскольку последнее как раз наследуется, это может привести к занятным побочным эффектам. Скажем, весьма забавно выглядит результат <html style=”border-spacing:100px”> на странице с Bootstrap 3 (и вообще везде, где активно используется «micro clearfix» Галлахера). Можете даже по-доброму разыграть так кого-то из менее опытных в CSS коллег (главное, потом всё-таки объясните им, в чем был фокус и откуда взялись гигантские отступы:).

Используемое значение (used value)

На этой стадии значения уже никуда не передаются, так что браузер может «допиливать» их до чего-то совсем конкретного: переводит в пиксели всё, что можно в них перевести (включая безразмерные line-height и процентные width), все значения типа normal и auto тоже переводятся в какие-то конкретные, нормальные числа.

Именно эти значения возвращает метод getComputedStyle для некоторых свойств (по историческим причинам). А вот если свойство к элементу не применяется, то и используемого значения у него нет.

Фактическое значение (actual value)

Но и используемое значение — не конец его приключений. Иногда браузер не может применить значение, не округлив его: скажем, вместо рамки толщиной 2.2px приходится рисовать просто 2px. Или упомянутые 9.6px для размера шрифта отображать как 10px (или 9px… когда-нибудь мы узнаем это более точно. Или не узнаем. Но попытаемся. На всякий случай не забывайте следить за обновлениями!:).

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

Управление каскадом

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

Для этого модуль каскада и наследования предусмотрел целых 4 универсальных значения, подходящих для всех свойств: initial, inherit, unset и revert. И целое универсальное свойство all, которое умеет применять эти универсальные значения ко всем свойствам сразу (ну, почти). Познакомимся с ними поближе?

Значение initial

Присваивает свойству его начальное значение. То самое, которое свойство получает при «неявке претендентов на турнир», при отсутствии каскадного. Для display это всегда будет inline, для большинства свойств шрифта — normal, для размеров — auto, для отступов — 0, для большинства «спецэффектов» — none

Значение inherit

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

Например, можно поставить width: inherit элементу, вложенному в контейнер с процентной шириной, и он всегда будет занимать в нем такую же часть, как сам контейнер в своем родителе. Получится такой «фрактальный» интерфейс, где части подобны целому (правда, не могу подобрать пример, где это надо:).

Значение unset

Фактически, отменяет каскадное значение, как будто для этого свойства элемента нигде никаких стилей не задано. Наследуемые свойства при этом получают родительское вычисленное значение (как при inherit), а ненаследуемые — «обнуляются» до своего начального (как при initial).

Фактически, это значение, особенно если «коврово» применить его для всего подряд — это готовый CSS-ресет («ластик»):

* {
  all: unset; /* отменит все браузерные и пользовательские стили без !important для всех элементов */
}

Только, пожалуй, слишком уж радикальный. Сбрасывать display у <div> и <p> до inline — наверное, чересчур…

Значение revert

Для тех, кому unset кажется слишком жестким решением, модуль каскада 4 уровня добавил более мягкую альтернативу: «обнулить» каскадное значение не полностью («вообще нигде никакого»), а отменить только один текущий уровень каскада. Если оно в авторских стилях — оставить только браузерные и пользовательские. Если в пользовательских — оставить только браузерные. Ну а если в браузерных — не оставлять ничего (там revert и unset эквивалентны). Стили анимаций для этого механизма (и только для него!) считаются частью авторских стилей.

К сожалению: опробовать это значение в деле труднее, чем предыдущие: поддерживается оно пока только в Safari. Но значения initial и unset (появившиеся еще на 3 уровне модуля) работают везде, кроме IE, а inherit (оно вообще еще из CSS2.1) — даже в нем.

Свойство all

Это уникальное свойство формально считается сокращением (shorthand) для всех остальных свойств CSS. За исключением кастомных свойств (которые CSS-переменные), а также (по историческим причинам) свойств, отвечающих за направление текста — direction и unicode-bidi. Впрочем, от использования последних спецификация всячески отговаривает — мол, за это должна отвечать разметка (атрибут dir и т.п.).

Но, в отличие от других сокращений, у него нет полной записи. И значение у него может быть только одним из четырех универсальных, перечисленных выше: все свойства сразу можно только либо сбросить на начальные значения, либо унаследовать от родителя, либо «отменить», как будто они вообще не были указаны, либо «откатить» на один уровень каскада (в нашем случае, до браузерных стилей).

CSS-ресет на этом свойстве — фактически в одну строчку — мы уже видели. Увы, использовать его на практике пока проблематично: по обыкновению, картину портят браузеры от Microsoft (и примкнувшая к ним Opera Mini), а существующие полифилы просто разворачивают его в огромный список свойств (может быть, в вашем частном случае это годится, но веб и так слишком раздут…). Но для современных браузеров уже есть возможность легко и быстро избавить компонент от любых зависимостей от внешних стилей. А нужные стили, например, для подстройки под тему оформления, передавать в него теми же CSS-переменными.

На Microsoft-овском форуме UserVoice у свойства all стоит статус «Мы подумаем», то есть «Поставлено на очередь» (on the backlog). Но почему-то за него отдано всего чуть больше 600 голосов. По-моему, маловато будет (для такой важной фичи-то). Давайте поднажмем и добьем хотя бы до тысячи! :)

Эпилог (в котором обязательно должна победить дружба)

Веб-платформа стала настолько необъятной, что в ней не всегда ориентируются даже ее создатели. CSS — важная часть платформы, большая и сложная. Он не идеален, но реальной альтернативы для него нет. Инструменты — пре-, пост-, и чего-угодно-процессоры, фреймворки, библиотеки и методологии — не заменяют его, а лишь упрощают работу с ним, помогая обойти типичные острые углы и очевидные грабли. У него немало недостатков, но многие из них — продолжение его сильных сторон.

CSS сложен. Времена, когда он был простым языком для раскрашивания ссылок, прошли тогда же, когда и JavaScript перестал быть несерьезным языком для менюшек и летающих по экрану снежинок. Не знать CSS не стыдно: он постоянно меняется и развивается, и знания в нем, как и в других областях IT, быстро устаревают. Стыдно — на мой взгляд — потешаться над людьми, которые знают его лучше, обладая лишь иллюзией знания а-ля «есть какой-то там “каскад”, он злая-бяка-выжечь-каленым железом, JS — спасение».

Так относится ли каскад к основам CSS, без знания которых нельзя и подступаться к нему, или это «тайное знание для маньяков», которое обычному человеку ни к чему? На мой взгляд, всё дело в глубине этого знания. Знать наизусть все тонкости специфичности, включая хаки вида .please.please.please {} и :not(#just-ordinary-class), пожалуй, и впрямь ни к чему. Но в принципе знать, что 1) все стили в вебе каскадные, 2) они специально устроены так, что значения для свойств могут приходить из нескольких мест и иметь разный приоритет в зависимости от каких-то условий, 3) если делать всё правильно — хорошими инструментами, по хорошей методологии и т.п. — то у нужных стилей будет достаточный приоритет, но 4) существуют особые случаи (как с теми же анимациями), и это не обязательно баг браузеров или стандарта — на мой взгляд, человек, считающий себя веб-разработчиком, всё-таки должен. А частности всегда можно уточнить в справочнике, спецификации… или у более опытных в CSS коллег:). Которые не «дурака валяют, раскрашивая буковки», а точно так же решают свою сложную часть общей задачи по созданию современного динамичного, интерактивного, адаптивно-отзывчивого, доступного, удобного, красивого и быстрого веб-интерфейса.

Сила веба — в объединении разных технологий, сила команды — в объединении разных знаний и умений. Давайте вместе делать веб лучше, учиться друг у друга и стремиться стать выдающимися специалистами!

P.S. Это тоже может быть интересно:

18 комментариев

  1. Спасибо за труд, за отличную познавательную статью. Вот вроде и 10 лет в профессии, только и узнаешь, что ничего не знаешь.

  2. Илья, это надо переводить на английский и публиковать на SM!
    Спасибо огромное!

  3. Здравствуйте. Почитал статью, очень познавательно. Верстал сегодня страницу и заметил одну особенность, которую раньше или не замечал, или не делал таким образом. Вот пример: https://jsfiddle.net/dn8cw7h3/

    По специфичности оба селектора равны, я ожидал, что цвет текста внешнего блока переопределит цвет внутреннего, и цвет текста для внутреннего блока не сработает, т. к. в css идет сначала правило для .block-outer, а после — .block-inner, но почему то этого не произошло. Почему так?

    В верстке относительно недавно, может что-то из азов недопонял.

    1. Стили, указанные непосредственно для самого элемента, всегда переопределяют унаследованные им от родителя. Даже если стили родителя заданы более приоритетным селектором, они применятся к потомку лишь в том случае, если своих стилей (точнее, своего каскадного значения для соответствующих свойств) у него вообще не будет.

  4. Спасибо. По этому вопросу, оказывается, был пробел у меня.

    Недавно узнал об этом сайте. Очень интересные и познавательные здесь статьи и переводы.

    1. Если уж совсем придираться, то не обязательно белого, а цвета фона их предка:). А пустые дивы без других стилей и вовсе будут невидимы из-за нулевого размера. Да, код в вопросе оставляет простор для трактовки. Но подразумевалось вычисленное значение их свойства color.

  5. Статья и вправду очень интересная! Очень много деталей и ссылок на ресурсы! Одного прочтения мало, конечно, чтобы во всем разобраться.

    Спасибо!

  6. Прочитала я втрой раз статью, и третий и четвертый. Одно пока не могу понять:
    И порядок приоритетности стилей по источнику и важности отныне такой (по убыванию):

    Стили во время CSS-переходов (transition)
    Браузерные стили с !important
    Пользовательские стили с !important
    Авторские (т.е. наши, разработческие) стили с !important
    Стили во время анимаций
    Обычные (т.е. без !important) авторские стили
    Обычные пользовательские стили
    Обычные браузерные стили

    Вроде все понятно на словах. Анимация не должна влиять на стили с !important (Firefix в этом плане работает плавильно). Но вот с transition все оказалось не так просто. transition стоит выше всех по приоритету, даже выше стилей с !important. Но на практике получается, что если на hover не задать !important, то transition не будет иметь никакого эффекта.
    https://codepen.io/cristinacavruc/pen/ReXxee
    Может я что-то упустила?

    1. Во-первых, прошу прощения за самоуправство спам-фильтра (из-за которого я не сразу увидел комментарий), мы провели с ним воспитательную работу, надеюсь, это недоразумение не повторится.

      Во-вторых, максимальная приоритетность при transition означает, что «мгновенный» текущий стиль ничем не перебить, когда переход уже идет. Например, не получится во время перехода мгновенно изменить стиль элемента скриптом — стиль всё равно будет меняться плавно (а если есть transition-delay, то еще и не сразу), даже если новое значение задать в атрибуте style с !important. Но само наличие свойства transition на приоритетность других свойств в том же правиле никак не влияет, они по-прежнему считаются просто авторскими стилями. И их приоритетность определяется по обычным правилам, по которым !important перекрывает стиль без !important даже из более специфичного селектора — а значит, стиль при :hover не изменится и, соответственно, transition не произойдет.

  7. Указанное значение (specified value)
    Право считаться указанным значением свойства для элемента — тот «приз», за который значения «бились» в каскаде. Но если каскадного значения нет (турнир не состоялся), указанное значение свойству всё равно назначается. Это значение по умолчанию. То, что помечено в спецификации для этого свойства как «начальное» (initial).

    Вот почему, например, все неизвестные HTML-элементы, для которых заведомо нет никаких браузерных стилей, отображаются как display:inline — это начальное значение для display.

    То есть сокращённые свойства, как оказывается, не Антипаттерн.

     

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

  8. В Chrome Canary уже есть исправление работы каскада для стилей при анимациях, видимо в этой части скоро большая часть браузеров будет работать по стандарту.

  9. До этой статьи почему-то думал, что каскад в CSS это что-то связанное с наследованием. Видимо, исходил из одного из значений этого слова «естественный или искусственный водопад, низвергающийся уступами, также система водопадов». Тут мне представлялось как заданное у родителя значение наследуемого свойства сначала спадает на его дочерние элементы, потом от них на внуков и т.д. Такой красивый каскад получается :)

    И, вот, в который раз убедился, что интуитивное понимание порой может сильно подвести. Каскад — это «турнир на выбывание». Но тогда почему этот турнир называют именно каскадом?

    1. Там действительно в основе была метафора водопада с уступами, но чуть в другом смысле: сверху (на вход) «выплескивают» кучу возможных значений для конкретного свойства конкретного элемента, из разных источников, они «растекаются» по «уступам» разных уровней в зависимости от разных критериев приоритетности, и каждое «более близкое к цели» отменяет все предыдущие, пока не останется одно последнее.

  10. Что же такой ваш «контекст инкапсуляции»?
    Можете поделиться ссылкой на материал или объяснить суть нововведения?

Оставить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Получать новые комментарии по электронной почте. Вы можете подписаться без комментирования.