Zurück zum Blog
7 Min. LesezeitFrontend

Modern CSS Features in 2025: Transforming How You Style

Explore cutting-edge CSS features like container queries, cascade layers, and has() selector that are revolutionizing how we build responsive, maintainable stylesheets.

Modern CSS Features in 2025: Transforming How You Style

CSS has evolved dramatically in recent years. New features are making stylesheets more powerful, maintainable, and expressive than ever before. Let's explore the game-changers that should be in every developer's toolkit.

Container Queries

Finally! Responsive design based on parent container size, not viewport:

Before: Media Queries

/* Based on viewport width */
@media (min-width: 768px) {
  .card {
    display: grid;
    grid-template-columns: 1fr 2fr;
  }
}

Problem: Cards look the same regardless of where they're placed.

After: Container Queries

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

.card {
  display: block;
}

@container sidebar (min-width: 400px) {
  .card {
    display: grid;
    grid-template-columns: 1fr 2fr;
  }
}

Result: Cards adapt to their container, not the viewport.

Real-World Example

.product-grid {
  container-type: inline-size;
}

.product-card {
  /* Mobile layout by default */
  display: flex;
  flex-direction: column;
}

/* When container has space, switch to horizontal */
@container (min-width: 400px) {
  .product-card {
    flex-direction: row;
  }
}

/* When container is large, add more columns */
@container (min-width: 800px) {
  .product-card {
    grid-column: span 2;
  }
}

The :has() Selector

CSS finally has a parent selector:

Select Parent Based on Children

/* Card with an image */
.card:has(img) {
  display: grid;
  grid-template-columns: 1fr 2fr;
}

/* Card without image */
.card:not(:has(img)) {
  display: block;
}

Conditional Styling

/* Form with errors */
.form:has(.error) {
  border: 2px solid red;
}

/* Article with figures */
.article:has(figure) {
  max-width: 800px;
}

Sibling Combinations

/* List item that has a nested list */
li:has(ul) {
  font-weight: bold;
}

/* Header followed by nav */
header:has(+ nav) {
  border-bottom: 1px solid #ccc;
}

Cascade Layers (@layer)

Control specificity and organize CSS:

Before: Specificity Wars

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

/* Component styles - needs higher specificity */
.primary-button.primary-button { background: green; }

/* Utility - needs even higher specificity */
.bg-red.bg-red.bg-red { background: red !important; }

After: Cascade Layers

@layer base, components, utilities;

@layer base {
  .button { background: blue; }
}

@layer components {
  .primary-button { background: green; }
}

@layer utilities {
  .bg-red { background: red; }
}

Result: Order matters, not specificity!

Practical Example

@layer reset, design-system, components, overrides;

@layer reset {
  * { margin: 0; padding: 0; }
}

@layer design-system {
  :root {
    --color-primary: #007bff;
    --spacing-unit: 8px;
  }
}

@layer components {
  .button {
    padding: var(--spacing-unit);
    background: var(--color-primary);
  }
}

@layer overrides {
  /* Always wins, regardless of specificity */
  .special-button {
    background: red;
  }
}

Nesting

Write nested CSS like Sass, natively:

Before: Flat CSS

.card { }
.card .title { }
.card .content { }
.card:hover { }
.card.featured { }

After: Native Nesting

.card {
  /* Base styles */

  & .title {
    font-size: 1.5rem;
  }

  & .content {
    padding: 1rem;
  }

  &:hover {
    transform: scale(1.05);
  }

  &.featured {
    border: 2px solid gold;
  }
}

Complex Nesting

.navigation {
  & ul {
    list-style: none;

    & li {
      display: inline-block;

      & a {
        text-decoration: none;

        &:hover {
          color: blue;
        }
      }
    }
  }
}

Color Functions

Modern color manipulation:

color-mix()

Mix colors without preprocessors:

.button {
  --primary: #007bff;

  background: var(--primary);

  &:hover {
    /* Mix with white for lighter shade */
    background: color-mix(in srgb, var(--primary) 80%, white);
  }

  &:active {
    /* Mix with black for darker shade */
    background: color-mix(in srgb, var(--primary) 80%, black);
  }
}

oklch() and oklab()

Perceptually uniform colors:

:root {
  /* Lightness, Chroma, Hue */
  --primary: oklch(60% 0.15 270);

  /* Lighter version - just increase lightness */
  --primary-light: oklch(80% 0.15 270);

  /* Darker version */
  --primary-dark: oklch(40% 0.15 270);
}

color() Function

Use any color space:

.element {
  /* Display P3 color space (wider gamut) */
  color: color(display-p3 1 0.5 0);

  /* Fallback for older browsers */
  color: rgb(255, 127, 0);
}

Subgrid

Finally! Grid alignment across nested grids:

.grid {
  display: grid;
  grid-template-columns: 1fr 2fr 1fr;
  gap: 1rem;
}

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

Result: Child grid aligns perfectly with parent grid.

Practical Example

.product-list {
  display: grid;
  grid-template-columns: auto 1fr auto;
  gap: 1rem;
}

.product {
  display: grid;
  grid-column: span 3;
  grid-template-columns: subgrid;

  /* All product images align vertically */
  & img {
    grid-column: 1;
  }

  /* All titles align */
  & h3 {
    grid-column: 2;
  }

  /* All prices align */
  & .price {
    grid-column: 3;
  }
}

:is() and :where() Selectors

Simplify complex selectors:

:is() for Grouping

/* Before */
.header a:hover,
.footer a:hover,
.sidebar a:hover {
  color: blue;
}

/* After */
:is(.header, .footer, .sidebar) a:hover {
  color: blue;
}

:where() for Zero Specificity

/* High specificity - hard to override */
:is(.header, .footer) a {
  color: blue;
}

/* Zero specificity - easy to override */
:where(.header, .footer) a {
  color: blue;
}

/* This works because :where() has 0 specificity */
.custom-link {
  color: red;
}

Scroll-Driven Animations

Animate based on scroll position:

@keyframes fade-in {
  from {
    opacity: 0;
    transform: translateY(20px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

.reveal {
  animation: fade-in linear;
  animation-timeline: view();
  animation-range: entry 0% cover 30%;
}

Scroll Progress Indicator

.progress {
  position: fixed;
  top: 0;
  width: 100%;
  height: 4px;
  background: linear-gradient(to right, blue var(--scroll), transparent 0);
  animation: scroll linear;
  animation-timeline: scroll();
}

@property Custom Properties

Typed, animatable CSS variables:

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

.animated-gradient {
  background: linear-gradient(
    var(--gradient-angle),
    blue,
    purple
  );
  animation: rotate-gradient 3s linear infinite;
}

@keyframes rotate-gradient {
  to {
    --gradient-angle: 360deg;
  }
}

Color Transitions

@property --color {
  syntax: '<color>';
  initial-value: red;
  inherits: false;
}

.box {
  background: var(--color);
  transition: --color 1s;
}

.box:hover {
  --color: blue;
}

View Transitions API

Smooth page transitions:

/* Define transition */
::view-transition-old(root),
::view-transition-new(root) {
  animation-duration: 0.3s;
}

/* Customize specific elements */
.card {
  view-transition-name: card;
}

::view-transition-old(card) {
  animation: fade-out 0.3s;
}

::view-transition-new(card) {
  animation: fade-in 0.3s;
}
// Trigger transition in JS
document.startViewTransition(() => {
  // Update DOM
  updateContent();
});

Logical Properties

Direction-agnostic layouts:

/* Before: Physical properties */
.element {
  margin-left: 1rem;
  margin-right: 1rem;
  border-top: 1px solid;
}

/* After: Logical properties */
.element {
  margin-inline: 1rem;  /* Left/right in LTR, right/left in RTL */
  border-block-start: 1px solid;  /* Top in LTR, bottom in RTL */
}

Full Example

.card {
  /* Padding on left/right (inline axis) */
  padding-inline: 2rem;

  /* Padding on top/bottom (block axis) */
  padding-block: 1rem;

  /* Margin at end of inline direction */
  margin-inline-end: 1rem;

  /* Border at start of block direction */
  border-block-start: 1px solid #ccc;
}

Aspect Ratio

Maintain proportions easily:

/* Before */
.video-container {
  position: relative;
  padding-bottom: 56.25%; /* 16:9 aspect ratio */
}

.video-container iframe {
  position: absolute;
  width: 100%;
  height: 100%;
}

/* After */
.video-container {
  aspect-ratio: 16 / 9;
}

.video-container iframe {
  width: 100%;
  height: 100%;
}

Responsive Images

.image {
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
}

Browser Support

Most modern features are well-supported:

/* Feature detection */
@supports (container-type: inline-size) {
  .card {
    /* Use container queries */
  }
}

@supports not (container-type: inline-size) {
  .card {
    /* Fallback to media queries */
  }
}

Conclusion

Modern CSS is incredibly powerful. Features like container queries, :has(), and cascade layers solve problems that previously required JavaScript or complex workarounds.

Start adopting these features today:

  1. Container queries for truly responsive components
  2. :has() for parent-child relationships
  3. Cascade layers for organized, maintainable CSS
  4. Native nesting for better developer experience

The future of CSS is here. Are you using it?

👨‍💻

Jordan Patel

Webentwickler & Technologie-Enthusiast