Ошибки и спорные вопросы в HTML-CSS вёрстке
Часть 3: стилизация
Набор подходов и решений в области HTML-CSS-верстки, которые я считаю спорными или ошибочными (ошибки описаны мои, моих коллег, студентов). Все статьи цикла:
- Часть 1: методологические и организационные
- Часть 2: разметка
- Часть 3: стили
Стилизация
Прежде чем продолжить, познакомьтесь с первым пунктом первой части этой серии статей (если коротко: некритическое восприятие подобных подборок является ошибкой).
Набор стилей без Emmet-а
Используйте аббревиатуры Emmet-а для набора свойств — это быстрее ручного набора (даже с автодополнением) и так гораздо сложнее допустить опечатку.
Игнорирование кодгайда
Примеры кодгайдов: руководство по написанию кода школы Epic Skills (целиком написано мной), Стиль кода Академии HTML (немного приложил руку), Code Guide by @mdo — кодгайд от Марка Отто (автор Bootstrap), именно этим материалом вдохновлены две предыдущие ссылки, Принципы написания однородного CSS-кода — от Николаса Галлахера.
Для читаемости и простоты модификации важно не только правильно форматировать код, но и писать правила в некоторой очередности (с автоматической сортировкой может помочь CSScomb).
Неучёт ограничений и особенностей CSS
Я не смогу упомянуть все особенности стилизации. Постараюсь привести наиболее важные/забавные.
- Внешние отступы перекрываются (для двух «блочных» соседей: если у первого есть
margin-bottom: 30px;
, а у второгоmargin-top: 20px;
, расстояние между ними будет 30px). Но есть исключения (см. ниже). - При подсчёте размеров берется прописанный размер и к нему добавляется ширина бордюра и внутреннего отступа (можно написать для блока
width: 100px; padding: 10px;
и получить реальную ширину 120px). Сделать подсчёт размера более интуитивным помогает глобальное изменениеbox-sizing
(см. ниже). - Внутри flex- и grid-контекстов перестают работать некоторые свойства (
float
,vertical-align
, CSS-колонки), а внешние отступы перестают схлопываться. - Если внутри родителя с измененным
transform
дать какому-либо потомкуposition: fixed;
, это не сработает, такой потомок не будет фиксирован во вьюпорте. - Flexbox прекрасен, но в ряде браузеров некоторые теги не могут стать flex-контейнерами. К примеру, в Safari невозможно сменить свойство
display
для теговbutton
,fieldset
,legend
, они не смогут стать flex-контейнерами. Учитывайте, однако, баги flexbox (перевод).
Любопытно, какие особенности использования CSS важны для вас. Напишите в комментариях.
Использование некроссбраузерных решений
Некоторые задачи могут быть легко решены CSS-свойствами, имеющими плохую поддержку браузерами. Использовать решения, приводящие к различному отображению в разных браузерах, допустимо редко и при условии согласования с заказчиком. Больше всего проблем кроссбраузерности наблюдается с продукцией Microsoft — Internet Explorer и Edge.
Примеры: CSS-фильтры, grid (поддержка старой версии спецификации означает, скорее, отсутствие поддержки).
Многосоставные селекторы
Многосоставные (2-х и более) селекторы во многих случаях приводят к проблемам:
- необходимость учитывать не только очередность, но и «вес» селектора,
- привязка стилей к определенной разметке (сложно модифицировать).
Многие методологии рекомендуют избегать вложений. Но это не означает его запрет, бывают ситуации, когда без этого сложно обойтись. К примеру, при стилизации выводимых из CMS фрагментов (контент-менеджер не должен заботиться о написании классов).
В БЭМ, который я использую (и всем советую) каскад напрямую не запрещен, но я ограничиваюсь двусоставным (крайне редко — трехсоставным).
Отдельно — о каскадной модификации. Это случай, когда вы меняете свойства вложенного блока, отталкиваясь каскадом от его родителя (вместо написания класса-модификатора). При необходимости получить такую же модификацию в другом месте начнутся проблемы. Однако, есть исключение: для некоторых проектов может быть удобно добавлять класс на корневом теге (html
) и менять что-либо каскадом, отталкиваясь от такого класса (может потребоваться при контроле загрузки шрифтов, определении браузера или его возможностей, изменении вида для авторизованного посетителя). Я считаю каскадную модификацию от корневого элемента допустимой.
Стилизация по тегам и по id
Атрибут class
специально существует для привязки стилей, используйте его. Редко допустима комбинация с тегами, псевдоселекторами или иными атрибутами. Не стилизуйте по id
, ибо такие селекторы «больше весят» и их невозможно повторить в рамках одной страницы.
Нюанс — стилизация заголовков: если использовать в селекторе тег, сложно добиться возможности быстро поменять уровень заголовка (может потребоваться в интересах SEO) или даже заменить заголовок на div
. Стилизовать неконтентные заголовки нужно по классу и так, чтобы при смене тега визуально ничего не изменилось.
Написание вендорных префиксов
Если на вашем проекте вообще есть необходимость писать правила с вендорными префиксами, оставьте это автоматике постобработки CSS.
Смена box-sizing
без возможности легко вернуть к умолчанию
Есть два способа сменить боксовую модель: простой и правильный.
Простой способ меняет это свойство для универсального селектора:
* { box-sizing: border-box; }
При этом если потребуется использовать в проекте сторонний компонент, автор которого не менял боксовую модель, стили компонента сломаются.
Есть более безопасный и более правильный путь:
html { box-sizing: border-box; }
*, *::before, *::after { box-sizing: inherit; }
То есть, все элементы наследуют box-sizing
от корневого, но если потребуется поменять, достаточно на каком-либо селекторе вернуть box-sizing: content-box;
и все его потомки унаследуют это свойство.
Использование !important
и/или overflow: hidden;
для исправления багов
Встречал такое: вместо поиска места проблемы (и ее ликвидации) автор пишет правило с !important
. Другой вариант: для ликвидации горизонтального скролла пишут overflow: hidden;
на одной из общих обёрток.
Допускаю, есть ситуации, когда такие «быстрые заплатки» допустимы (чужой чудовищно безграмотный код, который нужно исправить в условиях цейтнота). Однако если весь код написали вы, такие подходы — свидетельство вашей неспособности найти и решить проблему.
Игнорирование переполнения/недополнения контентом
Переполнение и недополнение контентом/субблоками должно быть главным, о чём вы думаете, когда пишите стилизацию.
Если где-то есть текст, его может стать много или совсем мало (при этом верстка не должна сломаться, должна вести себя логично). Познакомьтесь с «Электрокардиографической одиннадцатиклассницей, предпочитающей циклопентанпергидрофенантрен» и с «Ю Ли».
Если в дизайне показан поток из 6 блоков (по 2 в ряд), нужно учесть что блоков может стать от нуля до 13 (не особо важно сколько именно, важно что много и что будет неполный последний ряд).
Один из «рефлексов верстальщика»: зафиксировал блоку ширину или высоту — подумай о переполнении.
Объединение стилей оберток и отдельных блоков
Обычно, есть раскладка блоков по страницам и есть сами блоки. Смешивать на одном селекторе стили для раскладки и стили оформления блоков — порочный путь, ибо усложняет перенос блоков и другую модификацию.
Самые большие неприятности возникают, когда небольшим блокам (к примеру, виджетам сайдбара) фиксируют ширину — это увеличивает количество точек изменения при необходимости менять ширину нескольких блоков в потоке (ширину сайдбара в нашем примере).
Если не хочется писать лишних оберток, можно попробовать миксовать (термин из БЭМ) класс обертки с классом блока на одном теге, но в этом случае вы (или другой разработчик) сможете смешать правила.
Использование CSS-импортов
Обычно, я прошу студентов забыть, что в CSS есть @import
, на что есть несколько причин:
- если в одном CSS-файле указан импорт другого, то импортируемый фал начнет загружаться только после парсинга первого файла, то есть, загрузка будет последовательной (помните, что рендеринг блокирован во время загрузки стилевых файлов),
- до наступления эпохи http2 (когда за одно соединение с сервером можно будет получить два и более файла) лучше конкатенировать все стилевые файлы в один.
Программирование на CSS-препроцессорах (особенно если не умеете программировать)
Не программируйте на CSS-препроцессорах. Когда мы разбираемся в работе/багах применённых стилей, меньше всего нам хочется тратить время на разбор сложных структур, из которых эти стили скомпилировались.
Да, с Sass/LESS/stylus я могу написать код, который будет коротким и его будет довольно сложно понять, но это не признак крутости. Совсем наоборот: сложнее = хуже (дольше разбираться как это работает). Нечто, более-менее похожее на программирование допустимо в CSS-препроцессорах в примесях (к примеру, в примесях-генераторах правил модульной сетки).
Из допустимого (интуитивно понятного) на мой взгляд: вложения селекторов и @media
в контекст селекторов, амперсанд, переменные, разбиение на файлы (и препроцессорный импорт таких мелких файлов в диспетчере подключений). Подробнее — в коротком руководстве о том, как не выстрелить себе в ногу с препроцессорами и БЭМ.
Неучёт возможных проблем с загрузкой
Вы знаете что происходит внутри браузера, когда вы отправляете запрос на какой-либо адрес? Как он загружает ресурсы (и когда отправляет за ними запросы), что именно случается перед показом страницы на экране? Хорошо, если ответ положительный. Если же не уверены, то познакомьтесь с объемным описанием того, как работает браузер. Это материал примерно 2011 года, но, полагаю, если механизмы и изменились, то незначительно.
Если совсем коротко: загрузка стилевых файлов блокирует рендеринг (поэтому мы и пишем их в head
, чтоб скорее загрузились, читайте, так же, про Critical CSS), загрузка JS-файлов (без специальных атрибутов) останавливает выполнение очереди загрузки прочих файлов до загрузки и выполнения скрипта (поэтому мы стараемся писать подключения скриптов перед /body
(но не всегда получается)).
Неучёт количества будущих точек модификации
Хорошо написанные стили, помимо логичности и быстрой читаемости, обладают еще одним важным свойством: при модификации нужно менять правила в минимальном количестве мест.
Вернемся к примеру о блоках-виджетах в сайдбаре: если прописать ширину самим блокам-виджетам, то в случае необходимости сменить ширину сайдбара, править придется в нескольких местах (для каждого блока). Если же виджеты имеют ширину 100% и реальная ширина сайдбара прописана единожды для селектора сайдбара, точка модификации лишь одна.