Псевдоклассы задают стили элементу в определённом состоянии: при наведении курсора, в фокусе, при нажатии, или по позиции в DOM. Позволяют реагировать на действия пользователя без JavaScript.

- Псевдоклассы взаимодействия
- Структурные псевдоклассы
- Псевдоклассы форм
- :has() — родительский псевдокласс
- Практические паттерны
- Полная таблица псевдоклассов
- Часто задаваемые вопросы о псевдоклассах
- :nth-child и :nth-of-type — в чём разница?
- Как стилизовать placeholder в input?
- Как сделать hover только на устройствах с мышью?
Псевдоклассы взаимодействия
/* :hover — при наведении мыши */
a:hover { color: #2563eb; text-decoration: underline; }
button:hover { background: #1d4ed8; transform: translateY(-2px); }
.card:hover { box-shadow: 0 8px 24px rgba(0,0,0,0.15); }
/* :active — в момент нажатия (пока удерживается клик) */
button:active { transform: scale(0.97); }
a:active { color: #1d4ed8; }
.btn:active { box-shadow: none; }
/* :focus — когда элемент в фокусе (клик или Tab) */
input:focus {
outline: none;
border-color: #2563eb;
box-shadow: 0 0 0 3px rgba(37,99,235,0.15);
}
a:focus { outline: 2px solid #2563eb; outline-offset: 2px; }
/* :focus-visible — фокус ТОЛЬКО при клавиатурной навигации */
/* Не срабатывает при клике мышью — более аккуратно чем :focus */
button:focus-visible {
outline: 2px solid #2563eb;
outline-offset: 3px;
}
/* :focus-within — если фокус у любого потомка */
.form-group:focus-within label {
color: #2563eb; /* метка синеет когда input в фокусе */
}
/* :visited — посещённые ссылки */
a:visited { color: #7c3aed; }
/* :link — не посещённые ссылки */
a:link { color: #2563eb; }
/* Правильный порядок для ссылок (LVHA) */
a:link { color: blue; }
a:visited { color: purple; }
a:hover { color: red; }
a:active { color: darkred; }

Структурные псевдоклассы
/* :first-child — первый ребёнок родителя */
li:first-child { font-weight: bold; }
p:first-child { font-size: 1.1rem; }
/* :last-child — последний ребёнок */
li:last-child { border-bottom: none; margin-bottom: 0; }
td:last-child { text-align: right; }
/* :nth-child(n) — по порядковому номеру */
li:nth-child(1) { color: gold; } /* первый */
li:nth-child(2) { color: silver; } /* второй */
tr:nth-child(even) { background: #f9fafb; } /* чётные — зебра */
tr:nth-child(odd) { background: white; } /* нечётные */
li:nth-child(3n) { /* каждый третий: 3, 6, 9... */ }
li:nth-child(3n+1) { /* 1, 4, 7, 10... */ }
li:nth-child(-n+3) { /* первые три */ }
/* :nth-last-child(n) — считает с конца */
li:nth-last-child(1) { /* последний */ }
li:nth-last-child(2) { /* предпоследний */ }
/* :only-child — единственный ребёнок */
p:only-child { font-size: 1.2em; text-align: center; }
/* :nth-of-type — по порядку среди одинаковых тегов */
p:nth-of-type(1) { font-size: 1.15rem; font-weight: 500; }
img:nth-of-type(2) { float: right; }
/* :first-of-type, :last-of-type */
h2:first-of-type { margin-top: 0; }
img:last-of-type { margin-bottom: 0; }
/* :not() — исключение (отрицание) */
li:not(:last-child) { border-bottom: 1px solid #e5e7eb; }
button:not([disabled]) { cursor: pointer; }
p:not(.lead):not(.note) { color: #4b5563; }
input:not([type="submit"]) { border: 1px solid #d1d5db; }

Псевдоклассы форм
/* :checked — выбран чекбокс или радио */
input[type="checkbox"]:checked + label {
color: #2563eb;
font-weight: 600;
}
/* Кастомный чекбокс */
.custom-checkbox input:checked + span::before {
background: #2563eb;
content: '✓';
color: white;
}
/* :disabled — отключённый элемент */
button:disabled {
opacity: 0.5;
cursor: not-allowed;
pointer-events: none;
}
input:disabled {
background: #f3f4f6;
color: #9ca3af;
}
/* :enabled — активный (противоположность disabled) */
input:enabled:hover { border-color: #93c5fd; }
/* :valid / :invalid — встроенная валидация */
input:valid { border-color: #16a34a; }
input:invalid { border-color: #dc2626; }
/* Показывать ошибку только после взаимодействия */
input:invalid:not(:placeholder-shown) {
border-color: #dc2626;
}
/* :required — обязательное поле */
input:required {
border-left: 3px solid #dc2626;
}
label:has(+ input:required)::after {
content: ' *';
color: #dc2626;
}
/* :optional — необязательное поле */
input:optional { border-left: 3px solid #e5e7eb; }
/* :placeholder-shown — placeholder ещё виден (поле пустое) */
input:placeholder-shown { background: #fafafa; }
input:not(:placeholder-shown) { background: white; }
/* :empty — элемент без содержимого */
.error-message:empty { display: none; }
.badge:empty { display: none; }

:has() — родительский псевдокласс
:has() — самый мощный псевдокласс, добавленный в CSS. Позволяет стилизовать элемент на основе его потомков. Работает во всех современных браузерах (Chrome 105+, Firefox 121+, Safari 15.4+).
/* Карточка с картинкой — убрать внутренние отступы */
.card:has(img) { padding: 0; }
.card:has(img) .card-body { padding: 16px; }
/* Форма с невалидными полями — кнопку затемнить */
form:has(input:invalid) button[type="submit"] {
opacity: 0.5;
pointer-events: none;
}
/* Параграф после заголовка */
h2:has(+ p) { margin-bottom: 4px; }
/* Список без элементов — скрыть */
ul:not(:has(li)) { display: none; }
/* Навигация с активным пунктом */
nav:has(.nav-item.active) { background: #1e293b; }
/* Метка рядом с обязательным полем */
label:has(+ input:required) {
font-weight: 600;
}
label:has(+ input:required)::after {
content: ' *';
color: red;
}
/* Раздел с тёмным фоном — сделать текст белым */
section:has(.dark-bg) { color: white; }

Практические паттерны
/* 1. Полосатая таблица */
table tbody tr:nth-child(even) {
background: #f9fafb;
}
table tbody tr:hover {
background: #eff6ff;
cursor: pointer;
}
/* 2. Навигация — разделитель между пунктами */
.nav-item:not(:last-child) {
border-right: 1px solid rgba(255,255,255,0.2);
}
/* 3. Аккордеон на чистом CSS */
.accordion-input { display: none; }
.accordion-input:checked + .accordion-label {
background: #eff6ff;
color: #2563eb;
}
.accordion-input:checked ~ .accordion-content {
display: block;
}
.accordion-content { display: none; }
/* 4. Красивый focus-ring */
:focus-visible {
outline: 2px solid #2563eb;
outline-offset: 2px;
border-radius: 4px;
}
/* Убирает outline для мышиных кликов, сохраняет для клавиатуры */
/* 5. Стилизация placeholder */
input::placeholder { color: #9ca3af; font-style: italic; }
input::-webkit-input-placeholder { color: #9ca3af; }
/* 6. Первый параграф статьи */
article p:first-of-type {
font-size: 1.1rem;
color: #374151;
line-height: 1.7;
}

Полная таблица псевдоклассов
| Псевдокласс | Описание |
|---|---|
:hover | При наведении мыши |
:active | В момент нажатия |
:focus | В фокусе |
:focus-visible | В фокусе от клавиатуры |
:focus-within | Фокус у любого потомка |
:visited | Посещённая ссылка |
:link | Непосещённая ссылка |
:checked | Выбран (чекбокс/радио) |
:disabled | Отключён |
:enabled | Активен |
:valid | Значение прошло валидацию |
:invalid | Значение не прошло |
:required | Обязательное поле |
:optional | Необязательное поле |
:placeholder-shown | Поле пустое (виден placeholder) |
:empty | Нет содержимого |
:first-child | Первый ребёнок |
:last-child | Последний ребёнок |
:nth-child(n) | N-й ребёнок |
:nth-last-child(n) | N-й с конца |
:only-child | Единственный ребёнок |
:first-of-type | Первый такого тега |
:last-of-type | Последний такого тега |
:nth-of-type(n) | N-й такого тега |
:not(selector) | Не соответствует |
:is(selector) | Группировка |
:where(selector) | Группировка (нулевая специфичность) |
:has(selector) | Содержит потомка |

Часто задаваемые вопросы о псевдоклассах
:nth-child и :nth-of-type — в чём разница?
:nth-child считает всех детей родителя подряд. :nth-of-type считает только детей данного типа тега. Пример: p:nth-child(2) применится к <p> только если он второй ребёнок родителя (не важно какого типа первый). p:nth-of-type(2) применится ко второму <p> среди всех <p> внутри родителя — независимо от других тегов.

Как стилизовать placeholder в input?
Используй псевдоэлемент ::placeholder (два двоеточия): input::placeholder { color: #9ca3af; }. Для максимальной совместимости можно добавить вендорный префикс: ::-webkit-input-placeholder. Обрати внимание: не все стили работают с placeholder — поддерживаются color, font-size, font-style, opacity, но не background или padding.
Как сделать hover только на устройствах с мышью?
Используй медиазапрос @media (hover: hover) — он проверяет есть ли у устройства указатель с hover (мышь). На тачскрине hover нет, поэтому там :hover срабатывает при тапе и не убирается — это плохой UX. Правильный подход:
/* Только для устройств с мышью */
@media (hover: hover) and (pointer: fine) {
.card:hover {
transform: translateY(-4px);
box-shadow: 0 8px 24px rgba(0,0,0,0.15);
}
}




