Недавно Крис Койер отвечал на вопросы читателей Smashing Magazine. Один из вопросов был о том, как распознать код CSS с «душком»:
Я подумал, что могу расширить и дополнить ответ Криса исходя из собственного опыта.
Я работаю в BSkyB. Я делаю большие сайты — над последним из них я тружусь уже больше года. Плохой код CSS доставляет мне очень много проблем. Когда занимаешься одним сайтом месяцами, ты просто не можешь себе позволить плохой код, и его обязательно надо исправлять.
Я хочу поделиться несколькими вещами, на которые я обращаю внимание прежде всего, чтобы составить впечатление о качестве, сопровождаемости и чистоте кода CSS.
Любые правила CSS, которые отменяют ранее установленные стили (кроме случая сброса стилей) — это тревожный звоночек. Каскадные таблицы стилей по определению должны наследовать предыдущим определениям и дополнять их, а не отменять.
Любое определение вроде:
обычно не значит ничего хорошего. Если вам приходится обнулять
Здесь мы задаём элементам
Теперь у нас уже 10 строк кода и уродливое имя класса. Гораздо лучше будет сделать так:
8 строк, никакой отмены стилей и красивое, осмысленное имя класса.
Продвигаясь вниз по файлу стилей, добавляйте правила, а не отнимайте. Если вам приходится отменять ранее установленные стили, скорее всего вы добавили их слишком рано.
Представьте себе CSS-файл на десяток тысяч строк с подобными отменами стилей. Куча лишнего кода! Постепенно добавляйте новые определения поверх старых, более простых, не начинайте сооружать слишком сложные правила слишком рано, иначе вы напишете гораздо больше кода, а делать он будет гораздо меньше.
Когда я вижу, что какое-то правило отменяет предыдущее, я почти наверняка знаю, что стили организованы неправильно и нуждаются в переработке.
Их я особенно ненавижу! Магическое число — это бессмысленное значение, которое используется потому, что оно «просто работает». Например:
Проблема в том, что эти 37 пикселей — чистая случайность, и на эту константу совершенно нельзя положиться. Что если кто-то изменит размер шрифта в пункте меню, и он будет иметь 29, а не 37 пикселей в высоту? Что если в Chrome пункт меню будет иметь 37 пикселей высоту, а в IE — 36? Это число работает только в одном конкретном случае.
Никогда, никогда не используйте значения которые «просто работают». В предыдущем примере гораздо лучше было бы написать
И это не единственная проблема с магическими числами. Кроме ненадёжности они создают ещё и проблему коммуникации. Как другой разработчик сможет понять откуда взялось это число? Если ваш код больше и сложнее приведённого выше примера, и какое-то из магических чисел вдруг перестало работать, вы столкнётесь с тем, что:
Магические числа — это плохо. Они быстро устаревают, они мешают другим разработчикам, их нельзя объяснить и на них нельзя положиться.
Нет ничего хуже, чем наткнуться на такое необъяснимое число в чужом коде. Зачем оно здесь? Что оно значит? Можно ли его трогать или не стоит? Я задаю эти вопросы всякий раз, как вижу такое число. И самый главный вопрос: «Как можно добиться такого же результата без магии?»
Бегите от магических чисел как от чумы!
Примерно вот такие:
Это селекторы, в которые добавлены совершенно лишние уточнения. Они плохи потому что:
На самом деле их можно (и нужно) было бы записать так:
Вот теперь можно применить
Хотя производительность браузера страдает от таких селекторов не очень сильно, она всё же страдает. Зачем заставлять его перебирать все элементы
Все они могут быть сильно сокращены или переписаны:
Каждый раз, когда я натыкаюсь на чересчур подробные селекторы, я пытаюсь выяснить, почему они заданы именно так, и нельзя ли их сократить.
Так же как и магические числа, они не сулят ничего хорошего. Вот пример:
Гораздо лучше было бы написать:
И так каждый раз, когда изменится размер шрифта заголовка. Вот так гораздо лучше:
Вроде бы, разница невелика, но в крупном проекте она может иметь большое значение.
Кстати, это относится не только к
Единственный случай, когда действительно имеет смысл захардкодить абсолютное значение — это в случае работы со вещами, которые всегда должны иметь определённый размер, например, спрайтами.
Это один из крайних частных случаев жёстко заданных магических чисел и некоторых других приёмов, которые используются, чтобы заставить вёрстку работать:
Это ужасный стиль! Все эти уродливые правила наворочены с единственной целью — запихнуть элемент на нужное место любой ценой. Такой код говорит либо об очень плохо спроектированной вёрстке, либо о недостаточном понимании того, как работает блочная модель CSS, либо о том и другом одновременно.
Если вы хорошо продумали вёрстку и разбираетесь в блочной модели, вам вряд ли придётся использовать грубую силу. Если я вижу подобный код, я сразу стараюсь разобраться, в чём проблема, и не надо ли вернуться на несколько шагов назад, чтобы избавиться от необходимости писать такие костыли.
Под опасными селекторами я понимаю такие, которые намного шире, чем необходимо.Вот самый простой и очевидный пример такого селектора:
Зачем, зачем накрывать каждый
Давайте рассмотрим пример с
то это уже совсем не так правильно. Элемент
Задавать такие подробные стили для такого общего селектора очень опасно. Они просочатся в совершенно непредсказуемые места, как только вы начнёте повторно использовать этот элемент.
Убедитесь, что ваши селекторы бьют точно в цель.
Вот ещё пример:
Когда я вижу, как стили применяются к элементу или к сильно обобщённому классу, как в этом примере, я начинаю паниковать. Я знаю, что они чересчур универсальны и у меня будут проблемы. Эти стили могут быть унаследованы в таких местах, в которых это будет совершенно нежелательно, и мне придётся их отменять.
Использовать
Это значит, что его можно использовать тогда, когда вы абсолютно уверены, что вам всегда будет нужно, чтобы это стиль имел приоритет, и вы знаете об этом заранее.
Например, в уверены в том, что вы всегда хотите видеть ошибки красными:
Даже если сообщение об ошибке будет выведено внутри блока, в котором цвет текста синий, мы можем быть уверены, что ошибка останется красной. Мы всегда хотим сообщать об ошибке красным цветом, и поэтому мы сразу пишем
А вот когда мы используем
Реактивное использование
Этот вид «дурного запаха» очень важен при работе в большой команде. Я уже писал о том, что id — это плохо, потому что они сильно увеличивают специфичность селекторов. От них нет никакого толку, и их никогда не стоит использовать в CSS. Используйте их, чтобы связать элементы HTML с кодом на JavaScript, но не для того, чтобы задавать их стиль.
Причины этого просты:
Если вам этого мало, то я уже и не знаю, что тут ещё сказать…
Если я вижу id, я тут же стараюсь заменить его классом. Излишняя специфичность селекторов губит большие проекты, поэтому жизненно важно удерживать её как можно более низкой.
Напоследок — маленькое упражнение. Попробуйте элегантно решить эту проблему. Подсказка: так — не элегантно; и так — тоже.
Расплывчатое имя — это такое, которое недостаточно конкретно описывает назначение класса. Представьте себе класс
Это очень расплывчатое имя, а расплывчатые имена плохи из-за двух главных причин:
Первый пункт очень прост: что означает
Вторая проблема с расплывчатыми именами — их очень легко случайно переопределить. Допустим, вы работаете над сайтом интернет-магазина. Вы используете класс
Этого легко можно избежать, если использовать более точные имена классов. Классы вроде
Итак, мы рассмотрели несколько примеров кода «с душком». Это вещи, о которых надо помнить всегда и избегать их изо всех сил, вернее, только малая их часть, на самом деле их гораздо больше. Работая над крупным проектом, который длится месяцы и годы, жизненно важно держать код в хорошей форме.
Конечно, из каждого правила есть исключения, но к ним нужно подходить индивидуально. В большинстве же случаев, такого кода нужно тщательно избегать.
Как можно определить, что ваш CSS пованивает? Какие признаки указывают на то, что код неоптимален или что разработчик писал его спустя рукава? На что вы смотрите в первую очередь, чтобы определить, плох или хорош код?
Я подумал, что могу расширить и дополнить ответ Криса исходя из собственного опыта.
Я работаю в BSkyB. Я делаю большие сайты — над последним из них я тружусь уже больше года. Плохой код CSS доставляет мне очень много проблем. Когда занимаешься одним сайтом месяцами, ты просто не можешь себе позволить плохой код, и его обязательно надо исправлять.
Я хочу поделиться несколькими вещами, на которые я обращаю внимание прежде всего, чтобы составить впечатление о качестве, сопровождаемости и чистоте кода CSS.
Отмена стилей
Любые правила CSS, которые отменяют ранее установленные стили (кроме случая сброса стилей) — это тревожный звоночек. Каскадные таблицы стилей по определению должны наследовать предыдущим определениям и дополнять их, а не отменять.
Любое определение вроде:
border-bottom:none;
padding:0;
float:none;
margin-left:0;
обычно не значит ничего хорошего. Если вам приходится обнулять
border
, то, скорее всего вы слишком рано его установили. Это трудно объяснить, поэтому приведу пример:h2{
font-size:2em;
margin-bottom:0.5em;
padding-bottom:0.5em;
border-bottom:1px solid #ccc;
}
Здесь мы задаём элементам
h2
не только размер шрифта и отступы, но и поля, и подчёркивание снизу, чтобы визуально отделить заголовок от остального контента. Но, очень может быть, что в другом месте нам не понадобятся ни поля, ни подчёркивание. Возможно, мы напишем что-то вроде:h2{
font-size:2em;
margin-bottom:0.5em;
padding-bottom:0.5em;
border-bottom:1px solid #ccc;
}
.no-border{
padding-bottom:0;
border-bottom:none;
}
Теперь у нас уже 10 строк кода и уродливое имя класса. Гораздо лучше будет сделать так:
h2{
font-size:2em;
margin-bottom:0.5em;
}
.headline{
padding-bottom:0.5em;
border-bottom:1px solid #ccc;
}
8 строк, никакой отмены стилей и красивое, осмысленное имя класса.
Продвигаясь вниз по файлу стилей, добавляйте правила, а не отнимайте. Если вам приходится отменять ранее установленные стили, скорее всего вы добавили их слишком рано.
Представьте себе CSS-файл на десяток тысяч строк с подобными отменами стилей. Куча лишнего кода! Постепенно добавляйте новые определения поверх старых, более простых, не начинайте сооружать слишком сложные правила слишком рано, иначе вы напишете гораздо больше кода, а делать он будет гораздо меньше.
Когда я вижу, что какое-то правило отменяет предыдущее, я почти наверняка знаю, что стили организованы неправильно и нуждаются в переработке.
Магические числа
Их я особенно ненавижу! Магическое число — это бессмысленное значение, которое используется потому, что оно «просто работает». Например:
.site-nav{
/* [styles] */
}
.site-nav > li:hover .dropdown{
position:absolute;
top:37px;
left:0;
}
top:37px;
— это магическое число. Единственная причина, по которой оно здесь — так получилось, что элементы списка имеют 37 пикселей в высоту, и выпадающие подменю должны появляться внизу элемента меню.Проблема в том, что эти 37 пикселей — чистая случайность, и на эту константу совершенно нельзя положиться. Что если кто-то изменит размер шрифта в пункте меню, и он будет иметь 29, а не 37 пикселей в высоту? Что если в Chrome пункт меню будет иметь 37 пикселей высоту, а в IE — 36? Это число работает только в одном конкретном случае.
Никогда, никогда не используйте значения которые «просто работают». В предыдущем примере гораздо лучше было бы написать
top:100%;
вместо top:37px;
И это не единственная проблема с магическими числами. Кроме ненадёжности они создают ещё и проблему коммуникации. Как другой разработчик сможет понять откуда взялось это число? Если ваш код больше и сложнее приведённого выше примера, и какое-то из магических чисел вдруг перестало работать, вы столкнётесь с тем, что:
- другой разработчик, не зная, откуда взялось это число, будет вынужден писать правильный стиль для этого случая с нуля;
- или же, если он очень осторожен, он оставит число на месте и попытается решить проблему, не трогая его. Таким образом кривой и некрасивый костыль рискует остаться в коде навечно и обрасти новыми костылями.
Магические числа — это плохо. Они быстро устаревают, они мешают другим разработчикам, их нельзя объяснить и на них нельзя положиться.
Нет ничего хуже, чем наткнуться на такое необъяснимое число в чужом коде. Зачем оно здесь? Что оно значит? Можно ли его трогать или не стоит? Я задаю эти вопросы всякий раз, как вижу такое число. И самый главный вопрос: «Как можно добиться такого же результата без магии?»
Бегите от магических чисел как от чумы!
Излишне узкие селекторы
Примерно вот такие:
ul.nav{}
a.button{}
div.header{}
Это селекторы, в которые добавлены совершенно лишние уточнения. Они плохи потому что:
- их практически невозможно использовать повторно;
- они увеличивают специфичность;
- от них страдает производительность.
На самом деле их можно (и нужно) было бы записать так:
.nav{}
.button{}
.header{}
Вот теперь можно применить
.nav
к ol
, .button
к input
и быстро заменить div
с классом .header
на элемент header
, когда будем приводить сайт в соответствие с HTML5.Хотя производительность браузера страдает от таких селекторов не очень сильно, она всё же страдает. Зачем заставлять его перебирать все элементы
a
в поисках класса .button
, если можно ограничиться одним лишь классом? Вот ещё более экстремальные примеры:ul.nav li.active a{}
div.header a.logo img{}
.content ul.features a.butto
Все они могут быть сильно сокращены или переписаны:
.nav .active a{}
.logo > img {}
.features-button{}
Каждый раз, когда я натыкаюсь на чересчур подробные селекторы, я пытаюсь выяснить, почему они заданы именно так, и нельзя ли их сократить.
Жестко заданные, абсолютные значения
Так же как и магические числа, они не сулят ничего хорошего. Вот пример:
h1{
font-size:24px;
line-height:32px;
}
Гораздо лучше было бы написать:
line-height:1.333;
Интерльиньяж всегда лучше задавать относительно, чтобы код был гибче. При изменении размера шрифта он будет меняться автоматически. А если вы зададите его в пикселях, то вам придётся писать что-то вроде этого:h1{
font-size:24px;
line-height:32px;
}
/**
* Main site `h1`
*/
.site-title{
font-size:36px;
line-height:48px;
}
И так каждый раз, когда изменится размер шрифта заголовка. Вот так гораздо лучше:
h1{
font-size:24px;
line-height:1.333;
}
/**
* Main site `h1`
*/
.site-title{
font-size:36px;
}
Вроде бы, разница невелика, но в крупном проекте она может иметь большое значение.
Кстати, это относится не только к
line-height
. Практически любое жестко вписанное в код абсолютное значение должно вызывать подозрение.Единственный случай, когда действительно имеет смысл захардкодить абсолютное значение — это в случае работы со вещами, которые всегда должны иметь определённый размер, например, спрайтами.
Грубая сила
Это один из крайних частных случаев жёстко заданных магических чисел и некоторых других приёмов, которые используются, чтобы заставить вёрстку работать:
.foo{
margin-left:-3px;
position:relative;
z-index:99999;
height:59px;
float:left;
}
Это ужасный стиль! Все эти уродливые правила наворочены с единственной целью — запихнуть элемент на нужное место любой ценой. Такой код говорит либо об очень плохо спроектированной вёрстке, либо о недостаточном понимании того, как работает блочная модель CSS, либо о том и другом одновременно.
Если вы хорошо продумали вёрстку и разбираетесь в блочной модели, вам вряд ли придётся использовать грубую силу. Если я вижу подобный код, я сразу стараюсь разобраться, в чём проблема, и не надо ли вернуться на несколько шагов назад, чтобы избавиться от необходимости писать такие костыли.
Опасные селекторы
Под опасными селекторами я понимаю такие, которые намного шире, чем необходимо.Вот самый простой и очевидный пример такого селектора:
div{
background-color:#ffc;
padding:1em;
}
Зачем, зачем накрывать каждый
div
на странице этой ковровой бомбардировкой? Зачем кому-нибудь может понадобиться селектор вроде aside{}
? Или header{}
, или ul{}
? Такие селекторы намного, намного шире чем необходимо, и ведут к тому, что нам придется отменять стили, о чём мы уже говорили.Давайте рассмотрим пример с
header{}
более подробно. Многие используют этот элемент, чтобы создать шапку страницы, что совершенно правильно. Но если вы пишете стили для него вот так: header{
padding:1em;
background-color:#BADA55;
color:#fff;
margin-bottom:20px;
}
то это уже совсем не так правильно. Элемент
header
вовсе не обязательно подразумевает шапку всей страницы, он может использоваться несколько раз в разных контекстах. Гораздо лучше использовать класс, например .site-header{}
.Задавать такие подробные стили для такого общего селектора очень опасно. Они просочатся в совершенно непредсказуемые места, как только вы начнёте повторно использовать этот элемент.
Убедитесь, что ваши селекторы бьют точно в цель.
Вот ещё пример:
ul{
font-weight:bold;
}
header .media{
float:left;
}
Когда я вижу, как стили применяются к элементу или к сильно обобщённому классу, как в этом примере, я начинаю паниковать. Я знаю, что они чересчур универсальны и у меня будут проблемы. Эти стили могут быть унаследованы в таких местах, в которых это будет совершенно нежелательно, и мне придётся их отменять.
Реактивное использование !important
Использовать
!important
можно. И это действительно важный инструмент. Тем не менее, его стоит использовать с умом.!important
надо использовать проактивно, а не реактивно.Это значит, что его можно использовать тогда, когда вы абсолютно уверены, что вам всегда будет нужно, чтобы это стиль имел приоритет, и вы знаете об этом заранее.
Например, в уверены в том, что вы всегда хотите видеть ошибки красными:
.error-text{
color:#c00!important;
}
Даже если сообщение об ошибке будет выведено внутри блока, в котором цвет текста синий, мы можем быть уверены, что ошибка останется красной. Мы всегда хотим сообщать об ошибке красным цветом, и поэтому мы сразу пишем
!important
.А вот когда мы используем
!important
реактивно, то есть в ответ на возникшую проблему, когда мы запутались и вместо того, чтобы разобраться, прём напролом, тогда это плохо.Реактивное использование
!important
не решает проблему, а только прячет её. Надо лечить болезнь, а не симптомы. Проблема никуда не делась, мы просто перекрыли её сверх-специфичным селектором, тогда как нужно было заняться рефакторингом и архитектурой.ID
Этот вид «дурного запаха» очень важен при работе в большой команде. Я уже писал о том, что id — это плохо, потому что они сильно увеличивают специфичность селекторов. От них нет никакого толку, и их никогда не стоит использовать в CSS. Используйте их, чтобы связать элементы HTML с кодом на JavaScript, но не для того, чтобы задавать их стиль.
Причины этого просты:
- id можно использовать на странице только один раз;
- класс можно использовать сколько угодно;
- большинство правил, применяемых к id можно разбросать по нескольким классам;
- id в 255 раз специфичнее класса;
- Это значит, что вам понадобится применить 256 классов к элементу, чтобы перевесить один id.
Если вам этого мало, то я уже и не знаю, что тут ещё сказать…
Если я вижу id, я тут же стараюсь заменить его классом. Излишняя специфичность селекторов губит большие проекты, поэтому жизненно важно удерживать её как можно более низкой.
Напоследок — маленькое упражнение. Попробуйте элегантно решить эту проблему. Подсказка: так — не элегантно; и так — тоже.
Расплывчатые имена классов
Расплывчатое имя — это такое, которое недостаточно конкретно описывает назначение класса. Представьте себе класс
.card
. Что он делает?Это очень расплывчатое имя, а расплывчатые имена плохи из-за двух главных причин:
- вы не сможете догадаться, что оно означает;
- оно настолько общее, что легко может быть случайно переопределено другим разработчиком.
Первый пункт очень прост: что означает
.card
? Стиль чего он задаёт? Карточки задач в системе управления проектами? Игральную карту в онлайн-казино? Изображение кредитной карты? Трудно сказать, потому что имя слишком туманное. Допустим, мы имеем в виду кредитную карту. Тогда намного лучше назвать класс .сredit-card-image{}
. Да, намного длиннее, но и намного, намного лучше!Вторая проблема с расплывчатыми именами — их очень легко случайно переопределить. Допустим, вы работаете над сайтом интернет-магазина. Вы используете класс
.card
, подразумевая номер кредитки, привязанной к аккаунту. А другой разработчик в это время добавляет возможность купить подарок и приложить к нему карточку с поздравлением. И он тоже называет класс .card
, и пишет для него свои правила, которые конфликтуют с вашими.Этого легко можно избежать, если использовать более точные имена классов. Классы вроде
.card
или .user
слишком туманны. Они малоинформативны и их легко случайно переопределить. Имена классов должны быть точны, насколько возможно.Заключение
Итак, мы рассмотрели несколько примеров кода «с душком». Это вещи, о которых надо помнить всегда и избегать их изо всех сил, вернее, только малая их часть, на самом деле их гораздо больше. Работая над крупным проектом, который длится месяцы и годы, жизненно важно держать код в хорошей форме.
Конечно, из каждого правила есть исключения, но к ним нужно подходить индивидуально. В большинстве же случаев, такого кода нужно тщательно избегать.