Недавно Крис Койер отвечал на вопросы читателей Smashing Magazine. Один из вопросов был о том, как распознать код CSS с «душком»:
Как можно определить, что ваш 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
слишком туманны. Они малоинформативны и их легко случайно переопределить. Имена классов должны быть точны, насколько возможно.
Заключение
Итак, мы рассмотрели несколько примеров кода «с душком». Это вещи, о которых надо помнить всегда и избегать их изо всех сил, вернее, только малая их часть, на самом деле их гораздо больше. Работая над крупным проектом, который длится месяцы и годы, жизненно важно держать код в хорошей форме.
Кончено, из каждого правила есть исключения, но к ним нужно подходить индивидуально. В большинстве же случаев, такого кода нужно тщательно избегать.
Автор: ilya42