Справочник · CSS
Псевдоэлементы ::before и ::after
Псевдоэлемент — часть селектора после двух двоеточий, которая стилизует часть элемента или создаёт новую. ::before и ::after добавляют виртуальный блок в начало и конец содержимого элемента — без лишних тегов в HTML. Без свойства content они не появятся.
::beforeвставляет блок перед содержимым элемента,::after— после.- Без
contentпсевдоэлемент не создаётся; для пустого пишутcontent: "". - Подходят для декора: иконки, кавычки, затемнение, разделители.
- Не работают у
img,inputи других заменяемых элементов. - Два двоеточия — псевдоэлемент, одно — псевдокласс.
Минимальный пример
.external::after {
content: " ↗";
}
.note::before {
content: "Совет: ";
font-weight: 700;
color: #2a62d9;
}<a class="external" href="https://developer.mozilla.org/">MDN</a>
<p class="note">Проверяй селектор в DevTools.</p>У ссылки появилась стрелка после текста, у абзаца — жирное «Совет:» в начале. В HTML ничего не добавилось: эти части нарисованы стилями.
Свойство content
| Значение | Результат |
|---|---|
"" | пустой блок для фигур, линий, затемнений |
"текст" | строка; перенос — "\A" вместе с white-space: pre |
attr(data-label) | значение атрибута элемента |
url(icon.svg) | картинка |
counter(step) | значение CSS-счётчика |
open-quote, close-quote | кавычки из свойства quotes |
Если не указать content или написать content: none, псевдоэлемента не будет, и все остальные стили к нему не применятся. Это самая частая причина «::before не работает».
Псевдоэлемент — это строчный блок внутри элемента
Псевдоэлементы живут внутри элемента: ::before — первым ребёнком, ::after — последним. По умолчанию они строчные, поэтому width и height не работают, пока не задать display: block или inline-block, или не позиционировать их абсолютно.
.title::after {
content: "";
display: block;
width: 48px;
height: 3px;
margin-top: 8px;
background: currentColor;
}Затемнение карточки и растянутая ссылка
.card {
position: relative;
}
.card::after {
content: "";
position: absolute;
inset: 0;
background: linear-gradient(transparent 40%, rgb(0 0 0 / .65));
pointer-events: none; /* клики проходят сквозь */
}
/* вся карточка кликабельна, а в HTML одна ссылка */
.card a::before {
content: "";
position: absolute;
inset: 0;
}Абсолютный псевдоэлемент с inset: 0 растягивается на всю карточку, если у неё position: relative (подробнее — в уроке position). Второй приём делает кликабельной всю карточку, хотя в HTML одна ссылка в заголовке — удобно и для скринридера, и для пальца.
Частые приёмы
/* звёздочка у обязательного поля */
.label-required::after { content: " *"; color: #c93636; }
/* подпись из data-атрибута */
.tooltip::after { content: attr(data-hint); }
/* нумерация шагов CSS-счётчиком */
.steps { counter-reset: step; }
.steps li::before {
counter-increment: step;
content: counter(step) ". ";
font-weight: 700;
}
/* «ёлочки» у цитаты */
q { quotes: "«" "»"; }
q::before { content: open-quote; }
q::after { content: close-quote; }Псевдоэлементы хороши для повторяющегося оформления, которое не хочется копировать в HTML каждой карточки: значки, разделители, нумерация, служебные пометки.
Другие псевдоэлементы
| Псевдоэлемент | Что стилизует |
|---|---|
::first-letter | первую букву блока — буквица |
::first-line | первую строку текста |
::marker | маркер или номер пункта списка |
::placeholder | подсказку внутри поля ввода |
::selection | выделенный мышью текст |
::backdrop | фон под открытым dialog |
::file-selector-button | кнопку в input type="file" |
li::marker { color: #2a62d9; }
input::placeholder { color: #8a94a6; }
::selection { background: #ffe58a; }
dialog::backdrop { background: rgb(0 0 0 / .5); }Где ::before и ::after не работают
Псевдоэлементы вставляются внутрь содержимого элемента. У заменяемых элементов — img, input, video, iframe — своего содержимого нет, поэтому img::after не отрисуется. Если нужна подпись или значок у картинки, оберни её в figure или span и вешай псевдоэлемент на обёртку. То же с полями: звёздочку обязательного поля ставят на label.
Одно или два двоеточия
По современной записи псевдоэлементы пишут с двумя двоеточиями: ::before. Старые записи с одним — :before, :after, :first-letter, :first-line — браузеры понимают ради совместимости. В новом коде пиши два двоеточия, чтобы сразу отличать псевдоэлементы от псевдоклассов вроде :hover.
Псевдоэлемент или настоящий тег
Оформление без смысла: иконка, линия, затемнение, кавычки. Если стили отключить, ничего важного не пропадёт.
Содержание: текст, цена, ссылка, кнопка. Его нужно выделить, скопировать, найти поиском — это работает только с HTML.
Частые ошибки
- Забывают
content, и псевдоэлемент не появляется. - Пытаются повесить
::afterнаimgилиinput. - Задают width и height строчному псевдоэлементу без
display: blockили абсолютного позиционирования. - Кладут в content важный текст: его нельзя выделить, а поисковик может его не учесть.
- Затемнение
::afterперекрывает ссылки и кнопки — помогаетpointer-events: none.
Доступность
Скринридеры обычно читают текст из content, а значки вроде «↗» или «★» могут прозвучать странно. Для декоративного текста современные браузеры поддерживают альтернативную запись content: "★" / "";: после косой черты — текст для скринридера, пустая строка скрывает значок. Важную информацию в псевдоэлементы не выноси: её нельзя выделить и скопировать, и она пропадает без CSS.
Поддержка браузерами
::before, ::after, ::first-letter, ::first-line и ::selection работают во всех браузерах. ::marker, ::placeholder, ::backdrop и ::file-selector-button поддерживаются всеми актуальными браузерами. Альтернативный текст в content ("★" / "") появился позже — проверяй на Can I use. Старый браузер пропустит непонятное ему объявление, поэтому пиши два подряд: сначала content: "★";, затем content: "★" / ""; — каждый браузер возьмёт последнее, которое понимает.
Практика
Сделай три вещи без изменения HTML: стрелку «↗» после всех внешних ссылок (у которых href начинается с http), красную звёздочку после подписи обязательного поля и нумерацию «Шаг 1, Шаг 2…» у пунктов списка с классом steps.
Показать решение
a[href^="http"]::after { content: " ↗"; }
.label-required::after { content: " *"; color: #c93636; }
.steps { counter-reset: step; list-style: none; }
.steps li::before {
counter-increment: step;
content: "Шаг " counter(step) ": ";
font-weight: 700;
}Частые вопросы
Почему не работает ::before?
Чаще всего не задано свойство content. Без него псевдоэлемент не создаётся. Для пустого блока пиши content: "".
Чем ::before отличается от ::after?
Только местом: ::before вставляется первым ребёнком элемента, перед его содержимым, а ::after — последним, после содержимого.
Можно ли добавить ::after к картинке?
Нет. У img нет собственного содержимого, куда можно вставить псевдоэлемент. Оберни картинку в figure или span и стилизуй обёртку.
Читает ли скринридер текст из content?
Обычно да. Поэтому декоративные символы лучше скрыть записью content: "★" / "", а важный текст держать в HTML.
Писать :before или ::before?
В новом коде — ::before. Одно двоеточие браузеры понимают ради совместимости со старыми стилями.