← Назад в блог

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

Подробный разбор современных возможностей HTML и CSS в 2025–2026: dialog, inert, :has(), container queries, CSS nesting, @layer, dvh/svh. С примерами и пометками поддержки браузеров — что уже можно тащить в прод.

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.

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

Пример

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

<dialog id="modal">
  <h2>Заголовок</h2>
  <p>Контент модального окна</p>
  <button onclick="modal.close()">Закрыть</button>
</dialog>

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

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

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

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

Вывод

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

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

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

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

Поддержка

  • Chrome / Edge / Firefox / Safari — ✅

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

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

Поддержка

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

Вывод

Использовать только как progressive enhancement. Обязательно иметь fallback.


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

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

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

.card {
  padding: 1rem;

  &__title {
    font-weight: 600;
  }

  &:hover {
    background: #111;
  }
}

Поддержка

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

Что это даёт

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

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

.card:has(img) {
  padding-top: 0;
}

.form:has(input:invalid) {
  border-color: red;
}

Поддержка

  • 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;
  }
}

Поддержка

  • 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;
  }
}

Поддержка

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

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

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

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

min-height: 100dvh;

Что решает

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

Поддержка

  • Chrome / Firefox / Safari — ✅

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


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

:where(h1, h2, h3) {
  margin-block: 0.5em;
}
  • :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;
}

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

ФичаChromeFirefoxSafari
CSS Nesting
:has()
Container Queries
dialog⚠️
Popover API⚠️
dvh / svh
@layer

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

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

<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>

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

  • 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;
  }

  /* Для десктопа показываем рядом */
  @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);
    /* ВАЖНО: объявляем контейнер */
    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; /* базово — колонка */
  }

  .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;
  }

  /* Container Query: реагируем на ширину контейнера, а не окна */
  @container (min-width: 480px) {
    .card {
      grid-template-columns: 180px 1fr;
      align-items: stretch;
    }

    .media {
      height: auto; /* теперь высота определяется карточкой */
      min-height: 100%;
    }
  }

  /* И чуть более "богатый" режим */
  @container (min-width: 620px) {
    .card {
      grid-template-columns: 220px 1fr;
    }
  }
</style>

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

  • 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() — все современные браузеры ✅
0 просмотров

Комментарии

Загрузка комментариев...