***
Нужные
Сообщений 1 страница 6 из 6
Поделиться306-01-2026 19:01:31
[html]
<div class="hover-swap">
<div class="hover-swap__media">
<img class="img img--base" src="https://upforme.ru/uploads/001c/0a/76/4/613957.jpg">
<img class="img img--hover" src="https://upforme.ru/uploads/001c/0a/76/4/729374.jpg">
</div>
<div class="hover-swap__caption" aria-hidden="true">
<p>С Новым годом и Рождеством, Ривер.</p>
<p>
Пусть этот год будет к тебе чуть мягче, чем предыдущий: дел — меньше, сна — больше,
а поводов держать челюсти сжатыми — как можно реже. Пусть интуиция ведёт туда,
куда нужно, а не туда, куда безопасно, и рядом остаются те, ради кого вообще стоит держаться.
</p>
<p>
И пусть мир в новом году иногда по привычке недооценивает тебя — так ему проще.
Мы-то знаем, что твоя сила измеряется не сантиметрами роста, а тем, какая тень остаётся за спиной.
</p>
<p>
А подарки ты уже можешь найти в своей карточке:
</p>
<div class="hover-swap__gifts">
<div class="hover-swap__gift">
<div class="hover-swap__gift-title">Иконка:</div>
<img class="hover-swap__gift-img" src="https://upforme.ru/uploads/001c/82/f2/4/867040.png">
</div>
<div class="hover-swap__gift">
<div class="hover-swap__gift-title">Плашка:</div>
<img class="hover-swap__gift-img" src="https://upforme.ru/uploads/001c/82/f2/4/542544.png">
</div>
</div>
</div>
</div>
<style>
.hover-swap {
width: 300px;
display: inline-block;
line-height: 0;
}
.hover-swap__media {
position: relative;
}
.hover-swap .img {
display: block;
width: 100%;
height: auto;
}
.hover-swap .img--hover {
position: absolute;
inset: 0;
opacity: 0;
transition: opacity .25s ease;
}
.hover-swap__caption {
line-height: 1.4;
margin-top: 12px;
opacity: 0;
transform: translateY(12px);
filter: blur(4px);
transition:
opacity .45s ease,
transform .45s ease,
filter .45s ease;
transition-delay: .35s;
}
.hover-swap__caption p {
margin: 0 0 8px 0;
}
.hover-swap__caption p:last-child {
margin-bottom: 0;
}
.hover-swap__gifts {
margin-top: 10px;
line-height: 1.4;
text-align: center;
}
.hover-swap__gift {
display: block;
margin: 12px 0 0 0;
}
.hover-swap__gift:first-child {
margin-top: 0;
}
.hover-swap__gift-title {
font-weight: 700;
margin-bottom: 6px;
}
.hover-swap__gift-img {
display: block;
margin: 0 auto;
max-width: 100%;
height: auto;
}
.hover-swap:hover .img--hover {
opacity: 1;
}
.hover-swap:hover .hover-swap__caption {
opacity: 1;
transform: translateY(0);
filter: blur(0);
}
</style>
[/html]
Поделиться423-04-2026 12:51:13
Вообще, мне самому не часто приходится заниматься вёрсткой, хотя дело я от части люблю. Но именно из-за того, что делаю я это нечасто, мне периодически приходится обращаться к документации, чтобы вспомнить некоторые свойства и значения современного CSS.
В конечном итоге я решил накидать шпаргалку для себя, которая постепенно выросла в небольшую статью, представленную ниже. В ней описаны несколько действительно приятных современных возможностей CSS с примерами того, как стили писались в прошлом и тем, как это можно делать сейчас. Сама идея представленных возможностей достаточна простая: меньше дублирования, меньше костылей, больше компонентного подхода.
"CSS Nesting" и "&"
Что это такое:
CSS Nesting позволяет вкладывать один селектор в другой прямо в обычном CSS. Браузер при этом сам понимает такую запись, что делает стили короче и более читабельными. Символ "&" нужен, когда вложенный селектор должен "прицепиться" к своему родителю. Например, для :hover.
Как было раньше:
<div class="card">
<h2 class="card__title">Заголовок</h2>
<p class="card__text">Немного текста внутри карточки.</p>
<a class="card__link" href="#">Открыть</a>
</div>
<style>
.card {
padding: 16px;
border: 1px solid #ccc;
border-radius: 12px;
}
.card:hover {
border-color: #333;
}
.card__title {
margin: 0 0 8px;
}
.card__text {
margin: 0 0 12px;
color: #666;
}
.card__link {
color: royalblue;
text-decoration: none;
}
.card__link:hover {
text-decoration: underline;
}
</style>
Как можно сейчас:
<div class="card">
<h2 class="card__title">Заголовок</h2>
<p class="card__text">Немного текста внутри карточки.</p>
<a class="card__link" href="#">Открыть</a>
</div>
<style>
.card {
padding: 16px;
border: 1px solid #ccc;
border-radius: 12px;
&:hover {
border-color: #333;
}
.card__title {
margin: 0 0 8px;
}
.card__text {
margin: 0 0 12px;
color: #666;
}
.card__link {
color: royalblue;
text-decoration: none;
&:hover {
text-decoration: underline;
}
}
}
</style>
Результат:
[html]
<div class="card">
<h2 class="card__title">Заголовок</h2>
<p class="card__text">Немного текста внутри карточки.</p>
<a class="card__link" href="#">Открыть</a>
</div>
<style>
.card {
padding: 16px;
border: 1px solid #ccc;
border-radius: 12px;
&:hover {
border-color: #333;
}
.card__title {
margin: 0 0 8px;
}
.card__text {
margin: 0 0 12px;
color: #666;
}
.card__link {
color: royalblue;
text-decoration: none;
&:hover {
text-decoration: underline;
}
}
}
</style>
[/html]
Где особенно удобно: карточки, меню, формы, состояния вроде :hover, :focus, :disabled.
@layer
Что это такое:
@layer даёт нам явные слои каскада. Проще говоря, можно заранее решить, что reset слабее components, а utilities сильнее их обоих. Это полезно, когда проект растёт и обычная борьба специфичности начинает раздражать. У слоя приоритет определяется не тем, как сложно и хитро мы написали селектор, а порядком слоёв.
Как было раньше:
<style>
.button {
background: gray;
color: white;
}
.page .content .button-primary {
background: seagreen;
}
</style>
<button class="button button-primary">Кнопка</button>
Такой подход легко приводил нас к ситуациям, когда мы начинали писать всё более сложные и запутанные селекторы.
А вот как можно сейчас:
<style>
@layer reset, components, utilities;
@layer reset {
button {
font: inherit;
}
}
@layer components {
.button {
padding: 10px 16px;
border: 0;
border-radius: 10px;
background: gray;
color: white;
}
}
@layer utilities {
.button-primary {
background: seagreen;
}
}
</style>
<button class="button button-primary">Кнопка</button>
[html]
<style>
@layer reset, components, utilities;
@layer reset {
button {
font: inherit;
}
}
@layer components {
.button {
padding: 10px 16px;
border: 0;
border-radius: 10px;
background: gray;
color: white;
}
}
@layer utilities {
.button-primary {
background: seagreen;
}
}
</style>
<button class="button button-primary">Кнопка</button>
[/html]
На первый взгляд может показаться, что так сложнее и больше кода. Но всё меняется, когда разметка становится большой, а селекторов для неё много. Идея @layer простая: сначала объявили порядок слоёв, потом спокойно раскладываем стили по смыслу.
:is() и :where()
Что это такое:
Обе функции помогают группировать селекторы. При этом у них есть важная разница: :is() берёт специфичность самого "сильного" селектора внутри, а :where() всегда имеет нулевую специфичность. Поэтому :is() удобно использовать для сокращения записи, а :where() — для базовых стилей, которые потом должно быть легко переопределить.
Как было раньше:
<style>
.content h1,
.content h2,
.content h3 {
color: #222;
line-height: 1.2;
}
</style>
<section class="content">
<h1>Заголовок</h1>
<h2>Подзаголовок</h2>
<h3>Ещё один подзаголовок</h3>
</section>А вот как можно сейчас с :is()
<style>
.content :is(h1, h2, h3) {
color: #222;
line-height: 1.2;
}
</style>
<section class="content">
<h1>Заголовок</h1>
<h2>Подзаголовок</h2>
<h3>Ещё один подзаголовок</h3>
</section>
А так можно с :where()
<style>
:where(.site-footer a) {
color: tomato;
}
footer a {
color: steelblue;
}
</style>
<footer class="site-footer">
<a href="#">Ссылка в футере</a>
</footer>
[html]
<style>
:where(.site-footer a) {
color: tomato;
}
footer a {
color: steelblue;
}
</style>
<footer class="site-footer">
<a href="#">Ссылка в футере</a>
</footer>
[/html]
Здесь цвет из "footer a" легко победит правило с :where(), потому что у :where() нулевая специфичность, что удобно для базовых стилей.
:has()
Что это такое:
:has() является селектором отношений, который позволяет выбрать элемент, в случае если внутри него или рядом с ним есть что-то подходящее. Этот селектор можно стилизовать форму, если внутри есть невалидный input, карточку - если в ней есть картинка, заголовок - если после него идёт подзаголовок.
Как было раньше:
Обычно приходилось либо вешать класс через JavaScript, либо добавлять лишнюю служебную разметку.
<style>
.form-error {
border: 2px solid crimson;
}
</style>
<form class="form form-error">
<label>
Email
<input type="email" value="не email">
</label>
</form>
Как можно сейчас:
<style>
.form {
padding: 16px;
border: 2px solid #ccc;
border-radius: 12px;
}
.form:has(input:invalid) {
border-color: crimson;
background: #fff5f5;
}
</style>
<form class="form">
<label>
Email
<input type="email" value="не email" required>
</label>
</form>
[html]
<style>
.form {
padding: 16px;
border: 2px solid #ccc;
border-radius: 12px;
}
.form:has(input:invalid) {
border-color: crimson;
background: #fff5f5;
}
</style>
<form class="form">
<label>
Email
<input type="email" value="не email" required>
</label>
</form>
[/html]
Ещё один пример: выделение карточки, только если в ней есть элемент с классом (.badge):
<style>
.product-card {
padding: 16px;
border: 1px solid #ccc;
}
.product-card:has(.badge) {
border-color: orange;
}
</style>
<article class="product-card">
<span class="badge">new</span>
<h2>Наушники</h2>
</article>
[html]
<style>
.product-card {
padding: 16px;
border: 1px solid #ccc;
}
.product-card:has(.badge) {
border-color: orange;
}
</style>
<article class="product-card">
<span class="badge">new</span>
</article>
[/html]
@container
Что это такое:
Раньше мы почти всегда пользовались только viewport’ом: "если экран меньше 768px, делай так". Но компонент может жить и в широкой колонке, и в узком сайдбаре на одном и том же экране. @container позволяет менять стили по размеру контейнера, а не окна браузера. Для этого контейнер нужно явно объявить через container-type или shorthand container.
Как было раньше: только media query
<style>
.card {
padding: 16px;
font-size: 14px;
}
@media (min-width: 900px) {
.card {
font-size: 18px;
}
}
</style>
<div class="layout">
<article class="card">
<h2>Новость</h2>
<p>Карточка меняется только от размера окна.</p>
</article>
</div>
Тут возникала проблема: если карточка стоит в узкой колонке на широком экране, она всё равно получит "широкие" стили.
А вот как можно сейчас:
<style>
.sidebar {
container-type: inline-size;
width: 320px;
}
.card {
padding: 16px;
font-size: 14px;
border: 1px solid #ccc;
border-radius: 12px;
}
@container (width > 400px) {
.card {
font-size: 18px;
display: grid;
gap: 12px;
}
}
</style>
<div class="sidebar">
<article class="card">
<h2>Новость</h2>
<p>Теперь карточка смотрит на ширину родителя.</p>
</article>
</div>
[html]
<style>
.sidebar {
container-type: inline-size;
width: 320px;
}
.card {
padding: 16px;
font-size: 14px;
border: 1px solid #ccc;
border-radius: 12px;
}
@container (width > 400px) {
.card {
font-size: 18px;
display: grid;
gap: 12px;
}
}
</style>
<div class="sidebar">
<article class="card">
<h2>Новость</h2>
<p>Теперь карточка смотрит на ширину родителя.</p>
</article>
</div>
[/html]
А shorthand выглядит так:
.sidebar {
container: sidebar / inline-size;
}
dvh, lvh, svh
Что это такое:
Классическая проблема мобильной вёрстки - 100vh. На телефонах видимая область может меняться из-за адресной строки и интерфейса браузера. Новые viewport-единицы помогают описывать это точнее: svh — маленький viewport, lvh — большой, dvh — динамический текущий. Документация отдельно отмечает, что vh эквивалентен lvh, то есть ориентируется на большой viewport.
Как было раньше:
<style>
.hero {
height: 100vh;
display: grid;
place-items: center;
background: peachpuff;
}
</style>
<section class="hero">
<h1>Привет</h1>
</section>На мобильных такой блок иногда оказывается выше реально видимой области.
Как можно сейчас:
<style>
.hero {
min-height: 100dvh;
display: grid;
place-items: center;
background: peachpuff;
}
</style>
<section class="hero">
<h1>Привет</h1>
</section>
Полезная шпаргалка:
100svh - безопасный минимум, когда весь интерфейс браузера виден;
100lvh - максимум;
100dvh - текущее динамическое значение.
aspect-ratio
Что это такое:
aspect-ratio задаёт желаемое соотношение ширины к высоте, и браузер сам поддерживает пропорции элемента при расчёте размеров. Но есть нюанс: хотя бы одна из сторон должна быть автоматической, иначе свойство не влияет на размеры.
Как было раньше:
<style>
.video {
position: relative;
padding-top: 56.25%;
}
.video iframe {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
border: 0;
}
</style>
<div class="video">
<iframe src="about:blank" title="Видео"></iframe>
</div>
Как можно сейчас:
<style>
.video {
aspect-ratio: 16 / 9;
}
.video iframe {
width: 100%;
height: 100%;
border: 0;
}
</style>
<div class="video">
<iframe src="about:blank" title="Видео"></iframe>
</div>
[html]
<style>
.video {
aspect-ratio: 16 / 9;
}
.video iframe {
width: 100%;
height: 100%;
border: 0;
}
</style>
<div class="video">
<iframe src="about:blank" title="Видео"></iframe>
</div>
[/html]
Или для карточек-превью:
<style>
.thumb {
width: 100%;
aspect-ratio: 1 / 1;
background: linear-gradient(135deg, #89f7fe, #66a6ff);
border-radius: 16px;
}
</style>
<div class="thumb"></div>
[html]
<style>
.thumb {
width: 100%;
aspect-ratio: 1 / 1;
background: linear-gradient(135deg, #89f7fe, #66a6ff);
border-radius: 16px;
}
</style>
<div class="thumb"></div>
[/html]
Просто и читаемо.
color-scheme
Что это такое:
color-scheme говорит браузеру, в каких схемах элемент или страница должны отображаться: light, dark или оба варианта.
Как было раньше:
<style>
.panel {
background: #111;
color: #eee;
padding: 16px;
}
</style>
<form class="panel">
<input type="text" placeholder="Ваше имя">
<textarea placeholder="Комментарий"></textarea>
</form>
Проблема была достаточно явной: отдельные элементы могли выглядеть не очень согласованно.
Как можно сейчас:
<style>
.panel {
color-scheme: dark;
background: #111;
color: #eee;
padding: 16px;
border-radius: 12px;
}
.panel input,
.panel textarea {
width: 100%;
margin-top: 10px;
}
</style>
<form class="panel">
<input type="text" placeholder="Ваше имя">
<textarea placeholder="Комментарий"></textarea>
</form>
Или если хотите поддерживать обе темы:
<style>
.app {
color-scheme: light dark;
background: #fff;
color: #111;
padding: 16px;
}
@media (prefers-color-scheme: dark) {
.app {
background: #111;
color: #eee;
}
}
</style>
<div class="app">
<h2>Настройки</h2>
<input type="text" placeholder="Поиск">
</div>
[html]
<style>
.app {
color-scheme: light dark;
background: #fff;
color: #111;
padding: 16px;
}
@media (prefers-color-scheme: dark) {
.app {
background: #111;
color: #eee;
}
}
</style>
<div class="app">
<h2>Настройки</h2>
<input type="text" placeholder="Поиск">
</div>
[/html]
Итог:
Nesting и & позволяют писать меньше повторов и делают стили чище.
@layer позволяет облегчить работу со специфичностью.
:is() и :where() дают нам более короткие и аккуратные селекторы, плюс контроль над специфичностью.
:has() позволяет наконец-то можно смотреть внутрь элемента и стилизовать родителя.
@container делает компоненты по-настоящему самостоятельными.
dvh/lvh/svh облегчают работу с мобильной высотой.
aspect-ratio упрощают работу с соотношением сторон.
color-scheme - аккуратная интеграция светлой/тёмной темы с браузерным UI.
Поделиться524-04-2026 16:16:28
Вообще, мне самому нечасто приходится заниматься вёрсткой, хотя само это дело мне скорее нравится. Но именно из-за того, что возвращаюсь к нему не так уж регулярно, время от времени приходится снова заглядывать в документацию, чтобы освежить в памяти свойства и возможности современного CSS.
В какой-то момент я решил собрать для себя небольшую шпаргалку, а потом она постепенно выросла в короткую статью. Ниже — несколько действительно приятных современных возможностей CSS с короткой теорией, примерами того, как это обычно делали раньше, и тем, как можно писать сейчас. Сама идея у большинства этих возможностей довольно простая: меньше дублирования, меньше костылей, больше удобства и компонентного подхода.
CSS Nesting и &
Что это такое:
CSS Nesting позволяет вкладывать один селектор в другой прямо в обычном CSS. Браузер сам понимает такую запись, поэтому стили становятся компактнее и читаются заметно легче. Символ & нужен, когда вложенный селектор должен "прицепиться" к родителю без пробела. Например, для :hover, :focus, .is-active и похожих состояний.
Как было раньше:
Код:<div class="promo-card"> <div class="promo-card__badge">new</div> <div class="promo-card__title">Новая коллекция</div> <div class="promo-card__text">Лёгкие вещи на тёплую погоду.</div> <a class="promo-card__button" href="#">Смотреть</a> </div> <style> .promo-card { max-width: 320px; padding: 18px; border: 1px solid #d9d9d9; border-radius: 16px; background: #ffffff; } .promo-card:hover { border-color: #8c7ae6; box-shadow: 0 10px 24px rgba(0, 0, 0, 0.08); } .promo-card__badge { display: inline-block; margin-bottom: 12px; padding: 4px 10px; border-radius: 999px; background: #f3edff; color: #6c43d7; font-size: 12px; } .promo-card__title { margin-bottom: 8px; font-size: 20px; font-weight: 700; } .promo-card__text { margin-bottom: 14px; color: #666; } .promo-card__button { display: inline-block; padding: 10px 14px; border-radius: 10px; background: #8c7ae6; color: #fff; text-decoration: none; } .promo-card__button:hover { background: #735fe0; } </style>
Как можно сейчас:
Код:<div class="promo-card"> <div class="promo-card__badge">new</div> <div class="promo-card__title">Новая коллекция</div> <div class="promo-card__text">Лёгкие вещи на тёплую погоду.</div> <a class="promo-card__button" href="#">Смотреть</a> </div> <style> .promo-card { max-width: 320px; padding: 18px; border: 1px solid #d9d9d9; border-radius: 16px; background: #ffffff; &:hover { border-color: #8c7ae6; box-shadow: 0 10px 24px rgba(0, 0, 0, 0.08); } .promo-card__badge { display: inline-block; margin-bottom: 12px; padding: 4px 10px; border-radius: 999px; background: #f3edff; color: #6c43d7; font-size: 12px; } .promo-card__title { margin-bottom: 8px; font-size: 20px; font-weight: 700; } .promo-card__text { margin-bottom: 14px; color: #666; } .promo-card__button { display: inline-block; padding: 10px 14px; border-radius: 10px; background: #8c7ae6; color: #fff; text-decoration: none; &:hover { background: #735fe0; } } } </style>
Результат:
[html]
<div class="demo demo-nesting">
<div class="promo-card">
<div class="promo-card__badge">new</div>
<div class="promo-card__title">Новая коллекция</div>
<div class="promo-card__text">Лёгкие вещи на тёплую погоду.</div>
<a class="promo-card__button" href="#">Смотреть</a>
</div>
</div><style>
.demo-nesting {
padding: 20px;
border: 1px solid #e6e6e6;
border-radius: 18px;
background: linear-gradient(180deg, #fcfcff, #f6f3ff);
}.promo-card {
max-width: 320px;
padding: 18px;
border: 1px solid #d9d9d9;
border-radius: 16px;
background: #ffffff;
font-family: Arial, sans-serif;&:hover {
border-color: #8c7ae6;
box-shadow: 0 10px 24px rgba(0, 0, 0, 0.08);
}.promo-card__badge {
display: inline-block;
margin-bottom: 12px;
padding: 4px 10px;
border-radius: 999px;
background: #f3edff;
color: #6c43d7;
font-size: 12px;
text-transform: uppercase;
letter-spacing: 0.08em;
}.promo-card__title {
margin-bottom: 8px;
font-size: 20px;
font-weight: 700;
color: #1f1f1f;
}.promo-card__text {
margin-bottom: 14px;
color: #666;
line-height: 1.45;
}.promo-card__button {
display: inline-block;
padding: 10px 14px;
border-radius: 10px;
background: #8c7ae6;
color: #fff;
text-decoration: none;&:hover {
background: #735fe0;
}
}
}
</style>
[/html]
Где особенно удобно: карточки, меню, формы, состояния вроде :hover, :focus, :disabled, а также BEM-структуры.
@layer
Что это такое:
@layer даёт явные слои каскада. Проще говоря, можно заранее решить, что reset слабее components, а utilities сильнее их обоих. Это удобно, когда проект становится больше, а обычная борьба со специфичностью начинает раздражать.
Как было раньше:
Код:<style> .tag { display: inline-block; padding: 8px 12px; border-radius: 999px; background: #e5e5e5; color: #333; } .page .content .tag-green { background: #1f9d55; color: #fff; } </style> <div class="tag tag-green">Опубликовано</div>
Такой подход легко приводил к тому, что ради переопределения приходилось писать всё более тяжёлые селекторы.
Как можно сейчас:
Код:<style> @layer reset, components, utilities; @layer reset { * { box-sizing: border-box; } } @layer components { .tag { display: inline-block; padding: 8px 12px; border-radius: 999px; background: #e5e5e5; color: #333; font-family: Arial, sans-serif; } } @layer utilities { .tag-green { background: #1f9d55; color: #fff; } } </style> <div class="tag tag-green">Опубликовано</div>
Результат:
[html]
<div class="demo demo-layer">
<div class="tag">Черновик</div>
<div class="tag tag-green">Опубликовано</div>
<div class="tag tag-red">Ошибка</div>
</div><style>
@layer reset, components, utilities;@layer reset {
* {
box-sizing: border-box;
}
}@layer components {
.demo-layer {
padding: 20px;
border: 1px solid #e6e6e6;
border-radius: 18px;
background: #fafafa;
font-family: Arial, sans-serif;
}.tag {
display: inline-block;
margin-right: 8px;
margin-bottom: 8px;
padding: 8px 12px;
border-radius: 999px;
background: #e5e5e5;
color: #333;
font-size: 14px;
}
}@layer utilities {
.tag-green {
background: #1f9d55;
color: #fff;
}.tag-red {
background: #d64545;
color: #fff;
}
}
</style>
[/html]
На первый взгляд может показаться, что кода стало даже больше. Но на больших проектах @layer сильно упрощает жизнь: сначала объявили порядок слоёв, а потом спокойно раскладываем стили по смыслу.
:is() и :where()
Что это такое:
Обе функции помогают группировать селекторы. Но есть важная разница: :is() берёт специфичность самого "сильного" селектора внутри, а :where() всегда имеет нулевую специфичность. Поэтому :is() удобно использовать для сокращения записи, а :where() — для базовых стилей, которые потом должно быть легко переопределить.
Как было раньше:
Код:<style> .toolbar a, .toolbar button, .toolbar input { padding: 10px 12px; border: 1px solid #d8d8d8; border-radius: 10px; background: #fff; color: #333; font: inherit; } </style> <div class="toolbar"> <a href="#">Ссылка</a> <button type="button">Кнопка</button> <input type="text" value="Поле"> </div>
Как можно сейчас с :is()
Код:<style> .toolbar :is(a, button, input) { padding: 10px 12px; border: 1px solid #d8d8d8; border-radius: 10px; background: #fff; color: #333; font: inherit; } </style> <div class="toolbar"> <a href="#">Ссылка</a> <button type="button">Кнопка</button> <input type="text" value="Поле"> </div>
А так можно с :where()
Код:<style> :where(.article-card a) { color: #6c43d7; text-decoration: none; } .article-card--warning a { color: #c0392b; } </style> <div class="article-card">Обычная <a href="#">ссылка</a></div> <div class="article-card article-card--warning">Важная <a href="#">ссылка</a></div>
Результат:
[html]
<div class="demo demo-iswhere">
<div class="toolbar">
<a href="#">Ссылка</a>
<button type="button">Кнопка</button>
<input type="text" value="Поле">
</div><div class="article-card">
Базовая карточка со <a href="#">ссылкой</a>
</div><div class="article-card article-card--warning">
Важная карточка со <a href="#">ссылкой</a>
</div>
</div><style>
.demo-iswhere {
padding: 20px;
border: 1px solid #e6e6e6;
border-radius: 18px;
background: #fcfcfc;
font-family: Arial, sans-serif;
}.toolbar {
display: flex;
flex-wrap: wrap;
gap: 10px;
margin-bottom: 18px;
}.toolbar :is(a, button, input) {
padding: 10px 12px;
border: 1px solid #d8d8d8;
border-radius: 10px;
background: #fff;
color: #333;
font: inherit;
text-decoration: none;
}.article-card {
margin-top: 10px;
padding: 14px;
border-radius: 14px;
background: #f3f3f3;
color: #333;
}:where(.article-card a) {
color: #6c43d7;
text-decoration: none;
font-weight: 700;
}.article-card--warning {
background: #fff2f0;
border: 1px solid #ffd0ca;
}.article-card--warning a {
color: #c0392b;
}
</style>
[/html]
Здесь :is() сокращает запись, а :where() удобно использовать как мягкую базу, которую потом легко переопределить.
:has()
Что это такое:
:has() — это селектор отношений. Он позволяет выбрать элемент, если внутри него или рядом с ним есть что-то подходящее. Проще говоря, с его помощью можно стилизовать родителя в зависимости от содержимого.
Как было раньше:
Обычно приходилось либо добавлять класс через JavaScript, либо вручную прописывать служебные классы в разметке.
Код:<style> .option-selected { border-color: #4f46e5; background: #eef0ff; } </style> <label class="option option-selected"> <input type="checkbox" checked> Тёмная тема </label>
Как можно сейчас:
Код:<style> .option { display: flex; align-items: center; gap: 10px; padding: 14px; border: 2px solid #d9d9d9; border-radius: 14px; background: #fff; } .option:has(input:checked) { border-color: #4f46e5; background: #eef0ff; } </style> <label class="option"> <input type="checkbox" checked> Тёмная тема </label>
Результат:
[html]
<div class="demo demo-has">
<label class="option">
<input type="checkbox" checked>
<span>Тёмная тема</span>
</label><label class="option">
<input type="checkbox">
<span>Системные уведомления</span>
</label>
</div><style>
.demo-has {
padding: 20px;
border: 1px solid #e6e6e6;
border-radius: 18px;
background: #fafafe;
font-family: Arial, sans-serif;
}.option {
display: flex;
align-items: center;
gap: 10px;
margin-bottom: 10px;
padding: 14px;
border: 2px solid #d9d9d9;
border-radius: 14px;
background: #fff;
transition: 0.2s ease;
}.option:last-child {
margin-bottom: 0;
}.option:has(input:checked) {
border-color: #4f46e5;
background: #eef0ff;
box-shadow: 0 6px 16px rgba(79, 70, 229, 0.12);
}
</style>
[/html]
Это как раз тот случай, когда раньше очень часто приходилось лезть в JavaScript ради сравнительно простой визуальной реакции интерфейса.
@container
Что это такое:
Раньше мы почти всегда ориентировались только на viewport: "если экран меньше такой-то ширины, делай так". Но один и тот же компонент может стоять и в широкой колонке, и в узком сайдбаре. @container позволяет менять стили по размеру контейнера, а не окна браузера.
Как было раньше:
Код:<style> .news-card { padding: 16px; border: 1px solid #d9d9d9; border-radius: 14px; } @media (min-width: 900px) { .news-card { display: flex; gap: 14px; } } </style> <div class="news-card"> <div class="news-card__image"></div> <div class="news-card__body">Карточка зависит только от размера окна.</div> </div>
Проблема в том, что если такая карточка стоит в узкой колонке на широком экране, она всё равно получит "широкие" стили.
Как можно сейчас:
Код:<style> .column { container-type: inline-size; } @container (width > 420px) { .news-card { display: flex; gap: 14px; align-items: center; } .news-card__image { width: 120px; flex-shrink: 0; } } </style>
Результат:
[html]
<div class="demo demo-container">
<div class="column narrow">
<div class="news-card">
<div class="news-card__image"></div>
<div class="news-card__body">
<div class="news-card__title">Узкий контейнер</div>
<div class="news-card__text">Карточка остаётся вертикальной, потому что ей тесно.</div>
</div>
</div>
</div><div class="column wide">
<div class="news-card">
<div class="news-card__image"></div>
<div class="news-card__body">
<div class="news-card__title">Широкий контейнер</div>
<div class="news-card__text">А здесь тот же компонент уже перестраивается в строку.</div>
</div>
</div>
</div>
</div><style>
.demo-container {
display: grid;
gap: 16px;
padding: 20px;
border: 1px solid #e6e6e6;
border-radius: 18px;
background: #fcfcfc;
font-family: Arial, sans-serif;
}.column {
container-type: inline-size;
padding: 12px;
border-radius: 16px;
background: #f5f5f5;
}.column.narrow {
max-width: 280px;
}.column.wide {
max-width: 560px;
}.news-card {
padding: 14px;
border: 1px solid #d9d9d9;
border-radius: 14px;
background: #fff;
}.news-card__image {
width: 100%;
height: 90px;
margin-bottom: 12px;
border-radius: 12px;
background: linear-gradient(135deg, #7f7fd5, #86a8e7, #91eae4);
}.news-card__title {
margin-bottom: 8px;
font-size: 18px;
font-weight: 700;
color: #222;
}.news-card__text {
color: #666;
line-height: 1.45;
}@container (width > 420px) {
.news-card {
display: flex;
gap: 14px;
align-items: center;
}.news-card__image {
width: 120px;
height: 90px;
margin-bottom: 0;
flex-shrink: 0;
}
}
</style>
[/html]
Именно поэтому @container особенно хорош для компонентного подхода: один и тот же блок ведёт себя по-разному в зависимости от того, сколько места ему реально дали.
dvh, lvh, svh
Что это такое:
Классическая проблема мобильной вёрстки — 100vh. На телефонах видимая область может меняться из-за адресной строки и элементов интерфейса браузера. Новые единицы viewport помогают описывать это точнее:
[list]
[*]svh — маленький viewport;
[*]lvh — большой viewport;
[*]dvh — динамический текущий viewport.
[/list]
Как было раньше:
Код:<style> .welcome-screen { height: 100vh; display: grid; place-items: center; } </style> <div class="welcome-screen">Привет</div>
На мобильных это иногда приводило к тому, что блок оказывался выше реально видимой области.
Как можно сейчас:
Код:<style> .welcome-screen { min-height: 100dvh; display: grid; place-items: center; } </style> <div class="welcome-screen">Привет</div>
Результат:
[html]
<div class="demo demo-dvh">
<div class="welcome-screen">
<div class="welcome-screen__card">
<div class="welcome-screen__title">Полноэкранный блок</div>
<div class="welcome-screen__text">На телефонах для таких секций чаще полезнее использовать 100dvh, чем старый 100vh.</div>
</div>
</div>
</div><style>
.demo-dvh {
padding: 20px;
border: 1px solid #e6e6e6;
border-radius: 18px;
background: #fcfcff;
font-family: Arial, sans-serif;
}.welcome-screen {
min-height: 45dvh;
display: grid;
place-items: center;
border-radius: 16px;
background: linear-gradient(135deg, #ffecd2, #fcb69f);
}.welcome-screen__card {
max-width: 320px;
padding: 18px;
border-radius: 16px;
background: rgba(255, 255, 255, 0.85);
text-align: center;
}.welcome-screen__title {
margin-bottom: 8px;
font-size: 20px;
font-weight: 700;
color: #222;
}.welcome-screen__text {
color: #555;
line-height: 1.45;
}
</style>
[/html]
Полезная шпаргалка:
[list]
[*]100svh — безопасный минимум, когда интерфейс браузера виден;
[*]100lvh — максимум;
[*]100dvh — текущее динамическое значение.
[/list]
На десктопе разница может быть почти незаметной, а вот на мобильных это уже куда полезнее.
aspect-ratio
Что это такое:
aspect-ratio задаёт желаемое соотношение ширины к высоте, а браузер сам поддерживает пропорции элемента при расчёте размеров. Очень удобная вещь для превью, карточек, баннеров, видео и галерей.
Как было раньше:
Код:<style> .poster { position: relative; padding-top: 100%; border-radius: 16px; overflow: hidden; } .poster__inner { position: absolute; inset: 0; } </style> <div class="poster"> <div class="poster__inner"></div> </div>
Как можно сейчас:
Код:<style> .poster { aspect-ratio: 1 / 1; border-radius: 16px; } </style> <div class="poster"></div>
Результат:
[html]
<div class="demo demo-ratio">
<div class="gallery">
<div class="poster poster--square">1:1</div>
<div class="poster poster--video">16:9</div>
<div class="poster poster--book">3:4</div>
</div>
</div><style>
.demo-ratio {
padding: 20px;
border: 1px solid #e6e6e6;
border-radius: 18px;
background: #fafafa;
font-family: Arial, sans-serif;
}.gallery {
display: grid;
gap: 14px;
}.poster {
display: grid;
place-items: center;
border-radius: 16px;
color: #fff;
font-size: 22px;
font-weight: 700;
}.poster--square {
aspect-ratio: 1 / 1;
background: linear-gradient(135deg, #43cea2, #185a9d);
}.poster--video {
aspect-ratio: 16 / 9;
background: linear-gradient(135deg, #f7971e, #ffd200);
}.poster--book {
aspect-ratio: 3 / 4;
background: linear-gradient(135deg, #834d9b, #d04ed6);
}
</style>
[/html]
Такая запись куда проще, чем старые трюки с padding-top и абсолютным позиционированием.
color-scheme
Что это такое:
color-scheme говорит браузеру, в каких схемах элемент или страница должны отображаться: light, dark или оба варианта. Это особенно полезно для нативных элементов интерфейса — полей, кнопок, скроллбаров и прочих встроенных частей.
Как было раньше:
Код:<style> .panel { background: #111; color: #eee; padding: 16px; border-radius: 16px; } </style> <div class="panel"> <input type="text" placeholder="Ваше имя"> <textarea placeholder="Комментарий"></textarea> </div>
Проблема была в том, что часть встроенных элементов могла выглядеть не очень согласованно с общей темой.
Как можно сейчас:
Код:<style> .panel { color-scheme: dark; background: #111; color: #eee; padding: 16px; border-radius: 16px; } </style> <div class="panel"> <input type="text" placeholder="Ваше имя"> <textarea placeholder="Комментарий"></textarea> </div>
Результат:
[html]
<div class="demo demo-scheme">
<div class="panel">
<div class="panel__title">Настройки профиля</div>
<input type="text" placeholder="Ваше имя">
<textarea placeholder="Пара слов о себе"></textarea>
<button type="button">Сохранить</button>
</div>
</div><style>
.demo-scheme {
padding: 20px;
border: 1px solid #e6e6e6;
border-radius: 18px;
background: #f6f6f6;
font-family: Arial, sans-serif;
}.panel {
max-width: 360px;
color-scheme: dark;
background: #111827;
color: #eef2ff;
padding: 18px;
border-radius: 16px;
}.panel__title {
margin-bottom: 12px;
font-size: 18px;
font-weight: 700;
}.panel input,
.panel textarea,
.panel button {
width: 100%;
margin-top: 10px;
font: inherit;
}.panel textarea {
min-height: 90px;
resize: vertical;
}.panel button {
padding: 10px 14px;
border: 0;
border-radius: 10px;
background: #6366f1;
color: #fff;
cursor: pointer;
}
</style>
[/html]
Если хочется поддерживать обе темы, можно написать так:
Код:<style> .app { color-scheme: light dark; } @media (prefers-color-scheme: dark) { .app { background: #111; color: #eee; } } </style>
То есть color-scheme — это не замена полноценной тёмной теме, а способ аккуратнее подружить ваш интерфейс с браузерным UI.
Итог:
[list]
[*]CSS Nesting и & позволяют писать меньше повторов и делают структуру стилей заметно чище.
[*]@layer помогает навести порядок в каскаде и уменьшает боль от специфичности.
[*]:is() и :where() делают селекторы короче и аккуратнее, а заодно дают контроль над специфичностью.
[*]:has() позволяет стилизовать родителя в зависимости от содержимого.
[*]@container делает компоненты по-настоящему самостоятельными.
[*]dvh / lvh / svh упрощают работу с мобильной высотой.
[*]aspect-ratio делает работу с пропорциями заметно проще.
[*]color-scheme помогает аккуратно встроить светлую и тёмную тему в браузерный интерфейс.
[/list]
В общем, современный CSS стал заметно приятнее, чем был ещё несколько лет назад. И пусть далеко не каждый день приходится использовать все эти возможности разом, знать о них всё равно полезно: иногда одна такая мелочь способна заметно упростить и код, и жизнь.
Поделиться627-04-2026 12:20:15
[table widths=160px,30%,auto] [tr][th bg=#f2f2f2]Имя[/th][th]Описание[/th][th]Статус[/th][/tr] [tr][td bg=#fff7d6]Алиса[/td][td]Текст[/td][td bg=#e7ffe7]Активно[/td][/tr] [/table] [table] [tr][th]Заголовок[/th][th]Заголовок[/th][/tr] [tr][td]Ячейка[/td][td]Ячейка[/td][/tr] [tr][td]Ячейка[/td][td]Ячейка[/td][/tr] [tr][td]Ячейка[/td][td]Ячейка[/td][/tr] [/table] [table widths=80%,15%] [tr][th]Заголовок[/th][th]Заголовок[/th][/tr] [tr][td]Ячейка[/td][td]Ячейка[/td][/tr] [tr][td]Ячейка[/td][td]Ячейка[/td][/tr] [tr][td]Ячейка[/td][td]Ячейка[/td][/tr] [/table]



