{"id":9223,"date":"2026-04-07T09:15:10","date_gmt":"2026-04-07T14:15:10","guid":{"rendered":"https:\/\/frontendmasters.com\/blog\/?p=9223"},"modified":"2026-04-07T10:21:31","modified_gmt":"2026-04-07T15:21:31","slug":"name-only-containers-the-scoping-we-needed","status":"publish","type":"post","link":"https:\/\/frontendmasters.com\/blog\/name-only-containers-the-scoping-we-needed\/","title":{"rendered":"Name-Only Containers: The Scoping We Needed"},"content":{"rendered":"\n<p>I&#8217;ve done my time thinking about scope in CSS. I&#8217;ve done it my whole career, and did a presentation on it recently that <a href=\"https:\/\/frontendmasters.com\/blog\/scope-in-css\/\">I wrote up in full here<\/a>. That was on the heels of <a href=\"https:\/\/frontendmasters.com\/blog\/how-to-scope-css-now-that-its-baseline\/\"><code>@scope<\/code> becoming a thing in CSS<\/a>, which is, naturally, a part of the scope in CSS story. <\/p>\n\n\n\n<p>I don&#8217;t entirely dislike <code>@scope<\/code>, but I guess I&#8217;m comfortable saying I&#8217;m disappointed in it. It can do three things, all of which I find <em>quite<\/em> niche:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Donut scoping<\/li>\n\n\n\n<li>Proximity specificity<\/li>\n\n\n\n<li>DOM blasters<\/li>\n<\/ol>\n\n\n\n<p><a href=\"https:\/\/frontendmasters.com\/blog\/scope-in-css\/\">I covered these<\/a> in my talk. They have their uses, but again: <em>niche<\/em>. <\/p>\n\n\n\n<p>The kind of scoping that I want in CSS is the kind that we&#8217;ve been given by tools like <code>&lt;style scoped&gt;<\/code> in <code>.vue<\/code> files and Svelte components, and more broadly, <a href=\"https:\/\/github.com\/css-modules\/css-modules\">what CSS modules does<\/a>. <\/p>\n\n\n\n<p>I want to write:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-class\">.card<\/span> {\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>And have it turned into something like:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-attr\">&#91;data-style=<span class=\"hljs-string\">\"apio087df\"<\/span>]<\/span> {\n  <span class=\"hljs-comment\">\/* Or, .card-apio087df *\/<\/span>\n  <span class=\"hljs-comment\">\/* But I kinda like the data-attribute approach\n     better because it leaves the original class alone *\/<\/span>\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>(And have that attribute applied to my HTML so the new selector works.)<\/p>\n\n\n\n<p>The reason I want that is that I don&#8217;t want to worry about a class name I&#8217;m writing conflicting with an existing class. I just don&#8217;t want to think about it. Ever, ideally.<\/p>\n\n\n\n<p><span style=\"box-sizing: border-box; margin: 0px; padding: 0px;\">I don&#8217;t need this on&nbsp;<em>every<\/em>&nbsp;project I touch; I&nbsp;<em>want<\/em>&nbsp;it on large-scale projects with lots of components and a high number of style changes maintained over many years.<\/span><\/p>\n\n\n\n<p>To me, <strong>that&#8217;s a scoped style.<\/strong><\/p>\n\n\n\n<p>I already get it with CSS modules, and that&#8217;s fine. But I&#8217;m a big fan when the web platform steps in and helps us do things we&#8217;d otherwise use a build process and tooling to do. That&#8217;s what we didn&#8217;t get with <code>@scope<\/code>.<\/p>\n\n\n\n<p>Another alternative is just making all your class names unique. This works on the vast majority of projects and requires no technology we didn&#8217;t have pretty much since HTML and CSS began life.<\/p>\n\n\n\n<p>If class name scoping is all you or I ever do, that&#8217;s OK. I can live with that. <\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-large is-resized\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"576\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2026\/04\/Scope.010.webp?resize=1024%2C576&#038;ssl=1\" alt=\"\" class=\"wp-image-9225\" style=\"width:659px;height:auto\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2026\/04\/Scope.010.webp?resize=1024%2C576&amp;ssl=1 1024w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2026\/04\/Scope.010.webp?resize=300%2C169&amp;ssl=1 300w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2026\/04\/Scope.010.webp?resize=768%2C432&amp;ssl=1 768w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2026\/04\/Scope.010.webp?w=1536&amp;ssl=1 1536w\" sizes=\"auto, (max-width: 1000px) 100vw, 1000px\" \/><\/figure>\n<\/div>\n\n\n<h2 class=\"wp-block-heading\">But wait, name-only containers?<\/h2>\n\n\n\n<p>I read in <a href=\"https:\/\/webkit.org\/blog\/17862\/webkit-features-for-safari-26-4\/\">the Safari 26.4 release notes<\/a> that Safari is now supporting name-only containers. Like this:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-comment\">\/* Name a container *\/<\/span>\n<span class=\"hljs-selector-class\">.sidebar<\/span> {\n  <span class=\"hljs-attribute\">container-name<\/span>: sidebar;\n  <span class=\"hljs-attribute\">container-type<\/span>: inline-size;\n}\n\n<span class=\"hljs-comment\">\/* Write styles with that name only, no conditions *\/<\/span>\n<span class=\"hljs-keyword\">@container<\/span> sidebar {\n  <span class=\"hljs-selector-class\">.card<\/span> {\n    <span class=\"hljs-attribute\">padding<\/span>: <span class=\"hljs-number\">1rem<\/span>;\n  }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>No conditions? Isn&#8217;t the whole point of a container to style based on conditions (like, 98% of the time, being how wide it is)?<\/p>\n\n\n\n<p>Well, not if the only effect we want from this is scoping! (!!!)<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Components typically already have unique names.<\/h2>\n\n\n\n<p>Because components typically live in folders and folders have to have different names, components already have a forced uniqueness constraint.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large is-resized\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"391\" height=\"1024\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2026\/04\/Screenshot-2026-04-04-at-3.00.51-PM.png?resize=391%2C1024&#038;ssl=1\" alt=\"\" class=\"wp-image-9226\" style=\"width:333px;height:auto\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2026\/04\/Screenshot-2026-04-04-at-3.00.51-PM.png?resize=391%2C1024&amp;ssl=1 391w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2026\/04\/Screenshot-2026-04-04-at-3.00.51-PM.png?resize=115%2C300&amp;ssl=1 115w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2026\/04\/Screenshot-2026-04-04-at-3.00.51-PM.png?resize=587%2C1536&amp;ssl=1 587w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2026\/04\/Screenshot-2026-04-04-at-3.00.51-PM.png?w=710&amp;ssl=1 710w\" sizes=\"auto, (max-width: 391px) 100vw, 391px\" \/><\/figure>\n\n\n\n<p>Let&#8217;s just consider three. Here&#8217;s three design system (ds) components:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>&lt;ds-card&gt;<\/code><\/li>\n\n\n\n<li><code>&lt;ds-article&gt;<\/code><\/li>\n\n\n\n<li><code>&lt;ds-header&gt;<\/code><\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Styles in Components<\/h2>\n\n\n\n<p>Each of them has styles that get bundled into global CSS:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>ds-card.css<\/li>\n\n\n\n<li>ds-article.css<\/li>\n\n\n\n<li>ds-header.css<\/li>\n<\/ul>\n\n\n\n<p class=\"learn-more\">We&#8217;re not talking shadow DOM and web components here, I&#8217;m talking very generally about any design system of components, regardless of framework. <\/p>\n\n\n\n<p>Both a card and an article can very easily have a title. It&#8217;s entirely reasonable to write a class like:<\/p>\n\n\n\n<div class=\"wp-block-columns is-layout-flex wp-container-core-columns-is-layout-9d6595d7 wp-block-columns-is-layout-flex\">\n<div class=\"wp-block-column is-layout-flow wp-block-column-is-layout-flow\"><pre class=\"wp-block-code\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-comment\">\/* ds-card.css *\/<\/span>\n<span class=\"hljs-selector-class\">.title<\/span> {\n  <span class=\"hljs-attribute\">background<\/span>: rebeccapurple;\n  <span class=\"hljs-attribute\">color<\/span>: white;\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre><\/div>\n\n\n\n<div class=\"wp-block-column is-layout-flow wp-block-column-is-layout-flow\"><pre class=\"wp-block-code\" aria-describedby=\"shcb-language-5\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-comment\">\/* ds-article.css *\/<\/span>\n<span class=\"hljs-selector-class\">.title<\/span> {\n  <span class=\"hljs-attribute\">font-weight<\/span>: <span class=\"hljs-number\">300<\/span>;\n  <span class=\"hljs-attribute\">letter-spacing<\/span>: -<span class=\"hljs-number\">0.01em<\/span>;\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-5\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre><\/div>\n<\/div>\n\n\n\n<p>Just some contrived styles there. Those will overlap and both apply because of the identical class names in use. <\/p>\n\n\n\n<p><em>Usually<\/em> that&#8217;s not what we want. <em>Usually<\/em> we avoid this by just career-long muscle memory of knowing this and perhaps some BEM methodology or nesting.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-6\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-tag\">ds-card<\/span> {\n  .title {\n    <span class=\"hljs-comment\">\/* Title styles unique to the card *\/<\/span>\n  }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-6\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>That&#8217;s artificial specificity boosting just to avoid future trouble. Not the end of the world, but not ideal.<\/p>\n\n\n\n<p>It feels nicer not to think about it, which is what we get in a CSS modules approach. The styles can&#8217;t clash because they are programmatically randomized. And we don&#8217;t have to nest either, meaning we&#8217;re not bumping up specificity just for scoping.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Scoping Styles in Components<\/h2>\n\n\n\n<p>Let&#8217;s say we use the name of the component as the CSS <code>container-name<\/code> for every component.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-7\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-tag\">ds-card<\/span> {\n  <span class=\"hljs-attribute\">container-name<\/span>: ds-card;\n}\n<span class=\"hljs-selector-tag\">ds-article<\/span> {\n  <span class=\"hljs-attribute\">container-name<\/span>: ds-article;\n}\n<span class=\"hljs-selector-tag\">ds-header<\/span> {\n  <span class=\"hljs-attribute\">container-name<\/span>: ds-header;\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-7\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Now in the stylesheet for each of those components (which is again, probably bundled and put into global scope like regular CSS).<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-8\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-keyword\">@container<\/span> ds-card {\n\n}\n<span class=\"hljs-keyword\">@container<\/span> ds-article {\n\n}\n<span class=\"hljs-keyword\">@container<\/span> ds-header {\n  \n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-8\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Now I can do <em>literally anything I want<\/em> inside those <code>@container<\/code> blocks and it will not globally conflict.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-9\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-keyword\">@container<\/span> ds-card {\n  <span class=\"hljs-selector-class\">.title<\/span> {\n    <span class=\"hljs-attribute\">background<\/span>: rebeccapurple;\n    <span class=\"hljs-attribute\">color<\/span>: white;\n  }\n}\n<span class=\"hljs-keyword\">@container<\/span> ds-article {\n  <span class=\"hljs-selector-class\">.title<\/span> {\n    <span class=\"hljs-attribute\">font-weight<\/span>: <span class=\"hljs-number\">300<\/span>;\n    <span class=\"hljs-attribute\">letter-spacing<\/span>: -<span class=\"hljs-number\">0.01em<\/span>;\n  }\n}\n<span class=\"hljs-keyword\">@container<\/span> ds-header {\n  \n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-9\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>No conflicts there. Same class name, but scoped inside the relevant containers.<\/p>\n\n\n\n<p>That&#8217;s it! That&#8217;s the scoping power we want. <\/p>\n\n\n\n<p>And I&#8217;m fairly certain&#8230; no side effects. The WebKit blog post uses a <code>container-type: inline-size<\/code>, which would have side effects, but in my testing, that doesn&#8217;t seem necessary.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Demo<\/h2>\n\n\n\n<p>Name-only container styles I belive are only support in Safari 26.4+, so here&#8217;s hoping for broader support soon. <\/p>\n\n\n\n<div class=\"wp-block-cp-codepen-gutenberg-embed-block cp_embed_wrapper\"><iframe id=\"cp_embed_019d59f9-e6ff-7903-9aa8-932282b0c734\" src=\"\/\/codepen.io\/editor\/anon\/embed\/019d59f9-e6ff-7903-9aa8-932282b0c734?height=450&amp;theme-id=1&amp;slug-hash=019d59f9-e6ff-7903-9aa8-932282b0c734&amp;default-tab=result\" height=\"450\" scrolling=\"no\" frameborder=\"0\" allowfullscreen allowpaymentrequest name=\"CodePen Embed 019d59f9-e6ff-7903-9aa8-932282b0c734\" title=\"CodePen Embed 019d59f9-e6ff-7903-9aa8-932282b0c734\" class=\"cp_embed_iframe\" style=\"width:100%;overflow:hidden\">CodePen Embed Fallback<\/iframe><\/div>\n\n\n\n<p>This is related to several other ideological approaches I&#8217;m already a fan of:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/frontendmasters.com\/blog\/light-dom-only\/\">Light DOM Web Components<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/frontendmasters.com\/blog\/architecture-through-component-colocation\/\">Co-Located Components<\/a><\/li>\n<\/ul>\n\n\n\n<p>Pretty satisfying to see this evolve. <\/p>\n\n\n\n<p>Let&#8217;s see Chrome &amp; Firefox pick up these name-only containers, and let&#8217;s see Safari pick up import type assertions plz thx.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>If we give a `container-name` to the root of all our unique components, we can scope styles to them with a simple @container query.<\/p>\n","protected":false},"author":1,"featured_media":9239,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"sig_custom_text":"","sig_image_type":"featured-image","sig_custom_image":0,"sig_is_disabled":false,"inline_featured_image":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[1],"tags":[471,47,7,356],"class_list":["post-9223","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-blog-post","tag-container","tag-container-queries","tag-css","tag-scope-2"],"acf":[],"jetpack_featured_media_url":"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2026\/04\/isoloation.jpg?fit=2100%2C1300&ssl=1","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/9223","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/comments?post=9223"}],"version-history":[{"count":6,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/9223\/revisions"}],"predecessor-version":[{"id":9241,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/9223\/revisions\/9241"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/media\/9239"}],"wp:attachment":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/media?parent=9223"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/categories?post=9223"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/tags?post=9223"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}