Name-Only Containers: The Scoping We Needed

Chris Coyier Chris Coyier on

I’ve done my time thinking about scope in CSS. I’ve done it my whole career, and did a presentation on it recently that I wrote up in full here. That was on the heels of @scope becoming a thing in CSS, which is, naturally, a part of the scope in CSS story.

I don’t entirely dislike @scope, but I guess I’m comfortable saying I’m disappointed in it. It can do three things, all of which I find quite niche:

  1. Donut scoping
  2. Proximity specificity
  3. DOM blasters

I covered these in my talk. They have their uses, but again: niche.

The kind of scoping that I want in CSS is the kind that we’ve been given by tools like <style scoped> in .vue files and Svelte components, and more broadly, what CSS modules does.

I want to write:

.card {
}Code language: CSS (css)

And have it turned into something like:

[data-style="apio087df"] {
  /* Or, .card-apio087df */
  /* But I kinda like the data-attribute approach
     better because it leaves the original class alone */
}Code language: CSS (css)

(And have that attribute applied to my HTML so the new selector works.)

The reason I want that is that I don’t want to worry about a class name I’m writing conflicting with an existing class. I just don’t want to think about it. Ever, ideally.

I don’t need this on every project I touch; I want it on large-scale projects with lots of components and a high number of style changes maintained over many years.

To me, that’s a scoped style.

I already get it with CSS modules, and that’s fine. But I’m a big fan when the web platform steps in and helps us do things we’d otherwise use a build process and tooling to do. That’s what we didn’t get with @scope.

Another alternative is just making all your class names unique. This works on the vast majority of projects and requires no technology we didn’t have pretty much since HTML and CSS began life.

If class name scoping is all you or I ever do, that’s OK. I can live with that.

But wait, name-only containers?

I read in the Safari 26.4 release notes that Safari is now supporting name-only containers. Like this:

/* Name a container */
.sidebar {
  container-name: sidebar;
  container-type: inline-size;
}

/* Write styles with that name only, no conditions */
@container sidebar {
  .card {
    padding: 1rem;
  }
}Code language: CSS (css)

No conditions? Isn’t the whole point of a container to style based on conditions (like, 98% of the time, being how wide it is)?

Well, not if the only effect we want from this is scoping! (!!!)

Components typically already have unique names.

Because components typically live in folders and folders have to have different names, components already have a forced uniqueness constraint.

Let’s just consider three. Here’s three design system (ds) components:

  • <ds-card>
  • <ds-article>
  • <ds-header>

Styles in Components

Each of them has styles that get bundled into global CSS:

  • ds-card.css
  • ds-article.css
  • ds-header.css

We’re not talking shadow DOM and web components here, I’m talking very generally about any design system of components, regardless of framework.

Both a card and an article can very easily have a title. It’s entirely reasonable to write a class like:

/* ds-card.css */
.title {
  background: rebeccapurple;
  color: white;
}Code language: CSS (css)
/* ds-article.css */
.title {
  font-weight: 300;
  letter-spacing: -0.01em;
}Code language: CSS (css)

Just some contrived styles there. Those will overlap and both apply because of the identical class names in use.

Usually that’s not what we want. Usually we avoid this by just career-long muscle memory of knowing this and perhaps some BEM methodology or nesting.

ds-card {
  .title {
    /* Title styles unique to the card */
  }
}Code language: CSS (css)

That’s artificial specificity boosting just to avoid future trouble. Not the end of the world, but not ideal.

It feels nicer not to think about it, which is what we get in a CSS modules approach. The styles can’t clash because they are programmatically randomized. And we don’t have to nest either, meaning we’re not bumping up specificity just for scoping.

Scoping Styles in Components

Let’s say we use the name of the component as the CSS container-name for every component.

ds-card {
  container-name: ds-card;
}
ds-article {
  container-name: ds-article;
}
ds-header {
  container-name: ds-header;
}Code language: CSS (css)

Now in the stylesheet for each of those components (which is again, probably bundled and put into global scope like regular CSS).

@container ds-card {

}
@container ds-article {

}
@container ds-header {
  
}Code language: CSS (css)

Now I can do literally anything I want inside those @container blocks and it will not globally conflict.

@container ds-card {
  .title {
    background: rebeccapurple;
    color: white;
  }
}
@container ds-article {
  .title {
    font-weight: 300;
    letter-spacing: -0.01em;
  }
}
@container ds-header {
  
}Code language: CSS (css)

No conflicts there. Same class name, but scoped inside the relevant containers.

That’s it! That’s the scoping power we want.

And I’m fairly certain… no side effects. The WebKit blog post uses a container-type: inline-size, which would have side effects, but in my testing, that doesn’t seem necessary.

Demo

Name-only container styles I belive are only support in Safari 26.4+, so here’s hoping for broader support soon.

This is related to several other ideological approaches I’m already a fan of:

Pretty satisfying to see this evolve.

Let’s see Chrome & Firefox pick up these name-only containers, and let’s see Safari pick up import type assertions plz thx.

Wanna learn modern CSS layout?

Leave a Reply

Your email address will not be published. Required fields are marked *

$966,000

Frontend Masters donates to open source projects through thanks.dev and Open Collective, as well as donates to non-profits like The Last Mile, Annie Canons, and Vets Who Code.