Skip to content

Latest commit

 

History

History
510 lines (295 loc) · 39.4 KB

README.md

File metadata and controls

510 lines (295 loc) · 39.4 KB

CSS

  1. Общие требования
  2. Правила
  3. Шрифты
  4. БЭМ
  5. Возможные ошибки

Общие требования

  • 1.1 Главное! Используем pixel-perfect, если заказчик предоставил макет.

  • 1.2 Не подключать стили с удаленных источников — только с наших серверов.

  • 1.3 CSS — опасная сила.

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

    Для того чтобы такого не происходило, существуют разные методологии, например, БЭМ (прочитать про него обязательно хотя бы в общих чертах) или, например, rscss. Для начала можете изучить небольшой подход, который используют ребята из Isobar — он описан в рамках их стандарта и состоит из 7 небольших абзацев.

  • 1.4 Отступы делать пробелами, один уровень — 2 пробела.

  • 1.5 Использовать одинарные кавычки.

  • 1.6 Перед началом работы обязательно четко знать все приоритеты селекторов и все 4 вида возможных отношений.

    Отношения элементов между собой могут быть:

    • div p – элементы p, являющиеся потомками div
    • div > p – только непосредственные потомки
    • div ~ p – правые соседи: все p на том же уровне вложенности, которые идут после div
    • div + 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, и при горизонтально прокрутке получается такая штука:

    image

  • 1.13 При сборке проекта при отсутствии дизайна (на CSS-фреймворках, например) избегать "лепки".

    Если вы разрабатываете проект, у которого нет дизайна (при помощи CSS-фреймворков, например), то старайтесь избегать лепки.

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

    Хорошим детектором является тут БЭМ: старайтесь делать отступы между блоками минимум в два раза больше, чем внутри блока между элементами.

  • 1.14 Все стили одного блока должны быть в одном месте и отсортированы по порядку их расположения на странице.

    То есть, если есть шапка, а внутри нее поисковая форма, то в .css файлах нельзя сначала определить стили шапки, потом кучу стилей тела страницы и потом стили поисковой формы шапки. Должны идти стили самой шапки и сразу под ней стили формы.

    При этом стили сайдбара не могут идти раньше стилей шапки, а стили футера идти раньше стилей сайдбара. Лучше вообще эти стили разнести по разным файлам.

  • 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 у полоски.

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

    Статья про vertical-align

    Что нужно сделать, чтобы центрировать по высоте элемент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_selectedselected input — выделенный инпут или input is selected — инпут выделен
    • header_size_largeheader with large size — заголовок с большим размером.

    Примеры НЕкорректных имён:

    • form__save_small, для элемента save (который, например, вешается на кнопку) — small save — маленький сохранить, save small — сохранить маленький. Правильно будет употребить noun вместо verb и переименовать элемент в save-button; small save-button — маленькая кнопка сохранения

    • input_focusinput focus — инпут сфокусировать или input is focus — инпут это фокус. Правильно будет употребить adjective вместо noun/verb и переименовать модификатор в focused; input_focusedfocused input — сфокусированный инпут

    • row_errorrow error — ошибка строки или row is error — строка это ошибка. Правильно будет употребить adjectival phrase вместо noun и переименовать модификатор в with-error. row_with-errorrow 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 первого элемента смещает за собой всех родителей вниз.

    Допустим, желаемый вид должен быть таким:

    image

    Чтобы заголовок имел отступ от верхней границы, мы добавляем ему margin: 20px 0 0; однако получаем такое поведение:

    image

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

    image

  • 5.2 Родители игнорируют всех детей, у которых установлены float: left или float: right

  • 5.3 z-index свойство работает только для элементов, у которых значение position задано как absolutefixed или relative.

  • 5.4 А еще на z-index может повлиять opacity Почитать про это можно тут.

  • 5.5 height в процентах не работает, если высота родителя формируется от контента.

    На learn.javascript.ru даже статья есть.

  • 5.6 Все :hover эффекты проверять на мобильных устройствах внимательно в отдельном порядке, если проект поддерживает отображения на мобилах.

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

  • 5.7 Псевдоэлементы (::after/before) не работают с self-closing tags (<img />, <input />, etc.).

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

    Очень часто встречаются неудачные случаи с выпадающим меню, которое реализовано через псевдо-класс :hover, мало того, что это меню практически неработоспособно на мобильном устройстве, оно еще и доставляет немало головной боли пользователям десктопов. Решением в данном случае будет использование JavaScript.

    image

  • 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-плагином, который автоматически фиксит некоторые баги в написании стилей для флексов.