Pull to refresh
124.62
Maxilect
Карьера в IT: работай удаленно с экспертами

Как писать более чистый CSS: дюжина советов от банальных до неочевидных

Level of difficultyEasy
Reading time9 min
Views16K

Если CSS для вас все еще боль и страдания, добро пожаловать под кат. Мы наткнулись на интересную подборку советов по CSS на английском языке и решили перевести ее, дополнив собственными примерами с кодом.

CSS как технология действительно немного раздут, но не так хаотичен и сложен, как его иногда описывают. Просто нужно стремиться к лаконичности - в том числе, с помощью описанных подходов.

Ненависть к CSS вполне можно понять - в конце концов технология развивается уже более 25 лет. Новые браузеры, появляющиеся на рынке, реализуют поддержку CSS по-разному, что приводит к тому, что часть кода работает в одном браузере, но не работает в других. А разработчику приходится интегрировать в код кучу вендорных префиксов. А ведь это потом еще надо расширять и поддерживать! 

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

Подойдите к изучению CSS с правильной стороны

Лучший способ не изучать CSS — это использовать фреймворки, вроде Bootstrap или Tailwind CSS. Эти инструменты помогают быстро получить красивый пользовательский интерфейс, но никак не способствуют изучению основ CSS (используя их, вы учите только сам инструмент). И вы точно испытаете боль и страдания при попытке от них отказаться.

Изучение основ CSS дает больше контроля над кодом и творчеством. И лучший совет - начать с блочной модели CSS, потому что поняв ее, вы найдете больше смысла во всем остальном языке.

Box model
Box model

Просто думайте о каждом HTML-элементе, как о блоке, внутри которого находится контент. Блок может иметь высоту (height) и ширину (width), границу (border), отступы внутри (padding), а также пробелы вокруг этой границы снаружи (margin). Все, что в CSS связано с макетом и расположением, опирается на блочную модель.

Если вы откроете инструменты разработки Chrome, увидите, как вычисляется блочная модель для любого элемента на странице.

И это автоматически приводит нас к следующему совету…

Используйте Flexbox

Исторически расположение элементов друг относительно друга было одним из самых сложных аспектов CSS.

Ох уж этот извечный вопрос о том, как центрировать элемент div и по горизонтали, и по вертикали! Один из вариантов это реализовать — задать дочернему элементу абсолютное позиционирование, а затем переместить его в правый нижний угол, используя свойства top и left. Чтобы поместить в центр не левый верхний угол элемента, а именно его центр, нужно также указать свойство transform: translate(-50%, -50%). Данное свойство перемещает элемент вверх на 50% его высоты вдоль оси Y и влево на 50% его ширины вдоль ось X. В итоге в нужной точке окажется именно центр. 

.no-flex {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
}

Такой подход работает, но он очень не интуитивен.

Более современный инструмент CSS — это flex, который создает гибкий столбец или строку в любом месте пользовательского интерфейса. У flex-элемента также есть оси X и Y, по которым можно выровнять его дочерние элементы (их можно выровнять вдоль главной оси с помощью свойства justify-content: center). Переместить элемент в центр и по горизонтали, и по вертикали можно с помощью свойства align-items, выровняв его относительно поперечной оси.

.with-flex {
  display: flex;
  justify-content: center;
  align-items: center;
  height: 100vh;
}

Grid - отличный инструмент

Flex - это первый инструмент, к которому следует обращаться, когда дело касается макета. Но у него есть один существенный недостаток. Если пользовательский интерфейс большой и сложный, со множеством пересекающихся строк и столбцов,  в HTML-коде будет слишком много элементов-контейнеров. Эти элементы не имеют семантического значения и существуют просто для того, чтобы CSS-коду было, к чему прикрепиться.

На замену им есть современная функция CSS - Grid. В отличие от flex, который работает только с отдельными столбцами и строками, grid позволяет думать о макете в целом. Если вы занимаетесь веб-разработкой достаточно долго, эта идея может показаться вам знакомой, потому что очень напоминает таблицы. Просто grid намного более дружелюбен к разработчикам.

Устанавливая grid, вы можете определить его дочерние элементы как набор столбцов и строк. Столбцы имеют ширину, которую можно определить с помощью свойства grid-template-columns. Можно использовать значение fr или fractional unit, которая будет делить доступное пространство с другими столбцами в сетке. Мы также можем определить несколько строк, после чего каждый элемент внутри сетки будет располагаться автоматически.

В следующем примере дочерние элементы размещаются в сетке 2 строки и 3 столбца. Средний столбец имеет фиксированную ширину 500px, а остальные два столбца занимают оставшееся пространство. Строки имеют фиксированную высоту 100px и 200px соответственно.

.grid {
  display: grid;
  grid-template-columns: 1fr 500px 1fr;
  grid-template-rows: 100px 200px;
  place-items: center;
}

Используйте функцию Clamp

В контексте адаптивных макетов чаще всего речь идет просто об изменении ширины чего-либо в зависимости от доступного места на экране устройства или в области видимости. Есть несколько способов ее откорректировать. 

Допустим, вы хотите, чтобы статья занимала 50% ширины. Но на маленьких экранах ее необходимо сделать фиксированной - шириной в 200 пикселей, а на больших - в 800 пикселей. Вы можете сделать это, указав @media, которые будут применяться, в зависимости от размеров области просмотра. Проблема в том, что @media будут плодиться вместе с ростом проекта и поддерживать это станет сложно. 

article {
  width: 50%;
}

@media only screen and (max-width: 600px) {
  article {
    width: 200px;
  }
}

@media only screen and (min-width: 1200px) {
  article {
    width: 800px;
  }
}

Вы можете изменить ситуацию, используя функции min, max и clamp. С их помощью код легко реорганизуется в:

article {
	width: clamp(200px, 50%, 800px);
}

Здесь 200px - минимальное значение, 800px - максимальное, а 50% - предпочтительное.

Используйте aspect-ratio

Следующий совет для тех, кто вставляет на страницы адаптивные изображения или видео, поддерживающее определенное соотношение сторон, например 16:9.

Чтобы все нормально отображалось, для этого всегда приходилось добавлять сверху отступ padding-top: 56.25%, а затем задавать дочернему элементу абсолютное позиционирование. 

.container-16x9 {
  position: relative;
  padding-top: 56.25%;
}

video {
  width: 100%;
  position: absolute;
  top: 0;
}

Но все это можно упростить, используя такое свойство video, как aspect-ratio. В нашем случае - aspect-ratio: 16/9.

video {
  width: 100%;
  aspect-ratio: 16 / 9;
}

Задавайте пользовательские переменные 

Мы часто используем одно и то же значение цвета в нескольких местах на странице. Если нужно изменить цвет, приходится менять каждую строку кода, которая ссылается на него. 

p {
  color: rgb(255, 0, 0);
}

h1 {
  color: rgb(255, 0, 0);
}

h2 {
  color: rgb(255, 0, 0);
}

Но гораздо проще определить в корне глобальную переменную, на которую можно будет ссылаться везде, где это необходимо. Если придется изменить этот цвет, нужно будет поменять только одну строку.

:root {
  --main-color: hsl(0, 100%, 50%);
}

p {
  --main-color: green; /* переопределение, как в обычном css */
  color: var(--main-color);
}

h1 {
  color: var(--main-color);
}

h2 {
  color: var(--main-color);
}

Переменные каскадируются, как и все остальное в CSS. Это значит, что любую глобальную переменную можно задать в корне, но переопределить глубже в дереве. Также переменные можно комбинировать для составления более сложных значений. Например, цвет RGB можно определить на основе значения трех других переменных.

Ранее использование переменных было прерогативой препроцессоров CSS, очень приятно, что такой полезный инструмент наконец-то перекочевал и в сам CSS. Кстати, не так давно в CSS появился и nestling (ранее он также существовал только в препроцессорах).

Используйте простую математику

CSS на самом деле не является языком программирования в традиционном смысле. Но в нем есть возможность выполнять базовые вычисления с помощью функции calc, в которой доступны некоторые базовые математические вычисления. Но самое интересное, что в этой функции можно комбинировать разные единицы измерения. Например, можно вычесть 50 пикселей из текущей ширины области просмотра. 

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

.first {
  animation-delay: 100ms;
}
.second {
  animation-delay: 200ms;
}
.third {
  animation-delay: 300ms;
}
/*
<i class="drop first"></i>
<i class="drop second"></i>
<i class="drop third"></i>
*/

Но при большом количестве элементов такое трудно организовать в коде. Более сложный подход — определить встроенную переменную CSS для порядкового номера  каждого элемента. С ее помощью можно определить задержку анимации, как порядковый номер, умноженный на 100 миллисекунд. Так можно обрабатывать бесконечное количество элементов, не увеличивая объем CSS.

.drop {
  animation-delay: calc(100ms * var(--order));
}

/*
<i class="drop" style="--order: 1"></i>
<i class="drop" style="--order: 2"></i>
<i class="drop" style="--order: 3"></i>
*/

Управляйте состоянием

Выше мы упоминали, что CSS не является полноценным языком программирования, но на самом деле в него встроен механизм управления состоянием. Вы можете отслеживать текущую позицию в своем CSS-коде, не написав ни строчки JavaScript. 

Отличный пример - нумерация заголовков. Простой способ сделать это — вручную добавить эти числа в сам HTML. Но если вы когда-нибудь захотите добавить новый заголовок, придется перенумеровать все вручную.

Более разумный подход - счетчик CSS. Он создается через свойство сброса счетчика (counter-reset). Ему можно дать любое имя, а затем увеличивать каждый раз, когда применяется нужный селектор с помощью counter-increment. Такой счетчик начнется с 0 и будет каждый раз будет увеличиваться на единицу.

:root {
  counter-reset: headings;
}

h1 {
  counter-increment: headings;
}

h1::before {
  content: counter(headings);
}

Не пренебрегайте focus-within

Можно подумать, что при создании сложного раскрывающегося меню для управления открытым и закрытым состоянием должен использоваться некоторый JavaScript. Но только с CSS тоже можно продвинуться достаточно далеко.

Скорее всего вы знакомы с псевдоклассом focus, который применяется к элементу, когда вы вводите форму или нажимаете кнопку. Его можно использовать, чтобы открыть меню. Но при нажатии на что-то внутри, меню теряет фокус и закрывается. Именно в этот момент обычно используется JavaScript для управления состоянием. 

Однако внутри есть еще один псевдокласс - focus-within. Он остается активным, если в результате взаимодействия с элементами на странице фокус сохранился у каких-либо дочерних элементов меню. Так что в этом случае можно вообще не использовать JavaScript для включения и выключения состояний.

Используйте относительные единицы

Когда это возможно, вместо статических значений пикселей используйте относительные единицы. 

При разработке адаптивных макетов у вас могут быть заголовки или контейнеры с фиксированными полями и отступами. Вы меняете их на экранах меньшего размера с помощью @media. Если в точном соблюдении размеров этих полей нет необходимости, это overhead. Поддерживать такое сложно, плюс это не очень хорошо работает на устройствах, которые автоматически масштабируют шрифты под размер экрана.

Вместо фиксированных единиц, вроде пикселей, можно использовать относительные - em или rem:

  • em - автоматически рассчитает размер, опираясь на шрифт элемента (а поскольку размер шрифта наследуется и редко переопределяется, по факту em будет опираться на шрифт ближайшего родительского элемента). Если размер шрифта 16px, то 2em будет равно 32px;

  • rem - работает аналогично, но опирается на размер корневого шрифта. Этот способ более предпочтительный

Единственное замечание - em и rem реагируют на изменение размера шрифта браузера. Так что если кто-то в своем браузере увеличит размер текста, внешний вид страницы может измениться.

В CSS есть и другие относительные единицы, в частности ch - ширина символа. С помощью нее можно реализовать один трюк.

Если вы действительно серьезно относитесь к дизайну, возможно, вы читали «Основы стиля в типографике» Роберта Брингхерста. Он говорит, что оптимальный размер абзаца составляет от 45 до 75 символов. В CSS мы можем обеспечить примерное соблюдение этого правила с помощью функции clamp, которую упоминали выше (width: clamp(45ch, 50%, 75ch);). Так каждый абзац будет иметь идеальную ширину.

Откровенно говоря 45ch - это не ровно 45 символов, а расстояние, на котором можно разместить 45 нулей. Если при этом используется моноширинный шрифт, все работает идеально. Если нет, лучше проверить результат на практике.

Ориентируйтесь на цвета HSL (вместо RGB)

RGB - это красный, зеленый, синий, где каждый элемент может иметь значение от 0 до 255. Шестнадцатеричный формат содержит ту же информацию, но в более компактном виде, который еще сложнее интерпретировать.

HSL - это оттенок, насыщенность и яркость. И такой формат упрощает вычисление красивой цветовой палитры. Просто выберите насыщенность и яркость, а затем увеличьте оттенок на определенную величину, например на 25. Так можно получить несколько цветов, сочетание которых выглядит хорошо. Еще проще с помощью HSL затемнить или осветлить элемент интерфейса. В отличие от RGB, для этого не придется задавать отдельный цвет.

Не забывайте исправить прокрутку

На веб-сайтах с фиксированной панелью навигации часто встречается одна проблема. Когда вы переходите к якорной ссылке, страница автоматически прокручивается, но нужный заголовок закрывается фиксированной панелью навигации. 

Чтобы этого избежать, поправьте свойство scroll-padding, которое добавит дополнительный отступ.

P.S. Рука не поднялась объявить это отдельным советом, но в оригинальной подборке авторы рекомендовали использовать эмодзи в качестве имен классов. С их точки зрения так код станет более читаем. 

.💪 {
  display: flex;
}

.💄 {
  color: red;
}

.🌈 {
  background: linear-gradient(to right, red, orange, yellow, green, blue, indigo, violet);
}

/*
<div class="💪">
  <p class="💄">Hello</p>
  <p class="🌈">World</p>
</div>
*/

Если примените это на практике, расскажите потом, как отнесется к нововведению команда и ее руководитель.

P.P.S. Спасибо Антону Дмитриевскому за подготовку примеров и коллективу Максилект за ценные советы на стадии ревью текста.

Мы публикуем наши статьи на нескольких площадках Рунета. Подписывайтесь на нашу страницу в VK или на Telegram-канал, чтобы узнавать обо всех публикациях и других новостях компании Maxilect.

Tags:
Hubs:
Total votes 19: ↑19 and ↓0+19
Comments16

Articles

Information

Website
maxilect.com
Registered
Founded
2015
Employees
31–50 employees
Location
Россия