
Привет, Хабр!
Сегодня фронтендеры любят подход атомарных классов, в частности Tailwind. Я не имею ничего против этого подхода. Каждый волен выбирать сам, с чем ему работать.
Но если вы пишете стили по-старому с использованием семантических классов, то у меня есть несколько интересных трюков для вас. Ими пришёл поделиться с вами сегодня.
Мои трюки улучшают пользовательский опыт, помогают организовать стили на контентных проектах и многое другое. Надеюсь, вам будет интересно!
Давайте посмотрим, что я вам подготовил.
Стилизация элементов контента с помощью псевдо-класса :not()
В проектах, ориентированных на текстовый контент, постоянная задача у фронтендеров — разделить стили так, чтобы не было конфликтов. По сути, появляется две большие группы. В первой находятся стили, которые должны применяться к HTML-элементам, добавленным контент-менеджером, а во второй — уже все остальные.
Мне лично нравится подход с использованием псевдо-класса :not(). Он позволяет не добавлять лишние классы, а главное — не думать над их названием. Не люблю я много думать!
Суть метода заключается в использовании атрибута class. Я добавляю его к каждому элементу интерфейса, но кроме тех, которые добавляются через редактор. Это позволяет мне стилизовать заголовки, ссылки, параграфы и другие элементы с помощью псевдо-класса :not().
Для демонстрации рассмотрим стилизацию элемента a, который используется в навигации и в контенте.
<body> <nav class="nav"> <ul class="nav__list"> <li class="nav__group"><a href="#0" class="nav__link">Главная</a></li> <li class="nav__group"><a href="#0" class="nav__link">Обо мне</a></li> </ul> </nav> <p>Этот текст нужен для демонстрации стилизации <a href="#0">ссылок в тексте</a>.</p> </body>
a:not([class]) { color: tomato; } .nav__link { text-decoration: none; font-size: 1.25rem; color: #222; }

Как вы видите, для ссылки с классом .nav__link не добавляется свойство color со значением var(--typography-link-color). В свою очередь, к ссылке в тексте не применилась группа стилей из класса .nav__point. Нет никаких конфликтов!
Таким способом я описываю стили для каждого элемента, который может быть добавлен через редактор. На самом деле у меня заготовленный набор, который переходит из проекта в проект.
Как расширить кликабельную область у элементов при помощи псевдо-элементов ::before и ::after
Взаимодействие с интерактивными элементами — это база в пользовательском опыте. Множество специалистов уделяют этому аспекту очень много времени, в частности размеру области, по которой нужно кликнуть. Я её назову кликабельной.
Обычно она совпадает с фактическими размерами элемента. Но разработчики могут её увеличить, используя CSS. Например, с помощью псевдо-элементов ::before и ::after.
Если добавить их для элементов, у которых установлено событие клика, то оно будет срабатывать на этих псевдо-элементах тоже. Это удобно, когда они находятся за пределами самого элемента.
Давайте применим этот трюк в реальном интерфейсе. Я нашёл пример с кнопками переключения слайдов.

Фактические размеры кнопки «Вперёд» — 16 на 16 пикселей. По этой причине пользователям будет достаточно сложно попасть на неё. А если у человека тремор руки, то перед ним будет настоящее испытание.
Давайте увеличим кликабельную область до 32 на 32 пикселей.

.icon_recommendations-arrow { position: relative; isolation: isolate; } .icon_recommendations-arrow::before { content: ""; position: absolute; inset: -8px; z-index: -1; }
Вот теперь пользователю будет проще попасть на элемент.
Отдельно отмечу, что разработчики используют элемент div, а не элемент button. Пожалуйста, не надо так делать.
Я специально подобрал такой пример, чтобы показать, что псевдо-элементы ::before и ::after сработают, даже если элемент неинтерактивный и на нём установлено JavaScript-событие.
CSS-слои помогают использовать селекторы по типу без проблем со специфичностью
Я душный человек, если речь касается CSS. Примером моего занудства — это случай вёрстки кнопок. У меня класс .button, который сразу применялся к элементам a и button.
Ну, обычный набор стилей. А мне не нравилось, что свойство со значением none применялось к элементам button. Как такое возможно? У кнопок и так нет подчёркивания!
Я долгое время искал решения. Сначала я экспериментировал со сложными селекторами.
.uia-button { display: inline-flex; /* оставшиеся стили */ } a.uia-button { text-decoration: none; } button.uia-button { border: none; }
Этот вариант быстро отвалился, потому что из-за повышенной специфичности легко ломалась вёрстка.
Потом я писал утилитарные классы.
.uia-button { display: inline-flex; /* оставшиеся стили */ } .ra-link { text-decoration: none; } .ra-button { border: none; }
В прошлом году я снова вернулся к варианту на основе селектора по типу элемента. Только я использую его вместе с CSS-слоями. Они помогают избежать проблем со специфичностью.
В моих проектах я создаю слой reset. В нём сбрасываю браузерные стили, которые мне не нужны.
@layer reset { .uia-button { display: inline-flex; /* оставшиеся стили */ } a.uia-button { text-decoration: none; } button.uia-button { border: none; } }
Всё работает корректно, благодаря особенности слоёв. Стили внутри них всегда менее специфичны, чем без них. И не важно, какой используется селектор, потому что он работает только внутри слоя, где он объявлен.
Это позволяет писать CSS, не боясь. Новое правило может быть с любым селектором и находиться в любом месте.
Давайте попробуем переопределить свойство border.
@layer reset { .uia-button { display: inline-flex; /* оставшиеся стили */ } a.uia-button { text-decoration: none; } button.uia-button { border: none; } } .uia-button { border: 2px solid pink; /* браузеры применят это значение */ }
Я буду честен. Конфликт селекторов может случиться, но он произойдёт только в слое reset. Но в нём очень мало кода, и поэтому его легко контролировать и избегать конфликтов.
Универсальный метод растяжения элемента до последней колонки в сетке
CSS-гриды уже обычная составляющая современных интерфейсов. Многие фронтендеры их любят и используют. Только я не понимаю одного. Ответьте, пожалуйста, на вопрос: «Как вы будете устанавливать протяжённость элемента до последней колонки в грид-сетке?»
Например, в сетке есть три колонки. Нужно, чтобы элемент занимал место, начиная со второй колонки и заканчивая последней. Как можно это реализовать?
Есть вариант использовать ключевое слово span.
<body> <div class="awesome-container"> <span class="awesome-block">Этот элемент должен растянуться до последней колонки</span> </div> </body>
.awesome-container { display: grid; grid-template-columns: repeat(3, 1fr); box-sizing: border-box; min-height: 50dvh; border: 4px solid purple; } .awesome-block { grid-column: 2 / span 2; background-color: tomato; }

В этом решении устанавливается номер трека, с которого элемент с классом .awesome-block будет растягиваться. А после символа / объявлено, сколько колонок он займёт. В нашем примере — две.
Мне не нравится этот подход тем, что если изменится количество колонок в сетке, то элемент перестанет тянуться до последней колонки. Для демонстрации я заменю значение 3 на 6 в функции repeat().
.awesome-container { display: grid; grid-template-columns: repeat(6, 1fr); box-sizing: border-box; min-height: 50dvh; border: 4px solid purple; } .awesome-block { grid-column: 2 / span 2; background-color: tomato; }

Чтобы этого не было, достаточно использовать обратную нумерацию треков. Другими словами, объявить вместо span 2 значение -1.
.awesome-container { display: grid; grid-template-columns: repeat(6, 1fr); box-sizing: border-box; min-height: 50dvh; border: 4px solid purple; } .awesome-block { grid-column: 2 / -1; background-color: tomato; }

И теперь можно спокойно менять количество колонок. Браузеры всегда растянут элемент до последней. Также этот метод подходит и для строк. Так что пользуйтесь!
Установка пропорции для всех изображений на странице
Для меня загадка, почему в современных интерфейсах мало используется свойство aspect-ratio. Например, оно отлично подходит для борьбы с прыжками контента пока загружается изображение.
Я предлагаю следующее решение. Мы можем по умолчанию задавать пропорции изображений с помощью свойства aspect-ratio и пользовательских свойств.
Для этого будем смело использовать селектор по типу img.
img { display: block; max-width: 100%; aspect-ratio: var(--img-aspect-ratio); }
Далее мы уже можем задавать другое значение там, где это требуется, просто используя свойство --img-aspect-ratio.
<body> <img src="https://habrastorage.org/r/w1560/webt/fo/xp/wa/foxpwat1obcz0y0wf_gladedwju1.jpeg" alt=""> <p>Здесь текст для тестирования</p> </body>
:root { --img-aspect-ratio: 1; }

Браузеры зарезервировали место под изображение. Теперь не важно, загрузится оно или нет, контент прыгать не будет!
Заключение
Давайте подведём итог. В этой статье мы рассмотрели:
использование псевдо-класса
:not()для стилизации контентных элементов;расширение области элемента, по которому пользователи должны кликнуть;
сброс стилей браузера при помощи селектора по типу без проблем со специфичностью;
как значение
-1помогает растянуть элемент до последней колонки или ряда при использовании CSS-гридов;установку пропорции изображений по умолчанию с помощью свойства
aspect-ratioи пользовательский свойств.
Надеюсь, вы почерпнули что-то полезное для себя. Также делитесь в комментариях своими CSS-трюками. Мне будет интересно прочитать.
На этом я прощаюсь. Спасибо за чтение!
P. S. Помогаю больше узнать про CSS и дружелюбные интерфейсы в своих ТГ‑каналах CSS isn't magic и UX + Dev = a11y. Присоединяйтесь. Как вступить, написано в профиле.
© 2026 ООО «МТ ФИНАНС»
DmitryO
Потому что существуют пользователи с Safari 14 и 13 (а иногда даже заходят динозавры с 12).