{"id":5434,"date":"2025-03-20T10:32:23","date_gmt":"2025-03-20T15:32:23","guid":{"rendered":"https:\/\/frontendmasters.com\/blog\/?p=5434"},"modified":"2025-04-01T10:48:33","modified_gmt":"2025-04-01T15:48:33","slug":"one-thing-scope-can-do-is-reduce-concerns-about-source-order","status":"publish","type":"post","link":"https:\/\/frontendmasters.com\/blog\/one-thing-scope-can-do-is-reduce-concerns-about-source-order\/","title":{"rendered":"One Thing @scope Can Do is Reduce Concerns About Source Order"},"content":{"rendered":"\n<p>There is an already-classic <code>@scope<\/code> demo about theme colors. Let&#8217;s recap that and then I&#8217;ll show how it relates to any situation with modifier classes. (The <code>@scope<\/code> rule is a newish feature in CSS that is everywhere-but-Firefox, but is in Interop 2025, so shouldn&#8217;t be too long to be decently usable.)<\/p>\n\n\n\n<p>There are lots of different ways to implement color themes, but imagine a way where you do it with class names. I think it&#8217;s a valid way to do it, rather than, say, <a href=\"https:\/\/frontendmasters.com\/blog\/no-fuss-light-dark-modes\/\">only responding to system preferences<\/a>, because the classes might give you some on-page control. So you apply a theme at the top level. Then perhaps you have <em>some<\/em> elements that have <em>other<\/em> themes. Perhaps your site footer is <em>always<\/em> dark. <\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">body<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"theme-light\"<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">main<\/span>&gt;<\/span>\n    ...\n  <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">main<\/span>&gt;<\/span>\n   \n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">footer<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"site-footer theme-dark\"<\/span>&gt;<\/span>\n    <span class=\"hljs-symbol\">&amp;copy;<\/span>2025 <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">a<\/span> <span class=\"hljs-attr\">href<\/span>=<span class=\"hljs-string\">\"\/\"<\/span>&gt;<\/span>Frontend Masters<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">a<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">footer<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">body<\/span>&gt;<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>You set up those classes with colors, including <em>other<\/em> elements that need colors depending on that theme.<\/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-class\">.theme-dark<\/span> {\n  <span class=\"hljs-attribute\">background<\/span>: black;\n  <span class=\"hljs-attribute\">color<\/span>: white;\n\n  a {\n    <span class=\"hljs-attribute\">color<\/span>: <span class=\"hljs-number\">#90caf9<\/span>;\n  }\n}\n\n<span class=\"hljs-selector-class\">.theme-light<\/span> {\n  <span class=\"hljs-attribute\">background<\/span>: white;\n  <span class=\"hljs-attribute\">color<\/span>: black;\n\n  a {\n    <span class=\"hljs-attribute\">color<\/span>: <span class=\"hljs-number\">#1976d2<\/span>;\n  }\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><strong>There is already a problem with the HTML and CSS above.<\/strong> <\/p>\n\n\n\n<p>The <code>&lt;a&gt;<\/code> in the footer will have the color of the light theme, not the dark theme. This is that classic <code>@scope<\/code> demo you&#8217;re likely to see a lot (sorry). This is because of <strong>source order.<\/strong> The selector <code>.theme-light a<\/code> has the exact same specificity as <code>.theme-dark a<\/code> but the light theme comes after so it &#8220;wins&#8221;.<\/p>\n\n\n\n<p>One change to the above CSS will fix 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 shcb-code-table\"><mark class='shcb-loc'><span><span class=\"hljs-keyword\">@scope<\/span> (.theme-dark) {\n<\/span><\/mark><span class='shcb-loc'><span>  <span class=\"hljs-selector-tag\">background<\/span>: <span class=\"hljs-selector-tag\">black<\/span>;\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-selector-tag\">color<\/span>: <span class=\"hljs-selector-tag\">white<\/span>;\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-selector-tag\">a<\/span> {\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-attribute\">color<\/span>: <span class=\"hljs-number\">#90caf9<\/span>;\n<\/span><\/span><span class='shcb-loc'><span>  }\n<\/span><\/span><span class='shcb-loc'><span>}\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><mark class='shcb-loc'><span><span class=\"hljs-keyword\">@scope<\/span> (.theme-light) {\n<\/span><\/mark><span class='shcb-loc'><span>  <span class=\"hljs-selector-tag\">background<\/span>: <span class=\"hljs-selector-tag\">white<\/span>;\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-selector-tag\">color<\/span>: <span class=\"hljs-selector-tag\">black<\/span>;\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-selector-tag\">a<\/span> {\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-attribute\">color<\/span>: <span class=\"hljs-number\">#1976d2<\/span>;\n<\/span><\/span><span class='shcb-loc'><span>  }\n<\/span><\/span><span class='shcb-loc'><span>}\n<\/span><\/span><\/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>This is referred to as <em><strong>proximity.<\/strong><\/em><strong> <\/strong>It&#8217;s like a new part of the cascade (Bramus has <a href=\"https:\/\/developer.chrome.com\/docs\/css-ui\/at-scope\">a nice diagram here<\/a>). Above, because the specificity is the same in both cases, the closer-in-the-DOM proximity &#8220;wins&#8221;. And closer meaning &#8220;fewest generational or sibling-element hops&#8221;. Like:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"373\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/CleanShot-2025-03-20-at-07.12.40%402x.png?resize=1024%2C373&#038;ssl=1\" alt=\"\" class=\"wp-image-5436\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/CleanShot-2025-03-20-at-07.12.40%402x.png?resize=1024%2C373&amp;ssl=1 1024w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/CleanShot-2025-03-20-at-07.12.40%402x.png?resize=300%2C109&amp;ssl=1 300w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/CleanShot-2025-03-20-at-07.12.40%402x.png?resize=768%2C280&amp;ssl=1 768w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/CleanShot-2025-03-20-at-07.12.40%402x.png?resize=1536%2C560&amp;ssl=1 1536w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/CleanShot-2025-03-20-at-07.12.40%402x.png?w=1552&amp;ssl=1 1552w\" sizes=\"auto, (max-width: 1000px) 100vw, 1000px\" \/><\/figure>\n\n\n\n<p>So, appropriately, the link styling nested under <code>@scope (.theme-dark)<\/code>wins because the specificity is the same but the proximity of the <code>theme-dark<\/code> class is closer. <\/p>\n\n\n\n<p>What I like about this is that now the source order for those themes doesn&#8217;t matter. That&#8217;s nice as sometimes that&#8217;s hard to control. A good bundler should maintain source order after building, but perhaps these &#8220;variation classes&#8221; are in different files and the way they get built and loaded isn&#8217;t entirely predictable. Perhaps some lazy loading gets involved or the built files are purposefully spit or who-knows-what. I&#8217;ve seen too many <em>&#8220;it&#8217;s fine on dev but broken on prod&#8221;<\/em> bugs for one lifetime.<\/p>\n\n\n\n<p>Color themes was just an excuse to look at variation classes.<\/p>\n\n\n\n<p>Here&#8217;s another example:<\/p>\n\n\n<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-selector-class\">.card-big<\/span> {\n  <span class=\"hljs-attribute\">padding<\/span>: <span class=\"hljs-number\">2rem<\/span>;\n}\n\n<span class=\"hljs-selector-class\">.card<\/span> {\n  <span class=\"hljs-attribute\">padding<\/span>: <span class=\"hljs-number\">1rem<\/span>;\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>\n\n\n<p>Without even looking at HTML, you might consider this a &#8220;mistake&#8221; because, probably, <code>card-big<\/code> is a variation class of <code>.card<\/code>, except the thing that <em>should be an override<\/em> (the <code>padding<\/code>) won&#8217;t actually be overridden because of source order and equal specificity. I&#8217;d guess we all have some muscle memory for just ordering variation classes properly so this isn&#8217;t a problem, but it&#8217;s not ideal to me that we have to remember that, and that build tools and loading strategies might interfere anyway.<\/p>\n\n\n\n<p>Here&#8217;s some real-world-ish CSS I was playing with where I could use <code>@scope<\/code> to put my variation class first without worry:<\/p>\n\n\n<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-keyword\">@scope<\/span> (.card-big) {\n  <span class=\"hljs-selector-pseudo\">:scope<\/span> {\n    <span class=\"hljs-attribute\">grid-column<\/span>: span <span class=\"hljs-number\">2<\/span>;\n    <span class=\"hljs-attribute\">display<\/span>: flex;\n\n    img {\n      <span class=\"hljs-attribute\">width<\/span>: <span class=\"hljs-number\">50%<\/span>;\n      <span class=\"hljs-attribute\">height<\/span>: <span class=\"hljs-number\">100%<\/span>;\n    }\n  }\n}\n\n<span class=\"hljs-selector-class\">.card<\/span> {\n  img {\n    <span class=\"hljs-attribute\">width<\/span>: <span class=\"hljs-number\">100%<\/span>;\n    <span class=\"hljs-attribute\">aspect-ratio<\/span>: <span class=\"hljs-number\">16<\/span> \/ <span class=\"hljs-number\">9<\/span>;\n    <span class=\"hljs-attribute\">object-fit<\/span>: cover;\n  }\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>\n\n\n<p>Now if I&#8217;ve got two cards&#8230;<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-6\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"card\"<\/span>&gt;<\/span>\n  ...\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"card card-big\"<\/span>&gt;<\/span>\n  ...\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-6\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>I can rest easy knowing the <code>.card-big<\/code> styles will apply and appropriately override because of the proximity of the class to the things I want to style (including itself!)<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"473\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/Screenshot-2025-03-20-at-7.41.17%E2%80%AFAM.png?resize=1024%2C473&#038;ssl=1\" alt=\"\" class=\"wp-image-5439\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/Screenshot-2025-03-20-at-7.41.17%E2%80%AFAM.png?resize=1024%2C473&amp;ssl=1 1024w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/Screenshot-2025-03-20-at-7.41.17%E2%80%AFAM.png?resize=300%2C138&amp;ssl=1 300w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/Screenshot-2025-03-20-at-7.41.17%E2%80%AFAM.png?resize=768%2C354&amp;ssl=1 768w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/Screenshot-2025-03-20-at-7.41.17%E2%80%AFAM.png?resize=1536%2C709&amp;ssl=1 1536w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/Screenshot-2025-03-20-at-7.41.17%E2%80%AFAM.png?resize=2048%2C945&amp;ssl=1 2048w\" sizes=\"auto, (max-width: 1000px) 100vw, 1000px\" \/><\/figure>\n","protected":false},"excerpt":{"rendered":"<p>There is an already-classic @scope demo about theme colors. Let&#8217;s recap that and then I&#8217;ll show how it relates to any situation with modifier classes. (The @scope rule is a newish feature in CSS that is everywhere-but-Firefox, but is in Interop 2025, so shouldn&#8217;t be too long to be decently usable.) There are lots of [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":5516,"comment_status":"closed","ping_status":"closed","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":[243,7,157],"class_list":["post-5434","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-blog-post","tag-scope","tag-css","tag-dark-theme"],"acf":[],"jetpack_featured_media_url":"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2025\/03\/One-Thing-Scope-Can-Do.jpg?fit=2280%2C1352&ssl=1","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/5434","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=5434"}],"version-history":[{"count":6,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/5434\/revisions"}],"predecessor-version":[{"id":5443,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/5434\/revisions\/5443"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/media\/5516"}],"wp:attachment":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/media?parent=5434"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/categories?post=5434"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/tags?post=5434"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}