One of the most common and challenging issues faced by front-end engineers is CSS naming conventions. With the popularity of the Block Element Modifier (BEM) approach, many people have become accustomed to organizing their styles according to a maintainable pattern.
The upcoming implementation of @scope
in Chrome will further improve the performance of BEM by allowing block-level scoping of styles in stylesheets. This will make stylesheets easier to maintain while providing tighter control over CSS cascading.
In this article, we will show how to use the @scope
attribute in Chrome and how to use it to replace BEM in front-end projects. We’ll walk you through a few examples, which you can view and follow in the example project on GitHub.
What is CSS @scope?
In the upcoming Chrome 118 release, the @scope
attribute creates a block-level scope for CSS styles. This gives developers more control over CSS styling, as we can now explicitly define scopes for different parts of the view directly in the CSS file.
Take a look at the following HTML example:
<main className="sample-page"> <h1>With Scope</h1> <section className="first-section"> <p>some text</p> <p> some text and then a <a href="/">back link</a> </p> </section> <section className="second-section"> <h2>Dog Picture</h2> <div> <p>second section paragraph text</p> </div> <img src={'./DOG_1.jpg'} alt="dog" /> </section> </main>
In this HTML, we can style the elements within the second-section
style area using:
.second-section { display: flex; flex-direction: column; border: solid; padding: 40px; margin: 20px; } @scope (.second-section) { h2 { text-align: center; } img { max-width: 400px; max-height: 100%; } div { display: flex; justify-content: center; margin: 20px; } p { max-width: 200px; text-align: center; background-color: pink; color: forestgreen; padding: 10px; border-radius: 20px; font-size: 24px; } }
When using @scope
, you can also create a “donut” scope that defines the beginning and end of a set of styles and the elements within it. Using the same HTML as above, donut scope can define styles from the starting area of sample-page
to the second-section
style area:
/* donut scope */ @scope (.sample-page) to (.second-section) { p { font-size: 24px; background-color: forestgreen; color: pink; text-align: center; padding: 10px; } a { color: red; font-size: 28px; text-transform: uppercase; } }
The best part is that it functions very similarly to using BEM shapes, but with less code.
Browser supports CSS @scope
As of October 2, 2023, CSS @scope has not been officially released, so you need to turn on the experimental network features flag to use it. To do this, first open a tab in Chrome, go to chrome://flags/
, then search for and enable the “Experimental Web Platform Features” flag:
What is BEM
BEM is a way of grouping styles in an HTML view for easy navigation.
Consider that a large HTML page has many elements with different styles. After setting a few initial style names, it becomes difficult to maintain the style as the page expands. BEM attempts to alleviate this problem by building style names around the actual styles.
block
is a containing HTML element. Consider HTML like this
<main className="sample-page"> <h1 className="sample-page__title">With BEM</h1> <section className="sample-page__first-section"> <p className="sample-page__first-section--first_line"> some text </p> <p className="sample-page__first-section--second-line"> some text and then a{' '} <a className="sample-page__first-section--second-line-link" href="/" > back link </a> </p> </section> </main>
In this HTML
-
The block =
sample-page
style is the block style because it wraps a group of elements -
element = When styling the
element, the element is treated as an
element
, so an extra__
is added to the style name , thereby creatingsample-page__title
. The same goes forsample-page__first-section
.
Modifier =
when inside a
When the
element is styled, the style name will have an extra --first-line
, creating sample-page__first-section--first-line
, so:
-
The block is sample-page
-
The element is first-section
-
The modifier is first-line
BEM scales well, especially when using SASS to group styles and create similar content using the & amp; operator:
.sample-page { display: flex; flex-direction: column; margin-top: 10px; &__title { font-size: 48px; color: forestgreen; } &__first-section { font-size: 24px; border: solid; padding: 40px; margin: 20px; &--first-line{ font-size: 24px; background-color: forestgreen; color: pink; text-align: center; padding: 10px; } } }
The difficulty is that in a large project this results in very large CSS or SASS files that are still difficult to manage at scale. You can use @scope
to replace BEM styles to make style definitions smaller and more manageable.
Reconstruct BEM using @scope
The best way to demonstrate the benefits of using @scope
is to use @scope in an application that uses a mainstream framework or library like React. In the example app on GitHub, there is a project in the react-example
folder with pages first styled using BEM and then using @scope
Refactor.
You can run the application and click the WithBEM or WithScope button to see the implementation. Components and stylesheets have corresponding names, prefixed with WithBEM or WithScope , located in the pages and styles folders respectively.
Starting from the BEM style component WithBEMPage.tsx
, we first see the HTML style designed with the BEM method:
<main className="sample-page"> <h1 className="sample-page__title">With BEM</h1> <section className="sample-page__first-section"> <p className="sample-page__first-section--first_line"> some text </p> <p className="sample-page__first-section--second-line"> some text and then a{' '} <a className="sample-page__first-section--second-line-link" href="/" > back link </a> </p> </section> <section className="sample-page__second-section"> <h2 className="sample-page__second-section--title"> Dog Picture </h2> <div className="sample-page__second-section--div"> <p className="sample-page__second-section--div-paragraph"> second section paragraph text </p> </div> <img className="sample-page__second-section--image" src={'./DOG_1.jpg'} alt="dog" /> </section> </main>
In the component WithScopePage.tsx
we can see how clean the refactoring is with the following:
<main className="sample-page"> <h1>With Scope</h1> <section className="first-section"> <p>some text</p> <p> some text and then a <a href="/">back link</a> </p> </section> <section className="second-section"> <h2>Dog Picture</h2> <div> <p>second section paragraph text</p> </div> <img src={'./DOG_1.jpg'} alt="dog" /> </section> </main>
To refactor BEM to @scope
, just find the style group and add your scope styles appropriately. Let’s take a look at the title part first. In the original WithBEMPage.tsx
file, different styles are defined for each section. In the @scope
version, a more concise style is defined for specific elements:
.sample-page { display: flex; flex-direction: column; margin-top: 10px; } /* replaced */ /* .sample-page__title { font-size: 48px; color: forestgreen; } */ /* donut scope */ @scope (.sample-page) to (.first-section) { h1 { font-size: 48px; color: forestgreen; } }
Again, in the first part of the content, the original BEM style looks like this:
.sample-page__first-section { font-size: 24px; border: solid; padding: 40px; margin: 20px; } .sample-page__first-section--first_line { font-size: 24px; background-color: forestgreen; color: pink; text-align: center; padding: 10px; } .sample-page__first-section--second-line { font-size: 24px; background-color: forestgreen; color: pink; text-align: center; padding: 10px; } .sample-page__first-section--second-line-link { color: red; font-size: 28px; text-transform: uppercase; }
Refactoring the first part using @scope
, we now have a cleaner style definition:
.first-section { font-size: 24px; border: solid; padding: 40px; margin: 20px; } /* donut scope */ @scope (.sample-page) to (.second-section) { p { font-size: 24px; background-color: forestgreen; color: pink; text-align: center; padding: 10px; } a { color: red; font-size: 28px; text-transform: uppercase; } }
Another benefit of this is that the HTML view is smaller and easier to read. considering before
<section className="sample-page__first-section"> <p className="sample-page__first-section--first_line"> some text </p> <p className="sample-page__first-section--second-line"> some text and then a{' '} <a className="sample-page__first-section--second-line-link" href="/" > back link </a> </p> </section>
Then
<section className="first-section"> <p>some text</p> <p> some text and then a <a href="/">back link</a> </p> </section>
With these two example components, we can refactor each part. Finally notice how it makes the style cleaner and more readable.
Other advantages of @scope over BEM
In addition to the advantages of refactoring BEM into @scope
, using @scope
also provides greater control over CSS cascading. CSS cascade is an algorithm that defines how web browsers handle the style conditions that make up the elements on an HTML page.
When working on any front-end project, developers may need to deal with strange results due to style cascading. By using @scope
, you can control the side effects of cascading by tightly scoping elements.
The styles and some elements of the file no_scope.html
are defined as follows:
<!DOCTYPE html> <html> <head> <title>Plain HTML</title> <style> .light { background: #ccc; } .dark { background: #333; } .light a { color: red; } .dark a { color: yellow; } div { padding: 2rem; } div > div { margin: 0 0 0 2rem; } p { margin: 0 0 2rem 0; } </style> </head> <body> <div class="light"> <p><a href="#">First Level</a></p> <div class="dark"> <p><a href="#">Second Level</a></p> <div class="light"> <p><a href="#">Third Level</a></p> </div> </div> </div> </body> </html>
The result is as follows:
The problem here is that according to the defined CSS, the Third Level should be red text, not yellow. This is a side effect of CSS cascading because page styles are interpreted in terms of appearance order, so the Third Level is considered yellow instead of red. With the diagram in the original Bram.us article, we can see the order in which the CSS cascade evaluates selectors and styles:
Without @scop
e , the CSS cascade goes directly from “specificity” to “appearance order”. When using @scope, the CSS cascade will consider the @scope
element first. You can see the effect by adding @scope
to the .light
and .dark
styles in the example.
First, modify the original HTML and CSS as follows:
<!DOCTYPE html> <html> <head> <title>Plain HTML</title> <style> .light { background: #ccc; } .dark { background: #333; } div { padding: 2rem; } div > div { margin: 0 0 0 2rem; } p { margin: 0 0 2rem 0; } @scope (.light) { :scope { background: white; } a { color: red; } } @scope (.dark) { :scope { background: black; } a { color: yellow; } } </style> </head> <body> <div class="light"> <p><a href="#">First Level</a></p> <div class="dark"> <p><a href="#">Second Level</a></p> <div class="light"> <p><a href="#">Third Level</a></p> </div> </div> </div> </body> </html>
The output is as follows
Summary
In this article, we explore ways to refactor a BEM-style application to use the new @scope feature in Chrome. We introduced how @scope works, then refactored a simple page from BEM to @scope .
The new @scope
feature has the potential to be a big advantage for front-end developers. However, other browsers must also implement support, which may take time. Until then, this is definitely an interesting feature that could be a big help when styling front-end projects.
Welcome to long press the picture to add dishwasher as a friend and share Vue React Ts regularly.
at last:
vue2 and vue3 skills collection
VueUse source code interpretation
Because the rules of the WeChat official account have been modified, if you do not star or click “Read”, you may not receive the push of my official account’s articles. Please star this official account and remember after reading the article. Like it or Looking, thank you!