company_banner

Странности CSS, о которых полезно знать

Original author: Peedu Tuisk
  • Translation
В наших публикациях регулярно появляются статьи о CSS. Среди них — материал об истории CSS, рассказ о подборе имён для CSS-сущностей, статья о CSS-стилях для печати, о которых многие забывают. Мы писали о том, как работают CSS-селекторы, сравнивая происходящее с автосалоном, о сравнительно новой технологии CSS Grid Layout, и о том, что CSS — это не чёрная магия. Сегодня предлагаем вашему вниманию перевод материала, который посвящён странностям CSS, о которых, как полагает автор этого материала, мало кто знает.

image

Вертикальные поля


Что станет с элементом, если назначить ему свойство padding-top: 50%? Интуитивно понятно, что подобное свойство устанавливает размер поля от верхнего края содержимого элемента, размер которого составляет 50%… От чего берутся эти 50%? Собственно говоря, в определённый момент интуиция при разборе особенностей этого свойства оказывается бесполезной. Дело в том, что эти вот 50% берутся от ширины родительского элемента того элемента, которому назначают верхнее поле.

Вот пример, подготовленный средствами CodePen. Такие примеры вы найдёте и во многих других разделах этого материала.

Вышесказанное справедливо и для нижнего поля, задаваемого свойством padding-bottom. Знание этой особенности, в частности, позволяет создавать отзывчивые элементы, сохраняющие соотношение сторон:

.square {
  width: 100%;
  height: 0;
  padding-bottom: 100%;
}

О непостоянном схлопывании отступов


Расстояние между следующими элементами будет 20px, а не 40px:

<div style="margin-bottom:20px">foo</div>
<div style="margin-top:20px">foo</div>

Пример

Однако так бывает не всегда. Отступы не схлопываются при работе со следующими элементами:

  • Плавающие элементы.
  • Абсолютно позиционированные элементы.
  • Строчно-блочные (inline-block) элементы.
  • Элементы с параметром overflow, установленным в любое значение кроме visible (они не схлопывают отступы со своими элементами-потомками).
  • Элементы, к которым применено правило clear (их верхние отступы не схлопываются с нижними отступами их родительских блоков).
  • Корневой элемент дерева документа.

Уровень прозрачности и порядок наложения элементов


Предположим, что имеются три элемента <div>, каждый из них позиционирован абсолютно. Они содержат другие элементы, которым назначено свойство z-index со значениями от 1 до 3. Каждый следующий такой элемент выводится поверх предыдущего. Если теперь назначить z-index: 10 самому нижнему элементу, он будет выведен поверх двух других, порядок расположения которых не изменится. Пока всё выглядит так, как и ожидается. Если теперь назначить первому элементу <div>, тому, который теперь находится выше других, свойство opacity: 0.99, то он окажется под двумя другими.

Пример

Подробности о том, почему это происходит, можно найти здесь.

Кастомные свойства и переменные CSS


Используя SASS или LESS, можно решить, что кастомные свойства и переменные CSS эквивалентны возможностям, доступным в этих препроцессорах. Однако, тут имеются несколько отличий, на которые стоит обратить внимание.

Сначала рассмотрим основы:

// задавать и использовать кастомные свойства можно так
:root {
 --foo: #000;
}
button {
  background-color: var(--foo); //чёрный фон
}

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

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

.foo {
  color: var(—-my-var, 'blue');
}

Это приводит нас к основной разнице с препроцессорами: переменные CSS знают о структуре DOM и об изменениях, которые там происходят.

::root {
  --default-color: #000000;
}
header {
 --primary-color: #ff0000;
}
a {
 color: var(--primary-color, --default-color);
}
<a href="">this is black</a>
<header>
  <a href="">this is red.</a>
</header>

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

Пример

Более того, их легко можно менять, используя средства JavaScript:

// получить переменную из inline-стиля
element.style.getPropertyValue("--my-var");
// установить переменную в inline-стиле
element.style.setProperty("--my-var", jsVar + 4);

Эта возможность поддерживается начиная с Edge 15.

Конструкция vertical align: top | middle | bottom


Конструкция vertical align: top | middle | bottom работает только для inline-элементов (в том числе и для inline-block) и элементов table-cell. Этот способ не подходит для выравнивания элементов внутри их родительских элементов. Для этого нужно использовать средства flexbox-вёрстки, или то, что известно как «douchebag vertical align» (ниже мы об этом поговорим).

Свойство height: 100% может не давать ожидаемого эффекта


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

<html>
<body>
  <div style="height:100%;background:red;"></div>
</body>
</html>

Показанный здесь элемент <div> не закрасит всю страницу в красный цвет. Для того чтобы этого добиться, нужно установить свойство height в 100% и для элемента <body>, и для элемента <html>.

Стили идентификаторов и стили классов


Стили идентификаторов переопределяют стили, заданные на уровне классов. Происходит это из-за того, что id-селекторы точнее, чем селекторы классов. Так, например, правило, заданное для .foo.bar переопределит правила, заданные отдельно для .foo и для .bar.

#foo { color: red; } 
.bar { color: green; } 
<h1 id="foo" class="bar">this will be red not green</h1>

Выбор элементов с определённым атрибутом


Средствами CSS можно выбирать элементы на основе конкретных атрибутов и их содержимого. Например, это может быть содержимое атрибутов src или href.

// выбираем все ссылки на zip-файлы (нечувствительно к регистру)
a[href$=".zip" i] { }
// сделаем красными ссылки на google.com
[href*="google.com"] { color: red; }

Этот приём может оказаться полезным при отладке, например, для выделения всех элементов img с пустым атрибутом alt:

img:not([alt]) {
 border: 2px dashed red;
}
img[alt=""] {
 border: 2px dashed red;
}

Если вы используете Angular, этот подход, кроме того, может быть полезен для выбора элементов, которые содержат [ng-click]. Или, если надо, так можно визуально разделить ссылки на локальные ресурсы, и ссылки, которые начинаются с http или https.

Пример

О порядке следования свойств при указании значений параметров по горизонтали и вертикали


Когда задают некие значения, имеющие отношения к горизонтальной и вертикальной осям, первое число обычно задаёт вертикальное значение, а второе — горизонтальное. Например, в выражении padding: 10px 20px; 10px — это верхнее и нижнее поле, 20px — это правое и левое поле. Именно так это выглядит при настройке полей, отступов, границ, в целом — это так практически для всего, за исключением свойства border-spacing в таблицах, где значения расположены с точностью до наоборот: первое число устанавливает значение по горизонтали, второе — по вертикали.

Несколько фоновых изображений для одного элемента


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

background: url(example1.png’) no-repeat center 50px, url(‘example2.jpg’) no-repeat bottom top;

Эта возможность поддерживается начиная с IE11.

Наложение CSS-анимаций


Так же, как и при работе с фонами, можно накладывать друг на друга CSS-анимации:

@keyframes foo { 
 0% { opacity: 0; } 
 100% { opacity: 1; } 
}
@keyframes bar { 
 0% { transform: translateX(-100px); } 
 100% { transform: translateX(0px); } 
}
.element {
 animation: foo 2s 0s, bar 1s 0s;
}

О странном поведении position: fixed при использовании трансформации translateZ


Добавление трансформации translateZ(0); к контейнеру, который включает в себя элемент со свойством position: fixed; приводит к выравниванию элемента относительно контейнера, а не относительно окна.

Пример

Стилизация элементов, на которые осуществляется переход по адресам, содержащим символ решётки (/#foo)


Псевдо-класс :target можно использовать для стилизации элементов, на которые осуществляется переход при щелчке по ссылке с символом решётки. Так, например, щелчок по ссылке вида <a href="#foo">Go to Foo</a> прокрутит страницу до элемента <div id="foo">foo</div>. Теперь, если вы включили в CSS правило вида #foo:target { color: red; }, элемент <div> #foo будет окрашен в красный цвет.

Выделение таких элементов может оказаться полезным для тех посетителей сайта, которые попали на страницу по внешней ссылке вроде www.example.com/#foo. При таком подходе браузер прокрутит страницу к нужному элементу и этот элемент окажется выделенным. В наши дни так поступают редко, но этот приём способен улучшить впечатления пользователей от работы с сайтом.

Пример

Малоизвестные возможности content: ‘foo’;


▍Атрибуты данных


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

<div data-text="foo"></div>
div:before {
 content: attr(data-text);
}

Пример

Этот приём может оказаться полезным, если, например, нужно перевести текст псевдо-класса (скажем, используя его для всплывающих подсказок). Сейчас с помощью attr() можно работать лишь с контентом. Хотя не исключено появление поддержки этой конструкцией других свойств. Более того, значения, получаемые из attr() — это строки, поэтому они предназначены, в первую очередь, для контента и не могут использоваться для свойств, задающих размеры (например — font-size), или для ссылок (например, content: url()). Кстати, поговорим об этом.

▍Content: url()


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

<div></div>
div:before {
 content: url(image.jpg);
}

Пример

Однако если из DOM в CSS надо передавать произвольные данные, придётся обратиться к вышеописанным кастомным свойствам:

<div style="--background-image: url('http://via.placeholder.com/150x150');"></div>
div:after {
 content: '';
 background-image: var(--background-image);
}

▍Инкрементальный счётчик


Конструкцию content: counter() можно использовать для инкрементной нумерации псевдо-элементов:

p {
 counter-increment: myIndex;
}
p:before {
 content:counter(myIndex);
}
<p>foo</p>
<p>bar</p>

Пример

▍Открывающие и закрывающие кавычки


Свойство content псевдо классов вроде :before и :after может быть использовано для добавления к элементам открывающих и закрывающих кавычек:

q:before {
 content: open-quote;
}
q:after {
 content: close-quote;
}

Если говорить о кавычках, то скомбинировав этот приём с ранее упомянутым выбором атрибутов данных, можно даже использовать CSS для задания специфического локализованного стиля кавычек, основанного на языке сайта, с применением одного лишь свойства quotes:

html[lang="fr"] q {
 Quotes: "«" "»";
}

Использование свойства font


Свойство font позволяет, в сокращённом формате, задавать параметры шрифтов:

h1 {
 font-weight: bold;
 font-style: italic;
 font-size: 1rem;
 //etc…
}
// комбинированный вариант
h1 {
 font: italic lighter 1rem/150% Verdana, Helvetica, sans-serif;
}
// синтаксис
// font: font-style font-variant font-weight font-size/line-height font-family;

Директива supports


Директиву @supports можно использовать для проверки того, поддерживает ли браузер возможности, интересующие разработчика. Скажем, если display:flex планируется использовать только в том случае, если есть уверенность в поддержке этой возможности браузером, можно применить следующую конструкцию:

@supports (display: flex) {
 div {
   display: flex;
 }
}

Двоеточия в именах классов


Использование двоеточий в именах классов может оказаться полезным для создания более понятных имён, которые, при чтении, легче делить на части. Так, например, некоторые CSS UI-фреймворки (вроде Tailwind) используют следующие соглашения по именованию:

<div class="justify-start sm:justify-center md:justify-end lg:justify-between xl:justify-around">
<button class="bg-blue hover:bg-blue-dark text-white hover:text-blue-light">Button</button>

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

Для использования двоеточий в CSS их нужно экранировать:

.sm\:justify-center { }

Трёхэлементный селектор


Все, кто читают этот материал, должны знать о CSS-селекторе, состоящем из трёх элементов, который обычно называют «lobotomized owl selector». Вот как он выглядит:

* + * {
 margin-top: 2rem;
}

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

li + li {
 margin-top: 1rem;
}
// вместо
li {
 margin-bottom: 1rem;
}
li:last-of-type {
  margin-bottom: 0;
}

Вертикальное выравнивание методом «douchebag vertical align»


Раз уж мы заговорили о необычных селекторах — вспомним и о методике вертикального выравнивания, называемой «douchebag vertical align»:

.element {
 position: relative;
 top: 50%;
 transform: translateY(-50%);
}

Свойство font-feature-settings для шрифтов OpenType


Шрифты OpenType поддерживают настройку свойств. Эту особенность можно использовать для подгонки шрифта под нужды конкретного проекта благодаря свойству font-feature-settings.

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

font-feature-settings: "tnum";
font-variant-numeric: tabular-nums;

Обрезка текста с добавлением окончания в виде многоточия, "…"


Тут всё предельно просто:

p {
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

Пример

Итоги


Мы рассмотрели некоторые малоизвестные возможности CSS, которые, надеемся, пригодятся веб-разработчикам. Кстати, вот ещё одна интересная штука, не относящаяся, правда, к CSS. Это — HTML-элемент wbr, который позволяет отмечать места разрывов слов.

Уважаемые читатели! Знаете ли вы о каких-нибудь возможностях CSS, полезных, но не получивших широкой известности?

RUVDS.com
RUVDS – хостинг VDS/VPS серверов

Comments 13

    –2
    Наборчик — так себе: рассчитан на начинающе-средних верстальщиков, чтобы восхитить и усилить интерес к профессии. Много свойств, которые уже давно знают и немного намешано новых правил (font-feature-settings, Двоеточия в именах классов (тут не знаю, насколько ново, но в самом деле не встречается в обиходе)), counter-increment). И есть, наконец, хаки типа translateZ и прозрачности с absolute.

    Совет: про целевую аудиторию переводчикам хорошо было бы писать 2 строчки в начале: «Для начинающих, но кое-что редкое есть во второй половине». Это не составит трудов, но сразу сориентирует.
      +4
      Нормальный наборчик. Есть действительно неочевидные и малоизвестные вещи. А сказать что-то принципиально новое по теме CSS уже практически невозможно.
        0

        Неочевидный момент — чтобы элементы во флексбоксе не выпирали за пределы контейнера своим содержимым, нужно этому контейнеру задать overflow:hidden.

          0
          По-моему, это вполне очевидно. Для этого это правило и существует.
      • UFO just landed and posted this here
          +1

          Типичная ошибка интерполировать "мне известно" на "всем известно".

          0
          Да почему не возможно — это всего лишь вопрос времени, а время как известно — относительно. Да и есть ли оно вообще — еще разобраться надо
            +1
            Ну обычно подобные статьи действительно страдают тем, что трудно найти необходимую аудиторию — кому-то большинство пунктов известно, кому-то нет…
            Хотя однозначно польза в подобных статьях есть — например, я не знал раньше о «lobotomized owl selector» (точнее, не задумывался, что так удобнее) — а ведь действительно, на одну директиву меньше. Думаю, другие разработчики тоже найдут один-два пункты, о которых не знали — уже польза!

            Для меня, кстати, довольно неочевидным было то, что для inline-block указывать вертикальное позиционирование нужно для собственно тех элементов, которые их используют, хотя по логике казалось, что это нужно делать для родительского контейнера.
              +1

              Можно еще так написать:
              li:not(:last-child)
              Но решение и правда интересное.

                0

                Интересное, но не очевидное, навроде ~str.indexOf("foo") или foo >>> 0 в JS. Всё же лучше писать явно, li:not(:last-child).

              0
              А я вот про *+* впервые узнал, так что польза от таких статей всегда есть. Кто-то начинающий загуглит и найдет что ему надо.
              В статью я бы добавил еще где поддерживается, а где нет.
                0
                О странном поведении position: fixed при использовании трансформации translateZ
                При любой трансформации будет фиксед вести себя как абсолют.

                Вертикальное выравнивание методом «douchebag vertical align»
                К слову о неизвестной высоте, при релетиве, top: 50%, не сработает

                  0
                  Мне нравятся подобного рода статьи, какая-то новая инфа да проскочит.
                  Так что автору спасибо.

                  (надо бы сюда еще про белый полупрозрачный бордер, на белом фоне написать)

                  Only users with full accounts can post comments. Log in, please.