
Хабр, привет! Я снова пришёл к вам со статьёй, где показываю мои любимые техники вёрстки. Моя цель — поделиться опытом с вами. Я использую не только трюки известных экспертов, есть лично мои придумки. Но, пожалуйста, относитесь к этому контенту как просто к альтернативному мнению. Мои техники не являются единственными правильными решениями.
Сегодня мы рассмотрим:
- мой подход к написанию стилей для динамической сетки без использования БЭМ-модификаторов;
- как я перестал писать свойство
text-decorationсо значениемnoneдля элемента<button>; - способ для вычисления значения свойства
widthв зависимости от контента элемента; - почему вам стоит удалить все стили с использованием псевдо-класса
:focus.
Давайте посмотрим, что я вам подготовил.
▍ Стили для динамической сетки без БЭМ-модификаторов
По моим наблюдениям, в проектах с большой историей всегда есть одна сложность. Давайте представим тривиальную задачу. Нужно сверстать блок с контентом, который тянется на всю ширину.
<body>
<div class="page">
<main class="page__content"><!-- Здесь основной контент --></main>
</div>
</body>
.page {
display: grid;
}
Отлично, задача решена. Дальше проходит какое-то время. Полгода или больше. И тут приходит изменение в макет этой страницы. Появляется ещё один блок. Например, блок с рекламой. А главное, нужно сохранить старый вариант макета и реализовать новый.
Как быть? Лично я делал дополнительный класс БЭМ-модификатора.
<body>
<div class="page page_ad--yes">
<main class="page__content"><!-- Здесь основной контент --></main>
<div class="page__ad"><!-- Здесь реклама --></div>
</div>
</body>
.page {
display: grid;
}
.page_ad--yes {
grid-template-columns: 1fr 30%;
gap: 1rem;
}
Минус этого подхода заключается в том, что надо бесконечно создавать классы. Вечно придумывать названия. Да, ещё и разметка раздувается. В общем, это мне не нравилось. По этой причине я думал, как можно решить задачу по-другому. Недавно мне в голову пришло другое решение.
Давайте подумаем, когда появляется необходимость добавить новый класс? Когда в макете появляется новый элемент. Так это ведь то, что делает псевдо-класс :has()!
Когда у нас нет элемента, мы можем написать первый вариант стилей, а когда есть — второй. Вот так выглядит обновлённый код моего примера.
.page {
display: grid;
}
.page:has(.page__ad) {
grid-template-columns: 1fr 30%;
gap: 1rem;
}
Конечно, может показаться, что код стал громоздким из-за селектора. Но для меня это не проблема. А что думаете вы?
▍ Я больше не использую свойство text-decoration для элемента <button>
Четырнадцать лет вёрстки отложили свой отпечаток на моём психологическом состоянии. Я иногда рассказываю тут о своих загонах. Вот и сегодня расскажу ещё об одном.
По классике случился он после трудов дизайнеров. И я даже не буду ворчать на них. Они не виноваты, что делают ссылки, выглядящими как кнопки. И наоборот. Или виноваты?
В общем, в чём проблема. Когда мы создаём универсальный класс для реализации компонента «Кнопка», то мы вынуждены учитывать, что он может применяться сразу и к элементам <a>, и к элементам <button>.
Как вы знаете, у них есть браузерные стили. Например, у ссылок уже установлено подчёркивание с помощью свойства text-decoration, а у кнопок есть рамка, установленная свойством border.
В моём универсальном классе я вынужден сбросить эти свойства. В итоге получается, что свойство text-decoration будет сбрасывать подчёркивание у элемента <button>, а свойство border сбросит рамку у элемента <a>.
.uia-control {
display: inline-flex;
text-decoration: none;
border: none;
}
Для меня супер странно писать свойства, зная, что они применятся там, где и близко не нужны. Ведь у элемента <button> нет подчёркивания, а у элемента <a> нет рамки. Зачем тогда я сбрасываю их?
Конечно, я думал, как избежать этой ситуации. Первым решением было использовать комбинацию из селекторов по классу и по типу.
.uia-control {
display: inline-flex;
/* оставшиеся стили */
}
a.uia-control {
text-decoration: none;
}
button.uia-control {
border: none;
}
Для моих личных проектов решение подходило. Но для команд разработки это было сложно. Поскольку я повысил специфичность селектора, это выливалось в проблемы. Было сложно установить новое значение.
.uia-control {
display: inline-flex;
/* оставшиеся стили */
}
a.uia-control {
text-decoration: none;
}
button.uia-control {
border: none; /* для элемента <button> применится значение none */
}
.uia-control {
border: 2px solid lightblue;
}
В общем коллеги намекнули мне, что такой подход не очень. Поэтому я отказался от него и смири��ся с тем, что свойства используются нелогично.
А потом появились CSS слои. И у меня появилась новая надежда. Я сразу напишу код и объясню, как он работает.
@layer reset {
.uia-control {
/* правило А */
display: inline-flex;
/* оставшиеся стили */
}
a.uia-control {
text-decoration: none;
}
button.uia-control {
border: none;
}
}
.uia-control {
/* правило Б */
border: 2px solid lightblue; /* значение применится без проблем! */
}
В этом коде я создаю слой reset. В него добавил правила, которые сбрасывают стили. Теперь у селекторов a.uia-control и button.uia-control более высокий приоритет только внутри слоя. То есть у них приоритет над правилом А с селектором .uia-control.
Но у всего слоя меньший приоритет, чем у стилей без слоя. По этой причине правило Б с селектором .uia-control более специфично. Следовательно, свойства из этого правила без проблем применятся.
Таким способом я пишу только нужные для элемента стили. Мне нравится. Да и коллеги вроде заценили.
▍ Значение, позволяющее рассчитать размеры элемента по контенту
В вёрстке периодически приходится менять стандартное поведение элементов. Я часто вижу, что фронтендеры меняют значение свойства display, чтобы браузеры рассчитывали значение свойства width элемента с блочным значением по контенту.
Они используют значения inline-block, inline-flex или inline-grid.
<body>
<div class="awesome-block">
<span> Блочный элемент, значение свойства width рассчитано по контенту</span>
</div>
</body>
.awesome-block {
display: inline-flex; /* здесь может быть inline-block или inline-grid */
}
Восемь лет назад это был единственный способ. А CSS же развивается. Вот теперь можно не менять значения свойства display. Есть ключевое значение fit-content.
Оно помогает браузерам рассчитать значение для свойств width, height, min-width, min-height, max-width и max-height так, чтобы контент элемента стремился занять всё пространство. Проще говоря, оно рассчитается по контенту.
Это то, что нужно в нашей задаче. Поэтому удаляем свойство display и добавляем свойство width.
.awesome-block {
width: fit-content;
}

А самое главное, что в этом варианте не меняются стандартные свойства блочных значений. В первом способе с изменением значения свойства display при добавлении ещё одного элемента <div> они выстраиваются в строку. В способе с ключевым словом fit-content элементы отобразятся в столбец.
<body>
<div class="awesome-block">
<span> Блочный элемент, значение свойства width рассчитано по контенту</span>
</div>
<div class="awesome-block">
<span> Блочный элемент, значение свойства width рассчитано по контенту</span>
</div>
</body>

И последний плюс, чисто субъективный. Если мне нужно изменить принцип расчёта значения свойства width, то странно использовать свойство display. Это похоже на хитрый хак. Я понимаю, когда в CSS не было способа это сделать. Сейчас же есть. Поэтому я отдаю предпочтение ключевому слову fit-content.
▍ Псевдо-класс :focus устарел
Однажды у меня возник вопрос: «А зачем сегодня использовать псевдо-класс :focus?» Так-то не за чем. Я лично везде использую псевдо-класс :focus-visible. Единственно, я думал, что он всё ещё существует в стилях браузера. Решил проверить.
Для этого эксперимента я создал разметку с интерактивными элементами <button>, <a> и <input>. Дальше использовал инструменты разработчика. В них есть панелька, где можно включать различные состояния. Она отобразится, если кликнуть на кнопку «:hov».

Мне нужно было выбрать :focus. Раньше в стилях браузера использовалось свойство outline, которое добавляло обводку вокруг элемента. По моей логике, если состояние :focus всё ещё существует, то она должна была появиться. Вот что я увидел в своём эксперименте.

Обводки нет. Я не буду показывать остальные элементы. У них она тоже не появилась. Я проверил демонстрацию в браузерах Google Chrome, Firefox, Edge и Safari. Нет её. Получается, что в какой-то момент стили для псевдо-класса :focus были удалены.
Только если переключаться клавишей Tab, то обводка появляется у элементов. Я попробовал включить псевдо-класс :focus-visible. Она появилась. И даже у всех элементов.

Я всё ещё встречаю фронтендеров, которые по привычке используют псевдо-класс :focus. Коллеги, уже не надо. Удаляем его и заменяем на псевдо-класс :focus-visible.
▍ Заключение
Давайте подведём итог. В этой статье мы рассмотрели:
- технику вёрстки изменяемой сетки с помощью псевдо-класса
:has(); - метод сброса стилей с использованием CSS слоёв, позволяющий не добавлять ненужные свойства для элементов;
- ключевое слово
fit-contentдля вычисления значения свойстваwidthв зависимости от контента элемента; - псевдо-класс
:focus, который больше не нужен в проектах.
Другие статьи из серии можно найти по тегу «sm909_css_tricks».
Спасибо за чтение!
P.S. Помогаю больше узнать про CSS в своём ТГ-канале CSS isn't magic. Присоединяйтесь. Ссылка в профиле.
© 2025 ООО «МТ ФИНАНС»
Telegram-канал со скидками, розыгрышами призов и новостями IT 💻

