DEV Community

Андрей Викулов (VProger)
Андрей Викулов (VProger)

Posted on • Originally published at viku-lov.ru on

HTML и CSS в 2025–2026 без SCSS: :has(), Container Queries, Nesting, @layer и нативные модалки

Современный HTML и CSS в 2025–2026: что реально использовать без SCSS и без боли

Если коротко и честно:

SCSS больше не нужен в большинстве проектов.

HTML и CSS за последние годы дозрели:

  • появились вложенность,
  • нормальное управление каскадом,
  • адаптивность на уровне компонентов,
  • мощные селекторы,
  • нативные модалки и поповеры.

В 2025–2026 можно писать чистый CSS, не теряя удобства и контроля.

Разбираем всё по порядку — что использовать, как и с какой поддержкой браузеров.


HTML: нативные возможности, которые заменяют JS

HTML снова становится инструментом, а не контейнером под React.

— нативные модальные окна

Пример


<button onclick="modal.showModal()">Открыть</button>

<dialog id="modal">

<h2>Заголовок</h2>

<p>Контент модального окна</p>

<button onclick="modal.close()">Закрыть</button>

</dialog>

Enter fullscreen mode Exit fullscreen mode

Почему это важно

  • фокус ловится автоматически
  • блокируется фон
  • Esc работает из коробки
  • никакого JS-кода на управление состоянием

Поддержка браузеров

  • Google Chrome — ✅
  • Microsoft Edge — ✅
  • Mozilla Firefox — ✅
  • Apple Safari — ⚠️ (есть, но бывают баги с фокусом)

Вывод

  • ✔ Можно использовать в блогах, админках, статических сайтах
  • ⚠️ Для Safari — тестировать

inert — блокировка фона без JS


<div inert>

<!-- сюда нельзя кликнуть и сфокусироваться -->

</div>

Enter fullscreen mode Exit fullscreen mode

Работает идеально вместе с .

Поддержка

  • Chrome / Edge / Firefox / Safari — ✅

Popover API — подсказки и меню нативно


<button popovertarget="help">?</button>

<div id="help" popover>Подсказка</div>

Enter fullscreen mode Exit fullscreen mode

Поддержка

  • Chrome / Edge — ✅
  • Firefox — ⚠️ (включается постепенно)
  • Safari — ❌

Вывод

Использовать только как progressive enhancement.

Обязательно иметь fallback.


CSS в 2025–2026: можно без SCSS

Вот здесь начинается самое интересное.

CSS Nesting — нативная вложенность


.card {

padding: 1rem;

&\_\_title {

font-weight: 600;

}

&:hover {

background: #111;

}

}

Enter fullscreen mode Exit fullscreen mode

Поддержка

  • Chrome — ✅
  • Firefox — ✅
  • Safari 17+ — ✅

Что это даёт

  • SCSS-вложенность больше не нужна
  • код читается логично
  • меньше сборки и зависимостей

:has() — селектор родителя (революция)


.card:has(img) {

padding-top: 0;

}

.form:has(input:invalid) {

border-color: red;

}

Enter fullscreen mode Exit fullscreen mode

Поддержка

  • Chrome — ✅
  • Firefox — ✅
  • Safari — ✅

Почему это важно

Теперь можно:

  • реагировать на вложенные элементы
  • убирать JS-хаки
  • делать умные компоненты на CSS

👉 Одна из самых мощных CSS-фич за десятилетие.


Container Queries — адаптивность компонентов

Пример


.card {

container-type: inline-size;

}

@container (min-width: 420px) {

.card {

display: grid;

grid-template-columns: 1fr 2fr;

}

}

Enter fullscreen mode Exit fullscreen mode

Поддержка

  • Chrome — ✅
  • Firefox — ✅
  • Safari — ✅

Почему это лучше media queries

  • компонент адаптируется сам
  • не зависит от размера экрана
  • идеально для компонентного подхода, CMS, современных фреймворков

@layer — контроль каскада


@layer reset, base, components, utilities;

@layer base {

body {

font-family: system-ui;

}

}

@layer components {

.btn {

padding: 0.75rem 1rem;

}

}

Enter fullscreen mode Exit fullscreen mode

Поддержка

  • Все современные браузеры — ✅

Почему это важно

  • больше нет войн специфичности
  • удобно сочетать свой CSS и utility-фреймворки
  • архитектура CSS становится предсказуемой

Современные единицы высоты: dvh, svh, lvh


min-height: 100dvh;

Enter fullscreen mode Exit fullscreen mode

Что решает

  • мобильные адресные строки
  • прыгающие layout'ы
  • баги 100vh

Поддержка

  • Chrome / Firefox / Safari — ✅

👉 100vh можно считать устаревшим.


:where() и :is() — управление специфичностью


:where(h1, h2, h3) {

margin-block: 0.5em;

}

Enter fullscreen mode Exit fullscreen mode
  • :where() — 0 специфичность
  • :is() — берёт максимальную

Использовать для:

  • reset'ов
  • базовых стилей
  • безопасных селекторов

Современный CSS-стек без SCSS

Минимум (рекомендую)

  • HTML (семантика, dialog, inert)
  • CSS (nesting, :has, container queries)
  • PostCSS + Autoprefixer
  • Design tokens через :root

Пример токенов


:root {

--space-sm: 0.5rem;

--space-md: 1rem;

--radius-md: 12px;

--clr-bg: #0b0d10;

}

Enter fullscreen mode Exit fullscreen mode

Поддержка браузеров — кратко

| Фича | Chrome | Firefox | Safari |

| ----------------- | ------ | ------- | ------ |

| CSS Nesting | ✅ | ✅ | ✅ |

| :has() | ✅ | ✅ | ✅ |

| Container Queries | ✅ | ✅ | ✅ |

| dialog | ✅ | ✅ | ⚠️ |

| Popover API | ✅ | ⚠️ | ❌ |

| dvh / svh | ✅ | ✅ | ✅ |

| @layer | ✅ | ✅ | ✅ |


Практические примеры

Пример 1: Модальное окно с


<button id="openModal" type="button">Открыть модалку</button>

<dialog id="demoDialog">

<h3>Нативный dialog</h3>

<p>Работает без библиотек. Esc закрывает. Фокус внутри.</p>

<menu>

<button id="closeModal" type="button">Закрыть</button>

</menu>

</dialog>

<script>

const dialog = document.getElementById("demoDialog");

const openBtn = document.getElementById("openModal");

const closeBtn = document.getElementById("closeModal");

openBtn.addEventListener("click", () => dialog.showModal());

closeBtn.addEventListener("click", () => dialog.close());

// Закрытие по клику на backdrop

dialog.addEventListener("click", (e) => {

const rect = dialog.getBoundingClientRect();

const clickInDialog =

rect.top <= e.clientY &&

e.clientY <= rect.top + rect.height &&

rect.left <= e.clientX &&

e.clientX <= rect.left + rect.width;

if (!clickInDialog) dialog.close();

});

</script>

<style>

dialog {

padding: 1rem 1.25rem;

border: 1px solid rgba(255, 255, 255, 0.12);

border-radius: 14px;

background: #0b0d10;

color: #e8eaf0;

max-width: 520px;

}

dialog::backdrop {

background: rgba(0, 0, 0, 0.55);

backdrop-filter: blur(6px);

}

button {

border-radius: 10px;

border: 1px solid rgba(255, 255, 255, 0.14);

background: rgba(255, 255, 255, 0.06);

color: #e8eaf0;

padding: 0.6rem 0.9rem;

cursor: pointer;

}

menu {

display: flex;

justify-content: flex-end;

gap: 0.5rem;

padding: 0;

margin: 1rem 0 0;

}

</style>

Enter fullscreen mode Exit fullscreen mode

Как это работает

  • dialog.showModal() открывает модалку в модальном режиме (фон недоступен).
  • dialog.close() закрывает.
  • ::backdrop — стилизация затемнения.
  • Обработчик click сравнивает координаты клика с границами окна и закрывает по клику вне прямоугольника.

Пример 2: Container Queries в действии


<div class="wrap">

<div class="pane narrow">

<h4>Контейнер узкий</h4>

<article class="card">

<div class="media"></div>

<div class="content">

<div class="title">Card title</div>

<p>При узком контейнере — вертикальная компоновка.</p>

</div>

</article>

</div>

<div class="pane wide">

<h4>Контейнер широкий</h4>

<article class="card">

<div class="media"></div>

<div class="content">

<div class="title">Card title</div>

<p>При широком контейнере — сетка: медиа слева, текст справа.</p>

</div>

</article>

</div>

</div>

<style>

.wrap {

display: grid;

grid-template-columns: 1fr;

gap: 1rem;

}

/<i> Для десктопа показываем рядом </i>/

@media (min-width: 900px) {

.wrap {

grid-template-columns: 1fr 1fr;

}

}

.pane {

border: 1px solid rgba(255, 255, 255, 0.1);

border-radius: 16px;

padding: 1rem;

background: rgba(255, 255, 255, 0.03);

/<i> ВАЖНО: объявляем контейнер </i>/

container-type: inline-size;

}

.pane h4 {

margin: 0 0 0.75rem;

font-size: 0.95rem;

opacity: 0.85;

}

.card {

border-radius: 14px;

border: 1px solid rgba(255, 255, 255, 0.1);

background: #0b0d10;

color: #e8eaf0;

overflow: hidden;

display: grid;

grid-template-columns: 1fr; /<i> базово  колонка </i>/

}

.media {

height: 140px;

background: linear-gradient(

135deg,

rgba(0, 180, 255, 0.35),

rgba(160, 110, 255, 0.25)

);

}

.content {

padding: 0.9rem 1rem 1rem;

}

.title {

font-weight: 650;

margin-bottom: 0.35rem;

}

/<i> Container Query: реагируем на ширину контейнера, а не окна </i>/

@container (min-width: 480px) {

.card {

grid-template-columns: 180px 1fr;

align-items: stretch;

}

.media {

height: auto; /<i> теперь высота определяется карточкой </i>/

min-height: 100%;

}

}

/<i> И чуть более "богатый" режим </i>/

@container (min-width: 620px) {

.card {

grid-template-columns: 220px 1fr;

}

}

</style>

Enter fullscreen mode Exit fullscreen mode

Как это работает

  • container-type: inline-size; делает элемент контейнером, на который можно ориентироваться.
  • @container (min-width: 480px) — переключает layout когда контейнер стал шире, даже если окно не менялось.
  • Это идеально для компонентного подхода: карточка адаптируется в сайдбаре, сетке, модалке — везде.

Главный вывод

В 2025–2026:

  • SCSS не нужен по умолчанию
  • HTML снова решает задачи
  • CSS стал мощным и зрелым
  • JS — для логики, а не для вёрстки

Меньше сборки.

Меньше зависимостей.

Больше предсказуемости.

Поддержка браузеров (на практике):

  • dialog — Chrome/Edge/Firefox ✅, Safari ⚠️ (проверяй на реальных устройствах)
  • Container Queries — Chrome/Edge/Firefox/Safari ✅
  • CSS Nesting — Chrome/Firefox/Safari 17+ ✅
  • :has() — все современные браузеры ✅

Read more on viku-lov.ru

Top comments (0)