← Blog
explainx / blog

Modern CSS Features: A Complete Guide to CSS in 2026

Modern CSS 2026 guide: container queries, cascade layers, CSS nesting, :has() selector, custom properties, color functions, and CSS as a serious engineering tool.

5 min readExplainX Team
CSSWeb DevelopmentFrontendModern CSSContainer QueriesCSS Layers

Includes frontmatter plus an attribution block so copies credit explainx.ai and the canonical URL.

Modern CSS Features: A Complete Guide to CSS in 2026

CSS has undergone a renaissance. In 2026, it's no longer just a styling language—it's a serious engineering tool with features that rival what preprocessors offered for over a decade. Container queries, cascade layers, native nesting, :has() selector, and modern color functions have fundamentally changed how we build maintainable, scalable design systems.

This comprehensive guide covers the modern CSS landscape based on 2026 production patterns, with practical examples and adoption insights from real-world teams.

The CSS Evolution: What Changed?

Before (2015-2020)

/* Media queries only knew viewport */
@media (min-width: 768px) {
  .card { width: 50%; }
}

/* Specificity wars */
.card.card.card { color: red; } /* Hack to increase specificity */

/* No nesting */
.card { }
.card__header { }
.card__title { }

After (2026)

/* Container queries know parent size */
@container (min-width: 400px) {
  .card { width: 50%; }
}

/* Cascade layers manage priority */
@layer components {
  .card { color: blue; }
}

/* Native nesting */
.card {
  &__header {
    &__title { }
  }
}

Container Queries: The Game Changer

Container queries solve the fundamental problem with media queries: they only know about viewport size, not the container a component lives in.

The Problem with Media Queries

<!-- Same card component in two contexts -->
<aside>
  <card /> <!-- 300px wide (sidebar) -->
</aside>

<main>
  <card /> <!-- 800px wide (main content) -->
</main>

With media queries:

/* Both cards get same styles based on viewport */
@media (min-width: 768px) {
  .card { display: grid; grid-template-columns: 1fr 1fr; }
}
/* Breaks when card is in narrow sidebar on wide viewport! */

The Solution: Container Queries

/* Define container */
.sidebar,
.main-content {
  container-type: inline-size;
  container-name: card-container;
}

/* Query container, not viewport */
.card {
  /* Mobile-first: single column */
  display: grid;
  grid-template-columns: 1fr;
}

@container card-container (min-width: 400px) {
  .card {
    /* Two columns when container is wide enough */
    grid-template-columns: 1fr 1fr;
  }
}

Result: Card adapts to its container, not viewport. Truly modular components.

Container Query Units

.sidebar {
  container-type: inline-size;
}

.card {
  /* Container query units */
  padding: 2cqw;        /* 2% of container width */
  font-size: 4cqi;      /* 4% of container inline size */
  gap: 1cqh;            /* 1% of container height */
  margin: 2cqmin;       /* 2% of container's smaller dimension */
}

Real-World Example: Responsive Card Grid

.grid {
  container-type: inline-size;
  display: grid;
  gap: 1rem;
  grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
}

.card {
  background: white;
  border-radius: 8px;
  padding: 1rem;
}

/* Compact layout (narrow containers) */
@container (max-width: 350px) {
  .card {
    &__image { display: none; }
    &__title { font-size: 1rem; }
    &__description { display: none; }
  }
}

/* Medium layout */
@container (min-width: 351px) and (max-width: 600px) {
  .card {
    &__image { height: 150px; }
    &__title { font-size: 1.25rem; }
  }
}

/* Expanded layout (wide containers) */
@container (min-width: 601px) {
  .card {
    display: grid;
    grid-template-columns: 200px 1fr;
    gap: 1.5rem;

    &__image { height: 100%; }
    &__title { font-size: 1.5rem; }
  }
}

Browser Support (2026)

BrowserSupport
Chrome 105+✅ Full
Edge 105+✅ Full
Safari 16+✅ Full
Firefox 110+✅ Full

Adoption: 78% of production websites use container queries in 2026.

Cascade Layers: Taming Specificity

Cascade layers (@layer) let you explicitly define priority order for different CSS sources, independent of specificity.

The Problem: Specificity Wars

/* Base styles */
.button { background: blue; }

/* Component library */
.btn-primary { background: green; }

/* Your overrides */
.button.button.button { background: red; } /* Hack! */

/* Eventually... */
.button { background: orange !important; } /* Defeat */

The Solution: Cascade Layers

/* Define layer order (later = higher priority) */
@layer reset, base, components, utilities;

/* Reset layer (lowest priority) */
@layer reset {
  * { margin: 0; padding: 0; }
}

/* Base layer */
@layer base {
  button { background: blue; }
}

/* Component layer */
@layer components {
  .btn-primary { background: green; }
}

/* Utility layer (highest priority) */
@layer utilities {
  .bg-red { background: red; } /* Wins, even with lower specificity */
}

Key insight: Layer order trumps specificity. A simple class in a higher layer beats complex selectors in lower layers.

Real-World Example: Design System

@layer reset, tokens, base, components, utilities;

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

@layer tokens {
  :root {
    --color-primary: oklch(60% 0.15 250);
    --spacing-md: 1rem;
  }
}

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

@layer components {
  .card {
    padding: var(--spacing-md);
    background: white;
    border-radius: 8px;
  }

  .button {
    padding: 0.5rem 1rem;
    background: var(--color-primary);
    color: white;
    border: none;
  }
}

@layer utilities {
  .hidden { display: none; } /* Utility wins over component styles */
  .p-0 { padding: 0; }
}

Nested Layers

@layer framework {
  @layer reset, base, components;

  @layer reset {
    /* Framework reset */
  }

  @layer components {
    /* Framework components */
  }
}

/* Your app styles (higher priority than entire framework) */
@layer app {
  .custom-button { /* Overrides framework */ }
}

Native CSS Nesting

CSS nesting is now native—no Sass required.

Syntax (2026)

/* Modern nesting syntax */
.card {
  padding: 1rem;
  background: white;

  & h2 {
    font-size: 1.5rem;
    color: navy;
  }

  & p {
    color: gray;
  }

  &:hover {
    box-shadow: 0 4px 8px rgba(0,0,0,0.1);
  }

  &__header {
    border-bottom: 1px solid #eee;
  }

  @media (min-width: 768px) {
    padding: 2rem;
  }
}

Comparison: Sass vs Native CSS

FeatureSassNative CSS (2026)
Basic nesting
& parent selector
@media nesting
Compilation needed✅ Yes❌ No
Browser supportN/A✅ All modern
Mixins
Loops

Migration path: Many teams are dropping Sass for vanilla CSS + PostCSS in 2026.

The :has() Parent Selector

The :has() selector enables parent styling based on children—previously impossible without JavaScript.

Examples

Style parent based on child existence:

/* Highlight card if it contains an image */
.card:has(img) {
  border: 2px solid gold;
}

/* Style form if it has errors */
form:has(.error) {
  border-color: red;
}

/* Grid layout only if enough items */
.container:has(.item:nth-child(4)) {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
}

Sibling selectors:

/* Style heading if followed by paragraph */
h2:has(+ p) {
  margin-bottom: 0.5rem;
}

/* Style list item if it contains a checked checkbox */
li:has(input[type="checkbox"]:checked) {
  text-decoration: line-through;
  opacity: 0.6;
}

Complex patterns:

/* Article with featured image gets special layout */
article:has(.featured-image) {
  display: grid;
  grid-template-columns: 1fr 2fr;

  .featured-image {
    grid-row: 1 / -1;
  }
}

/* Navigation with active item gets different styling */
nav:has(.active) {
  background: var(--color-primary);
  color: white;
}

Browser Support (2026)

BrowserSupport
Chrome 105+✅ Full
Safari 15.4+✅ Full
Firefox 121+✅ Full
Edge 105+✅ Full

Adoption: Universal support achieved 2023-2024; widespread production use by 2026.

Modern Color Functions

CSS color has evolved beyond hex codes and rgb().

oklch() - Perceptually Uniform Colors

Why oklch(): Better than HSL—perceptually uniform (equal lightness steps look equally different to humans).

:root {
  /* oklch(lightness chroma hue) */
  --color-primary: oklch(60% 0.15 250);      /* Blue */
  --color-primary-light: oklch(80% 0.15 250); /* Lighter (same hue) */
  --color-primary-dark: oklch(40% 0.15 250);  /* Darker (same hue) */
}

/* Generate palette */
.btn-primary { background: var(--color-primary); }
.btn-primary:hover { background: var(--color-primary-dark); }

color-mix() - Blend Colors

:root {
  --color-base: oklch(60% 0.15 250);
}

/* Mix with white for tints */
.light-variant {
  background: color-mix(in oklch, var(--color-base) 20%, white);
}

/* Mix with black for shades */
.dark-variant {
  background: color-mix(in oklch, var(--color-base) 80%, black);
}

/* Mix two theme colors */
.accent {
  color: color-mix(in oklch, var(--color-primary), var(--color-secondary));
}

Relative Colors

:root {
  --color-primary: oklch(60% 0.15 250);
}

/* Derive variations */
.lighter {
  /* Increase lightness by 20% */
  background: oklch(from var(--color-primary) calc(l + 0.2) c h);
}

.saturated {
  /* Increase chroma */
  background: oklch(from var(--color-primary) l calc(c + 0.05) h);
}

.rotated {
  /* Rotate hue */
  background: oklch(from var(--color-primary) l c calc(h + 60));
}

Enhanced Custom Properties

Registered Custom Properties (@property)

Define custom properties with types and fallbacks:

@property --rotation {
  syntax: '<angle>';
  initial-value: 0deg;
  inherits: false;
}

@property --color-primary {
  syntax: '<color>';
  initial-value: blue;
  inherits: true;
}

/* Now animatable! */
.box {
  transform: rotate(var(--rotation));
  transition: --rotation 0.3s;
}

.box:hover {
  --rotation: 45deg; /* Smooth animation */
}

Container Query Length Units with Custom Properties

.card {
  container-type: inline-size;

  --padding-scale: 2; /* Configurable */
  padding: calc(var(--padding-scale) * 1cqw);
}

Logical Properties: International-Ready CSS

Logical properties adapt to writing direction (LTR, RTL, vertical).

/* Old way (assumes LTR) */
.box {
  margin-left: 1rem;
  padding-right: 2rem;
  border-top: 1px solid;
}

/* Modern way (writing-mode aware) */
.box {
  margin-inline-start: 1rem;   /* Left in LTR, right in RTL */
  padding-inline-end: 2rem;     /* Right in LTR, left in RTL */
  border-block-start: 1px solid; /* Top in horizontal, left in vertical */
}

Mapping:

PhysicalLogical
margin-leftmargin-inline-start
margin-rightmargin-inline-end
margin-topmargin-block-start
margin-bottommargin-block-end
widthinline-size
heightblock-size

Subgrid: True Grid Alignment

Subgrid lets nested grids align with parent grid tracks.

.page-layout {
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  gap: 1rem;
}

.card {
  grid-column: span 4;
  display: grid;
  grid-template-columns: subgrid; /* Inherit parent's 4 columns */

  &__image { grid-column: span 2; }
  &__content { grid-column: span 2; }
}

Real-World Design System (2026)

Combining modern features:

@layer reset, tokens, base, components, utilities;

@layer tokens {
  @property --color-primary {
    syntax: '<color>';
    initial-value: oklch(60% 0.15 250);
    inherits: true;
  }

  :root {
    --spacing-unit: 0.25rem;
    --font-base: system-ui, sans-serif;
  }
}

@layer base {
  * { box-sizing: border-box; }

  body {
    font-family: var(--font-base);
    margin: 0;
  }
}

@layer components {
  .card {
    container-type: inline-size;
    background: white;
    border-radius: 8px;
    padding: calc(var(--spacing-unit) * 4);

    &:has(.featured) {
      border: 2px solid var(--color-primary);
    }

    @container (min-width: 400px) {
      display: grid;
      grid-template-columns: 200px 1fr;
      gap: calc(var(--spacing-unit) * 6);
    }

    &__title {
      color: oklch(from var(--color-primary) calc(l - 0.2) c h);
      font-size: clamp(1rem, 3cqi, 2rem);
    }
  }
}

@layer utilities {
  .hidden { display: none; }
  .sr-only {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
  }
}

Performance Considerations

FeaturePerformance Impact
Container queriesMinimal (optimized in engines)
Cascade layersNone (compile-time)
:has()Moderate (avoid deep nesting)
Custom propertiesMinimal (small overhead)
color-mix()Minimal (computed once)

Best practices:

  • Avoid deeply nested :has() queries (e.g., :has(.a :has(.b :has(.c))))
  • Define @property in CSS, not JavaScript
  • Use container-type: inline-size (not size) when possible (better performance)

Browser Support Summary (2026)

FeatureChromeFirefoxSafariEdge
Container queries105+110+16+105+
Cascade layers99+97+15.4+99+
CSS nesting112+117+16.5+112+
:has()105+121+15.4+105+
oklch()111+113+15.4+111+
color-mix()111+113+16.2+111+
@property85+128+16.4+85+
Subgrid117+71+16+117+

All modern features have full support in 2026.

Migrating from Sass/Less

What you lose:

  • Mixins (use CSS custom properties + utility classes)
  • Loops (move to PostCSS or design tokens)
  • Conditionals (not needed with layers + custom properties)

What you gain:

  • No build step (faster dev)
  • Browser DevTools work perfectly
  • Smaller bundles (no runtime overhead)
  • Modern features (container queries, :has())

Recommended stack (2026):

  • Native CSS for styling
  • PostCSS for vendor prefixes, minification
  • Lightning CSS for fast processing
  • Design tokens (JSON) for theming

Conclusion

CSS in 2026 is a serious engineering tool. With container queries, cascade layers, :has(), and modern color functions, you can build maintainable, scalable design systems without preprocessors.

Key takeaways:

  • Container queries enable truly modular responsive components
  • Cascade layers eliminate specificity wars
  • :has() unlocks parent styling without JavaScript
  • oklch() and color-mix() provide sophisticated color systems
  • Native nesting removes need for Sass in many projects

Start adopting these features—they're universally supported and production-ready.

For AI-powered CSS generation, explore the MCP ecosystem and agent skills for design system automation.

Resources

Further reading:

Happy styling!

Related posts