Physical Address
304 North Cardinal St.
Dorchester Center, MA 02124
Physical Address
304 North Cardinal St.
Dorchester Center, MA 02124

@scope Does the rule finally give developers the confidence to write CSS that can keep up with modern interfaces?
By learning the principles of basic CSS, you are taught to write modular, reusable, and descriptive styles to ensure maintainability. But when developers get involved with real-world applications, it often seems impossible to add UI features without styles leaking into unwanted areas.
This problem often becomes a self-fulfilling snowball; Styles that are theoretically limited to one element or class start appearing where they don’t belong. This forces the developer to create even more specific selectors to override filtered styles, which then accidentally override global styles, and so on.
Rigid class naming conventions, such as GOODThey are a theoretical solution to this problem. He BEM Methodology (Block, Element, Modifier) it’s a systematic way to name CSS classes to ensure reusability and structure within CSS files. Naming conventions like this can Reduce cognitive load by leveraging domain language to describe elements and their state.and if implemented correctly, It can make styles for large applications easier to maintain..
However, in the real world this is not always the case. Priorities can change, and with change, implementation becomes inconsistent. Small changes to the HTML structure can require many CSS class name revisions. With highly interactive UI applications, class names that follow the BEM pattern can become long and unwieldy (e.g. app-user-overview__status--is-authenticating), and not fully complying with the nomenclature rules breaks the structure of the system, thus denying its benefits.
Given these challenges, it’s no surprise that developers have turned to frameworks, with Tailwind being the most popular CSS framework. Instead of trying to fight what seems like an unwinnable war of specificity between styles, it’s easier to give up the CSS Cascade and use tools that guarantee total isolation.
How do we know that some developers want to avoid cascading styles? It is the rise of “modern” front-end tools, such as CSS frameworks in JS — designed specifically for that purpose. Working with isolated styles that are strictly limited to specific components can seem like a breath of fresh air. Eliminates the need to name things. remains one of the most hated and time-consuming front-end tasks – and allows developers to be productive without fully understanding or taking advantage of the benefits of CSS inheritance.
But getting rid of CSS Cascade comes with its own problems. For example, composing styles in JavaScript requires heavy compilation configurations and often leads to styles that are awkwardly intermixed with component or HTML markup. Instead of carefully considered naming conventions, we allow build tools to automatically generate selectors and identifiers (e.g. .jsx-3130221066), requiring developers to keep up with another pseudolanguage itself. (As if the cognitive load of understanding what all your components are useEffectWhat we did was no longer enough!)
Further abstracting the work of naming classes for tools means that basic debugging is often limited to specific application versions compiled for development, rather than taking advantage of native browser features that support live debugging, such as developer tools.
Fortunately, modern CSS features not only make writing standard CSS more flexible, but they also give developers like us a lot more power to manage the cascade and make it work for us. CSS Cascading Layers are a great example, but there is another feature that receives a surprising lack of attention, although that is changing now that it has recently become Baseline compatible.
@scope In orderI consider the css @scope in order is a potential cure for the kind of style leak-induced anxiety we’ve covered, one that doesn’t force us to compromise the advantages of the native web for additional abstractions and compilation tools.
“He
@scopeCSS at-rule allows you to select elements in specific DOM subtrees, targeting elements precisely without writing overly specific selectors that are difficult to override and without coupling your selectors too closely to the DOM structure.”— MDN
In other words, we can work with isolated styles in specific instances. without sacrificing inheritance, cascading, or even basic separation of concerns that’s been a long-standing guiding principle of front-end development.
Furthermore, it has excellent browser coverage. In fact, Firefox 146 additional support for @scope in December, doing so Baseline compatible for the first time. Here is a simple comparison between a button that uses the BEM pattern versus the @scope ruler:
He @scope the rule allows precision with less complexity. The developer no longer needs to create boundaries using class names, which in turn allows them to write selectors based on native HTML elements, thus eliminating the need for prescriptive CSS class naming patterns. By simply eliminating the need to manage class names, @scope can alleviate the fear associated with CSS in large projects.
To get started, add the @scope rule to your CSS and insert a root selector to which the styles will be applied:
@scope () {
/* Styles scoped to the */
}
So for example, if we were to apply styles to a
@scope (nav) {
a { /* Link styles within nav scope */ }
a:active { /* Active link styles */ }
a:active::before { /* Active link with pseudo-element for extra styling */ }
@media (max-width: 768px) {
a { /* Responsive adjustments */ }
}
}
This, by itself, is not a groundbreaking feature. However, a second argument can be added to the scope to create a lower limiteffectively defining the start and end points of the scope.
/* Any `a` element inside `ul` will not have the styles applied */
@scope (nav) to (ul) {
a {
font-size: 14px;
}
}
This practice is called donut reachand there are several approaches one could use, including a series of similar, highly specific selectors tightly coupled to the DOM structure, a :not pseudo-selector, or assign specific class names to elements within the
Regardless of these other approaches, the @scope The method is much more concise. More importantly, it avoids the risk of broken styles if class names change or are used incorrectly or if the HTML structure is modified. Now that @scope is compatible with Baseline, we no longer need workarounds!
We can take this idea further with multiple trailing limits to create a “figure-of-eight style”:
Compare that to a version handled without the @scope rule, where the developer has to “reset” the styles to their default values:
main a {
font-size: 14px;
}
main p {
line-height: 16px;
color: darkgrey;
}
main aside a,
main nav a {
font-size: inherit; /* or whatever the default should be */
}
main aside p,
main nav p {
line-height: inherit; /* or whatever the default should be */
color: inherit; /* or a specific color */
}
Look at the following example. Do you realize how easy it is to target some nested selectors and exempt others?
See the Pen (@scope (forked)) example (https://codepen.io/smashingmag/pen/wBWXggN) by Blake Lundquist.
Consider a scenario where unique styles need to be applied to the slotted content inside web components. When you insert content into a web component, that content becomes part of the Shadow DOM, but still inherits styles from the main document. The developer may want to implement different styles depending on the web component in which the content is inserted:
Jane Doe
Jane Doe
In this example, the developer might want have different styles only if it is represented within :
@scope (team-roster) {
user-card {
display: inline-flex;
align-items: center;
gap: 0.5rem;
}
user-card img {
border-radius: 50%;
width: 40px;
height: 40px;
}
}
There are additional ways that @scope you can eliminate the need for class management without resorting to utilities or JavaScript-generated class names. For example, @scope opens the possibility of easily target descendants of any selectornot just class names:
/* Only div elements with a direct child button are included in the root scope */
@scope (div:has(> button)) {
p {
font-size: 14px;
}
}
and they can be nestedcreating scopes within scopes:
@scope (main) {
p {
font-size: 16px;
color: black;
}
@scope (section) {
p {
font-size: 14px;
color: blue;
}
@scope (.highlight) {
p {
background-color: yellow;
font-weight: bold;
}
}
}
}
Additionally, the root scope can be easily referenced within the @scope ruler:
/* Applies to elements inside direct child `section` elements of `main`, but stops at any direct `aside` that is a direct chiled of those sections */
@scope (main > section) to (:scope > aside) {
p {
background-color: lightblue;
color: blue;
}
/* Applies to ul elements that are immediate siblings of root scope */
:scope + ul {
list-style: none;
}
}
He @scope at-rule also introduces a new proximity dimension to CSS specificity resolution. In traditional CSS, when two selectors match the same element, the selector with higher specificity wins. With @scopeWhen two elements have the same specificity, the one whose reach root is closest to the matching element wins. This eliminates the need to override parent styles by manually increasing the specificity of an element, as internal components naturally override external element styles.
Utility CSS frameworks, such as Tailwind, work well for prototyping and smaller projects. However, their benefits quickly diminish when used in larger projects involving more than a couple of developers.
Front-end development has become increasingly complicated in recent years and CSS is no exception. While the @scope The rule is not a panacea, but it can reduce the need for complex tools. When used instead of or in conjunction with strategic class naming, @scope can make it easier and more fun to write maintainable CSS.