├── README Russian.md └── README.md /README Russian.md: -------------------------------------------------------------------------------- 1 | # Основные правила и принципы написания CSS 2 | 3 | Большие, долгоживущие проекты с большим количеством разработчиков требуют от 4 | нас выполнять нашу работу определённым образом для достижения следующих целей: 5 | 6 | * Легко поддерживаемые файлы стилей 7 | * Прозрачность и читаемость кода 8 | * Масштабируемость стилей 9 | 10 | Несколько техник помогут нам достичь этих целей. 11 | 12 | Первая часть этого документа расскажет про синтаксис, форматирование и анатомию CSS, 13 | вторая часть раскроет темы методов, способа мышления и отношения к написанию 14 | и созданию CSS-архитектуры. Звучит многообещающе? 15 | 16 | ## Содержание 17 | 18 | * [Анатомия CSS-документа](#Анатомия-css-документа) 19 | * [Общие моменты](#Общие-моменты) 20 | * [Один файл или много файлов](#Один-файл-или-много-файлов) 21 | * [Оглавление](#Оглавление) 22 | * [Названия секций](#Названия-секций) 23 | * [Организация правил](#Организация-правил) 24 | * [Анатомия CSS-правила](#Анатомия-css-правила) 25 | * [Соглашения об именовании](#Соглашения-об-именовании) 26 | * [JS-классы](#js-классы) 27 | * [Интернационализация](#Интернационализация) 28 | * [Комментарии](#Комментарии) 29 | * [Комментарии на стероидах](#Комментарии-на-стероидах) 30 | * [Квази-селекторы](#Квази-селекторы) 31 | * [Теги в коде](#Теги-в-коде) 32 | * [Ссылки между объектом и его расширением](#Ссылки-между-объектом-и-его-расширением) 33 | * [Напиcание CSS](#Напиcание-css) 34 | * [Создание новых компонентов](#Создание-новых-компонентов) 35 | * [OOCSS](#oocss) 36 | * [Разметка](#Разметка) 37 | * [Единицы измерения](#Единицы-измерения) 38 | * [Размер текста](#Размер-текста) 39 | * [Сокращённая запись](#Сокращённая-запись) 40 | * [Идентификаторы](#Идентификаторы) 41 | * [Селекторы](#Селекторы) 42 | * [Перенасыщенные селекторы](#Перенасыщенные-селекторы) 43 | * [Производительность селекторов](#Производительность-селекторов) 44 | * [Последовательность CSS-селекторов](#Последовательность-css-селекторов) 45 | * [`!important`](#important) 46 | * [Магические числа и абсолютные значения](#Магические-числа-и-абсолютные-значения) 47 | * [Стили в условных комментариях](#Стили-в-условных-комментариях) 48 | * [Отладка](#Отладка) 49 | * [Препроцессоры](#Препроцессоры) 50 | 51 | --- 52 | 53 | ## Анатомия CSS-документа 54 | 55 | Мы всегда должны стараться соблюдать принятое форматирование. Это означает согласованные комментарии, синтаксис и соглашение об именовании. 56 | 57 | ### Общие моменты 58 | 59 | Ограничьте строки 80 символами, где это только возможно. Исключениями могут быть синтаксис градиентов и ссылки в комментариях. Это нормально, мы ничего не можем с этим сделать. 60 | 61 | Я предпочитаю отступы в четыре пробела и писать многострочный CSS. 62 | 63 | ### Один файл или много файлов 64 | 65 | Некоторые предпочитают работать с одиночными, большими файлами. Это хорошо и при следовании данному руководству у вас не прибавится проблем. Начав использовать Sass, я начал разбивать мои стили на множество маленьких, подключаемых через `include`. Это тоже хорошо… Как бы там ни было, правила и рекомендации, приведённые ниже, применимы к обоим способам. Единственное замечание относится к «Оглавлению» и «Названиям секций». Продолжайте читать, чтобы узнать подробности. 66 | 67 | ### Оглавление 68 | 69 | В начале CSS файла я храню таблицу содержимого, которая уточняет какие секции содержатся в этом файле, например: 70 | 71 | /*------------------------------------*\ 72 | $CONTENTS 73 | \*------------------------------------*/ 74 | /** 75 | * CONTENTS............You’re reading it! 76 | * RESET...............Set our reset defaults 77 | * FONT-FACE...........Import brand font files 78 | */ 79 | 80 | Оглавление подскажет следующему разработчику, что ему можно ожидать в этом файле. Каждый пункт оглавления совпадает с названием секции. 81 | 82 | Если вы работаете с одним большим файлом стилей, то секция, попавшая в оглавление, также должна находиться в этом файле. Если вы работаете со стилями, разнесёнными в несколько файлов, то каждый пункт оглавления привязан к команде подключения файла с этой секцией. 83 | 84 | ### Названия секций 85 | 86 | Оглавление будет бесполезно пока не будет соотноситься с названиями секций. Оформляйте секцию таким способом: 87 | 88 | /*------------------------------------*\ 89 | $RESET 90 | \*------------------------------------*/ 91 | 92 | Знак `$` префиксно дополняет название секции, позволяя искать по файлу такой шаблон `$[НАЗВАНИЕ-СЕКЦИИ]` и, таким образом, **производить поиск только по названиям секций**. 93 | 94 | Работая с большим файлом стилей, оставляейте отступ в пять строк между секциями, вот так: 95 | 96 | /*------------------------------------*\ 97 | $RESET 98 | \*------------------------------------*/ 99 | [Our 100 | reset 101 | styles] 102 |   103 |   104 |   105 |   106 |   107 | /*------------------------------------*\ 108 | $FONT-FACE 109 | \*------------------------------------*/ 110 | 111 | Этот участок пустого пространство легко и быстро бросается в глаза при пролистывании большого файла. 112 | 113 | При работе с несколькими, подключаемыми файлами стилей, начинайте каждый файл с названия секции и в этом случае нет необходимости в пустых строках. 114 | 115 | ## Организация правил 116 | 117 | Возьмите за правило писать стили в порядке специфичности. Это обеспечит гарантию того, что вы используете все преимущества вложенности и каскада. 118 | 119 | Порядок правильно организованного файла стилей похож на это: 120 | 121 | 1. **Reset** – сброс, платформа для написания кода стилей. 122 | 2. **Элементы** – `h1`, `ul` и подобные без указания классов. 123 | 3. **Объекты и абстракции** — общие конструкции без дизайна. 124 | 4. **Компоненты** – компоненты, построенные на объектах и их расширениях. 125 | 5. **Полезные стили** – состояния ошибок и другое. 126 | 127 | Такая структура означает, что каждая секция, написанная ниже, строится и наследует правила, описанные в предыдущих. Это приведёт к меньшему количеству проблем специфичности и отменяющих переопределений, а также к более качественной CSS архитектуре. 128 | 129 | Для дальнейшего чтения по этой теме, я не могу отозваться о книге [SMACSS](http://smacss.com) от Джонатана Снука настолько хорошо, насколько она того заслуживает. 130 | 131 | ## Анатомия CSS-правила 132 | 133 | [Селектор] { 134 | [Свойство]:[Значение] 135 | [<- Объявление ->] 136 | } 137 | 138 | Я руководствуюсь несколькими правилами при написании CSS-правил. 139 | 140 | * Дефис как разделитель (Исключая БЭМ запись, 141 | [смотрите ниже](#Соглашения-об-именовании)) 142 | * Отступ в 4 пробела 143 | * Многострочность 144 | * Свойства сортируются в порядке релевантности (НЕ в алфавитном) 145 | * Выравнивайте свойства с вендорными префиксами так, чтобы их значения были друг под другом. 146 | * Используйте отступы в стилях, чтобы отразить структуру HTML. 147 | * Всегда заканчивайте объявление знаком точки с запятой 148 | 149 | И сразу пример: 150 | 151 | .widget{ 152 | padding:10px; 153 | border:1px solid #BADA55; 154 | background-color:#C0FFEE; 155 | -webkit-border-radius:4px; 156 | -moz-border-radius:4px; 157 | border-radius:4px; 158 | } 159 | .widget-heading{ 160 | font-size:1.5rem; 161 | line-height:1; 162 | font-weight:bold; 163 | color:#BADA55; 164 | margin-right:-10px; 165 | margin-left: -10px; 166 | padding:0.25em; 167 | } 168 | 169 | Наглядно видно, что `.widget-heading` является дочерним элементом `.widget`, так как `.widget-heading` имеет дополнительный отступ относительно селектора `.widget`. Эту полезную информацию разработчики могут считать просто взглянув на отступы в наших стилях. 170 | 171 | Также мы видим, что объявления селектора `.widget-heading` отсортированы в порядке релевантности; `.widget-heading` вероятнее всего текстовый элемент, поэтому мы начинаем правило с типографских свойств, за которыми уже следуют все остальные. 172 | 173 | Единственное исключение в многострочном CSS может быть в таком случае: 174 | 175 | .t10 { width:10% } 176 | .t20 { width:20% } 177 | .t25 { width:25% } /* 1/4 */ 178 | .t30 { width:30% } 179 | .t33 { width:33.333% } /* 1/3 */ 180 | .t40 { width:40% } 181 | .t50 { width:50% } /* 1/2 */ 182 | .t60 { width:60% } 183 | .t66 { width:66.666% } /* 2/3 */ 184 | .t70 { width:70% } 185 | .t75 { width:75% } /* 3/4*/ 186 | .t80 { width:80% } 187 | .t90 { width:90% } 188 | 189 | В этом примере (из [сеточной системы inuit.css](https://github.com/csswizardry/inuit.css/blob/master/inuit.css/partials/base/_tables.scss#L88)) 190 | гораздо больше смысла использовать однострочные правила. 191 | 192 | ## Соглашения об именовании 193 | 194 | В большинстве случаев я использую дефис как разделитель слов в классах (например `.foo-bar`, не `.foo_bar` и не `.fooBar`), но в некоторых случаях я использую БЭМ-запись. 195 | 196 | БЭМ — методология именования и категоризации CSS селекторов для приведения их в чёткий порядок, придания прозрачности и информативности. 197 | 198 | Соглашение об именовании выражается следующим шаблоном: 199 | 200 | .block{} 201 | .block__element{} 202 | .block--modifier{} 203 | 204 | * `.block` представляет собой наивысший уровень абстракции или компонента. 205 | * `.block__element` является вложенным элементом `.block`, формирующим `.block` как сущность. 206 | * `.block--modifier` — класс отражающий другое состояние или версию `.block`. 207 | 208 | **Аналогия**, иллюстрирующая работу БЭМ, может быть такой: 209 | 210 | .person{} 211 | .person--woman{} 212 | .person__hand{} 213 | .person__hand--left{} 214 | .person__hand--right{} 215 | 216 | В этом примере приведен базовый объект описывающий человека, и существует отличный от базового тип человека — женщина. Также наглядно видно, что у людей есть руки; они являются частями человека, и рука может быть как левой, так и правой. 217 | 218 | Теперь мы можем создавать селекторы, опираясь на базовые объекты и также без проблем понимаем, что делает каждый конкретный селектор; является ли он частью компонента (`__`) или же модификацией (`--`)? 219 | 220 | Итак, `.page-wrapper` является отдельным селектором; он не формирует часть компонента и, значит, является валидным селектором. В противовес этому, селектор `.widget-heading` относится к компоненту; он является дочерним элементом `.widget` и поэтому мы вынуждены переименовать этот класс в `.widget__heading`. 221 | 222 | БЭМ непригляднее и перегруженней, но, тем не менее, он предоставляет огромные возможности, с помощью которых мы можем определить назначение и отношения элементов просто посмотрев на их классы. Также, БЭМ обычно сжимается c помощью gzip, после использования минификаторов, которые первоклассно работают с повторениями. 223 | 224 | Независимо от того, используете вы БЭМ или нет, всегда заботьтесь о грамотном именовании классов; классы должны быть короткими, насколько это возможно, но настолько длинными, насколько это необходимо. Убедитесь, что абстракции имеют очень общие классы (например `.ui-list`, `.media`) для возможного переиспользования. Расширения абстракций должны иметь гораздо более конкретные классы (например `.user-avatar-link`). Не беспокойтесь о длинных классах; gzip выполнит свою работу потрясающе хорошо. 225 | 226 | ### Классы в HTML 227 | 228 | Для большей читаемости разделяйте классы в разметке двумя (2) пробелами: 229 | 230 |
231 | 232 | Увеличенные отступы между классами позволят легко вычленять отдельные классы. 233 | 234 | ### JS-классы 235 | 236 | **Никогда не используйте в JavaScript-логике обычные, уже используемые для _стилизации_ классы.** Связывание логики скриптов с оформлением ведёт к тому, что мы не сможем использовать одно без другого. 237 | 238 | Если вам требуется привязать к вёрстке какую-то логику, используйте специальный JS-класс — обычный класс, дополненный префиксом `.js-`, например `.js-toggle`, `.js-drag-and-drop`. Данный приём позволяет добавлять JS- и CSS-классы, без создания самому себе проблем в будущем, а также разделяет логику поведения и оформление страницы друг от друга. 239 | 240 | 241 | 242 | 243 | Приведенная в пример разметка имеет два класса; один предназначен для оформления сортируемых табличных колонок и другой для того, чтобы вы могли добавить возможность сортировки. 244 | 245 | ### Интернационализация 246 | 247 | Несмотря на то, что я британский разработчик и привык писать colour вместо color, считаю, что для улучшения однородности лучше использовать американский английский при написании CSS. CSS, как и другие (если не все) языки, 248 | написан на американском английском, так что сочетание `color:red;` с классами `.colour-picker{}` вредит однородности кода. Я ранее предлагал и отстаивал написание селекторов на двух языках, например: 249 | 250 | .color-picker, 251 | .colour-picker{ 252 | } 253 | 254 | Тем не менее, работая недавно над очень большим Sass-проектом, в котором было множество colour-переменных (например, `$brand-color`, `$highlight-color` и т.д.), я осознал, что поддержка двух версий каждой переменной очень скоро становится утомительна. Это также означает в два раза больше работы с такими вещами, как поиск и замена. 255 | 256 | Для однородности всегда называйте классы и переменные на той локализации языка, которая в нём принята. 257 | 258 | ## Комментарии 259 | 260 | Я использую комментарии в стиле docBlock, которые я ограничиваю в 80 символов: 261 | 262 | /** 263 | * Пример комментария в стиле docBlock 264 | * 265 | * Более длинное описание комментария, описывающего код в больших 266 | * подробностях. Комментарии ограничиваются в 80 символов. 267 | * 268 | * Можно вставлять разметку в комментарии. Делается это так: 269 | * 270 |
271 |

Lorem

272 |
273 | * 274 | * Разметка префиксно не дополняется звездочками, чтобы оставить возможность 275 | * легкого копипаста. 276 | * 277 | */ 278 | 279 | Вы должны документировать и комментировать так много, насколько возможно; всё, что кажется вам прозрачным и говорящим за себя, может не быть таковым для другого разработчика. Пишите кусок кода и сразу после этого объясняйте его. 280 | 281 | ### Комментарии на стероидах 282 | 283 | Существуют несколько продвинутых техник, связанных с комментариями: 284 | 285 | * Квази-селекторы 286 | * Теги в коде 287 | * Ссылка на расширяемый компонент 288 | 289 | #### Квази-селекторы 290 | 291 | Вы никогда не должны перегружать селектор; скажем так, вы никогда не должны писать `ul.nav{}`, если вы можете написать просто `.nav{}`. Перегрузка селекторов приводит к уменьшению производительности селекторов (увеличению времени рендеринга страницы), исключает возможность потенциального переиспользования селектора для элемента другого типа и увеличивает специфичность селектора. Это все те вещи, которые должны избегаться при любых условиях. 292 | 293 | Иногда бывает полезно сообщить следующему разработчику, в каком контексте вы собираетесь использовать селектор. Давайте рассмотрим `.product-page`. Этот класс выглядит так, как будто должен использоваться с корневыми элементами, такими как `html` и `body`, но глядя только на `.product-page` нет возможности утверждать наверняка, с каким именно. 294 | 295 | Используя смешанные селекторы (например, комментирование первого простого селектора по типу), мы можем сообщить с каким элементом следует использовать этот класс: 296 | 297 | /*html*/.product-page{} 298 | 299 | В этом примере мы чётко видим к какому элементу принадлежит данный класс, без проблем со специфичностью и переиспользованием этого кода. 300 | 301 | Другие примеры могут быть такими: 302 | 303 | /*ol*/.breadcrumb{} 304 | /*p*/.intro{} 305 | /*ul*/.image-thumbs{} 306 | 307 | Теперь мы можем увидеть к чему применён каждый класс, не увеличивая специфичность селектора. 308 | 309 | #### Теги в коде 310 | 311 | Когда вы пишете новый компонент, указывайте над ним несколько тегов исходя из его назначения, например: 312 | 313 | /** 314 | * ^navigation ^lists 315 | */ 316 | .nav{} 317 | 318 | /** 319 | * ^grids ^lists ^tables 320 | */ 321 | .matrix{} 322 | 323 | Теги позволяют разработчикам находить сниппеты поиском по названию тега. Если 324 | разработчику понадобится поработать со списками, он может начать искать `^lists` и найдёт объекты `.nav` и `.matrix` (и, возможно, больше). 325 | 326 | #### Ссылки между объектом и его расширением 327 | 328 | Используя методы OOCSS, вы часто будете иметь два участка кода (один — скелет приложения (объект) и второй будет его кожей (расширение)), при этом сильно связанных, но, как часто бывает, находящихся в разных файлах. Для установления чёткой связи между объектом и его расширением мы применяем ссылки между объектом и его расширением. Данные ссылки являются такими комментариями: 329 | 330 | В вашем основном файле стилей: 331 | 332 | /** 333 | * Extend `.foo` in theme.css 334 | * (Дополнен классом `.foo` в theme.css) 335 | */ 336 | .foo {} 337 | 338 | В вашем файле стилей темы: 339 | 340 | /** 341 | * Extends `.foo` in base.css 342 | * (Дополняет класс `.foo` из base.css) 343 | */ 344 | .bar{} 345 | 346 | Тем самым мы получили устойчивую связь между двумя связаными логически, но физически разделёнными кусками кода. 347 | 348 | --- 349 | 350 | ## Напиcание CSS 351 | 352 | Предыдущая часть раскрывает, как мы структурируем и форматируем наш CSS; здесь существуют более-менее чёткие правила. 353 | 354 | Следущая часть будет немного более теоретической и повествует о наших способах мышления и подходах. 355 | 356 | ## Создание новых компонентов 357 | 358 | При создании нового компонента пишите разметку **до того**, как напишите хоть одну строчку CSS. Это позволяет увидеть какие свойства наследовались и избежать повторного применения избыточных стилей. 359 | 360 | Если вы будете писать сначала разметку, то сможете сфокусироваться на информации, контенте и семантике и только _после всего этого_ применять соответствующие классы и стили. 361 | 362 | ## OOCSS 363 | 364 | Я использую OOCSS подход; Я разделяю компоненты на структуру (объекты) и оформление (расширения). Как **аналогию** (не пример) можно рассмотреть следующее: 365 | 366 | .room{} 367 | 368 | .room--kitchen{} 369 | .room--bedroom{} 370 | .room--bathroom{} 371 | 372 | Мы располагаем комнатами нескольких типов, но все комнаты имеют похожие свойства; в каждой комнате есть пол, потолок, стены и двери. Мы можем обобщить эту информацию в общем классе `.room{}`. Тем не менее, у нас есть специальные отличающиеся друг от друга типы комнат; кухня должна иметь плитку на полу, спальня — ковёр; в ванной не должно быть окна, а в спальне, напротив, должно. В каждой комнате скорее всего стены покрашены в свой цвет. OOCSS учит нас абстрагировать похожие свойства в базовый объект и в дальнейшем дополнять его с помощью расширяющих классов для добавления особенных качеств. 373 | 374 | Вместо того, чтобы плодить множество уникальных компонентов, постарайтесь обнаружить среди всех шаблонов в дизайне повторяющиеся и создайте соответствующие им классы для многократного использования; создайте этот скелет, как объекты-основу, а затем используйте уточняющие классы, расширяя их стили в определённых уникальных ситуациях. 375 | 376 | Если вы вынуждены создать новый компонент, то разделите его на структуру и декоративное оформление; постройте структуру используя общие классы, тем самым давая возможность использования структуры компонента в других местах вашего проекта, и затем, используя более специфичные классы, оформите компонент в соответствии с требованиями дизайна. 377 | 378 | ## Разметка 379 | 380 | Все компоненты должны быть полностью независимы от ширины; они должны оставаться резиновыми и их ширина должна контролироваться их обёрткой или системой модульных сеток. 381 | 382 | Высота **никогда** не должна назначаться элементам. Высота применяется только на сущности, имевшие размеры _до того_, как попали на сайт (например, картинки и спрайты). Никогда не устанавливайте высоту на `p`, `ul`, `div`, ни на что. Часто вы можете добиться желаемого эффекта при помощи гораздо более гибкого `line-height`. 383 | 384 | Систему модульных сеток следует понимать, как книжные полки. На них находится важное содержимое, но они не содержат его в себе. Вы создаёте свои полки, а затем наполняете их своими вещами. Отделяя систему модульных сеток от созданных нами компонентов, вы можете менять расположение компонентов на сайте намного проще, чем если бы размеры были бы заданы напрямую; это делает нашу разработку клиентской части более гибкой и быстрой в работе. 385 | 386 | Вы никогда не должны применять никаких стилей на ячейку сетки, так как они нужны лишь для разметки. Применяйте стили только к _содержимому ячейки_. Никогда, _ни при каких обстоятельствах_ не применяйте свойства меняющие поведение `box-model` к ячейкам сетки. 387 | 388 | ## Единицы измерения 389 | 390 | Я использую сочетание методов задания размеров интерфейса: проценты, пиксели, `ems`, `rems` или вообще не задаю единицы измерения. 391 | 392 | Ячейки сетки в идеале должны иметь размеры в процентах. Используя сетку для управления колонками на страницах, я оставляю компоненты полностью свободными от размеров (как я рассказывал ранее). 393 | 394 | Размеры шрифтов я устанавливаю в `rem` c запасным решением с использованием пикселей. Этот метод предоставляет доступность контента как с `em` и уверенность при использовании пикселей. Вот простой Sass-миксин для одновременной работы с пикселями и `rem` (вы должны определить переменную `$base-font-size` ранее в стилях): 395 | 396 | @mixin font-size($font-size){ 397 | font-size:$font-size +px; 398 | font-size:$font-size / $base-font-size +rem; 399 | } 400 | 401 | Я использую пиксели только для элементов, имеющих размеры и вне контекста сайта — в основном, это картинки и спрайты, чьи размеры изначально заданы в этих единицах. 402 | 403 | ### Размер текста 404 | 405 | Я составил список классов (похожих на модульную сетку) для задания размера текста. Эти классы могут быть использованы стилизации текста в двухцепочечной иерархии. Моя статья «[Pragmatic, practical font-sizing in CSS](http://csswizardry.com/2012/02/pragmatic-practical-font-sizing-in-css)» расскажет вам, как это работает. 406 | 407 | ## Сокращённая запись 408 | 409 | **Сокращённую запись следует использовать с осторожностью.** 410 | 411 | Это может показаться заманчивым использовать правила похожие на `background: red;`, но делая это, мы на самом деле говорим: «Я хочу, чтобы фоном не было скролящейся, находящейся сверху-слева и повторяющейся по горизонтали и вертикали картинки и чтобы цвет фона был красный». В девяти случаях из десяти это не вызовет никаких проблем, но в 10% обязательно доставит достаточно неприятностей, чтобы не использовать сокращенные записи. Вместо этого используйте `background-color: red;`. 412 | 413 | Например, ситуация с правилом `margin: 0;` — оно ясное и короткое, но черезчур **точное**. Если вы на самом деле хотите воздействовать на нижний отступ элемента, то гораздо более подходяще будет использовать `margin-bottom: 0;`. 414 | 415 | Старайтесь сохранять чёткое представление о свойствах, которые вы устанавливаете и следите за тем, чтобы случайно не сбросить свойства других элементов, используя сокращенную запись. Например, если вы хотите сбросить нижний отступ, то нет никакой необходимости в сбрасывании всех отступов с помощью `margin: 0;`. 416 | 417 | Сокращённая запись хороша, но ей легко злоупотреблять. 418 | 419 | ## Идентификаторы 420 | 421 | Небольшое замечание об идентификаторах в CSS, прежде чем мы углубимся в селекторы в целом. 422 | 423 | **НИКОГДА не используйте идентификаторы в CSS.** 424 | 425 | Они могут встречаться в вашей разметке для JS и идентификации фрагментов, но для стилизации используйте только классы. Вам должно быть неприятно увидеть даже один идентификатор в любом из ваших файлов стилей! 426 | 427 | Преимущество классов в их многоразовости (даже если мы не хотим, мы можем) и у них есть хорошая, низкая специфичность. Специфичность это один из скорейших путей к преодолению проблем в проектах и позволяет всё время сокращать их количество, что является необходимым. Идентификаторы в **255** раз более специфичны, чем классы, так что больше не используйте их в CSS _никогда_. 428 | 429 | ## Селекторы 430 | 431 | Сохраняйте селекторы короткими, эффективными и переносимыми. 432 | 433 | Тяжелые, глубоко вложенные селекторы никуда не годятся по ряду причин. Например возьмем `.sidebar h3 span {}`. Этот селектор сильно привязан к существующей разметке и поэтому нет возможности переместить `span` из `h3` и из `.sidebar`, следовательно, нет возможности обеспечить поддержку стилей на должном уровне. 434 | 435 | Слишком длинные селекторы также вызывают проблемы производительности; чем больше проверок в селекторе (например, селектор `.sidebar h3 span` имеет три проверки, а `.content ul p a` — четыре), тем больше работы выполняет браузер. 436 | 437 | Убедитесь, что ваши стили не зависят от вложенности, насколько это возможно, а также что ваши селекторы короткие и легко воспринимаемые. 438 | 439 | Селекторы в основном должны быть короткими (например, состоящими из одного класса), но имена классов должны быть настолько длинными, насколько это требуется. Класс `.user-avatar` намного лучше, чем `.usr-avt`. 440 | 441 | **Запомните:** Классы на самом деле ни семантичны, ни не семантичны; они могут использоваться рационально или нерационально! Перестаньте беспокоиться о «семантике» имён классов и выберите что-нибудь рациональное и безопасное в будущем. 442 | 443 | ### Перенасыщенные селекторы 444 | 445 | Как говорилось выше, увидеть специфичный селектор — всегда плохой знак. 446 | 447 | Перенасыщенный (гиперспецифичный) селектор это такой, как `div.promo`. Скорее всего, мы можем достичь тот же самый эффект, используя лишь `.promo`. Конечно, иногда мы _хотим_ указать зависимость класса от элемента (например, если у вас есть общий класс `.error`, который должен выглядеть по разному при разных элементах (например, `.error { color: red; }` `div.error { padding: 14px; }`)), но по возможности избегайте этого, где это только возможно. 448 | 449 | Другим примером слишком перенасыщенного селектора может быть `ul.nav li a {}`. Как описано выше мы сразу можем выкинуть `ul`, и так как мы знаем, что `.nav` это список, то ссылка будет вложена только в `li`, поэтому мы можем сократить `ul.nav li a {}` до `.nav a`. 450 | 451 | ### Производительность селекторов 452 | 453 | Хоть это и правда, что браузеры только улучшают свои показатели в скорости рендеринга CSS, эффективность — это то, на чём мы можем быть сфокусированы всегда. Короткие, не вложенные селекторы, избежание универсального (`* {}`) селектора, как основного, и избежание больших комбинаций CSS3-селекторов должны помочь обойти проблемы производительности. 454 | 455 | ## Последовательность CSS-селекторов 456 | 457 | Вместо использования селекторов, спускающихся по всему DOM-дереву, чаще удобнее добавить требуемому элементу класс. Давайте рассмотрим конкретную ситуацию на примере `.header ul{}`… 458 | 459 | Давайте представим, что `ul` действительно главная навигация на вашем сайте. Этот элемент находится в шапке сайта, как вы и ожидаете, и при этом не повторяется; `.header ul{}` будет работать, но он не идеален и не рекомендуем. Этот селектор небезопасен в будущем и, конечно, недостаточно точен. Проблема станет очевидна так скоро, как вы добавите ещё один `ul` в шапку сайта — новый элемент унаследует все стили нашего главного меню, и шансы, что мы так и задумали, весьма невелики. Это приведёт к вынужденному рефакторингу большого количества кода _или_ отмене большинства стилей для второго `ul`, чтобы уничтожить последствия недостаточно специфичного селектора. 460 | 461 | Составляющие селектора должны отвечать причинам стилизации чего-либо; спросите себя **«Я пишу этот селектор именно, потому что `ul` вложен в `.header` или из-за того, что этот элемент — это главное меню на моём сайте?»**. Ответ опреляет правильный селектор. 462 | 463 | Убедитесь, что ключевые элементы селектора не являются простыми селекторами элемента/типа или классами объекта/абстракции. Вы никогда не должны писать селекторы похожие на `.sidebar ul{}` или `.footer .media{}` в вашем файле стилей темы. 464 | 465 | Будьте точны, конкретны; указывайте именно тот элемент, на который вы хотите воздействовать, не его родитель. Никогда не предполагайте, что разметка будет неизменной. **Пишите селекторы, нацеленные на нужные элементы, а не на те, которые есть в данный момент.** 466 | 467 | Для полного объяснения прочитайте мою статью [Shoot to kill; CSS selector intent](http://csswizardry.com/2012/07/shoot-to-kill-css-selector-intent/) 468 | 469 | ## `!important` 470 | 471 | Допустимо использовать `!important` только на вспомогательных классах. Превентивно добавлять `!important` удобно и полезно, например, если вы знаете, что селектор `.error { color: red !important; }` **всегда** должен нуждаться в наибольшем приоритете. 472 | 473 | Не приветствуется использование `!important` для исправления ошибок, например, чтобы помочь выбраться себе из ситуации с запутанной специфичностью. Переработайте ваш CSS и старайтесь избегать этих проблем рефакторингом ваших селекторов. Сохраняйте ваши селекторы короткими, откажитесь от ID — и ваша жизнь станет проще. 474 | 475 | ## Магические числа и абсолютные значения 476 | 477 | Магическое число — число используемое лишь потому, что «это просто работает». Это порочная практика, так как очень редко она работает по какой-либо реальной причине и обычно достаточно недальновидна, негибка и причина появления самого числа вероятнее всего забудется. Магические числа устраняют симптомы, но не никак не влияют на проблему. 478 | 479 | Например, использование `.dropdown-nav li:hover ul { top: 37px; }`, чтобы расположить выпадающее меню внизу навигации не принесёт ничего хорошего, так как `37px` магическое число. Оно работает только потому, что в этом конретном сценарии элемент `.dropdown-nav` оказался высотой в 37 пикселей. 480 | 481 | Вместо этого мы должны использовать `.dropdown-nav li:hover ul { top: 100%; }`, что означает: вне зависимости какой высоты будет меню `dropdown-nav`, выпадающее всегда будет сдвинуто на 100% от верхней границы родителя. 482 | 483 | Каждый раз, когда вы жёстко задаёте число, подумайте дважды; если вы можете избежать этого — избегайте, например, используя ключевые слова или синонимы (`top: 100%;` для того, чтобы сдвинуть на 100% от верха) или — даже лучше — не используя никаких единиц измерений. 484 | 485 | Каждое установленное вами числовое значение, скорее всего, было необязательным. 486 | 487 | ## Стили в условных комментариях 488 | 489 | Использования отдельных файлов стилей для IE, по большому счёту, можно избежать. Исключением может быть необходимость восполнить вопиющие недостатки поддержки различных свойств (например, PNG с альфа–каналом в IE6). 490 | 491 | Главное правило: вся разметка и `box-model` правила могут и _будут_ работать без дополнительных файлов стилей, если вы отрефакторите и переработаете ваш CSS. Это означает, что мы никогда не будем рады, если увидим `` или любой подобный CSS, используемый лишь для того, чтобы «заставить что-то работать правильно». 492 | 493 | ## Отладка 494 | 495 | Если вы столкнулись с проблемой в CSS, то **удаляйте куски кода, до того как начать добавлять новые правила** в попытке решить проблему. Проблема кроется в уже написанном CSS, написать ещё больше стилей — не самое верное решение! 496 | 497 | Удаляйте куски разметки и стилей, пока проблема не исчезнет, затем определите, в какую часть кода закралась проблема. 498 | 499 | Это бывает достаточно удобно добавить `overflow: hidden;` нужному элементу, чтобы избавиться от результатов кривой вёрстки, но `overflow` сам по себе вряд ли был проблемой; **исправляйте проблему, а не её симптомы.** 500 | 501 | ## Препроцессоры 502 | 503 | Sass — мой выбор среди препроцессоров. **Используйте его с умом** 504 | Используйте Sass, чтобы сделать ваш CSS более мощным, но избегайте вложенности, как чумы! Используйте вложенность только тогда, когда это действительно могло бы быть необходимо в чистом CSS, например: 505 | 506 | .header{} 507 | .header .site-nav{} 508 | .header .site-nav li{} 509 | .header .site-nav li a{} 510 | 511 | Это было бы полностью избыточно в нормальном CSS, следовательно, следущий Sass-код будет **плох**: 512 | 513 | .header{ 514 | .site-nav{ 515 | li{ 516 | a{} 517 | } 518 | } 519 | } 520 | 521 | Иcпользуя Sass, пишите это так: 522 | 523 | .header{} 524 | .site-nav{ 525 | li{} 526 | a{} 527 | } 528 | 529 | 530 | 531 | 532 | ![](https://secure.gaug.es/track.gif?h[site_id]=52036088108d7b260f0002f9&h[resource]=https%3A%2F%2Fgithub.com%2Fmatmuchrapna%2FHampi%2Fblob%2Fmaster%2FREADME.md) 533 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # General CSS notes, advice and guidelines 2 | 3 | --- 4 | 5 | ## Translations 6 | 7 | * [Russian/русский](https://github.com/matmuchrapna/CSS-Guidelines/blob/master/README%20Russian.md) 8 | 9 | --- 10 | 11 | In working on large, long running projects with dozens of developers, it is 12 | important that we all work in a unified way in order to, among other things: 13 | 14 | * Keep stylesheets maintainable 15 | * Keep code transparent and readable 16 | * Keep stylesheets scalable 17 | 18 | There are a variety of techniques we must employ in order to satisfy these 19 | goals. 20 | 21 | The first part of this document will deal with syntax, formatting and CSS 22 | anatomy, the second part will deal with approach, mindframe and attitude toward 23 | writing and architecting CSS. Exciting, huh? 24 | 25 | ## Contents 26 | 27 | * [CSS document anatomy](#css-document-anatomy) 28 | * [General](#general) 29 | * [One file vs. many files](#one-file-vs-many-files) 30 | * [Table of contents](#table-of-contents) 31 | * [Section titles](#section-titles) 32 | * [Source order](#source-order) 33 | * [Anatomy of rulesets](#anatomy-of-rulesets) 34 | * [Naming conventions](#naming-conventions) 35 | * [JS hooks](#js-hooks) 36 | * [Internationalisation](#internationalisation) 37 | * [Comments](#comments) 38 | * [Comments on steroids](#comments-on-steroids) 39 | * [Quasi-qualified selectors](#quasi-qualified-selectors) 40 | * [Tagging code](#tagging-code) 41 | * [Object/extension pointers](#objectextension-pointers) 42 | * [Writing CSS](#writing-css) 43 | * [Building new components](#building-new-components) 44 | * [OOCSS](#oocss) 45 | * [Layout](#layout) 46 | * [Sizing UIs](#sizing-uis) 47 | * [Font sizing](#font-sizing) 48 | * [Shorthand](#shorthand) 49 | * [IDs](#ids) 50 | * [Selectors](#selectors) 51 | * [Over qualified selectors](#over-qualified-selectors) 52 | * [Selector performance](#selector-performance) 53 | * [CSS selector intent](#css-selector-intent) 54 | * [`!important`](#important) 55 | * [Magic numbers and absolutes](#magic-numbers-and-absolutes) 56 | * [Conditional stylesheets](#conditional-stylesheets) 57 | * [Debugging](#debugging) 58 | * [Preprocessors](#preprocessors) 59 | 60 | --- 61 | 62 | ## CSS Document Anatomy 63 | 64 | No matter the document, we must always try and keep a common formatting. This 65 | means consistent commenting, consistent syntax and consistent naming. 66 | 67 | ### General 68 | 69 | Limit your stylesheets to a maximum 80 character width where possible. 70 | Exceptions may be gradient syntax and URLs in comments. That’s fine, there’s 71 | nothing we can do about that. 72 | 73 | I prefer four (4) space indents over tabs and write multi-line CSS. 74 | 75 | ### One file vs. many files 76 | 77 | Some people prefer to work with single, large files. This is fine, and by 78 | sticking to the following guidelines you’ll encounter no problems. Since moving 79 | to Sass I have started sharding my stylesheets out into lots of tiny includes. 80 | This too is fine… Whichever method you choose, the following rules and 81 | guidelines apply. The only notable difference is with regards our table of 82 | contents and our section titles. Read on for further explanation… 83 | 84 | ### Table of contents 85 | 86 | At the top of stylesheets, I maintain a table of contents which will detail the 87 | sections contained in the document, for example: 88 | 89 | /*------------------------------------*\ 90 | $CONTENTS 91 | \*------------------------------------*/ 92 | /** 93 | * CONTENTS............You’re reading it! 94 | * RESET...............Set our reset defaults 95 | * FONT-FACE...........Import brand font files 96 | */ 97 | 98 | This will tell the next developer(s) exactly what they can expect to find in 99 | this file. Each item in the table of contents maps directly to a section title. 100 | 101 | If you are working in one big stylesheet, the corresponding section will also be 102 | in that file. If you are working across multiple files then each item in the 103 | table of contents will map to an include which pulls that section in. 104 | 105 | ### Section titles 106 | 107 | The table of contents would be of no use unless it had corresponding section 108 | titles. Denote a section thus: 109 | 110 | /*------------------------------------*\ 111 | $RESET 112 | \*------------------------------------*/ 113 | 114 | The `$` prefixing the name of the section allows us to run a find ([Cmd|Ctrl]+F) 115 | for `$[SECTION-NAME]` and **limit our search scope to section titles only**. 116 | 117 | If you are working in one large stylesheet, you leave five (5) carriage returns 118 | between each section, thus: 119 | 120 | /*------------------------------------*\ 121 | $RESET 122 | \*------------------------------------*/ 123 | [Our 124 | reset 125 | styles] 126 | 127 | 128 | 129 | 130 | 131 | /*------------------------------------*\ 132 | $FONT-FACE 133 | \*------------------------------------*/ 134 | 135 | This large chunk of whitespace is quickly noticeable when scrolling quickly 136 | through larger files. 137 | 138 | If you are working across multiple, included stylesheets, start each of those 139 | files with a section title and there is no need for any carriage returns. 140 | 141 | ## Source order 142 | 143 | Try and write stylesheets in specificity order. This ensures that you take full 144 | advantage of inheritance and CSS’ first C; the cascade. 145 | 146 | A well ordered stylesheet will be ordered something like this: 147 | 148 | 1. **Reset** – ground zero. 149 | 2. **Elements** – unclassed `h1`, unclassed `ul` etc. 150 | 3. **Objects and abstractions** — generic, underlying design patterns. 151 | 4. **Components** – full components constructed from objects and their 152 | extensions. 153 | 5. **Style trumps** – error states etc. 154 | 155 | This means that—as you go down the document—each section builds upon and 156 | inherits sensibly from the previous one(s). There should be less undoing of 157 | styles, less specificity problems and all-round better architected stylesheets. 158 | 159 | For further reading I cannot recommend Jonathan Snook’s 160 | [SMACSS](http://smacss.com) highly enough. 161 | 162 | ## Anatomy of rulesets 163 | 164 | [selector]{ 165 | [property]:[value]; 166 | [<- Declaration ->] 167 | } 168 | 169 | I have a number of standards when structuring rulesets. 170 | 171 | * Use hyphen delimited class names (except for BEM notation, 172 | [see below](#naming-conventions)) 173 | * 4 space indented 174 | * Multi-line 175 | * Declarations in relevance (NOT alphabetical) order 176 | * Indent vendor prefixed declarations so that their values are aligned 177 | * Indent our rulesets to mirror the DOM 178 | * Always include the final semi-colon in a ruleset 179 | 180 | A brief example: 181 | 182 | .widget{ 183 | padding:10px; 184 | border:1px solid #BADA55; 185 | background-color:#C0FFEE; 186 | -webkit-border-radius:4px; 187 | -moz-border-radius:4px; 188 | border-radius:4px; 189 | } 190 | .widget-heading{ 191 | font-size:1.5rem; 192 | line-height:1; 193 | font-weight:bold; 194 | color:#BADA55; 195 | margin-right:-10px; 196 | margin-left: -10px; 197 | padding:0.25em; 198 | } 199 | 200 | Here we can see that `.widget-heading` must be a child of `.widget` as we have 201 | indented the `.widget-heading` ruleset one level deeper than `.widget`. This is 202 | useful information to developers that can now be gleaned just by a glance at the 203 | indentation of our rulesets. 204 | 205 | We can also see that `.widget-heading`’s declarations are ordered by their 206 | relevance; `.widget-heading` must be a textual element so we begin with our 207 | text rules, followed by everything else. 208 | 209 | One exception to our multi-line rule might be in cases of the following: 210 | 211 | .t10 { width:10% } 212 | .t20 { width:20% } 213 | .t25 { width:25% } /* 1/4 */ 214 | .t30 { width:30% } 215 | .t33 { width:33.333% } /* 1/3 */ 216 | .t40 { width:40% } 217 | .t50 { width:50% } /* 1/2 */ 218 | .t60 { width:60% } 219 | .t66 { width:66.666% } /* 2/3 */ 220 | .t70 { width:70% } 221 | .t75 { width:75% } /* 3/4*/ 222 | .t80 { width:80% } 223 | .t90 { width:90% } 224 | 225 | In this example (from [inuit.css’s table grid system]( 226 | https://github.com/csswizardry/inuit.css/blob/master/inuit.css/partials/base/_tables.scss#L88)) 227 | it makes more sense to single-line our CSS. 228 | 229 | ## Naming conventions 230 | 231 | For the most part I simply use hyphen delimited classes (e.g. `.foo-bar`, not 232 | `.foo_bar` or `.fooBar`), however in certain circumstances I use BEM (Block, 233 | Element, Modifier) notation. 234 | 235 | BEM is a methodology for naming 236 | and classifying CSS selectors in a way to make them a lot more strict, 237 | transparent and informative. 238 | 239 | The naming convention follows this pattern: 240 | 241 | .block{} 242 | .block__element{} 243 | .block--modifier{} 244 | 245 | * `.block` represents the higher level of an abstraction or component. 246 | * `.block__element` represents a descendent of `.block` that helps form `.block` 247 | as a whole. 248 | * `.block--modifier` represents a different state or version of `.block`. 249 | 250 | An **analogy** of how BEM classes work might be: 251 | 252 | .person{} 253 | .person--woman{} 254 | .person__hand{} 255 | .person__hand--left{} 256 | .person__hand--right{} 257 | 258 | Here we can see that the basic object we’re describing is a person, and that a 259 | different type of person might be a woman. We can also see that people have 260 | hands; these are sub-parts of people, and there are different variations, 261 | like left and right. 262 | 263 | We can now namespace our selectors based on their base objects and we can also 264 | communicate what job the selector does; is it a sub-component (`__`) or a 265 | variation (`--`)? 266 | 267 | So, `.page-wrapper` is a standalone selector; it doesn’t form part of an 268 | abstraction or a component and as such it named correctly. `.widget-heading`, 269 | however, _is_ related to a component; it is a child of the `.widget` construct 270 | so we would rename this class `.widget__heading`. 271 | 272 | BEM looks a little uglier, and is a lot more verbose, but it grants us a lot of 273 | power in that we can glean the functions and relationships of elements from 274 | their classes alone. Also, BEM syntax will typically compress (gzip) very well 275 | as compression favours/works well with repetition. 276 | 277 | Regardless of whether you need to use BEM or not, always ensure classes are 278 | sensibly named; keep them as short as possible but as long as necessary. Ensure 279 | any objects or abstractions are very vaguely named (e.g. `.ui-list`, `.media`) 280 | to allow for greater reuse. Extensions of objects should be much more explicitly 281 | named (e.g. `.user-avatar-link`). Don’t worry about the amount or length of 282 | classes; gzip will compress well written code _incredibly_ well. 283 | 284 | ### Classes in HTML 285 | 286 | In a bid to make things easier to read, separate classes is your HTML with two 287 | (2) spaces, thus: 288 | 289 |
290 | 291 | This increased whitespace should hopefully allow for easier spotting and reading 292 | of multiple classes. 293 | 294 | ### JS hooks 295 | 296 | **Never use a CSS _styling_ class as a JavaScript hook.** Attaching JS behaviour 297 | to a styling class means that we can never have one without the other. 298 | 299 | If you need to bind to some markup use a JS specific CSS class. This is simply a 300 | class namespaced with `.js-`, e.g. `.js-toggle`, `.js-drag-and-drop`. This means 301 | that we can attach both JS and CSS to classes in our markup but there will never 302 | be any troublesome overlap. 303 | 304 | 305 | 306 | 307 | The above markup holds two classes; one to which you can attach some styling for 308 | sortable table columns and another which allows you to add the sorting 309 | functionality. 310 | 311 | ### Internationalisation 312 | 313 | Despite being a British developer—and spending all my life writing colour 314 | instead of color—I feel that, for the sake of consistency, it is better 315 | to always use US-English in CSS. CSS, as with most (if not all) other languages, 316 | is written in US-English, so to mix syntax like `color:red;` with classes like 317 | `.colour-picker{}` lacks consistency. I have previously suggested and advocated 318 | writing bilingual classes, for example: 319 | 320 | .color-picker, 321 | .colour-picker{ 322 | } 323 | 324 | However, having recently worked on a very large Sass project where there were 325 | dozens of colour variables (e.g. `$brand-color`, `$highlight-color` etc.), 326 | maintaining two versions of each variable soon became tiresome. It also means 327 | twice as much work with things like find and replace. 328 | 329 | In the interests of consistency, always name classes and variables in the locale 330 | of the language you are working with. 331 | 332 | ## Comments 333 | 334 | I use a docBlock-esque commenting style which I limit to 80 characters in length: 335 | 336 | /** 337 | * This is a docBlock style comment 338 | * 339 | * This is a longer description of the comment, describing the code in more 340 | * detail. We limit these lines to a maximum of 80 characters in length. 341 | * 342 | * We can have markup in the comments, and are encouraged to do so: 343 | * 344 |
345 |

Lorem

346 |
347 | * 348 | * We do not prefix lines of code with an asterisk as to do so would inhibit 349 | * copy and paste. 350 | */ 351 | 352 | You should document and comment our code as much as you possibly can, what may 353 | seem or feel transparent and self explanatory to you may not be to another dev. 354 | Write a chunk of code then write about it. 355 | 356 | ### Comments on steroids 357 | 358 | There are a number of more advanced techniques you can employ with regards 359 | comments, namely: 360 | 361 | * Quasi-qualified selectors 362 | * Tagging code 363 | * Object/extension pointers 364 | 365 | #### Quasi-qualified selectors 366 | 367 | You should never qualify your selectors; that is to say, we should never write 368 | `ul.nav{}` if you can just have `.nav`. Qualifying selectors decreases selector 369 | performance, inhibits the potential for reusing a class on a different type of 370 | element and it increases the selector’s specificity. These are all things that 371 | should be avoided at all costs. 372 | 373 | However, sometimes it is useful to communicate to the next developer(s) where 374 | you intend a class to be used. Let’s take `.product-page` for example; this 375 | class sounds as though it would be used on a high-level container, perhaps the 376 | `html` or `body` element, but with `.product-page` alone it is impossible to 377 | tell. 378 | 379 | By quasi-qualifying this selector (i.e. commenting out the leading type 380 | selector) we can communicate where we wish to have this class applied, thus: 381 | 382 | /*html*/.product-page{} 383 | 384 | We can now see exactly where to apply this class but with none of the 385 | specificity or non-reusability drawbacks. 386 | 387 | Other examples might be: 388 | 389 | /*ol*/.breadcrumb{} 390 | /*p*/.intro{} 391 | /*ul*/.image-thumbs{} 392 | 393 | Here we can see where we intend each of these classes to be applied without 394 | actually ever impacting the specificity of the selectors. 395 | 396 | #### Tagging code 397 | 398 | If you write a new component then leave some tags pertaining to its function in 399 | a comment above it, for example: 400 | 401 | /** 402 | * ^navigation ^lists 403 | */ 404 | .nav{} 405 | 406 | /** 407 | * ^grids ^lists ^tables 408 | */ 409 | .matrix{} 410 | 411 | These tags allow other developers to find snippets of code by searching for 412 | function; if a developer needs to work with lists they can run a find for 413 | `^lists` and find the `.nav` and `.matrix` objects (and possibly more). 414 | 415 | #### Object/extension pointers 416 | 417 | When working in an object oriented manner you will often have two chunks of CSS 418 | (one being the skeleton (the object) and the other being the skin (the 419 | extension)) that are very closely related, but that live in very different 420 | places. In order to establish a concrete link between the object and its 421 | extension with use object/extension pointers. These are simply comments 422 | which work thus: 423 | 424 | In your base stylesheet: 425 | 426 | /** 427 | * Extend `.foo` in theme.css 428 | */ 429 | .foo{} 430 | 431 | In your theme stylesheet: 432 | 433 | /** 434 | * Extends `.foo` in base.css 435 | */ 436 | .bar{} 437 | 438 | Here we have established a concrete relationship between two very separate 439 | pieces of code. 440 | 441 | --- 442 | 443 | ## Writing CSS 444 | 445 | The previous section dealt with how we structure and form our CSS; they were 446 | very quantifiable rules. The next section is a little more theoretical and deals 447 | with our attitude and approach. 448 | 449 | ## Building new components 450 | 451 | When building a new component write markup **before** CSS. This means you can 452 | visually see which CSS properties are naturally inherited and thus avoid 453 | reapplying redundant styles. 454 | 455 | By writing markup first you can focus on data, content and semantics and then 456 | apply only the relevant classes and CSS _afterwards_. 457 | 458 | ## OOCSS 459 | 460 | I work in an OOCSS manner; I split components into structure (objects) and 461 | skin (extensions). As an **analogy** (note, not example) take the following: 462 | 463 | .room{} 464 | 465 | .room--kitchen{} 466 | .room--bedroom{} 467 | .room--bathroom{} 468 | 469 | We have several types of room in a house, but they all share similar traits; 470 | they all have floors, ceilings, walls and doors. We can share this information 471 | in an abstracted `.room{}` class. However we have specific types of room that 472 | are different from the others; a kitchen might have a tiled floor and a bedroom 473 | might have carpets, a bathroom might not have a window but a bedroom most likely 474 | will, each room likely has different coloured walls. OOCSS teaches us to 475 | abstract the shared styles out into a base object and then _extend_ this 476 | information with more specific classes to add the unique treatment(s). 477 | 478 | So, instead of building dozens of unique components, try and spot repeated 479 | design patterns across them all and abstract them out into reusable classes; 480 | build these skeletons as base ‘objects’ and then peg classes onto these to 481 | extend their styling for more unique circumstances. 482 | 483 | If you have to build a new component split it into structure and skin; build the 484 | structure of the component using very generic classes so that we can reuse that 485 | construct and then use more specific classes to skin it up and add design 486 | treatments. 487 | 488 | ## Layout 489 | 490 | All components you build should be left totally free of widths; they should 491 | always remain fluid and their widths should be governed by a parent/grid system. 492 | 493 | Heights should **never** be be applied to elements. Heights should only be 494 | applied to things which had dimensions _before_ they entered the site (i.e. 495 | images and sprites). Never ever set heights on `p`s, `ul`s, `div`s, anything. 496 | You can often achieve the desired effect with `line-height` which is far more 497 | flexible. 498 | 499 | Grid systems should be thought of as shelves. They contain content but are not 500 | content in themselves. You put up your shelves then fill them with your stuff. 501 | By setting up our grids separately to our components you can move components 502 | around a lot more easily than if they had dimensions applied to them; this makes 503 | our front-ends a lot more adaptable and quick to work with. 504 | 505 | You should never apply any styles to a grid item, they are for layout purposes 506 | only. Apply styling to content _inside_ a grid item. Never, under _any_ 507 | circumstances, apply box-model properties to a grid item. 508 | 509 | ## Sizing UIs 510 | 511 | I use a combination of methods for sizing UIs. Percentages, pixels, ems, rems 512 | and nothing at all. 513 | 514 | Grid systems should, ideally, be set in percentages. Because I use grid systems 515 | to govern widths of columns and pages, I can leave components totally free of 516 | any dimensions (as discussed above). 517 | 518 | Font sizes I set in rems with a pixel fallback. This gives the accessibility 519 | benefits of ems with the confidence of pixels. Here is a handy Sass mixin to 520 | work out a rem and pixel fallback for you (assuming you set your base font 521 | size in a variable somewhere): 522 | 523 | @mixin font-size($font-size){ 524 | font-size:$font-size +px; 525 | font-size:$font-size / $base-font-size +rem; 526 | } 527 | 528 | I only use pixels for items whose dimensions were defined before the came into 529 | the site. This includes things like images and sprites whose dimensions are 530 | inherently set absolutely in pixels. 531 | 532 | ### Font sizing 533 | 534 | I define a series of classes akin to a grid system for sizing fonts. These 535 | classes can be used to style type in a double stranded heading hierarchy. For a 536 | full explanation of how this works please refer to my article 537 | [Pragmatic, practical font-sizing in CSS](http://csswizardry.com/2012/02/pragmatic-practical-font-sizing-in-css) 538 | 539 | ## Shorthand 540 | 541 | **Shorthand CSS needs to be used with caution.** 542 | 543 | It might be tempting to use declarations like `background:red;` but in doing so 544 | what you are actually saying is ‘I want no image to scroll, aligned top-left, 545 | repeating X and Y, and a background colour of red’. Nine times out of ten this 546 | won’t cause any issues but that one time it does is annoying enough to warrant 547 | not using such shorthand. Instead use `background-color:red;`. 548 | 549 | Similarly, declarations like `margin:0;` are nice and short, but 550 | **be explicit**. If you actually only really want to affect the margin on 551 | the bottom of an element then it is more appropriate to use `margin-bottom:0;`. 552 | 553 | Be explicit in which properties you set and take care to not inadvertently unset 554 | others with shorthand. E.g. if you only want to remove the bottom margin on an 555 | element then there is no sense in setting all margins to zero with `margin:0;`. 556 | 557 | Shorthand is good, but easily misused. 558 | 559 | ## IDs 560 | 561 | A quick note on IDs in CSS before we dive into selectors in general. 562 | 563 | **NEVER use IDs in CSS.** 564 | 565 | They can be used in your markup for JS and fragment identifiers but use only 566 | classes for styling. You don’t want to see a single ID in any stylesheets! 567 | 568 | Classes come with the benefit of being reusable (even if we don’t want to, we 569 | can) and they have a nice, low specificity. Specificity is one of the quickest 570 | ways to run into difficulties in projects and keeping it low at all times is 571 | imperative. An ID is **255** times more specific than a class, so never ever use 572 | them in CSS _ever_. 573 | 574 | ## Selectors 575 | 576 | Keep selectors short, efficient and portable. 577 | 578 | Heavily location-based selectors are bad for a number of reasons. For example, 579 | take `.sidebar h3 span{}`. This selector is too location-based and thus we 580 | cannot move that `span` outside of a `h3` outside of `.sidebar` and maintain 581 | styling. 582 | 583 | Selectors which are too long also introduce performance issues; the more checks 584 | in a selector (e.g. `.sidebar h3 span` has three checks, `.content ul p a` has 585 | four), the more work the browser has to do. 586 | 587 | Make sure styles aren’t dependent on location where possible, and make sure 588 | selectors are nice and short. 589 | 590 | Selectors as a whole should be kept short (e.g. one class deep) but the class 591 | names themselves should be as long as they need to be. A class of `.user-avatar` 592 | is far nicer than `.usr-avt`. 593 | 594 | **Remember:** classes are neither semantic or insemantic; they are sensible or 595 | insensible! Stop stressing about ‘semantic’ class names and pick something 596 | sensible and futureproof. 597 | 598 | ### Over-qualified selectors 599 | 600 | As discussed above, qualified selectors are bad news. 601 | 602 | An over-qualified selector is one like `div.promo`. You could probably get the 603 | same effect from just using `.promo`. Of course sometimes you will _want_ to 604 | qualify a class with an element (e.g. if you have a generic `.error` class that 605 | needs to look different when applied to different elements (e.g. 606 | `.error{ color:red; }` `div.error{ padding:14px; }`)), but generally avoid it 607 | where possible. 608 | 609 | Another example of an over-qualified selector might be `ul.nav li a{}`. As 610 | above, we can instantly drop the `ul` and because we know `.nav` is a list, we 611 | therefore know that any `a` _must_ be in an `li`, so we can get `ul.nav li a{}` 612 | down to just `.nav a{}`. 613 | 614 | ### Selector performance 615 | 616 | Whilst it is true that browsers will only ever keep getting faster at rendering 617 | CSS, efficiency is something you could do to keep an eye on. Short, unnested 618 | selectors, not using the universal (`*{}`) selector as the key selector, and 619 | avoiding more complex CSS3 selectors should help circumvent these problems. 620 | 621 | ## CSS selector intent 622 | 623 | Instead of using selectors to drill down the DOM to an element, it is often best 624 | to put a class on the element you explicitly want to style. Let’s take a 625 | specific example with a selector like `.header ul{}`… 626 | 627 | Let’s imagine that `ul` is indeed the main navigation for our website. It lives 628 | in the header as you might expect and is currently the only `ul` in there; 629 | `.header ul{}` will work, but it’s not ideal or advisable. It’s not very future 630 | proof and certainly not explicit enough. As soon as we add another `ul` to that 631 | header it will adopt the styling of our main nav and the the chances are it 632 | won’t want to. This means we either have to refactor a lot of code _or_ undo a 633 | lot of styling on subsequent `ul`s in that `.header` to remove the effects of 634 | the far reaching selector. 635 | 636 | Your selector’s intent must match that of your reason for styling something; 637 | ask yourself **‘am I selecting this because it’s a `ul` inside of `.header` or 638 | because it is my site’s main nav?’**. The answer to this will determine your 639 | selector. 640 | 641 | Make sure your key selector is never an element/type selector or 642 | object/abstraction class. You never really want to see selectors like 643 | `.sidebar ul{}` or `.footer .media{}` in our theme stylesheets. 644 | 645 | Be explicit; target the element you want to affect, not its parent. Never assume 646 | that markup won’t change. **Write selectors that target what you want, not what 647 | happens to be there already.** 648 | 649 | For a full write up please see my article 650 | [Shoot to kill; CSS selector intent](http://csswizardry.com/2012/07/shoot-to-kill-css-selector-intent/) 651 | 652 | ## `!important` 653 | 654 | It is okay to use `!important` on helper classes only. To add `!important` 655 | preemptively is fine, e.g. `.error{ color:red!important }`, as you know you will 656 | **always** want this rule to take precedence. 657 | 658 | Using `!important` reactively, e.g. to get yourself out of nasty specificity 659 | situations, is not advised. Rework your CSS and try to combat these issues by 660 | refactoring your selectors. Keeping your selectors short and avoiding IDs will 661 | help out here massively. 662 | 663 | ## Magic numbers and absolutes 664 | 665 | A magic number is a number which is used because ‘it just works’. These are bad 666 | because they rarely work for any real reason and are not usually very 667 | futureproof or flexible/forgiving. They tend to fix symptoms and not problems. 668 | 669 | For example, using `.dropdown-nav li:hover ul{ top:37px; }` to move a dropdown 670 | to the bottom of the nav on hover is bad, as 37px is a magic number. 37px only 671 | works here because in this particular scenario the `.dropdown-nav` happens to be 672 | 37px tall. 673 | 674 | Instead you should use `.dropdown-nav li:hover ul{ top:100%; }` which means no 675 | matter how tall the `.dropdown-nav` gets, the dropdown will always sit 100% from 676 | the top. 677 | 678 | Every time you hard code a number think twice; if you can avoid it by using 679 | keywords or ‘aliases’ (i.e. `top:100%` to mean ‘all the way from the top’) 680 | or—even better—no measurements at all then you probably should. 681 | 682 | Every hard-coded measurement you set is a commitment you might not necessarily 683 | want to keep. 684 | 685 | ## Conditional stylesheets 686 | 687 | IE stylesheets can, by and large, be totally avoided. The only time an IE 688 | stylesheet may be required is to circumvent blatant lack of support (e.g. PNG 689 | fixes). 690 | 691 | As a general rule, all layout and box-model rules can and _will_ work without an 692 | IE stylesheet if you refactor and rework your CSS. This means you never want to 693 | see `` or other such 694 | CSS that is clearly using arbitrary styling to just ‘make stuff work’. 695 | 696 | ## Debugging 697 | 698 | If you run into a CSS problem **take code away before you start adding more** in 699 | a bid to fix it. The problem exists in CSS that is already written, more CSS 700 | isn’t the right answer! 701 | 702 | Delete chunks of markup and CSS until your problem goes away, then you can 703 | determine which part of the code the problem lies in. 704 | 705 | It can be tempting to put an `overflow:hidden;` on something to hide the effects 706 | of a layout quirk, but overflow was probably never the problem; **fix the 707 | problem, not its symptoms.** 708 | 709 | ## Preprocessors 710 | 711 | Sass is my preprocessor of choice. **Use it wisely.** Use Sass to make your CSS 712 | more powerful but avoid nesting like the plague! Nest only when it would 713 | actually be necessary in vanilla CSS, e.g. 714 | 715 | .header{} 716 | .header .site-nav{} 717 | .header .site-nav li{} 718 | .header .site-nav li a{} 719 | 720 | Would be wholly unnecessary in normal CSS, so the following would be **bad** 721 | Sass: 722 | 723 | .header{ 724 | .site-nav{ 725 | li{ 726 | a{} 727 | } 728 | } 729 | } 730 | 731 | If you were to Sass this up you’d write it as: 732 | 733 | .header{} 734 | .site-nav{ 735 | li{} 736 | a{} 737 | } 738 | --------------------------------------------------------------------------------