Когда несколько правил CSS претендуют на один и тот же элемент, побеждает не то, что написано раньше или позже само по себе, а то, что выигрывает по специфичности - весу селектора. Специфичность считается по трём разрядам: id, классы/атрибуты/псевдоклассы, теги/псевдоэлементы - и сравниваются они не суммой, а по старшинству разряда, как в системе счисления.
В этом уроке
Попробуй прямо в этом окне: текст становится зелёным, хотя по обычной специфичности должен был победить #main с его id и стать оранжевым (tomato) - именно !important на классе .txt перебивает id без !important. Удали строку с !important - и текст сразу станет оранжевым: сработает id, у которого специфичность выше, чем у класса и тега.
Специфичность - как считается вес селектора
У каждого селектора есть вес из трёх чисел: сколько в нём id, сколько классов/атрибутов/псевдоклассов и сколько тегов/псевдоэлементов. Эти три числа сравниваются по очереди слева направо, а не складываются в одно общее число - поэтому один id всегда побеждает, сколько бы классов ни было в конкурирующем селекторе.
#main { } (1 id, 0 классов, 0 тегов) побеждает .a.b.c.d.e { } (0 id, 5 классов, 0 тегов) - даже пять классов не перевешивают один id.
.card .title.big.red { }, ожидая, что три класса "победят сильнее" - три класса всё равно остаются во втором разряде, а не превращаются в id. Побеждать будут именно по числу классов в этом разряде, а не по общему "напору" селектора.
Каскад - что решает, если вес одинаковый
Если у двух правил специфичность получилась абсолютно одинаковой, вступает в игру каскад: побеждает то правило, которое в коде написано НИЖЕ. Но это самый последний аргумент - он работает только при полном равенстве специфичности, а не всегда.
.txt { color: blue; } ... .txt { color: green; } - специфичность у обоих правил одинаковая, побеждает то, что ниже, - зелёный.
#main { color: tomato; } написанное ВЫШЕ по коду, чем .txt { color: green; }, ожидая, что порядок в коде важнее специфичности - на самом деле id выигрывает в любом случае, независимо от того, что написано ниже: порядок в коде решает только при равной специфичности.
!important - козырь, который ломает обычный порядок
!important, дописанный после значения свойства, переворачивает обычный порядок: такое правило побеждает почти всё, включая инлайн-стиль без !important, - даже если у него самого низкая специфичность, вроде обычного тега. Если !important стоит у нескольких конкурирующих правил, между ними снова решает специфичность, а при её равенстве - порядок в коде.
p { color: black !important; } победит даже <p style="color: tomato;"> - !important на теге перебивает инлайн-стиль без !important, хотя обычно инлайн-стиль сильнее любого селектора.
.fix { color: red !important; } как быстрый способ "просто заставить работать" - в реальном проекте единственный способ потом переопределить такое правило - это другой !important с не меньшей специфичностью, и с каждым таким патчем становится сложнее понять, что вообще управляет стилями страницы.
Частые ошибки новичков
| Ошибка | Что происходит | Как исправить |
|---|---|---|
| Думать, что специфичность складывается в одно число | На самом деле три разряда (id / класс / тег) сравниваются по очереди, а не суммируются - десять классов не обгонят один id | Сравнивать разряды по старшинству: сначала id, потом классы, потом теги |
| Хвататься за !important, чтобы "просто заставить работать" | Переопределить такое правило потом можно только другим !important не меньшей специфичности - с каждым таким патчем разобраться в стилях становится сложнее | Сначала понять, почему нужное правило не побеждает, и поправить сам селектор, а не глушить проблему через !important |
| Считать, что порядок правил в коде решает всегда | Порядок в коде - это самый последний аргумент, он работает только при полностью одинаковой специфичности у конкурирующих правил | Сначала сравнивать специфичность, и только при равенстве смотреть, какое правило ниже |
| Забывать, что комбинаторы и универсальный селектор * не добавляют веса | article > p и article p дают одинаковую специфичность (0 id, 0 классов, 2 тега) - сам знак > ничего не весит | Считать специфичность только по реальным id/классам/тегам внутри селектора, комбинаторы и * пропускать |
Шпаргалка
| Что | Вес |
|---|---|
| id | Старший разряд - побеждает любое число классов и тегов |
| Класс, атрибут, псевдокласс | Средний разряд |
| Тег, псевдоэлемент | Младший разряд |
| Инлайн-стиль (style=" ") | Выше всех трёх разрядов, но проигрывает !important |
| !important | Переворачивает обычный порядок - побеждает почти всё, кроме более "важного" правила с не меньшей специфичностью |
Коротко: отвечаем на вопросы
Можно ли переопределить правило с !important?
Да, но только другим правилом с !important, у которого специфичность не меньше, а при равной специфичности - которое написано ниже по коду. Обычное правило без !important, даже с более высокой специфичностью, его не перебьёт.
Когда использование !important вообще оправдано?
В основном в редких случаях: переопределить инлайн-стиль, который нельзя поправить в разметке, точечный фикс в большом старом проекте, где менять исходные стили рискованно, или стили браузерного расширения. В обычной вёрстке сайта частое появление !important - сигнал разобраться со специфичностью, а не решение по умолчанию.
Складывается ли специфичность нескольких классов в одно число вместе с тегами?
Нет, три разряда (id, классы, теги) сравниваются отдельно по старшинству, а не суммируются в одно число - поэтому десять тегов в селекторе всё равно проиграют одному-единственному классу.