Проблема специфичности CSS и ключевое слово !important

Проблема специфичности CSS и ключевое слово !important

В этой статье мы проведем рефакторинг CSS-кода одного проекта с проблемами специфичности.

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

Определение

MDN Web Docs описывает специфичность, как: способ, с помощью которого браузеры определяют, какие значения свойств CSS наиболее соответствуют элементу и, следовательно, будут применены


Правила

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



В таком случае будет использоваться специфичность CSS-селектора. Чем выше его специфичность, тем больше шансов, что браузер применит именно это объявление CSS.


nav a {
	color: green;
}

a {
	color: red;
}


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


Важный момент: !important – это не CSS- селектор, а ключевое слово, которое принудительно переопределяет CSS-правила вне зависимости от значения специфичности, источника или исходного порядка следования селекторов. Несколько примеров использования:


  • Временные решения (пример: вы временно заматываете изолентой подтекающую трубу);
  • Переопределение встроенного стиля;
  • Тестирование/отладка.


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

Проект

Пара слов о проекте, который будем рефакторить: это лендинг в стиле Netflix с использованием интерфейса MovieDB.



Таблица стилей

Основная цель – удалить ключевое слово “!important” из CSS-правил и провести рефакторинг кода, чтобы он применял только правила специфичности.

Ниже представлена таблица стилей проекта.


@import url("https://fonts.googleapis.com/css?family=Montserrat:400,400i,700");
body {
	margin: 0;
	padding: 0;
	overflow-x: hidden;
}
.wrapper {
	width: 100%;
}
.wrapper #header {
	position: fixed;
	z-index: 300;
	padding: 15px;
	width: calc(100% - 30px);
	display: flex;
	justify-content: space-between;
	align-items: center;
	background: linear-gradient(to bottom, black 0%, transparent 100%);
}
.wrapper #header #brand-logo {
	color: #d32f2f;
	text-shadow: 1px 1px 2px black;
	letter-spacing: 5px;
	text-transform: uppercase;
	font-family: Montserrat;
	font-weight: bold;
	font-size: 22px;
}
.wrapper #header #menu-icon {
	display: none;
}
.wrapper #header .nav-link,
.wrapper #header .icon {
	color: #bdbdbd;
	cursor: pointer;
}
.wrapper #header .nav-menu {
	width: 400px;
	display: flex;
	justify-content: space-around;
	align-items: center;
}
.wrapper #header .nav-link {
	padding: 5px 10px;
	font-size: 15px;
	font-family: century gothic;
	text-decoration: none;
	transition: background-color 0.2s ease-in;
}
.wrapper #header .nav-link:hover {
	color: #c62828;
	background-color: rgba(0, 0, 0, 0.7);
}
.wrapper #header .icon {
	font-size: 16px;
}
.wrapper #header .icon:hover {
	color: #c62828;
}
.wrapper #site-banner,
.wrapper #categories {
	width: 100%;
}
.wrapper #site-banner {
	height: 550px;
	background-image: url("https://s1.gifyu.com/images/rampage_2018-1024x576.jpg");
	background-size: cover;
	background-position: center;
	background-repeat: no-repeat;
	background-attachment: fixed;
}
.wrapper #site-banner .main-movie-title,
.wrapper #site-banner .watch-btn,
.wrapper #site-banner .main-overview {
	position: absolute;
	z-index: 3;
}
.wrapper #site-banner .main-movie-title, .wrapper #site-banner .watch-btn {
	text-transform: uppercase;
}
.wrapper #site-banner .main-movie-title {
	top: 120px;
	left: 20px;
	background: -webkit-linear-gradient(#ff9100, #dd2c00);
	-webkit-background-clip: text;
	-webkit-text-fill-color: transparent;
	font-size: 55px;
	font-family: Montserrat;
	font-weight: bold;
}
.wrapper #site-banner .main-overview {
	width: 400px;
	top: 230px;
	left: 25px;
	color: #fafafa;
	line-height: 25px;
	font-family: helvetica;
}
.wrapper #site-banner .watch-btn {
	width: 150px;
	height: 35px;
	top: 350px;
	left: 25px;
	border: none;
	border-radius: 20px;
	color: #fafafa;
	cursor: pointer;
	transition: all 0.2s ease-in;
	background-color: #ff0000;
	box-shadow: 1px 5px 15px #940000;
}
.wrapper #site-banner .watch-btn:hover {
	color: #F5F5F5;
	background-color: #940000;
}
.wrapper .after {
	position: relative;
	top: 0;
	left: 0;
	z-index: 2;
	width: 100%;
	height: 100%;
	background-color: rgba(0, 0, 0, 0.3);
}
.wrapper #categories {
	padding: 30px 0;
	display: flex;
	flex-direction: column;
	background: linear-gradient(to top, #090909 0%, #000000 100%);
	overflow: hidden;
}
.wrapper #categories .category {
	margin: 30px 0;
}
.wrapper #categories .category-header, .wrapper #categories .content {
	margin-left: 20px;
	color: #B0BEC5;
	font-family: helvetica;
}
.wrapper #categories .category-header {
	margin-bottom: 50px;
	font-weight: normal;
	letter-spacing: 5px;
}
.wrapper #categories .content {
	position: relative;
	right: 0;
	display: flex;
	justify-content: flex-start;
	transition: all 3s ease-in-out;
}
.wrapper #categories .movie {
	margin-right: 10px;
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: flex-start;
}
.wrapper #categories .movie-img {
	transition: all 0.2s ease-in;
}
.wrapper #categories .movie-img:hover {
	-webkit-filter: contrast(1.1);
	filter: contrast(1.1);
	-webkit-transform: scale(1.05);
	transform: scale(1.05);
	cursor: pointer;
}
.wrapper #footer {
	width: 100%;
	height: 120px;
	background-color: #090909;
	display: flex;
	align-items: flex-end;
	justify-content: flex-start;
}
.wrapper #footer #copyright-label {
	margin-left: 20px;
	padding: 10px;
	color: rgba(255, 255, 255, 0.3);
	opacity: 0.7;
	letter-spacing: 2px;
	font-family: helvetica;
	font-size: 12px;
}
//Media Query
@media (max-width: 750px) {
	.nav-menu {
		visibility: hidden;
	}
	#menu-icon {
		display: block !important;
		font-size: 22px;
 	}
	.main-movie-title {
		font-size: 45px !important;
	}
	.main-overview {
		width: 350px !important;
		font-size: 14px !important;
	}
	.watch-btn {
		width: 130px !important;
		height: 25px !important;
		font-size: 13px;
	}
	.movie-img {
		width: 170px;
	}
}


Мы видим, что чаще всего !important используется в разделе с медиа-запросами, описывающими стили, которые браузер применяет при ширине экрана меньше 750 пикселей.


Что же произойдет, если удалить ключевое слово !important из правил CSS, к которым оно применяется? У нас потеряется «верховод», который принудительно переопределяет CSS правила других селекторов для одного HTML-элемента. А конфликты между CSS-правилами браузер начнет проверять строго по таблице стилей.


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


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


Проблема определена. Пора переходить к ее решению!


Для начала найдем CSS-селекторы, соответствующие селекторам медиа-запросов.


.wrapper #header #menu-icon {
	display: none;
}
.wrapper #site-banner .main-movie-title {
	...
	font-size: 55px;
	...
}
.wrapper #site-banner .main-overview {
	width: 400px;
	...
}
.wrapper #site-banner .watch-btn {
	width: 150px;
	height: 35px;
	...
}
@media (max-width: 750px) {
#menu-icon {
	display: block !important;
	...
}
.main-movie-title {
	font-size: 45px !important;
}
.main-overview {
	width: 350px !important;
	font-size: 14px !important;
}
.watch-btn {
	width: 130px !important;
	height: 25px !important;
	...
}
}


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


Чтобы исправить ситуацию, необходимо увеличить значение специфичности CSS-селекторов в медиа-запросах. Тогда при одинаковой специфичности нескольких селекторов для одного HTML-элемента, браузер будет руководствоваться исходным порядком следования. Тогда при ширине экрана менее 750 пикселей будут применяться CSS-правила из медиа-запросов (которые расположены ниже по таблице стилей).


Конечный результат выглядит так:

.wrapper #header #menu-icon {
	display: none;
}
.wrapper #site-banner .main-movie-title {
	...
	font-size: 55px;
	...
}
.wrapper #site-banner .main-overview {
	width: 400px;
	...
}
.wrapper #site-banner .watch-btn {
	width: 150px;
	height: 35px;
	...
}
@media (max-width: 750px) {
	.wrapper #header #menu-icon {
		display: block;
		...
	}
	.wrapper #site-banner .main-movie-title {
		font-size: 45px;
	}
	.wrapper #site-banner .main-overview {
		width: 350px;
		font-size: 14px;
	}
	.wrapper #site-banner .watch-btn {
		width: 130px;
		height: 25px;
		font-size: 13px;
	}
}

Вот и все! Мы удалили все следы присутствия ключевого слова !important в стилях. Читаемость кода возрастает на глазах. После рефакторинга такую таблицу будет легче применять и поддерживать.

Заключение

Мы узнали о том, как именно браузеры выбирают CSS стилей по исходному порядку следования, специфичности и источникам селекторов. Познакомились с проблемами, возникающими при использовании !important в CSS, и научились сводить количество этих ключевых слов в коде к минимуму.


Больше не нужно добавлять !important для решения проблем – есть куда более достойные решения.


Сама концепция специфичности достаточно объемна. Но стоит разобраться в ней на практическом примере с подробной документацией, как вы научитесь применять эти знания в своих проектах.

(3.3)
27 Ноября 2018

Возврат к списку