Код CSS «с душком»

Недавно Крис Койер отвечал на вопросы читателей 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 слишком туманны. Они малоинформативны и их легко случайно переопределить. Имена классов должны быть точны, насколько возможно.

Заключение

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

Кончено, из каждого правила есть исключения, но к ним нужно подходить индивидуально. В большинстве же случаев, такого кода нужно тщательно избегать.

Поделиться с друзьями:

Блог веб-разработчика

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