Back to Blog
·4 min read

Modern CSS Features You Should Be Using

Discover powerful CSS features that have gained widespread browser support and can dramatically simplify your stylesheets. From container queries to the :has() selector, here's what you need to know.

CSSWeb DevelopmentFrontend

CSS has evolved dramatically over the past few years. Features that once required JavaScript or complex workarounds are now native to the language. Let's explore the most impactful modern CSS features you should start using today.

Container Queries

Container queries are a game-changer for component-based design. Unlike media queries that respond to the viewport, container queries let components adapt based on their parent container's size.

.card-container {
  container-type: inline-size;
  container-name: card;
}
 
@container card (min-width: 400px) {
  .card {
    display: grid;
    grid-template-columns: 200px 1fr;
    gap: 1rem;
  }
}
 
@container card (max-width: 399px) {
  .card {
    display: flex;
    flex-direction: column;
  }
}

This means your card component can intelligently reflow whether it's in a narrow sidebar or a wide main content area - no JavaScript required.

The :has() Selector

Often called the "parent selector," :has() is one of CSS's most requested features. It lets you style an element based on its descendants.

/* Style a card differently if it contains an image */
.card:has(img) {
  padding-top: 0;
}
 
/* Highlight form fields with errors */
.form-group:has(input:invalid) {
  border-color: red;
}
 
/* Style navigation when a submenu is open */
nav:has(.submenu:hover) {
  background: var(--nav-active-bg);
}

Practical Example: Dynamic Form Labels

/* Float label up when input has content */
.input-group:has(input:not(:placeholder-shown)) label {
  transform: translateY(-100%);
  font-size: 0.75rem;
  color: var(--accent-color);
}

CSS Nesting

Native CSS nesting is finally here! No more preprocessors required for this beloved feature.

.card {
  background: white;
  border-radius: 8px;
 
  & .header {
    padding: 1rem;
    border-bottom: 1px solid #eee;
  }
 
  & .content {
    padding: 1rem;
  }
 
  &:hover {
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
  }
 
  @media (prefers-color-scheme: dark) {
    background: #1a1a1a;
  }
}

Note: The & is optional for most cases in modern browsers, but it's required when your nested selector starts with a letter (like element selectors).

Subgrid

Subgrid allows nested grid items to align with their parent grid's tracks. This solves the long-standing problem of aligning content across cards.

.grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 2rem;
}
 
.card {
  display: grid;
  grid-template-rows: subgrid;
  grid-row: span 3; /* Card spans 3 rows */
}

Now all your card headers, content, and footers align perfectly across the row, regardless of content length.

The color-mix() Function

Create color variations without preprocessors or JavaScript:

:root {
  --primary: #6366f1;
  --primary-light: color-mix(in srgb, var(--primary) 70%, white);
  --primary-dark: color-mix(in srgb, var(--primary) 70%, black);
  --primary-transparent: color-mix(in srgb, var(--primary) 50%, transparent);
}
 
.button {
  background: var(--primary);
 
  &:hover {
    background: var(--primary-dark);
  }
 
  &:disabled {
    background: var(--primary-light);
  }
}

View Transitions API

While technically a JavaScript API, view transitions are CSS-powered and enable smooth page transitions:

/* Default crossfade for all elements */
::view-transition-old(root),
::view-transition-new(root) {
  animation-duration: 0.3s;
}
 
/* Custom animation for specific elements */
.hero-image {
  view-transition-name: hero;
}
 
::view-transition-old(hero) {
  animation: slide-out 0.3s ease-out;
}
 
::view-transition-new(hero) {
  animation: slide-in 0.3s ease-out;
}

Scroll-Driven Animations

Tie animations to scroll progress without JavaScript:

@keyframes fade-in {
  from {
    opacity: 0;
    transform: translateY(20px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}
 
.animate-on-scroll {
  animation: fade-in linear;
  animation-timeline: view();
  animation-range: entry 0% cover 30%;
}

This creates a scroll-triggered fade-in effect that's buttery smooth and performant.

Wrapping Up

Modern CSS has become incredibly powerful. These features reduce our reliance on JavaScript for presentational logic and make our stylesheets more maintainable.

My recommendation? Start incorporating these features into your projects today. Browser support is strong (check caniuse.com for specifics), and they'll genuinely improve your development experience.

What's your favorite modern CSS feature? I'd love to hear how you're using these in your projects.

🚀

Let's Work Together

Got an exciting project? I'd love to hear about it. Let's chat and make something amazing together.

hello@danhobson.co.uk
or find me on