Содержание

Единицы измерения

Как отображаются элементы на странице?

Как вы уже знаете, любая веб-страница состоит из элементов. Но как они отображаются на экране?

https://itchief.ru/assets/images/html-and-css/box-model/elements-are-rectangles.png

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

Но, браузеру чтобы нарисовать эти прямоугольники, необходимо знать их размеры. Их расчёт выполняется в соответствии с алгоритмом, который в CSS называется блочной моделью (box model).

Блочная модель

Блочная модель CSS описывает устройство внешнего вида элемента и его расположение на странице.

Представить себе эту модель можно в виде прямоугольных коробок вложенных друг в друга по типу матрешки. Самая большая из них – это внешние отступы (margin). Затем идёт рамка (border). Далее – внутренние отступы (padding), и самая последняя из них – это содержимое (content).

https://itchief.ru/assets/images/html-and-css/box-model/box-model.png

Содержимое – это область, в которой отображается контент элемента. Его размерами при необходимости можно управлять с помощью CSS-свойств width (ширина) и height (высота). Кроме них имеются ещё следующие: min-width (минимальная ширина), max-width (максимальная ширина), min-height (минимальная высота) и max-height (максимальная высота). При этом задавать размеры можно с помощью различных единиц измерения: px, rem, %, vw и так далее.

https://itchief.ru/assets/images/html-and-css/box-model/width-and-height.png

Внутренние отступы (padding) распологаются вокруг содержимого в виде пустого пространства. Их можно установить как все сразу с помощью свойства padding, так и по отдельности для разных сторон (padding-left, padding-right, padding-top и padding-bottom). С помощью этих свойств задаётся отступ между содержимым и границей элемента:

.box {
  padding: 15px; /* со всех сторон */
  padding: 10px 15px; /* 10px – сверху и снизу, 15px – слева и справа */
  padding: 5px 10px 15px; /* 5px – сверху, 10px – слева и справа, 15px – снизу */
  padding: 5px 10px 15px 20px; /* 5px – сверху, 10px – справа, 15px – снизу, 20px – слева */
  padding-top: 5px; /* сверху */
  padding-right: 10px; /* справа */
  padding-bottom: 15px; /* снизу */
  padding-left: 20px; /* слева */
}

Рамка (border) позволяет нам установить цвет, толщину и тип линии границы, которую нужно нарисовать вокруг внутренних отступов (padding). Для этого можно использовать как сокращённое свойство border, так и отдельные: border-width, border-color и border-style.

.box {
  border: 3px solid #000;
  /* тоже самое только с помощью отдельных свойств */
  border-width: 3px; /* толщина рамки */
  border-style: solid; /* стиль линии */
  border-color: #000; /* цвет */
}

Область элемента заканчивается рамкой и не распространяется за её пределы. Внешний отступ (margin) не участвует напрямую в фактическом расчёте размера элемента и не занимает место внутри него.

В результате общие размеры элемента в CSS складываются из ширины и высоты контентной области, padding и border:

https://itchief.ru/assets/images/html-and-css/box-model/html-element-size.png

Допустим, у нас имеется элемент к которому применены следующие стили:

.box {
  display: block;
  width: 300px;
  height: 200px;
  margin: 20px;
  padding: 15px;
  border: 5px solid black;
}

https://itchief.ru/assets/images/html-and-css/box-model/box.png

Таким образом:

  • ширина – 340px, где: 300px (ширина контентной области) + 15px (padding-left) + 15px (padding-right) + 5px (border-left-width) + 5px (border-right-width);
  • высота – 240px, где: 200px (высота контентной области) + 15px (padding-top) + 15px (padding-bottom) + 5px (border-top-width) + 5px (border-bottom-width).

https://itchief.ru/assets/images/html-and-css/box-model/properties-of-box-model.png

Здесь:

  • border-left-width – толщина левой границы рамки;
  • border-right-width – толщина правой границы рамки;
  • border-top-width – толщина верхней границы рамки;
  • border-bottom-width – толщина нижней границы рамки.

Внешний отступ (margin) определяет область вокруг границы элемента и может влиять на его размеры лишь косвенно. Он используется для создания отступов между элементами и управлением их расположением на странице.

Альтернативная блочная модель

Блочную модель, приведённую выше, очень не удобно использовать при веб-разработке. Так как нам проще оперировать общими размерами элементов, а не вычислять их каждый раз, складывая width и height с padding и border.

Поэтому в CSS была добавлена ещё одна блочная модель – альтернативная. В отличие от дефолтной, в ней мы с помощью width и height устанавливаем уже общие размеры элементам, а не контентной области. Ширина и высота контентной области в этом случае вычисляется исходя из общих размеров. То есть: width (height) - border - padding.

https://itchief.ru/assets/images/html-and-css/box-model/alternative-box-model.png

В большинстве случаев веб-разработчики перед началом написания стилей устанавливают сразу всем элементам, в том числе псевдоэлементам ::after и ::before, альтернативный способ расчёта размеров. Для этого они в самом начале CSS-кода прописывают следующее правило:

*,
*::before,
*::after {
  box-sizing: border-box;
}

Это же правило использует самый популярный в мире CSS-фреймворк Bootstrap для установки всем элементам на странице альтернативный способ расчёта их размеров.

Осуществляется это как вы уже поняли с помощью CSS-свойства box-sizing. Для этого ему в качестве значения нужно указать border-box. По умолчанию данное свойство имеет значение content-box, что соответствует расчёту общей ширины и высоты элемента по дефолтной модели CSS.

Теперь свойства width, height, min-width, min-height, max-width и max-height будут задавать именно общие размеры элементам.

Рассмотрим пример, в котором некоторый контейнер .container содержит два элемента .border-box и .content-box. Этим элементам назначены одинаковые свойства width, height, padding, border и margin. Единственное различие между ними в том, что второму элементу установлен альтернативный способ расчёта ширины с помощью свойства box-sizing:

<style>
  .border-box, .content-box {
    border: 10px solid #8d6e63;
    background-color: #d7ccc8;
    padding: 25px;
    margin: 50px;
    width: 300px;
    height: 200px;
  }
  
  .border-box {
    box-sizing: border-box;
  }
</style>

<div class="container">
  <div class="content-box"></div>
  <div class="border-box"></div>
</div>

Что нужно прописать для .border-box чтобы его размеры были такими же, как у первого элемента (.content-box)?

Правильно, установить свойствам width и height этого элемента соответственно общую ширину и высоту .content-box:

.border-box {
  box-sizing: border-box;
  width: 370px;
  height: 270px;
}

Блочные элементы

Сейчас, в HTML5, нет деления элементов на строчные и блочные. При написании HTML-документа, каждый элемент в нём нужно использовать в соответствии с его предназначением (смыслом), а не потому что он по умолчанию выглядет тем или иным образом на экране.

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

Свойством CSS, с помощью которого определяется как элемент будет отображаться на экране, является display.

Упрощённо в CSS можно выделить элементы с блочным (display: block), строчным (display: inline) и строчно-блочным (display: inline-block) отображением.

Элементы с display: block полностью соответствуют блочной модели CSS и ведут себя следующим образом:

  • всегда начинаются с новой строки;
  • по умолчанию занимают всю доступную ширину родительского элемента;
  • CSS-свойства width и height измененяют их размеры;
  • padding, border и margin, установленные для элемента отодвигают от него другие элементы на расстояние, соответствующее их значениям.

Рассмотрим пример. Для этого создадим три элемента <div> со следующими стилями:

<style>
  .box-1 {
    width: 80px;
    margin: 5px;
    padding: 5px;
    border: 1px solid #8bc34a;
    background-color: #c5e1a5;
  }

  .box-2 {
    width: 70px;
    margin: 5px;
    padding: 5px;
    border: 1px solid #03a9f4;
    background-color: #81d4fa;
  }

  .box-3 {
    margin: 5px;
    padding: 5px;
    border: 1px solid #ba68c8;
    background-color: #e1bee7;
  }
</style>

<body>
  <div class="box-1">80 x auto</div>
  <div class="box-2">70 x auto</div>
  <div class="box-3">auto x auto</div>

https://itchief.ru/assets/images/html-and-css/box-model/block-layout.png

Элементам <div> мы не стали явно прописывать display: block, так как эти стили <div> имеет уже по умолчанию. Они заданы им на уровне браузера.

https://itchief.ru/assets/images/html-and-css/box-model/div-display-ua.png

Элементам .box-1 и .box-2 мы явно установили ширину, чтобы показать специфику элементов с блочным отображением. То есть, несмотря на то , что после .box-1 достаточно места для .box-2, он разместился на новой строке. В этом суть элементов с display: block, они всегда начинаются с новой строки.

Элементу .box-3 мы не стали явно задавать ширину. В результате, он занял всю доступную ширину области контента родительского элемента. Это поведение элементы с display: block имеет по умолчанию.

Свойство width, в данном случае, имеет своё начальное значение auto. Оно означает, что браузеру необходимо автоматически расчитать ширину элемента исходя из доступной ширины родительского элемента.

При этом это значение не всегда равно 100% даже для элемента, которому установлен box-sizing: border-box. Так как элемент может содержать margin и в этом случае его внешняя ширина (с учётом внешних отступов) превысит доступную ширину контнетной области родительского элемента и выйдет за её пределы:

<style>
  .row {
    width: 100%;
    margin-left: 50px;
  }
</style>

<div class="container">
  <div class="row"></div>
</div>

https://itchief.ru/assets/images/html-and-css/box-model/width-overflow.png

Здесь показано как .row выйдет за пределы ширины области содержимого .container. Его ширина в этом случае будет 100% + 50px.

Если изменить width: 100% на width: auto для .row, то ширина этого элемента будет вычислена браузером так, чтобы он занял всю доступную ширину родителя. В данном случае это будет 100% - 50px.

По умолчанию высота элемента с display: block вычисляется браузером автоматически исходя из его содержимого таким образом, чтобы показать весь его контент:

.box {
  padding: 15px;
  border: 1px solid #ffeb3b;
  background-color: #fff59d;
}

https://itchief.ru/assets/images/html-and-css/box-model/height-auto.png

В этом примере height имеет своё начальное значение auto. При явной установке высоты элементу, содержимое которое не поместилось в его контентную область будет переполнять этот блок и по умолчанию выходить за его пределы:

.box {
  height: 180px;
  padding: 15px;
  border: 1px solid #ffeb3b;
  background-color: #fff59d;
}

https://itchief.ru/assets/images/html-and-css/box-model/height-overflow.png

Явная установка ширины (width) и высоты (height) не является хорошей практикой, так как это может привести к различным проблемам, особенно при создании адаптивных и отзывчивых веб-дизайнов.

В содержимое элементов с display: block может помещать любые другие элементы.

На элементы с display: block не действуют свойства, предназначенные, например, специально для элементов с display: inline. Например, vertical-align.

Схлопывание внешних отступов по вертикали

Ещё очень важным моментом в CSS является такой алгоритм как схлопывание вертикальных margin. В результате они объединяются в один отступ, размер которого равен наибольшему из них. Горизонтальных margin это не касается.

Схлопывание внешних отступов происходит в следующих случаях:

  • соседних элементов, имеющих одного родителя;
  • родителя и его первого (последнего) дочернего элемента;
  • пустого блока.

Рассмотрим примеры схлопывания вертикальных margin.

1. Два срседних элемента отодвигаются друг от друга по вертикали с помощью margin:

<style>
  .row {
    min-height: 50px;
    margin-bottom: 15px;
  }
  
  .row-2 {
    min-height: 50px;
    margin-top: 30px;
  } 
</style>

<div class="container">
  <div class="row-1"></div>
  <div class="row-2"></div>
</div>

В результате вертикальное расстояние между .row-1 и .row-2 будет 30px, а не 45px (15px + 30px). Так как в CSS вертикальные margin схлопываются и расстояние между блоками определяется по максимальному значению. В данном случае – это 30px.

https://itchief.ru/assets/images/html-and-css/box-model/collapse-of-vertical-margin.png

2. Схлопывание margin родителя и его первого дочернего элемента:

<style>
  .container {
    min-height: 30px;
    background-color: #673ab7;
  }

  .row {
    margin-top: 20px;
    min-height: 50px;
    background-color: #8bc34a;
  }
</style>

<div class="container"></div>

<div class="container">
  <div class="row"></div>
</div>

В этом примере верхние внешние отступы родителя и его первого дочернего элемента соприкасаются и в результате схлопываются. Это происходит потому у родителя отстутствует верхние border и padding.

Из двух схлопывающихся margin, максимальное значение имеет .row, он равен 20px. У родителя margin явно не задан и, следовательно, он по умолчанию – 0. В итоге, схлопнутый margin будет заканчиваться за пределами родительского элемента и отодвигать от себя вверх другой элемент на 20px. Этот алгоритм ещё называют «выпадением» margin из родительского элемента.

https://itchief.ru/assets/images/html-and-css/box-model/margin-collapse-parent-child.png

3. Схлопывание нижнего и верхнего margin пустого блока .empty:

<style>
  .row-top {
    margin-bottom: 20px;
    min-height: 15px;
    background-color: #673ab7;
  }

  .empty {
    margin-top: 20px;
    margin-bottom: 20px;
  }

  .row-bottom {
    margin-top: 20px;
    min-height: 15px;
    background-color: #7CB342;
  }
</style>

<div class="container">
  <div class="row-top"></div>
  <div class="empty"></div>
  <div class="row-bottom"></div>
</div>

В этом примере элемент .empty является пустым блоком, так как у него отсутствует то, чтобы разделяло его верхний и нижний margin. У .empty нет border, padding, контента, height или min-height.

В результате у него соприкасаются верхний и нижний margin и они схлопываются.

https://itchief.ru/assets/images/html-and-css/box-model/margin-collapse-empty-block.png

Строчные элементы

В отличие от display: block, элементы со строчным отображением (display: inline) соотвествуют блочной модели только частично.

1. Элементы со строчным отображением распологаются последовательно друг за другом в одной строке. То есть на той же строке, что и предыдущий элемент.

Если какой-то текст не помещается на строке, то он переносится на новую. Разделение текста на несколько строк, по умолчанию происходит в месте пробела.

2. Им нельзя напрямую установить ширину (width) и высоту (height), так как их размеры определяются в соотвествии с содержимым. Если эти свойства назначить элементам, то они будут просто проигнорированы.

3. Элементам со строчным отображением можно установить margin, padding и border. Но отодвигать другие элементы с display: inline они будут только по горизонтали. По вертикали это происходить не будет. То есть, например, с margin-top и margin-bottom отодвинуть другие элементы со строчным отображением у нас не получится.

Элементы с display: inline в основном используются для стилизации, помещенного в них текста, или в качестве контейнера для помещения в них других элементов со строчным отоборажением.

По умолчанию в браузере display: inline, например, имеют следующие элементы: <a>, <span>, <em>, <strong> и так далее.

Строчно-блочные элементы

Элементы со строчно-блочным отображением (display: inline-block) сочетают в себе особенности строчных и блочных элементов.

Как это? Очень просто, так как у элемента кроме внешнего отображения имеется ещё внутреннее. В данном случае он внешне будет вести себя как display: inline, а внутренне отображаться как display: block.

То есть его размер также как элемента с display: inline по умолчанию будет определяться в соответствии с его содержимым, он будет распологаться на той же строке и переноситься на новую только по мере необходимости. Но в тоже время он внутренне будет являться блоком и мы можем устанавливать ему width, height, margin, border и padding как элементу с display:block.

По умолчанию в браузере следующие элементы имеют display: inline-block: <button>, <input>, <textarea> и <select>.

В CSS кроме inline, block и inline-block имеется ещё много других способов отображения элементов, но эти являются самыми основными.

Например, для создания разметки сейчас очень часто используют такие значения display как flex, grid, inline-flex и inline-grid.

Эти свойства чем-то похожи на display: inline-block. Они также имеют внутреннее и внешнее отображение. Например, элементы с display: flex и display: inline-flex свои непосредственные дочерние элементы размещают в соотвествии с алгоритмом Flexbox. Это будет их внутреннее отображение. Но, внешне они ведут себя как block или inline.

Точно также работают гриды. Элемент с display: grid внешне ведёт себя как display: block, а display: inline-grid – как display: inline. Но внутренне их отображение соотвествует алгоритму Grid.

Размеры

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

Пиксели: px

Пиксель px – это самая базовая, абсолютная и окончательная единица измерения.

Количество пикселей задаётся в настройках разрешения экрана, один px – это как раз один такой пиксель на экране. Все значения браузер в итоге пересчитает в пиксели.

Пиксели могут быть дробными, например размер можно задать в 16.5px. Это совершенно нормально, браузер сам использует дробные пиксели для внутренних вычислений. К примеру, есть элемент шириной в 100px, его нужно разделить на три части – волей-неволей появляются 33.333...px. При окончательном отображении дробные пиксели, конечно же, округляются и становятся целыми.

Для мобильных устройств, у которых много пикселей на экране, но сам экран маленький, чтобы обеспечить читаемость, браузер автоматически применяет масштабирование.

Достоинства:

  • Главное достоинство пикселя – чёткость и понятность

Недостатки:

  • Другие единицы измерения – в некотором смысле «мощнее», они являются относительными и позволяют устанавливать соотношения между различными размерами

Давно на свалке: mm, cm, pt, pc

Существуют также «производные» от пикселя единицы измерения: mm, cm, pt и pc, но они давно отправились на свалку истории.

Вот, если интересно, их значения:

  • 1mm (мм) = 3.8px
  • 1cm (см) = 38px
  • 1pt (типографский пункт) = 4/3 px
  • 1pc (типографская пика) = 16px

Так как браузер пересчитывает эти значения в пиксели, то смысла в их употреблении нет.

Почему в сантиметре cm содержится ровно 38 пикселей?

В реальной жизни сантиметр – это эталон длины, одна сотая метра. А пиксель может быть разным, в зависимости от экрана.

Но в формулах выше под пикселем понимается «сферический пиксель в вакууме», точка на «стандартизованном экране», характеристики которого описаны в спецификации.

Поэтому ни о каком соответствии cm реальному сантиметру здесь нет и речи. Это полностью синтетическая и производная единица измерения, которая не нужна.

Относительно шрифта: em

1em – текущий размер шрифта.

Можно брать любые пропорции от текущего шрифта: 2em, 0.5em и т.п.

Размеры в emотносительные, они определяются по текущему контексту.

Например, давайте сравним px с em на таком примере:

<div style="font-size:24px">
  Страусы
  <div style="font-size:24px">Живут также в Африке</div>
</div>

24 пикселей – и в Африке 24 пикселей, поэтому размер шрифта в <div> одинаков.

А вот аналогичный пример с em вместо px:

<div style="font-size:1.5em">
  Страусы
  <div style="font-size:1.5em">Живут также в Африке</div>
</div>

Так как значение в em высчитывается относительно текущего шрифта, то вложенная строка в 1.5 раза больше, чем первая.

Выходит, размеры, заданные в em, будут уменьшаться или увеличиваться вместе со шрифтом. С учётом того, что размер шрифта обычно определяется в родителе, и может быть изменён ровно в одном месте, это бывает очень удобно.

Что такое размер шрифта?

Что такое «размер шрифта»? Это вовсе не «размер самой большой буквы в нём», как можно было бы подумать.

Размер шрифта – это некоторая «условная единица», которая встроена в шрифт.

Она обычно чуть больше, чем расстояние от верха самой большой буквы до низа самой маленькой. То есть, предполагается, что в эту высоту помещается любая буква или их сочетание. Но при этом «хвосты» букв, таких как р, g могут заходить за это значение, то есть вылезать снизу. Поэтому обычно высоту строки делают чуть больше, чем размер шрифта.

Единицы ex и ch

В спецификации указаны также единицы ex и ch, которые означают размер символа "x" и размер символа "0".

Эти размеры присутствуют в шрифте всегда, даже если по коду этих символов в шрифте находятся другие значения, а не именно буква "x" и ноль "0". В этом случае они носят более условный характер.

Эти единицы используются чрезвычайно редко, так как «размер шрифта» em обычно вполне подходит.

Проценты: %

Проценты %, как и em – относительные единицы.

Когда мы говорим «процент», то возникает вопрос – «Процент от чего?»

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

Это очень важная особенность процентов, про которую, увы, часто забывают.

Отличный источник информации по этой теме – стандарт, Visual formatting model details.

Вот пример с %, он выглядит в точности так же, как с em:

<div style="font-size:150%">
  Страусы
  <div style="font-size:150%">Живут также в Африке</div>
</div>

В примере выше процент берётся от размера шрифта родителя.

А вот примеры-исключения, в которых % берётся не так:

margin-left

При установке свойства margin-left в %, процент берётся от ширины родительского блока, а вовсе не от его margin-left.

line-height

При установке свойства line-height в %, процент берётся от текущего размера шрифта, а вовсе не от line-height родителя. Детали по line-height и размеру шрифта вы также можете найти в статье Свойства font-size и line-height.

width/height

Для width/height обычно процент от ширины/высоты родителя, но при position:fixed, процент берётся от ширины/высоты окна (а не родителя и не документа). Кроме того, иногда % требует соблюдения дополнительных условий, за примером – обратитесь к главе Особенности свойства height в %.

Единица rem: смесь px и em

Итак, мы рассмотрели:

  • px – абсолютные, чёткие, понятные, не зависящие ни от чего.
  • em – относительно размера шрифта.
  • % – относительно такого же свойства родителя (а может и не родителя, а может и не такого же – см. примеры выше).

Вернёмся к теме шрифтов. Бывают задачи, когда мы хотим сделать на странице большие кнопки «Шрифт больше» и «Шрифт меньше». При нажатии на них будет срабатывать JavaScript, который будет увеличивать или уменьшать шрифт.

Вообще-то это можно сделать без JavaScript, в браузере обычно есть горячие клавиши для масштабирования вроде Ctrl++, но они работают слишком тупо – берут и увеличивают всю страницу, вместе с изображениями и другими элементами, которые масштабировать как раз не надо. А если надо увеличить только шрифт, потому что посетитель хочет комфортнее читать?

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

Следующие кандидаты – em и %.

Разницы между ними здесь нет, так как при задании font-size в процентах, эти проценты берутся от font-size родителя, то есть ведут себя так же, как и em.

Вроде бы, использовать можно, однако есть проблема.

Попробуем использовать этот подход для <li>.

Протестируем на таком списке:

<ul>
<li>Собака
  <ul>
  <li>бывает
    <ul>
    <li>кусачей
      <ul>
      <li>только
        <ul>
        <li>от жизни
          <ul>
          <li>собачей</li>
          </ul>
        </li>
        </ul>
      </li>
      </ul>
    </li>
    </ul>
  </li>
  </ul>
</li>
</ul>

Пока это обычный вложенный список.

Теперь уменьшим размер шрифта до 0.8em, вот что получится:

<style>
  li {
    font-size: 0.8em;
  }
</style>

<ul>
<li>Собака
  <ul>
  <li>бывает
    <ul>
    <li>кусачей
      <ul>
      <li>только
        <ul>
        <li>от жизни
          <ul>
          <li>собачей</li>
          </ul>
        </li>
        </ul>
      </li>
      </ul>
    </li>
    </ul>
  </li>
  </ul>
</li>
</ul>

Проблема очевидна. Хотели, как лучше, а получилось… Мелковато. Каждый вложенный <li> получил размер шрифта 0.8 от родителя, в итоге уменьшившись до нечитаемого состояния. Это не совсем то, чего мы бы здесь хотели.

Можно уменьшить размер шрифта только на одном «корневом элементе»… Или воспользоваться единицей rem, которая, можно сказать, специально придумана для таких случаев!

Единица rem задаёт размер относительно размера шрифта элемента <html>.

Как правило, браузеры ставят этому элементу некоторый «разумный» (reasonable) размер по умолчанию, который мы, конечно, можем переопределить и использовать rem для задания шрифтов внутри относительно него:

<style>
  html {
    font-size: 14px;
  }
  li {
    font-size: 0.8rem;
  }
</style>

<div><button id="up">Кликните, чтобы увеличить размер шрифта</button></div>

<img src="https://js.cx/clipart/angry_dog.png">

<ul>
<li>Собака
  <ul>
  <li>бывает
    <ul>
    <li>кусачей
      <ul>
      <li>только
        <ul>
        <li>от жизни
          <ul>
          <li>собачей</li>
          </ul>
        </li>
        </ul>
      </li>
      </ul>
    </li>
    </ul>
  </li>
  </ul>
</li>
</ul>

<script>
let html = document.documentElement;
up.onclick = function() {
  // при помощи JS увеличить размер шрифта html на 2px
  html.style.fontSize = parseInt(getComputedStyle(html, '').fontSize) + 2 + 'px';
};
</script>

Получилось удобное масштабирование для шрифтов, не влияющее на другие элементы.

Элементы, размер которых задан в rem, не зависят друг от друга и от контекста – и этим похожи на px, а с другой стороны они все заданы относительно размера шрифта <html>.

Единица rem не поддерживается в IE8-.

Относительно экрана: vw, vh, vmin, vmax

Во всех современных браузерах, исключая IE8-, поддерживаются новые единицы из черновика стандарта CSS Values and Units 3:

  • vw – 1% ширины окна
  • vh – 1% высоты окна
  • vmin – наименьшее из (vw, vh), в IE9 обозначается vm
  • vmax – наибольшее из (vw, vh)

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

Их основное преимущество – в том, что любые размеры, которые в них заданы, автоматически масштабируются при изменении размеров окна.

Этот текст написан с размером `5vh`.

Вы сможете легко увидеть, как работает vh, если поменяете высоту окна браузера. Текст выше будет расти/уменьшаться.

Итого

Мы рассмотрели единицы измерения:

  • px – абсолютные пиксели, к которым привязаны и потому не нужны mm, cm, pt и pc. Используется для максимально конкретного и точного задания размеров.
  • em – задаёт размер относительно шрифта родителя, можно относительно конкретных символов: "x"(ex) и "0"(ch), используется там, где нужно упростить масштабирование компоненты.
  • rem – задаёт размер относительно шрифта <html>, используется для удобства глобального масштабирования: элементы которые планируется масштабировать, задаются в rem, а JS меняет шрифт у <html>.
  • % – относительно такого же свойства родителя (как правило, но не всегда), используется для ширин, высот и так далее, без него никуда, но надо знать, относительно чего он считает проценты.
  • vw, vh, vmin, vmax – относительно размера экрана.

Цвета в CSS

Свойство color

На MDN можно найти сведения о том, что CSS-свойство color позволяет задавать основной цвет элемента (foreground color, его также называют «цветом переднего плана»), который описывает цвет текста элемента и цвет элементов оформления текста. На основе значения этого свойства устанавливается значение currentColor.

Итак, свойство color позволяет задать основной цвет элемента. При настройке этого свойства можно использовать различные значения:

  • Названия именованных цветов.
  • Шестнадцатеричные коды цветов.
  • Цвета, задаваемые с помощью функций rgb() и hsl().
  • Глобальные значения inherit, initial, unset.
  • Ключевое слово currentColor.

Рассмотрим разные способы описания цветов в CSS.

Именованные цвета

CSS поддерживает стандартные наименования цветов, которые можно использовать, просто указывая в качестве значения свойства color имя нужного цвета. Вот как это выглядит:

.heading {
    color: brown;
    color: green;
    color: black;
}

Вот, если интересно, полный список имён цветов. Тут стоит сказать о том, что некоторые имена цветов поддерживаются не во всех браузерах.

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

*, *:before, *:after {
    outline: solid 1px red;
}

Здесь я использую красный цвет (red) для того чтобы быстро показать обводку всех элементов страницы. Цвет red популярен в подобных ситуациях, мне даже доводилось видеть мемы об этом.

Шестнадцатеричные цветовые значения

Шестнадцатеричный код цвета состоит из шести символов. Например, значение #ffffff представляет белый цвет. При описании цветов с использованием шестнадцатеричных значений есть одна особенность, позволяющая, в определённых ситуациях, использовать сокращённую запись кода цвета.

Дело в том, что если пара значений в описании одного компонента цвета идентична, одно из таких значений можно убрать. Каждая пара значений представляет собой описание одного из компонентов цвета. Это, соответственно, компоненты Red (красный), Green (зелёный) и Blue (Синий). Рассмотрим следующий пример:

body { color: #222222; /* Превратится в #222 */
body { color: #000000; /* Превратится в #000 */

Значение #222222 эквивалентно значению #222. Если представить исходный шестнадцатеричный код в виде 22, 22, 22, то, чтобы получить из него код 2, 2, 2, достаточно взять из каждой пары значений по одному.

https://habrastorage.org/r/w1560/getpro/habr/post_images/a0e/bb1/8a3/a0ebb18a3ec1c42f0868d6646b7697cf.jpg
Сокращение шестнадцатеричных кодов цветов

Цвета, задаваемые в формате RGB/RGBA

При использовании цветовой модели RGB (Red, Green, Blue — красный, зелёный, синий) цвета представляют тремя значениями, описывающими цветовые каналы — соответственно — красный, зелёный и синий канал. Каждое из значений можно представить числом, находящимся в диапазоне от 0 до 255, или процентным значением — от 0 до 100%.

Если три значения равны 0, то получится чёрный цвет. А если все три цветовых компонента установлены в значение 255, то получится белый. То же самое справедливо и при использовании процентных значений.

.element {
    /* Белый */
    color: rgb(255, 255, 255);
    color: rgb(100%, 100%, 100%);

    /* Чёрный */
    background-color: rgb(0, 0, 0);
    background-color: rgb(0%, 0%, 0%);
}

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

.element {
    /* Чёрный фон с 50% прозрачностью */
    background-color: rgba(0, 0, 0, 0.5);
}

Если значение, соответствующее альфа-каналу, равно нулю, тогда основной или фоновый цвет элемента окажется полностью прозрачным, то есть — невидимым.

Цвета, задаваемые в формате HSL

Я редко вижу использование цветовой модели HSL (Hue, Saturation, Lightness — тон, насыщенность, светлота), но в последнее время эта цветовая модель стала привлекать к себе всё больше внимания со стороны дизайнеров и разработчиков.

https://habrastorage.org/r/w1560/getpro/habr/post_images/53e/7b4/735/53e7b4735aa6f367d77400bd9dd016d0.jpg
Цветовой круг

На цветовом круге каждому цвету соответствует определённый угол, описывающий цветовой тон (hue). Для описания HSL-цвета, помимо тона, нужно указать значения, отвечающие за насыщенность и светлоту цвета.

Разберём процесс описания HSL-цветов. Представим, что нас интересует цветовой тон, показанный на следующем рисунке.

https://habrastorage.org/r/w1560/getpro/habr/post_images/f79/eb0/ffe/f79eb0ffe8ccdedcf04ab9dfed5d21ba.jpg
Выбор цветового тона

Для выбора насыщенности и светлоты можно воспользоваться удобным инструментом, который находится здесь. А именно, после выбора тона, насыщенность и светлоту цвета можно представить так, как показано ниже.

https://habrastorage.org/r/w1560/getpro/habr/post_images/d4e/84b/a01/d4e84ba01aa20a21b742c0aa3e897a3a.jpg
Выбор насыщенности и светлоты

Шкала насыщенности, в начале, представлена серым цветом, а в конце — цветом, который мы выбрали на цветовом круге. Шкала светлоты начинается с чёрного цвета, доходит до выбранного цветового тона и заканчивается белым цветом.

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

Ключевое слово currentColor

Ключевое слово currentColor хранит значение CSS-свойства color. Его можно использовать для настройки элементов, которые, по умолчанию, не наследуют цвета от их родительских элементов. Вот некоторые свойства, значения которых, по умолчанию, устанавливаются равными currentColor:

  • border-color
  • text-decoration-color
  • outline-color
  • box-shadow

Тут может возникнуть такое ощущение, что currentColor — это нечто вроде CSS-переменной. Но это ключевое слово появилось ещё до того, как в CSS стало можно работать с переменными. Рассмотрим следующий пример:

.element {
    color: #222;
    border: 2px solid;
}

Как думаете, каким будет значение свойства border-color? Очевидно — таким же, как и значение свойства color. Это так из-за того, что, по умолчанию, значением border-color является значение currentColor. Возможно, это легче представить себе, если переписать предыдущий пример так:

.element {
    color: #222;
    border: 2px solid currentColor;
}

Интересной особенностью currentColor является тот факт, что это ключевое слово можно использовать и на уровне родительского элемента, и для дочерних элементов.

<h1>
    This is a title
    <span>I'm a child element</span>
</h1>

В этом примере currentColor можно использовать и для элемента <h1>, и для элемента <span>:

h1 {
    color: #222;
    background-color: currentColor;
}

Использование инструментов разработчика для исследования цветов

При проведении экспериментов с CSS-цветами можно воспользоваться одной полезной возможностью инструментов разработчика Chrome. Для вызова этой возможности нужно выбрать некий элемент, цвета которого заданы в шестнадцатеричном формате, и переключиться в режим его исследования. Затем нужно щёлкнуть по маленькому квадратику, который представляет цвет, используемый элементом в данный момент.

https://habrastorage.org/r/w1560/getpro/habr/post_images/78e/b84/72a/78eb8472aaf0d8327512b78fe0fb5044.jpg
Переключатель

Этот приём подходит только для шестнадцатеричных цветов и для свойств, при настройке которых используются CSS-переменные:

.btn {
    background-color: var(--brand-primary);
}

Тут надо сказать о том, что при использовании HSL-цветов в комбинации с CSS-переменными всё ещё можно столкнуться с неоднородным поведением браузеров. Например, вывод вышеприведённого примера в Firefox не приведёт к показу цветного квадратика. Вот как это выглядит в разных браузерах.

https://habrastorage.org/r/w1560/getpro/habr/post_images/e91/dbc/d2c/e91dbcd2c53dee88f5bed196512af93f.jpg
Браузеры по-разному работают с переменными

Синий квадратик, выводимый в Safari, демонстрирует вычисленное значение используемого CSS-свойства. Это лучше, чем ничего.

Возможно, вы сейчас задаётесь вопросом о том, зачем нам вообще нужен этот квадратик, представляющий вычисленный цвет и выводимый около CSS-свойства? Дело в том, что без него мы не сможем открыть средства инструментов разработчика для работы с цветом, которые помогают исследовать доступность используемого цветового решения.

Вышеописанная проблема проявляется не всегда. Например, основной цвет кнопки задан в виде color: #fff, а её фоновый цвет указан с использованием hsl(). В такой ситуации можно открыть палитру инструментов разработчика, обратившись к свойству color, и взглянуть на уровень контраста. Правда, для элементов, которые используют HSLA-значения с CSS-переменными, этого сделать нельзя.

Цветовые значения, разделённые пробелами

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

/* До */
.element {
  background-color: rgb(0, 0, 0);
  background-color: rgba(0, 0, 0, 1);
}

/* После */
div {
  background-color: rgb(0 0 0);
  background-color: rgb(0 0 0 / 0.5);
  background-color: rgb(0 0 0 / 50%);
}

Учитывайте то, что отделение сведений о прозрачности с помощью косой черты — это лишь дополнительная возможность. Ей стоит пользоваться только тогда, когда нужно настроить прозрачность цвета.

Новый способ описания цветов лучше старого. Рекомендуется пользоваться именно им, так как новые CSS-функции, используемые для описания цвета (lab(), lch() и color()), будут работать только с переданными им значениями, разделёнными пробелами. Поэтому полезно будет привыкнуть к такому формату описания цветов, предусмотрев, что несложно, запасной вариант для тех браузеров, которые этот формат не поддерживают.

Хочу отметить, что хотя я и порекомендовал пользоваться в функциях цветовыми значениями, разделёнными пробелами, я не применял их в статье, так как это — достаточно новая возможность CSS. А мне не хотелось бы запутывать читателей.

Глобальные значения (inherit, initial, unset)

Для того чтобы элемент унаследовал бы цвет от родительского элемента, можно воспользоваться глобальным значением inherit. Для того чтобы сбросить цвет, можно прибегнуть к значениям initial и unset. Рассмотрим пример, который поможет нам понять особенности использования этих значений.

Ключевое слово inherit

Вот HTML-код верхней части страницы, в которой имеется заголовок, описание и ссылка:

<section class="hero">
    <h2><font color="#3AC1EF">Page title</font></h2>
    <p>Some description content</p>
    <a href="#">View all</a>
</section>

Вот стиль:

.hero { color: #222; }

Учитывая то, что свойство color родительского элемента установлено в значение #222, как вы думаете, каким будет цвет дочерних элементов? Элементы <h2><font color="#3AC1EF"> и <p> унаследуют этот цвет. А элемент <a> — нет, что обусловлено особенностями его стандартного поведения. Интересно, правда?

Цвет ссылок, используемый по умолчанию, выглядит так:

:link { color: #0000ee; }

Если надо — можно, с помощью конструкции color: inherit, сделать так, чтобы ссылка, всё же, унаследовала бы цвет от родительского элемента.

Ключевое слово unset

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

Вернёмся к предыдущему примеру и сделаем так, чтобы ссылка унаследовала бы цвет от родительского элемента:

.hero a { color: unset; }

Использование вышеприведённой конструкции позволит сбросить цвет до цвета, который может быть унаследован от элемента .hero. А это — именно то, что нам нужно.

Здесь со всем этим можно поэкспериментировать.

Сценарии использования и практические примеры

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

Полупрозрачный RGBA-цвет

Мне часто приходилось видеть, как функция rgba() используется в CSS для создания дизайна, вариант которого представлен на следующем рисунке.

https://habrastorage.org/r/w1560/getpro/habr/post_images/397/708/3b1/3977083b1060bfeec12fb1e13c7c0a92.jpg
Внутренняя граница

Обратите внимание на то, что у окружностей имеются границы, которые темнее, чем фон этих окружностей. Как сделать это динамически? А сделать это можно, воспользовавшись границей, полупрозрачный цвет которой задан с применением функции rgba():

.item {
    border: 10px solid rgba(0, 0, 0, 0.2);
    /* Чёрная граница с 20% альфа-значением */
}

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

https://habrastorage.org/r/w1560/getpro/habr/post_images/7cc/ee0/142/7ccee01429119486a5890a4fc4f1f3b5.jpg
Полупрозрачный фон

Использование HSL-цветов

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

Это может пригодиться в паре ситуаций. Например, тогда, когда нужно сделать так, чтобы светлота цвета кнопки становилась бы меньше при наведении на эту кнопку мыши.

https://habrastorage.org/r/w1560/getpro/habr/post_images/14f/e6c/54d/14fe6c54d19c73c82262b9995cc7c813.jpg
Кнопка становится темнее при наведении на неё указателя мыши

.btn {
  /* Тон: 97, Насыщенность: 50%, Светлота: 41% */
    background: hsl(97, 50%, 41%);
}

.btn:hover {
    background: hsl(97, 50%, 36%);
}

Эту идею можно развить, скомбинировав функцию hsl() и CSS-переменные для создания цветовой палитры, которая легко поддаётся изменениям.

:root {
  --primary-h: 97;

  --brand-primary: hsl(var(--primary-h), 50%, 41%);
  --brand-primary-darker: hsl(var(--primary-h), 50%, 36%);

  --brand-grey-1: hsl(0, 0%, 0%);
  --brand-grey-2: hsl(0, 0%, 10%);
  --brand-grey-3: hsl(0, 0%, 20%);
  --brand-grey-4: hsl(0, 0%, 30%);
  --brand-grey-5: hsl(0, 0%, 40%);
  --brand-grey-6: hsl(0, 0%, 50%);
}

Указывая основной тон, мы можем использовать его, задавая цвета, позволяющие делать элементы светлее или темнее.

Особенно мне здесь нравится организация работы с оттенками серого цвета. Мне всегда было тяжело запоминать правильные шестнадцатеричные значения для оттенков серого. При использовании hsl() работа с такими цветами значительно упрощается.

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

https://habrastorage.org/r/w1560/getpro/habr/post_images/acc/c2f/043/accc2f043346e25947e63cabf3d67f97.jpg
Цвет и изменение светлоты

Полагаю, никто не станет спорить с тем, что при таком подходе очень просто работать с оттенками серого. Мне это очень нравится, я планирую использовать этот приём в своих будущих проектах.

При работе над большими проектами я использовал бы следующий подход, подразумевающий применение CSS-переменных и задействующий изменение светлоты цвета:

:root {
  --primary-h: 97;
  --primary-l: 41%;

  --brand-primary: hsl(var(--primary-h), 50%, var(--primary-l));
  --brand-primary-darker: hsl(var(--primary-h), 50%, 36%);
}

Здесь я определил переменную --primary-h, хранящую сведения о цветовом тоне, и переменную --primary-l, в которую записано значение, представляющее собой базовую светлоту. Самое приятное тут то, что у меня теперь есть возможность настраивать цвет, меняя лишь переменную --primary-l:

.btn {
    --primary-l: 45%;
    background-color: hsl(var(--primary-h), 50%, var(--primary-l));
}

.footer {
    --primary-l: 55%;
    background-color: hsl(var(--primary-h), 50%, var(--primary-l));
}

.section {
    --primary-l: 50%;
    background-color: hsl(var(--primary-h), 50%, var(--primary-l));
}

Здесь можно найти рабочий пример.

Использование ключевого слова currentColor с SVG-иконками

Хороший сценарий использования ключевого слова currentColor представлен ситуациями, когда нужно управлять цветом SVG-иконок. Представим, что у нас имеется иконка, после которой идёт текст. Значок иконки и текст должны быть окрашены в один и тот же цвет.

https://habrastorage.org/r/w1560/getpro/habr/post_images/207/786/83a/20778683a91dfa857ecdf107c5a52f2e.jpg
Значок и текст должны быть одного цвета

Ключевое слово currentColor можно использовать для настройки SVG-атрибута fill, а затем назначить нужный цвет родительскому элементу. Взгляните на следующий HTML-код:

<a href="#" class="link">
    <svg fill="currentColor"></svg>
    <span>Show more</span>
</a>

Вот применяемый здесь стиль:

.link {
    color: #3766dc;
}

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

Более того, тот же подход можно использовать для того чтобы создавать иконки, заключённые в прямоугольные полупрозрачные области, атрибут fill которых также задаётся с помощью currentColor. Это позволяет использовать CSS-свойство color для решения следующих задач:

  1. Назначение цвета иконке.
  2. Назначение прямоугольнику полупрозрачного варианта исходного цвета.

Взгляните на следующий рисунок.

https://habrastorage.org/r/w1560/getpro/habr/post_images/1da/564/b89/1da564b8975253ffbabfd616e974d638.jpg
Настройка иконки, заключённой в цветной полупрозрачный прямоугольник

Вот соответствующий CSS-код:

.icon {
  color: hsl(var(--primary-h), 50%, var(--primary-l));
}

.icon-2 {
  --primary-h: 26;
}

.icon-3 {
  --primary-h: 292;
}

Обратите внимание на то, что у элемента <rect> задан атрибут opacity. В разных вариантах иконки меняется лишь значение, соответствующее цветовому тону. Такой подход может оказаться крайне полезным в дизайн-системах, так как он позволяет, не прибегая к каким-то сложным конструкциям, создавать различные варианты значков и согласовывать их цвет с цветом обрамляющих их элементов.