С помощью CSS-стилей на сайте можно делать много полезного и красивого: выравнивать элементы, добавлять тени, плавные переходы и анимации, создавать элементы необычной формы — и всё это без изменения HTML-разметки. А ещё есть псевдоэлементы — это как бы стили для стилей, но без стилей. Если освоить их — всё, вы прошли всю вёрстку, ничего более сложного в CSS уже не будет. В общем, про это сегодня и поговорим.
Что такое псевдоэлементы в CSS
Псевдоэлементы в CSS — это способ видоизменять определённые части реальных HTML-элементов, не указывая стили в самой разметке. С помощью псевдоэлементов можно добавить что-то перед элементом, после него или изменить стиль первого символа или строки.
По сути, это дополнительный слой на странице. Когда браузер читает CSS-файл и доходит до псевдоэлемента, то вставляет фантомный элемент на страницу, как если бы увидел такое указание в HTML-файле. То есть псевдоэлемента как бы нет на самом деле, но браузер может с ним работать так, будто он есть.
С помощью псевдоэлементов можно добавлять разные декоративные штуки, не усложняя структуру документа. Забавы ради представим, что нам нужно добавить жирную синюю стрелку перед началом каждого абзаца в тексте. Если делать это стандартными средствами, то нам понадобится указать стиль стрелки в CSS:
/* задаём стиль стрелки */
.decorator {
/* синий цвет */
color: blue;
/* жирное начертание */
font-weight: bold;
}
А затем добавить символ стрелки в HTML-разметку к каждому абзацу:
<!-- добавляем стрелку с соответствующим стилем к абзацу -->
<p><span class="decorator">→</span> Это первый абзац с декоративной стрелкой перед ним.</p>
<!-- добавляем стрелку с соответствующим стилем к абзацу -->
<p><span class="decorator">→</span> Это второй абзац с декоративной стрелкой перед ним.</p>
Теперь сделаем то же самое с помощью псевдоэлемента в CSS:
/* добавляем элемент перед каждым абзацем */
p::before {
/* указываем элемент */
content: "→";
/* синий цвет */
color: blue;
/* жирное начертание */
font-weight: bold;
/* отступ после элемента */
margin-right: 5px;
}
Но в HTML нам ничего добавлять не нужно, потому что с псевдоэлементом стрелка и так появится перед каждым абзацем. В итоге разметка страницы стала чище, а вся магия происходит как бы сама по себе:
<p>Это первый абзац с декоративной стрелкой перед ним.</p>
<p>Это второй абзац с декоративной стрелкой перед ним.</p>
Синтаксис псевдоэлементов
В спецификации описаны четыре псевдоэлемента, которые используются в современной вёрстке:
::before
добавляет контент перед содержимым элемента.::after
добавляет контент после содержимого элемента.::first-line
применяет стили к первой строке текста в элементе.::first-letter
применяет стили к первой букве текста в элементе.
Псевдоэлемент задаётся через двойное двоеточие после селектора:
.div::pseudoelement {
/* стили */
}
Элементы ::before
и ::after
задаются через свойство content
. При этом достаточно даже пустой строки в значении свойства — content: ""
.
.div::after {
content: ""
}
👉 По умолчанию псевдоэлементы ::before
и ::after
— inline-элементы, как <span>
и <a>
, то есть их содержимое отображается на одной строке с остальным текстом. Если нужно, чтобы ::before
и ::after
вели себя как блочные элементы, можно задать свойство display: block;
. Тогда контент этих псевдоэлементов будет начинаться с новой строки и занимать всю ширину.
Для чего нужны псевдоэлементы
Псевдоэлементы ::before
и ::after
часто используются для добавления декоративных элементов без изменения HTML-кода.
Например, с помощью ::before
легко сделать свои маркеры в списках. Для этого нужно отключить стандартное форматирование маркеров и добавить стиль для псевдоэлемента. Добавим свой маркер в виде эмодзи списку small-list
:
.small-list {
/* отключаем стандартные маркеры */
list-style-type: none;
padding: 0;
}
.small-list li {
/* позиционируем относительно элементов списка */
position: relative;
}
.small-list li::before {
/* добавляем содержимое псевдоэлемента */
content: '🤯';
/* позиционируем псевдоэлемент */
position: absolute;
/* размещаем эмодзи слева от текста, выравнивая с его началом */
left: -1.5em;
/* выравниваем по верху элемента списка */
top: 0;
}
Псевдоэлементы ::first-line
и ::first-letter
позволяют стилизовать первую строку или первую букву параграфа. В CSS-правилах нужно использовать только свойства, относящиеся к шрифту, изменению цвета текста и фона.
Для примера стилизуем первую букву в заголовке статьи в «Коде». Обычно заголовки наших статей выглядят как-то так:
Теперь мы добавим к селектору заголовка псевдоэлемент ::first-letter
и пропишем его свойства:
/* стилизуем первую букву */
.post__title::first-letter {
/* размер шрифта */
font-size: 66px;
/* жирное начертание */
font-weight: bold;
/* цвет */
color: #ff6347;
/* отступ справа */
padding-right: 2px;
/* высота строки */
line-height: 1;
/* семейство шрифтов */
font-family: "Courier New", Courier, monospace;
}
Вот что получилось. Красота же:
Аналогично можно добавить стили первой строке текста с помощью ::first-line
. Псевдоэлемент будет действовать на строку так, как она отображается в браузере, и изменяться в зависимости от ширины контейнера и размеров шрифта:
/* стилизуем первую строку */
.post__title::first-line {
/* размер шрифта */
font-size: 66px;
/* жирное начертание */
font-weight: bold;
/* цвет */
color: #ff6347;
/* семейство шрифтов */
font-family: "Courier New", Courier, monospace;
/* сделать заглавными */
text-transform: uppercase;
}
Выглядит уже слишком агрессивно, поэтому применяйте с умом:
Это базовые варианты использования псевдоэлементов. Но на самом деле с ними можно делать много других интересных штук: рисовать фигуры, добавлять сложные украшения, играться со шрифтами, делать анимации и интересные ховер-эффекты — и всё это без изменения основного кода и структуры документа.
Например, мы уже писали о том, как сделать у текста глитч-эффект. И вся суть глитч-эффекта там реализована именно через псевдоэлементы.
Рисуем фигуры, используя псевдоэлементы
С псевдоэлементами можно рисовать разные фигуры: сердечки, звёзды, кристаллы, изогнутые стрелки или что-то посложнее.
Для примера нарисуем в CSS сердечко. Фишка в том, что мы просто создаём обычный квадрат, переворачиваем его, а недостающие детали сердца рисуем уже с помощью псевдоэлементов:
Делаем контейнер для фигуры:
<div class="heart"></div>
Задаём стили:
/* стиль основной части сердца */
.heart {
/* относительное позиционирование */
position: relative;
/* ширина */
width: 100px;
/* высота */
height: 100px;
/* цвет фона */
background-color: red;
/* поворот на 45° */
transform: rotate(-45deg);
/* отступ */
margin: 50px;
}
/* псевдоэлементы, создающие верхние части сердца */
.heart::before,
.heart::after {
content: "";
/* позиционируем относительно родительского элемента */
position: absolute;
/* ширина круга, равная ширине квадрата */
width: 100px;
/* высота круга, равная высоте квадрата */
height: 100px;
/* цвет фона */
background-color: red;
/* создаём круги из псевдоэлементов */
border-radius: 50%; }
/* позиционируем левый верхний круг */
.heart::before {
/* перемещаем круг вверх на 50 пикселей, выравнивая его с верхним краем сердца */
top: -50px;
/* устанавливаем круг на левую границу родительского элемента */
left: 0;
}
/* позиционируем правый верхний круг */
.heart::after {
/* оставляем круг на уровне верхнего края родительского элемента */
top: 0;
/* перемещаем круг вправо на 50 пикселей, выравнивая его с правым краем сердца */
left: 50px;
}
Вот что получилось:
Если мы проверим наш контейнер heart
в web-инспекторе браузера, то увидим, что сердце как будто состоит из трёх элементов (что, по сути, так и есть):
Анимации с псевдоэлементами
Теперь добавим кнопке красивую анимацию, которая будет запускаться при наведении курсора. Для этого нам понадобится градиентный слой в псевдоэлементе ::after
, который при наведении курсора будет сдвигаться и вращаться.
Сделаем основной стиль для кнопки:
button {
/* цвет фона */
background: #ff32d3;
/* цвет кнопки */
color: #fff;
/* размер шрифта */
font-size: 14px;
/* скругление углов */
border-radius: 0.5em;
/* без рамки */
border: none;
/* отступы */
padding: 0 1em;
/* относительное позиционирование */
position: relative;
/* скрываем всё, что выходит за границы */
overflow: hidden;
/* высота строки */
line-height: 32px;
}
Теперь добавим псевдоэлемент, где нарисуем градиент:
button::after {
content: "";
position: absolute;
top: -50%;
right: -50%;
bottom: -50%;
left: -50%;
background: linear-gradient(
/* направление градиента сверху вниз */
to bottom,
/* прозрачный цвет в начале градиента */
rgba(229, 172, 142, 0),
/* белый цвет с прозрачностью 0,5 в середине градиента */
rgba(255, 255, 255, 0.5) 50%,
/* прозрачный цвет в конце градиента */
rgba(229, 172, 142, 0)
);
/* вращаем и сдвигаем псевдоэлемент */
transform: rotateZ(60deg) translate(-5em, 7.5em);
}
button:hover::after,
button:focus::after {
/* применяем анимацию при наведении или фокусе */
animation: sheen 1s forwards;
}
Создадим анимацию, где будет изменяться положение псевдоэлемента:
@keyframes sheen {
100% {
transform: rotateZ(60deg) translate(1em, -9em);
}
}
Вот что получилось:
Рамка с градиентом
Ну и напоследок добавим к контейнеру рамку с градиентом. Через псевдоэлемент ::before
мы создаём отдельный градиентный слой и помещаем его ниже того слоя, которому хотим добавить рамку.
На странице журнала «Код» есть контейнер со статьёй post.post--wide
. Добавим ему псевдоэлемент в виде рамки:
div.post.post--wide::before {
content: "";
position: absolute;
/* сдвигаем верхнюю границу выше на 10 пикселей */
top: -10px;
/* сдвигаем левую границу на 10 пикселей */
left: -10px;
/* сдвигаем нижнюю границу на 10 пикселей */
bottom: -10px;
/* сдвигаем правую границу на 10 пикселей */
right: -10px;
/* задаём линейный градиент фона, переходящий от тёмного к синему цвету */
background-image: linear-gradient(#1a1a1a, #1560bd);
/* перемещаем псевдоэлемент на задний план, чтобы он находился под содержимым элемента .post.post--wide */
z-index: -1;
border-radius: 90px;
}
Вот что получилось в результате: