HTML / CSS

Elevate Your Frontend Skills with the Latest Techniques and Features in CSS, July 2023

CSS has shipped more meaningful features in the last two years than in the previous decade. Container queries, style queries, nesting, layers — these aren...

15 Oct 2023

Elevate Your Frontend Skills with the Latest Techniques and Features in CSS, July 2023

CSS has shipped more meaningful features in the last two years than in the previous decade. Container queries, style queries, nesting, layers — these aren't incremental improvements. They change how you architect stylesheets.

Here are the features I find most impactful.

1. Container Queries

Media queries respond to the viewport. Container queries respond to the parent element. This is the difference between "how wide is the screen?" and "how wide is the space I'm in?"

Text
.card-container {
  container-type: inline-size;
}

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

Why this matters: Components can now be truly reusable. A card component adapts to its container whether it's in a sidebar or a full-width layout. No more writing media queries that assume a specific page structure.

The catch: You need to explicitly set container-type on the parent. It's one more thing to remember in your component architecture.

2. Style Queries

Style queries let you apply styles based on a container's computed styles — not just its size.

Text
@container style(--theme: dark) {
  .card {
    background: #1a1a1a;
    color: white;
  }
}

This is powerful for theming. Instead of cascading classes down the DOM, you set a custom property on a container and all descendants can query it.

The catch: Browser support is still emerging. Use as progressive enhancement, not as your primary theming strategy.

3. CSS Nesting

Native CSS nesting. No preprocessor needed.

Text
.card {
  padding: 1rem;

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

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

If you've used Sass or Less, this feels natural. The syntax is slightly different (the & is required in more places), but the mental model is the same.

Benefit: Fewer files, clearer relationships between parent and child styles.

Cost: Deep nesting creates specificity problems. The same discipline you needed in Sass applies here.

4. CSS Layers (@layer)

Layers give you explicit control over the cascade. Define layers in order, and styles in earlier layers are always overridden by later layers — regardless of specificity.

Text
@layer base, components, overrides;

@layer base {
  a { color: blue; }
}

@layer overrides {
  a { color: red; }
}

Why this matters: Third-party CSS, reset styles, and your own components can coexist without specificity wars. Layer order determines the winner, not selector weight.

5. :has() Selector

The "parent selector" CSS never had. :has() lets you style an element based on its children or subsequent siblings.

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

This was previously impossible in CSS. You'd need JavaScript to add a class to the parent when a child's state changed.

Benefit: Eliminates a class of JavaScript-for-styling hacks.

Cost: Performance can degrade with overly broad :has() selectors. Keep them scoped.

The Big Picture

These features together represent a shift from "CSS as a styling language" to "CSS as a component architecture tool." Container queries, style queries, and :has() give CSS the contextual awareness it's always lacked. Nesting and layers give you the organizational tools to manage complexity.

The trade-off is learning curve. Modern CSS is significantly more powerful than it was five years ago — but it also requires more architectural thinking.