Черт тебя возьми, CSS

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

У CSS много моментов, которые сбивают столку. Разработчики плюются от него. А мне нравится CSS, несмотря на мои потраченные нервы. Подумав, как помочь другим меньше мучаться, я собрал ряд неочевидных моментов. Они сбивали с толку меня и моих знакомых. Надеюсь, вам будет полезно.


▍ Свойство display


Однажды на собеседовании мне задали вопрос: «Какое значение свойства display у элемента с классом child будет во вкладке Computed в DevTools?»


<div class="parent">
  <span class="child">Я дочерний элемент внутри родителя с display: flex</span>
</div>

.parent {
  display: flex;
}

.child {
  display: inline-grid; /* какое итоговое значение свойства display будет здесь? */
}

Если вы думаете, что inline-grid, то вы попались на удочку, как и я. Нет, это не правильный ответ. Правильный ответ — grid.


Данный ответ обоснован тем, что после добавления display: flex к элементу, браузеры сделают проверку значения для свойства display у его дочерних элементов.


Если используются значения inline, inline-block, inline-flex, inline-grid или inline-table, то они трансформируются. Значение inline и inline-block в block, inline-flex в flex, inline-grid в grid и inline-table в table.


По этой причине мы увидим grid во вкладке Computed в DevTools.


.parent {
  display: flex;
}

.child {
  display: inline-grid; /* Здесь будет значение grid, а не inline-grid */
}

Также мне сказали, что точно такое же поведение есть при использовании значений grid, inline-grid и inline-flex:


<div class="parent-grid">
  <span class="child">Я дочерний элемент внутри родителя с display: grid</span>
</div>
<div class="parent-inline-grid">
  <span class="child">Я дочерний элемент внутри родителя с display: inline-grid</span>
</div>
<div class="parent-inline-flex">
  <span class="child">Я дочерний элемент внутри родителя с display: inline-flex</span>
</div>

.parent-grid {
  display: grid;
}

.parent-inline-grid {
  display: inline-grid;
}

.parent-inline-flex {
  display: inline-flex;
}

.child {
  display: inline-flex; /* Во всех случаях здесь будет значение flex, а не inline-flex */
}

После собеседования, я попытался найти объяснение этому процессу. Ответ был в стандартах CSS Flexible Box Layout Module Level 1 и CSS Grid Layout Module Level 1.


В них говорится, что значение свойства display у дочерних элементов внутри флекс-контейнера и грид-контейнера становится blockified, т.е inline-* значение трансформируется, как мне сказали на собеседовании.


Получается, я долгое время делал ошибку, определяв display: block для псевдо-элемента ::before или ::after, находящегося внутри элемента с display: flex:


.parent {
  display: flex
}

.parent::before {
  content: "";
  display: block;
 
  width: 1rem;
  height: 1rem;
  background-color: mediumspringgreen;
}

По умолчанию для псевдо-элементов ::before и ::after значение свойства display установлено, как inline. Для таких элементов браузеры не могут задать размеры с помощью свойств width и height, поэтому я объявлял display: block.


Во вкладке Compted в DevTools я видел display: block, но это не тот display: block, которые я устанавливал. Браузеры сами добавили display: block.


Удалив его из кода, я убедился, что изменений нет.


.parent {
  display: flex
}

.parent::before {
  content: "";  
  width: 1rem;
  height: 1rem;
  background-color: mediumspringgreen;
}

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


.parent::before {
  content: "";
  display: block;
  background-color: mediumspringgreen;
 
  position: absolute;
  inset: 0;
}

Догадались, какое свойство можно удалить? Конечно, свойство display. Браузеры делают проверку значения свойства display у элемента, если при объявлении свойства position используется значение absolute или fixed.


Согласно таблице описанной в стандарте значение inline,inline-block или любое из table-*трансформируется в значение block, а inline-table в table. Значений inline-flex и inline-grid нет в стандарте, но я проверил их вручную. Результат — они изменяются на flex и grid.


Вернусь к коду и удалю display: block.


.parent::before {
  content: "";
  background-color: mediumspringgreen;

  position: absolute;
  inset: 0;
}

▍ Математическая функция calc()


У меня есть привычка не писать единицы измерения вместе с 0. Однажды мне нужно было использовать математическую функцию calc() для вычисления ширины элемента следующим образом:


:root {
  --default-gap: 1rem;
}

.container {
  width: calc(0 + var(--default-gap));
}

К моему удивлению — этот код не работал. Открыв стандарт CSS Values and Units Module Level 3, я нашёл уточнение. При использовании математической функции calc() для свойства со значением типа <length>, 0 без единицы измерения не поддерживается.


Здесь, как мне кажется, нужно пояснить. В функции можно использовать несколько типов значений:


  • <integer> — число, состоящие из одной или несколько цифр в диапазоне от 0 до 9. Перед первой цифрой может быть установлен знак — или +;
  • <number> — число как <integer>, но может быть записано с помощью экспоненциальной формы записи;
  • <length> — тип для измерения дистанции, который является числом с единицей измерения.

Вернусь кпримеру. Браузеры не могут понять, что 0 без единицы измерения используется как тип <length>. Нужно исправить:


:root {
  --default-gap: 1rem;
}

.container {
  width: calc(0px + var(--default-gap));
}

Ещё в стандарте есть неочевидные требования, которые следует помнить при работе с calc(). Так, при сложении и вычитании оба аргумента должны быть либо одного типа, либо один из аргументов должен быть типом <number>, а другой <integer>.


Соответственно, если используется один аргумент типом <length>, а другой <integer> или <number>, то будет ошибка.


/* правильный пример */

.container {
  width: calc(1000px + 3rem);
  opacity: calc(0.5 + 0.35);
  z-index: calc(1E2 + 1);
}

.container {
  width: calc(1000px - 3rem);
  opacity: calc(0.5 - 0.35);
  z-index: calc(1E2 - 1);
}

/* неправильный пример */

.container {
  width: calc(1000px + 32);
  height: calc(1000px + 1E1);
}

.container {
  width: calc(1000px - 32);
  height: calc(1000px - 1E1);
}

При умножении и делении один из аргументов обязательно должен быть типом <integer> или <number>. Если оба аргумента являются типом <length>, то будет ошибка. При делении, если правый аргумент является типом <integer>, то браузеры трансформируют его в тип <number>.


/* правильный пример */

.container {
  width: calc(10px * 1E1);
  height: calc(100px * 2);
  opacity: calc(0.25 * 2);
  z-index: calc(1E2 * 1E1);
}

.container {
  width: calc(900px / 3E1);
  height: calc(100px / 2);
  opacity: calc(1 / 2);
  z-index: calc(1E2 / 1E1);
}

/* неправильный пример */

.container {
  width: calc(1000px * 10px);
}

.container {
  width: calc(60000px / 6px);
}

▍ Свойство min-width


Однажды мой ученик сделал демку и задал мне вопрос: «Почему в devTools у элемента с классом container у свойства min-width установлено значение 0, а у элемента с классом contentauto?


<body>
  <div class="container">
    <div class="content">здесь какой-то контент</div>
  </div>
</body>

.container {
  display: grid;
}

.content {
  box-sizing: border-box;
  max-width: 100rem;
  padding-inline: 1rem;
}

В стандарте CSS Box Sizing Module Level 3 сказано, что у свойства min-width есть значение по умолчанию auto. Так почему мы видим 0? В разделе описания допустимых значений для свойства есть пояснение.


Браузеры преобразуют значение auto в 0, если для родительского элемента определено свойство display со значениями block, inline, inline-block, table или со всеми table-* значениями.


В примере элемент div с классом container находится внутри элемента body, у которого по умолчанию установлено display: block. По этой причине браузеры неявно устанавливают значение 0 для свойства min-width. А элемент с классом content находится внутри элемента container c display: grid, поэтому в devTools мы увидим уже min-width: auto.


▍ Свойство position и значение fixed


Вы знали, что есть несколько случаев, когда элемент с position: fixed не будет зафиксированным на экране, а его размеры не будут рассчитаны от вьюпорта? Например, как в следующем коде:


.demo {
  width: 300px;
  height: 200px;
  background-color: mediumspringgreen;
  transform: translate3d(0, 0, 0);
}
 
.demo::before {
  content: "";
  width: 50%;
  height: 50%;
  background-color: lightgoldenrodyellow;

  position: fixed;
  top: 1rem;  
  right: 1rem;
}

Для понимания причины такого поведения нужно вспомнить, что у каждого элемента есть родительская область относительно, которой рассчитываются размеры и позиция дочернего элемента. В стандарте CSS Display Module Level 3 ей дали название containing block. У элемента с position: fixed такой областью обычно является вьюпорт.


А теперь самое интересное. Существует ряд свойств, которые влияют на определение такой области. В частности свойство transform. В стандарте CSS Transforms Module Level 1 сказано, что если для элемента указано свойство transform со значением отличного от none, то все его дочерние элементы с position: fixed будут использовать его, как containing block. Таким образом — они перестают быть „зафиксированными“.


Именно это произошло в моём примере. Определив transform: translate3d(0, 0, 0) для элемента .demo, я изменил область расчётов позиции и размеров у псевдо-элемента .demo::before.


.demo {
  width: 300px;
  height: 200px;
  background-color: mediumspringgreen;
  transform: translate3d(0, 0, 0);
}
 
.demo::before {
  content: "";
  width: 50%; /* здесь итоговое значение будет 150px, а не половина ширины вьюпорта */
  height: 50%; /* здесь итоговое значение будет 100px, а не половина высоты вьюпорта */
  background-color: lightgoldenrodyellow;

  position: fixed;
  top: 1rem;  
  right: 1rem;
}

Кроме свойства transform также влияют:


  • свойства perspective, backdrop-filter и filter со всеми значениями, кроме none;
  • свойство contain со значениями layout, paint, strict и content;
  • свойство will-change со значениями transform, filter и backdrop-filter.

▍ Вместо заключения


А какие у вас были случаи, когда CSS сбил вас с толку? Делитесь в комментариях. Наберём на новую статью! Спасибо.


Выиграй телескоп и другие призы в космическом квизе от RUVDS. Поехали?
Источник: https://habr.com/ru/companies/ruvds/articles/746478/


Интересные статьи

Интересные статьи

Картинка Нина Ватолина, Николай Денисов / gallerix.ru За долгие годы жизни, окружённые привычными вещами, мы даже не задумываемся об этом и принимаем как данность, что они должны выглядеть именно ...
 Все персонажи и события являются вымышленными. Любое совпадение с реально живущими или когда-либо жившими людьми случайно.  - А почему вы используете в расчете КП наценку и маржу?- Дир...
Сегодня разговор пойдет о прекрасных существах, называемых морскими ангелами и морскими чёртиками. Данные животные, обладающие потрясающей красотой, являются брюхоногими моллюсками...
Об авторе: Пол Букхайт — создатель Gmail и FriendFeed, партнер Y Combinator. Инстинктивная (интуитивная) прозорливость, серендипность (англ. serendipity) — способность, делая глубо...
Осторожно, этот текст написан настоящим прокрастинатором. Желание написать статью на тему борьбы с прокрастинацией появилось у меня летом 2019 года, приступила к работе в ноябре и думала, что вып...