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

Ниже предоставлено несколько моментов, на которые стоит обратить внимание в коде CSS, которые дадут вам представление о его качестве, его доступности и целостности...

Отмена стилей

Любой CSS, который сбрасывает стили (стандартный reset не считаем), должен свидетельствовать о тревоге, поскольку сама природа CSS такова, что свойства отрабатывают каскадно и наследование происходит от элементов, стили который определены ранее. Набор правил для элементов должен всегда наследовать и расширяться, а не перетирать.

Подобная CSS запись:

border-bottom: none;
padding: 0;
float: none;
margin-left: 0;

.. считается типично плохой. Если, допустим, ван нужно удалить рамку, вероятнее всего, стили применены слишком рано.

Рассмотрим пример:

h2 {
  font-size: 2em;
  margin-bottom: 0.5em;
  padding-bottom: 0.5em;
  border-bottom: 1px solid #ccc;
}

Как видим, мы задали всем нашим h2 размер шрифта, внешний и внутренний отступ снизу, и нижнюю рамку. Но, возможно, у нас дальше появится такой заголовок 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, состоящий из десятков тысяч строк, в котором много расширения свойств и много перетирания. Примените более простые стили общим элементам и не делайте их слишком сложными, рискуя, что вам придется переписывать стили позже. Вы в конечном итоге напишете больше CSS, чтобы добиться меньшего количества стилей.

Магические числа

Этот раздел особенно будоражит.

Магическое число - это значение, которое используется «потому что оно просто работает». Рассмотрим следующий пример:

.site-nav {
  [styles]
}
.site-nav > li:hover .dropdown {
  position: absolute;
  top: 37px;
  left: 0;
}

top: 37px; это магическое число; единственная причина, по которой оно работает, по-видимому, заключается в том, что li внутри .site-nav имеет высоту 37 пикселей, а всплывающее меню .dropdown должно появиться внизу.

Проблема здесь в том, что 37px является косвенной величиной и поэтому мы не должны доверять этому числу. Что, если кто-то изменит размер шрифта в .site-nav, и теперь высота li будет 29 пикселей? Число 37 больше не действительно, и следующий разработчик должен знать его, чтобы его также обновить.

А что произойдёт, если Chrome отрендерит li с высотой 37 пикселей, а IE отрендерит с высотой 36 пикселей? Получается наше магическое число работает только для одной ситуации.

Никогда не используйте числа только потому, что с ними так работает. В этой ситуации было бы намного лучше заменить top: 37px; на top: 100%;, что вцелом означает «всегда под».

С магическими числами связано несколько проблем. Как указано выше, на них нельзя полагаться, но также, согласно их трактовке использования «просто потому, что это работает», сложно сообщить другому разработчику, откуда взялось это число. Если у вас был более сложный пример, в котором использовалось магическое число - и потом это магическое число стало недействительным, - наверняка вы столкнулись с одной или несколькими из следующих проблем:

  • Следующий разработчик не знает, откуда взялось магическое число, поэтому он удаляет его и переделывает по своему.
  • Следующий разработчик - осторожный, и поскольку он не знает, откуда взялось магическое число, решает попытаться исправить проблему, не касаясь этого магического числа. Это означает, что в коде остается старое, устаревшее, хакерское магическое число, и следующий разработчик просто взламывает его, перетирая на своё. И теперь он - новый хакер )

Если у вашего проекта есть магические числа - это плохие новости: они быстро устаревают, они сбивают с толку других разработчиков, их нельзя объяснить, им нельзя доверять.

Нет ничего хуже, чем продолжить писать чужой код и увидеть необъяснимое число. Вам остается только гадать, что, черт возьми, он делает, зачем оно нужно и стоит ли вам осмелиться к нему прикоснуться.

Избегайте магических чисел, как очень опасную болезнь.

Ограниченные селекторы

К ограниченным селекторам относят подобные:

ul.nav {}
a.button {}
div.header {}

В основном, это селекторы, которым без нужды предшествует элемент. Если вы используете подобные - это плохие новости, т.к.:

  • Они полностью препятствуют повторному использованию для другого элемента.
  • Они повышают специфичность.
  • Они увеличивают нагрузку на браузер (снижая производительность)

Все это плохие качества. Эти селекторы могут и должны быть такими:

.nav {}
.button {}
.header {}

Теперь можно применить .nav и к ol, можно применить .button к input, и если сайт не обходит стороной семантику, то можно применить вместо div элемент header, не беспокоясь об аннулировании каких-либо стилей.

Что касается производительности таких селекторов, то это не очень большая проблема, но, тем не менее, проблема. Зачем заставлять браузер искать ссылку a с классом .button, если можно просто попросить его найти .button и все готово? Ограничивая селекторы, вы увеличиваете нагрузку на браузер.

Более экстримальные примеры:

ul.nav li.active a {}
div.header a.logo img {}
.content ul.features a.button {}

Каждый из них можно избавить от массивности, или вообще переписать:

.nav .active a {}
.logo > img  {}
.features-button {}

Чем нам помогает такое упрощение:

  • Экономится значительное количество кода
  • Повышается производительность
  • Обеспечивается хорошая гибкость кода
  • Уменьшается специфичность

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

Жёсткие значения

В отличие от магических чисел, жесткие значения также являются плохой практикой. Жесткое значение (другими словами - строго-заданное или захардкодженное) может быть примерно таким:

h1 {
  font-size: 24px;
  line-height: 32px;
}

line-height: 32px; - не есть хорошо. Правильно писать так - line-height: 1.333

Значение line-height всегда должно быть относительным, это делает его простым и гибким. Если вы захотите изменить размер шрифта h1, вы должны быть уверены, что высота строки line-height соответственно перестроится. Отсутствие относительной величины line-height означает, что если вам когда-нибудь понадобится изменить размер h1, вы, скорее всего, запишите что-то вроде этого:

h1 {
  font-size: 24px;
  line-height: 32px;
}

/* стиль для h1 с классом .site-title */
.site-title {
  font-size: 36px;
  line-height: 48px;
}

В этом примере мы вынуждены продолжать добавлять жёсткое значение для line-height, поскольку изначально заданное было недостаточно гибким. Используя относительную величину, наш код будет значительно проще:

h1 {
  font-size: 24px;
  line-height: 1.3333;
}

/* стиль для h1 с классом .site-title */
.site-title {
  font-size: 36px;
}

На первый взгляд выглядит как несущественная разница, но для каждого текстового элемента в большом проекте такой подход имеет большое влияние.

Следует отметить, что этот подход относится не только к line-height. К любому жесткому значению в стилях следует относиться с осторожностью и подозрением.

Жестко заданные значения не надежные и не гибкие, поэтому их следует избегать. Единственное, где действительно имеет место задавать жесткие значения - это, например, спрайты, которые всегда должны быть определенного размера несмотря ни на что.

Грубое форсирование

Это похоже на жестко запрограммированные значения, но немного конкретней. Грубая форсирование CSS - это когда вы используете жестко запрограммированные магические числа и множество других методов, чтобы заставить макет выглядеть как требуется. Возьмем, к примеру:

.foo {
  margin-left: -3px;
  position: relative;
  z-index: 99999;
  height: 59px;
  float: left;
}

Такой CSS выглядит весьма печально. Все эти значения являются жесткими и грубо форсированными, и они явно используются только для того, чтобы заставить что-то отображаться так, как это нужно.

Такие строки в CSS указывают либо на плохую верстку, которая требует подобного рода манипуляций, либо вообще на отсутствие понимания блочной модели и разметки вцелом, либо и то и другое.

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

Опасные селекторы

«Опасный селектор» - термин, обладающий широким охватом. Вот действительно очевидный и простой пример опасного селектора:

div {
  background-color: #ffc;
  padding: 1em;
}

Такая запись моментальное вызывает возмущение; с какой стати мы должны так стилить каждый div на сайте? И это хороший вопрос. Ведь следуя такой логике, почему бы тогда не иметь селектор aside {}, например? Или header {}, или ul {}? Подобные селекторы слишком обобщённые и в конечном итоге приведут к тому, что нам придется перетирать CSS, как было описано в предыдущем разделе.

Давайте посмотрим на пример header {} более внимательно…

Многие люди используют элемент header для разметки основного заголовка своего сайта - и это нормально, - однако, если вы зададите такой стиль заголовка для всего сайта:

header {
  padding: 1em;
  background-color: #BADA55;
  color: #fff;
  margin-bottom: 20px;
}

... то это не так уже и нормально. Элемент header не означает «основной заголовок вашего сайта», и, согласно спецификации, элемент header может использоваться несколько раз в разных контекстах. Лучшее решение - застилить ваш основной header с помощью селектора, похожего на .site-header {} к примеру.

Задавать такие специфические стили подобным универсальным селекторам опасно. Ваши стили будут просачиваться в те области, в которых их быть не должно, и вам нужно будет отменять/перезаписывать стили (т.е. добавлять дополнительный код для удаления стилей).

Убедитесь, что ваши селекторы предназначены именно для того, что нужно.

Рассмотрим такую запись:

ul {
  font-weight: bold;
}

header .media {
  float: left;
}

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

Реактивный !important

!important - это нормально. Это нормально, и это важный инструмент. Однако !important следует использовать только при определенных обстоятельствах.

!important следует использовать только проактивно, а не реактивно.

Под этим подразумевается то, что бывают случаи, когда вы знаете, что абсолютно всегда какой-то стиль должен иметь приоритет. При этом вы знаете об этом заранее.

Например, вы знаете, что вам всегда нужно, чтобы ошибки были красными, поэтому это правило вполне подходит:

.error-text {
  color: #c00 !important;
}

Если ошибка возникает в элементе div, где текст всегда синий и мы уверены, что хотим нарушить это правило в случае ошибок - мы можем добавить important, потому что мы знаем, что всегда хотим, чтобы ошибки отображались красным цветом.

Где !important плох, так это когда он используется реактивно, то есть используется, чтобы поменять специфичность поведения элемента, или чтобы строго заставить все работать. Такое использование считается плохой практикой.

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

Не должно быть вопросов в отношении important, если он используется проактивно. Как только видите реактивное использование, уделите внимание написанному CSS. Решением такой ситуации является рефакторинг стилей.

Айдишники (ID)

Использование id - очень специфичная штука. Хороший тон вёрстки гласит, что чрезмерное использование идентификаторов - плохая идея из-за их повышенной специфичности; они никому не нужны и никогда не должны использоваться в CSS. Использовать идентификаторы в HTML нужно для идентифицирования фрагментов кода и обращения к нему с помощью JS, но никогда не через CSS.

Причины просты:

  • Идентификаторы никогда не могут использоваться на странице более одного раза.
  • Классы, в свою очередь, могут использоваться на странице сколько угодно раз.
  • Идентификаторы могут именоваться так, как именуются применённые классы.
  • Идентификатор имеет больший вес для селектора, чем класс.
  • А это значит что его стили сложней будет переопределить.

Последний пункт должен быть максимально убеждающим…

Как только вы видите идентификатор в таблице стилей, попытайтесь заменить его классом. Развитие проектов происходит по спирали, поэтому очень важно делать подобные вещи на низком уровне разработки.

Веселое упражнение: попробуйте элегантно решить с помощью CSS следующую задачу - обе ссылки на Твиттер должны быть серыми.

Подсказка: решения типа #page-head .twitter a { color:#333; } или !important элегантными не считаются.

Размытое именование классов

"Размытое" имя класса - это имя, которое недостаточно конкретно для его предполагаемого назначения. Представьте себе класс .card. Что это значит?

Это имя класса очень размытое, и такие именования плохи по двум основным причинам:

  • Невозможно описать конкретную цель такого класса
  • Другой разработчик примет его за слишком обобщённый и просто переопределит его

Первый пункт прост: что означает .card, что он стилит? Это карточка в Trello? Это класс, который вы добавляете к игральной карте на покерном сайте? Может быть это элемент кредитной карты? Трудно узнать, потому что он слишком размытый, неточный. Представим, что это означает кредитную карту, тогда этот класс был бы намного лучше, если бы он был .credit-card. Он длиннее, да, но насколько он лучше!

Вторая проблема заключается в том, что их очень легко (случайно) переназначить / переопределить. Допустим, вы снова работаете над коммерческим сайтом и там используете класс .card, и это относится к кредитной карте пользователя, привязанной к его аккаунту. А теперь представьте, что приходит другой разработчик и хочет добавить некоторые фичи, с помощью которых вы можете отправить кому-нибудь покупку в подарок, с возможностью добавить карточку с сообщением на ней. У него может возникнуть соблазн тоже использовать .card, что неверно и может привести к переопределению и перезаписи вашего уже имеющегося класса .card.

Всего этого можно избежать, если использовать более строгие имена классов. Такие классы, как .card, .user и подобные, слишком размытые, что затрудняет их быстрое понимание и их легко случайно повторно использовать / переопределить.

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

Итог

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

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