Modern CSS 2026: Container Queries, CSS Grid, Layers, and New Features

Sanjeev SharmaSanjeev Sharma
5 min read

Advertisement

Modern CSS 2026: The Platform Has Caught Up

CSS in 2026 is more powerful than any CSS-in-JS solution from 2020. Native container queries, cascade layers, :has(), nesting, and animations that were impossible before are now standard.

Container Queries: Component-Based Responsiveness

Media queries respond to the viewport. Container queries respond to the parent element:

/* Define a container */
.card-grid {
  container-type: inline-size;
  container-name: cards;
}

/* Style children based on container width */
@container cards (min-width: 600px) {
  .card {
    display: grid;
    grid-template-columns: 200px 1fr;
    gap: 1rem;
  }
}

@container cards (min-width: 900px) {
  .card {
    grid-template-columns: 280px 1fr;
  }
}

/* Named container queries */
.sidebar {
  container: sidebar / inline-size;
}

@container sidebar (width < 250px) {
  .nav-label { display: none; }
  .nav-icon { font-size: 1.5rem; }
}

CSS Cascade Layers

/* Control specificity without !important hell */
@layer reset, base, components, utilities;

@layer reset {
  *, *::before, *::after { box-sizing: border-box; }
  body { margin: 0; }
}

@layer base {
  h1 { font-size: 2rem; font-weight: 700; }
  a { color: var(--color-primary); }
}

@layer components {
  .btn {
    padding: 0.5rem 1rem;
    border-radius: 0.375rem;
    font-weight: 500;
  }

  .btn-primary {
    background: var(--color-primary);
    color: white;
  }
}

@layer utilities {
  /* Utilities always win over components */
  .text-center { text-align: center; }
  .hidden { display: none; }
}

/* Styles outside layers trump all layers */
.critical { color: red; }

:has() — The Parent Selector

/* Style parent based on children */

/* Card with image gets different layout */
.card:has(img) {
  display: grid;
  grid-template-columns: auto 1fr;
}

/* Form field with error */
.field:has(input:invalid) label {
  color: red;
}

/* Navigation with open dropdown */
nav:has(.dropdown:hover) .overlay {
  display: block;
}

/* Article with no headings gets fallback */
article:not(:has(h2, h3)) .toc {
  display: none;
}

/* Sibling relationships — style preceding elements */
.gallery:has(.item:hover) .item:not(:hover) {
  opacity: 0.5;
}

Native CSS Nesting

/* No preprocessor needed */
.card {
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 1rem;

  & .title {
    font-size: 1.25rem;
    font-weight: 600;
  }

  & .description {
    color: var(--text-muted);
  }

  &:hover {
    border-color: var(--color-primary);
    box-shadow: 0 4px 20px rgb(0 0 0 / 0.1);
  }

  /* BEM modifier */
  &--featured {
    border-color: var(--color-primary);
    background: var(--color-primary-50);
  }

  @media (max-width: 640px) {
    padding: 0.75rem;
  }
}

OKLCH Colors: Better Color Manipulation

/* OKLCH: perceptually uniform — "lighter" actually looks lighter */
:root {
  --primary: oklch(55% 0.2 250);       /* Base blue */
  --primary-light: oklch(75% 0.15 250); /* Actually lighter */
  --primary-dark: oklch(35% 0.2 250);   /* Actually darker */
  --primary-muted: oklch(55% 0.1 250);  /* Same lightness, less chroma */
}

/* Dynamic color scales */
.badge {
  background: oklch(from var(--color) l c h);       /* Same as base */
  background: oklch(from var(--color) 90% 0.05 h);  /* Light tint */
  border-color: oklch(from var(--color) 40% c h);   /* Dark border */
}

/* Color-mix */
.overlay {
  background: color-mix(in oklch, var(--primary) 20%, transparent);
}

Scroll-Driven Animations

/* Animate based on scroll position — no JavaScript! */
@keyframes reveal {
  from { opacity: 0; transform: translateY(20px); }
  to { opacity: 1; transform: translateY(0); }
}

.section {
  animation: reveal linear;
  animation-timeline: view();
  animation-range: entry 0% entry 25%;  /* Animate as element enters viewport */
}

/* Progress bar */
@keyframes grow {
  from { transform: scaleX(0); }
  to { transform: scaleX(1); }
}

.reading-progress {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 3px;
  background: var(--color-primary);
  transform-origin: left;
  animation: grow linear;
  animation-timeline: scroll();  /* Based on page scroll */
}

CSS View Transitions

/* Animate between pages/states */

/* Enable view transitions */
@view-transition {
  navigation: auto;
}

/* Customize transition for specific element */
.hero-image {
  view-transition-name: hero;  /* Must be unique */
}

@keyframes fade-in {
  from { opacity: 0; }
}

@keyframes fade-out {
  to { opacity: 0; }
}

::view-transition-old(root) {
  animation: fade-out 0.2s ease;
}

::view-transition-new(root) {
  animation: fade-in 0.2s ease;
}

/* Shared element transition (hero to detail) */
::view-transition-old(hero) {
  animation: none;
}

::view-transition-new(hero) {
  animation: none;
}

Anchor Positioning

/* Position tooltip relative to any element */
.trigger {
  anchor-name: --trigger;
}

.tooltip {
  position: absolute;
  position-anchor: --trigger;
  bottom: anchor(top);         /* Align tooltip bottom to trigger top */
  left: anchor(center);
  translate: -50% -0.5rem;
}

/* Automatic overflow handling */
.dropdown {
  position-try-fallbacks: flip-block, flip-inline, flip-block flip-inline;
}

CSS Grid Advanced Patterns

/* Subgrid — child grids align to parent columns */
.card-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 1rem;
}

.card {
  display: grid;
  grid-row: span 3;
  grid-template-rows: subgrid;  /* Inherit parent row sizing */
}

/* All cards: title, description, and button align perfectly */
.card .title { grid-row: 1; }
.card .body { grid-row: 2; }
.card .footer { grid-row: 3; margin-top: auto; }

/* Masonry-style layout */
.masonry {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
  grid-template-rows: masonry;  /* Experimental but shipping 2026 */
}

Browser Support Summary (2026)

FeatureChromeFirefoxSafariGlobal
Container Queries✓ 105+✓ 110+✓ 16+93%
:has()✓ 105+✓ 121+✓ 15.4+90%
CSS Nesting✓ 120+✓ 117+✓ 17.2+88%
Scroll Animations✓ 115+✓ 110+Partial75%
View Transitions✓ 111+Partial✓ 18+82%
Anchor Positioning✓ 125+PartialPartial70%

Modern CSS reduces the need for JavaScript by 30-40% for UI interactions. Start with container queries and :has() today — they solve real layout problems.

Advertisement

Sanjeev Sharma

Written by

Sanjeev Sharma

Full Stack Engineer · E-mopro