- 1.1 Главное! Используем pixel-perfect, если заказчик предоставил макет.
- 1.2 Не подключать стили с удаленных источников — только с наших серверов.
-
1.3 CSS — опасная сила.
Все стили, подключаемые к странице, являются глобальными, и, если этим злоупотреблять, то можно легко довести проект до состояния крайне болезненного внедрения любого нового элемента или изменения старого. Это чаще всего происходит, когда создаются "общие" классы, потом крепятся к разным элементам на странице (банальный пример — шапка, у которой внутри форма поиска, которая есть на всех страницах с одинаковыми классами) и в итоге, когда по дизайну на разных страницах появляются небольшие отличия в шапке, плодятся классы, переписывающие свойства. И так почти для всех элементов каждой страницы. В итоге, меняя крайнюю правую ссылку в сайдбаре на странице новостей, вы можете поломать верстку всего сайдбара на какой-нибудь другой странице, например, на странице личного кабинета.
Для того чтобы такого не происходило, существуют разные методологии, например,
БЭМ
(прочитать про него обязательно хотя бы в общих чертах) или, например, rscss. Для начала можете изучить небольшой подход, который используют ребята изIsobar
— он описан в рамках их стандарта и состоит из 7 небольших абзацев.
- 1.4 Отступы делать пробелами, один уровень — 2 пробела.
- 1.5 Использовать одинарные кавычки.
-
1.6 Перед началом работы обязательно четко знать все приоритеты селекторов и все 4 вида возможных отношений.
Отношения элементов между собой могут быть:
div p
– элементы p, являющиеся потомками divdiv > p
– только непосредственные потомкиdiv ~ p
– правые соседи: все p на том же уровне вложенности, которые идут после divdiv + p
– первый правый сосед: p на том же уровне вложенности, который идёт сразу после div (если есть)
А про приоритеты можно почитать здесь и во многих других местах.
-
1.7 Не использовать
@import
внутри файлов.Имеется в виду, что в конечном
css
файле, который получает клиент, не должно быть этих конструкций. Но если настроенWebPack
иcss-loader
, который как раз этот случай обрабатывает, либо используются компилируемые аналоги, которые умеют вместо импортов подставлять непосредственное содержимое файлов, то импорты возможны, так как браузер все равно их не увидит и не получит никогда в реальной работе.Можно выделить пару причин, почему не стоит использовать
@import
:- Некоторые старые браузеры не поддерживают использование правила
@import
, и стили, которые мы подгружаем через это правило, будут потеряны @import
блокирует параллельную загрузку стилей, а это означает, что браузер будет ожидать завершения загрузки импортированного файла, прежде чем начнёт обрабатывать остальную часть содержимого.- Более подробно можно почитать здесь.
- Некоторые старые браузеры не поддерживают использование правила
- 1.8 Все правила должны быть разбиты на разные файлы по страницам и блокам, каждый файл должен быть не длиннее, чем 1000 строк.
-
1.9 Каждое CSS-правило должно быть на отдельной строке.
Это очень поможет как минимум при просмотре diff-ов в системе контроля версий. Плюс это позволит избежать горизонтальной прокрутки.
- 1.10 Для всех интерактивных элементов (ссылки, кнопки, дропдауны, инпуты, селекты) дизайнер может прорисовать отдельные состояния и спрятать в ближайшем слое — всегда проверять для каждого элемента, есть ли отрисованное состояние.
-
1.11 Дизайнеры могут ошибаться — иногда надо делать запросы на изменение макета, если они нарисовали что-то переусложненное.
Например, в макете может быть отцентрированная по вертикали относительно текста полоска:
эта картинка просто пример, не факт, что тут как раз такая ошибка Дизайнер может просто на глаз положить эту полоску на среднем уровне нижних букв текста, однако вы как верстальщики просто примените vertical-align: middle и увидите, что полоска выше/ниже уровня, который показал дизайн, и Pixel Perfect явно это подсвечивает. Чаще всего не надо хардкодить какой-нибудь неочевидный отступ, просто чтобы идеально соответствовать макету — лучше сказать дизайнеру, что автоматом полоска ставится на другое место и её лучше там и оставить. А дизайнеру подучить типографику.
-
1.12 Пытаться по максимуму делать сайт резиновым (ставьте ширину в процентах и выставляйте max-width, min-width) — прибегайте к фиксированной ширине элемента, только если по макету от этого никак не уйти.
Если все-таки сайт должен быть с фиксированный шириной, обязательно проверяйте правую сторону каждой страницы на маленьких экранах. Очень частая ошибка новичков не все элементы делать с min-width, и при горизонтально прокрутке получается такая штука:
-
1.13 При сборке проекта при отсутствии дизайна (на CSS-фреймворках, например) избегать "лепки".
Если вы разрабатываете проект, у которого нет дизайна (при помощи CSS-фреймворков, например), то старайтесь избегать лепки.
Правила те же, что и при написании программных модулей: старайтесь придать пространства между компонентами, ограничить их друг от друга визуально (осуществляется слабое зацепление), но вместе с этим, сами компоненты должны выглядеть самодостаточно, т.е. обладать всем необходимым, но без избыточности, функционалом, чтобы это можно было назвать полноценной компонентой (осуществляется сильная связанность).
Хорошим детектором является тут БЭМ: старайтесь делать отступы между блоками минимум в два раза больше, чем внутри блока между элементами.
-
1.14 Все стили одного блока должны быть в одном месте и отсортированы по порядку их расположения на странице.
То есть, если есть шапка, а внутри нее поисковая форма, то в .css файлах нельзя сначала определить стили шапки, потом кучу стилей тела страницы и потом стили поисковой формы шапки. Должны идти стили самой шапки и сразу под ней стили формы.
При этом стили сайдбара не могут идти раньше стилей шапки, а стили футера идти раньше стилей сайдбара. Лучше вообще эти стили разнести по разным файлам.
- 1.15 Футер страницы должен быть всегда прижат к низу страницы, даже если на странице мало контента (5 методов, как сделать это).
-
1.16 Имена.
Использовать
lower-case-hyphenated
(то есть неmySuperAwesomeElement
и неmy_super_awesome_element
)Имена должны отражать смысл, а не описание стилей (
"loading"
, а не"big-yellow-spinny-thing"
)
-
2.1 Не использовать
!important
.В 99% случаях проблему можно решить, грамотно используя
БЭМ
илиCSS modules
. В сложных редких случаях лучше увеличивать приоритет селекторов (например, когда надо стили библиотеки переопределить).
- 2.2 Не использовать
position: absolute
для стилизации, где это можно заменить другими подходами.
-
2.3 Не двигать элемент при помощи
top
,left
,right
илиbottom
приposition: relative
, где это можно заменить другими подходами.Можно использовать сам
position: relative
(без свойствtop
,left
,right
илиbottom
) дляz-index
и для того, чтобы указать потомкам элемента ориентир.
-
2.4 Не использовать отрицательные margin.
На самом деле, отрицательные
margin
— это валидные правила, они поддерживаются спецификациейCSS
, и их даже используют такие гиганты, как Bootstrap (посмотрите стили для.row
).Но это очень неочевидные правила, их тяжело обнаружить, и их почти всегда можно заменить более легкими и предсказуемыми свойствами.
-
2.5 Избегать фиксирования высоты.
Почти всегда использование фиксированного в пикселях, процентах и других единицах
height
является признаком неправильного кода. Это делает очень неочевидным поведение элементов, которые следуют за зафиксированным по высоте, может произойти обрезание контента или его наложение на следующие элементы.Когда это все-таки может быть нужно:
-
У элемента надо ручками подстроиться под высоту потомков, которых "убрали" из общего потока. "Убрать" из потока можно через
position: absolute
, например. Это может быть слайдер, у которого потомки крутятся как раз через позиционирование, а не смещениеmargin
-ом -
По дизайну надо подстроить все элементы под определенную высоту, а контент, вылезающий за пределы, обрезать.
-
-
2.6 Избегать фиксированных выравниваний.
Если блок должен быть отцентрирован (хоть по горизонтали, хоть по вертикали), то центрировать его динамически, а не фиксированным
margin-top
.Например:
Здесь картинку, текст и полоску надо центрировать через
vertical-align
, а неtop: 55px
у полоски.Часто будут попадаться случаи более сложного центрирования — почти для каждого есть свой метод, надо их отдельно изучать.
Что нужно сделать, чтобы центрировать по высоте элемент1 внутри элемента2:
-
Оба элемента должны быть любыми inline элементами
-
У элемента2 должен быть указан font-size или line-height. Элемент2 отцентрируется только в зависимости от font-size или line-height и не обратит внимания на height.
-
-
2.7 Прописывать дефолтный цвет фона, цвет шрифта,
font-size
и семейство шрифта уhtml
.Далее размер шрифта будет ориентиром для всех элементов, у которых используется rem для единиц измерения кегля.
-
2.8 При тестировании обязательно посмотреть цвет
:visited
ссылки.Браузер по умолчанию такие ссылки фиолетовыми делает, что чаще всего недопустимо в дизайне.
- 2.9 Добавлять элементу
cursor: pointer
, если он кликабелен и по умолчанию этого не поддерживает.
- 2.10 Удалять все неиспользуемые правила и правила, которые никак не влияют на отображение (например, дублирующие свойства по умолчанию или нулевые
margin
, если они и так не выставлены) — они могут в дальнейшем при правках внести неочевидное поведение и из-за них тяжело разбираться в проекте.
- 2.11 Слова, целиком состоящие из верхнего регистра, предпочтительно делать через стиль
text-transform: uppercase
, а не реальным вводом текста заглавными буквами.
-
2.12 При установке элементу
outline: none
обязательно выставить стили для:focus
этому элементу.outline
очень полезен, когда юзер заполняет форму, используя клавишуtab
для переключения между инпутами и кнопками. Еслиoutline
убрать, станет менее заметно (или незаметно вовсе для кнопок), какой элемент сейчас выбран.
- 2.13 Все использования
@media
должны идти сразу после основных правил элемента, для того чтобы можно было увидеть все правила и все возможные состояния элемента в одном экране без прокручивания.
-
2.14 Вендорные префиксы должны идти перед общим стилем.
Стандартное правило всегда должно быть под специфичными правилами для каждого движка.
Пример:
.thing { -webkit-transition: all 100ms; -moz-transition: all 100ms; -o-transition: all 100ms; transition: all 100ms; }
- 3.1 Если клиент предоставил макеты с кастомными шрифтами, проверить, свободные ли они и, если нет, то запросить купленные файлы шрифтов.
- 3.2 Если не требуется поддержка старых браузеров, шрифты должны быть в форматах
.woff2
,.woff
. Сначала подключать.woff2
шрифты, затем —.woff
(пример ниже).
- 3.3 Если в проекте должна быть поддержка нелатинских символов (например, русских), проверьте, что в файле шрифта эти символы есть.
-
3.4 Разные начертания одного и того же шрифта подключайте под одним именем, но для разных
font-weight
иfont-style
.@font-face { font-family: "Lato"; src: url("../fonts/lato-regular.woff2"), url("../fonts/lato-regular.woff"); font-weight: 400; font-style: normal; } @font-face { font-family: "Lato"; src: url("../fonts/lato-italic.woff2"), url("../fonts/lato-italic.woff"); font-weight: 400; font-style: italic; } @font-face { font-family: "Lato"; src: url("../fonts/lato-light.woff2"), url("../fonts/lato-light.woff"); font-weight: 300; font-style: normal; } @font-face { font-family: "Lato"; src: url("../fonts/lato-lightitalic.woff2"), url("../fonts/lato-lightitalic.woff"); font-weight: 300; font-style: italic; }
-
3.5 Всегда использовать как минимум один запасной шрифт и одно запасное семейство.
Их перечисляют через запятую в
font-family
, так что каждое использованиеfont-family
должно происходить по схеме:block { font-family: Helvetica, Arial, sans-serif; }
Здесь
Helvetica
— нестандартный подключаемый шрифт.Arial
— стандартный шрифт, который используется почти на всех клиентах, он будет использоваться, если не удалось подключитьHelvetica
.sans-serif
— это семейство всех шрифтов без засечек (каковыми являются иHelvetica
, иArial
). Если дажеArial-а
на компе нет, то поставится какой-то системный дефолтный шрифт без засечек. Выбор здесь надо делать из"serif,"
"sans-serif,"
или"monospace"
. Для шрифтов без засечек использовать по умолчаниюArial
, для шрифтов с засечками —Georgia
(а неTimes New Roman
), а для моноширинных —"Courier New"
-
3.6 Названия шрифтов, содержащие пробелы, цифры или знаки пунктуации, кроме дефисов, заключать в кавычки.
Пример:
block { font-family: 'Times New Roman', serif; }
-
4.1 Именование БЭМ-сущностей должно следовать следующим ограничениям:
Блок, элемент — всегда имя существительное (noun)
Модификатор — должен удовлетворять свойствам модификатора из английского языка. Всегда
adjective
илиadjectival phrase
.Таким образом, фразы на английском языке
"$BLOCK_NAME [is] $MODIFIER_NAME"
,"$ELEMENT_NAME [is] $MODIFIER_NAME"
или"$MODIFIER_NAME $BLOCK_NAME"
,"$MODIFIER_NAME $ELEMENT_NAME"
,"$ELEMENT_NAME WITH $MODIFIER_NAME $MODIFIER_KEY"
должны быть синтаксически корректными словосочетаниями.Примеры корректных имён:
input_selected
—selected input
— выделенный инпут илиinput is selected
— инпут выделенheader_size_large
—header with large size
— заголовок с большим размером.
Примеры НЕкорректных имён:
-
form__
save_small
, для элемента save (который, например, вешается на кнопку) —small save
— маленький сохранить,save small
— сохранить маленький. Правильно будет употребить noun вместоverb
и переименовать элемент вsave-button;
small save-button
— маленькая кнопка сохранения -
input_
focus —input focus
— инпут сфокусировать илиinput is focus
— инпут это фокус. Правильно будет употребитьadjective
вместоnoun/verb
и переименовать модификатор вfocused; input_
focused —focused input
— сфокусированный инпут -
row_
error —row error
— ошибка строки илиrow is error
— строка это ошибка. Правильно будет употребитьadjectival phrase
вместоnoun
и переименовать модификатор вwith-error
.row_
with-error
—row with error
— строка с ошибкой.
Обоснование: мы сможем всегда однозначно и без лишних затрат энергии истолковать сущность элемента вёрстки и накладываемые на него свойства.
-
4.2 Верстаем всегда по БЭМу, архитектура верстки должна быть компонентной.
Каждый блок должен лежать в своей папке.
Папки блоков нельзя вкладывать друг в друга.
- 4.3 Каждый компонент — отдельный блок из методологии БЭМ.
-
4.4 Каждый компонент должен иметь только явные зависимости, быть самодостаточным.
Все зависимости делать явно, импортируя в начале компонента и вставляя в нужное место верстки.
Самодостаточность говорит о том, что каждый компонент должен внутри себя содержать все необходимое — всю верстку, все стили и все js-скрипты.
Ничего лишнего в компоненте быть не должно:
- Не должно быть определения других блоков внутри этого блока
- Не должно быть стилей, которые бы влияли на другие блоки любым способом
- Не должно быть глобальных стилей (например, на все теги
span
) - Компонент не должен влиять на
DOM
другого компонента (менять верстку крайне запрещено)
-
4.5 Кастомизировать компоненты только через модификаторы, никаких примесей.
Почти в любом проекте возникает необходимость кастомизировать блоки. Например, есть вёрстка из 20 страниц. На 15 из этих страниц встречается хэдер, причём у 10 из них хэдер синего цвета, на трёх — серый, и на двух — прозрачный. Это значит, что компонент хэдер должен быть кастомизируемым, и осуществляются подобного рода кастомизации через добавление модификаторов к блоку, а не путём передачи отдельных классов со стилями.
Предположим, что на фоне у промо страницы есть видео, и по дизайну нам нужно сделать прозрачный хэдер со светлым шрифтом (по умолчанию — тёмный).
Плохо:
.promo-page +header({ classname: 'promo-page__header' })
Хорошо:
.promo-page +header({ theme: 'transparent', font: 'light' })
При правильном подходе, естественно, надо будет научиться принимать эти два параметра, добавлять самому модификаторы к нужным классам и в CSS компонента прописывать правила для этих модификаторов.
Это правило внедрено после болезненного опыта поддержки проекта средней сложности, и вот какие шишки набиты с использованием примесей:
-
Нарушается инкапсуляции — использование внешних стилей внутри компоненты ведёт к проблемам в сопровождении в дальнейшем. Дело в том, что стили компоненты являются её составной частью и не должны влиять на то, что её окружает. Также справедливо обратное — внешние стили не должны влиять на компоненту. Иными словами, компонента должна быть самодостаточна. Прибегая к использованию внешних стилей, мы создаём зависимость между двумя стилями — внешним и внутренним. Каждому из стилей нужно знать, из чего состоит другой, чтобы глобальный стиль не поломал стиль компоненты. Это и есть нарушение инкапсуляции
-
Если компонента используется на 20 разных страницах, то после изменения одного свойства в самом компоненте придется пройти по всем 20 страницам вручную и проверить, что ничего не сломалось, потому что неизвестно, какие могут быть стили навешаны в местах использования через кастомные классы, и надо самому проверить все комбинации
-
Примесь обычно вешается только на верхний уровень компонента, но иногда надо кастомизировать что-нибудь внутреннее, тогда получается, надо уже две примеси передавать и принимать в компоненте, одна примесь — для свойств всего блока, вторая — для какого-то элемента, а когда понадобится еще другой элемент кастомизировать, придется добавить третью примесь :)
-
Не получится четко составить список всех возможных состояний компоненты, а при подходе с модификаторами мы явно видим все возможные параметры на входе, и когда их число будет зашкаливать, можем пнуть дизайнера, что он слишком расфантазировался и пора бы переходить к единому стайлгайду
-
-
4.6 Для позиционирования блока в родительском контейнере тоже не надо использовать миксы — решается данная проблема путем добавления специальных контейнеров.
Допустим, есть родительский блок, в нем — дочерний блок, который нам и необходимо спозиционировать. Для позиционирования внутреннего блока создаем в родительском элемент-обертку и просто задаем свойства (margin, padding, position etc) этому контейнеру. Таким образом, позиционирование блока у нас находится в файле родительского блока и никак не влияет на стили самого блока.
Данный способ позволяет, не используя миксы, спозиционировать дочерний блок.
Например, в хэдере у нас находится обычная голубая кнопка, но именно тут, в шапке, она должна иметь отступ с левой стороны, для этого мы добавим
div.header__button
:div.header div.header__button +button({ color:blue })
А в стили хэдера просто добавим:
.header__button { margin-left: 3rem; }
В css есть несколько очень неочевидных правил, лучше изучить их на теории заранее, чем ломать голову часами, почему верстка едет
-
5.1 Верхний margin первого элемента смещает за собой всех родителей вниз.
Допустим, желаемый вид должен быть таким:
Чтобы заголовок имел отступ от верхней границы, мы добавляем ему margin: 20px 0 0; однако получаем такое поведение:
Несмотря на то, что маржин был поставлен только заголовку, все его родители тоже съехали вниз:
- 5.2 Родители игнорируют всех детей, у которых установлены
float: left
илиfloat: right
- 5.3
z-index
свойство работает только для элементов, у которых значение position задано какabsolute
,fixed
илиrelative
.
-
5.5
height
в процентах не работает, если высота родителя формируется от контента.На
learn.javascript.ru
даже статья есть.
-
5.6 Все
:hover
эффекты проверять на мобильных устройствах внимательно в отдельном порядке, если проект поддерживает отображения на мобилах.В разных браузерах ховеры ведут себя по-разному и это надо согласовывать с дизайнерами отдельно — иногда, чтобы он заработал, надо, например, отдельный тап по элементу делать. Соответственно, чтобы сработал клик, надо тапнуть два раза, что тоже для некоторых элементов будет очень странно.
- 5.7 Псевдоэлементы (
::after/before
) не работают сself-closing tags
(<img />
,<input />
, etc.).
-
5.8 Не использовать простой :hover для отображения новых элементов, которые нелегко достигнуть мышкой.
Очень часто встречаются неудачные случаи с выпадающим меню, которое реализовано через псевдо-класс
:hover
, мало того, что это меню практически неработоспособно на мобильном устройстве, оно еще и доставляет немало головной боли пользователям десктопов. Решением в данном случае будет использование JavaScript.
-
5.9 В ряде браузеров (Chrome, Opera, Edge, Firefox 62-, Safari 10-) теги форм, такие как
button
,fieldset
иlegend
, не могут стать flex-контейнерами.Возможное решение — использовать элемент-обертку внутри тега (например,
span class="button__text"
внутри кнопки), и уже для него указать свойствоdisplay: flex
:<button class="button"> <span class="button__text">...</span> </button>
.button__text { display: flex; ...; }
Подробнее об этом и других багах, возникающих при работе с флексами, можно прочитать на Flexbugs, перевод материала на русский здесь.
Для решения проблем с флексами можно воспользоваться неплохим postcss-плагином, который автоматически фиксит некоторые баги в написании стилей для флексов.