├── 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 |
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 | 
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 |
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 |
--------------------------------------------------------------------------------