Привет, Хабр!

Сегодня фронтендеры любят подход атомарных классов, в частности 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 ООО «МТ ФИНАНС»

Комментарии (1)


  1. DmitryO
    18.08.2026 07:58

    Для меня загадка, почему в современных интерфейсах мало используется свойство aspect-ratio

    Потому что существуют пользователи с Safari 14 и 13 (а иногда даже заходят динозавры с 12).