Movatterモバイル変換


[0]ホーム

URL:


Skip to content
DEV Community
Log in Create account

DEV Community

Cover image for How CSS @scope can replace BEM
LogRocket profile imageMatt Angelosanto
Matt Angelosanto forLogRocket

Posted on • Originally published atblog.logrocket.com

     

How CSS @scope can replace BEM

Written byAndrew Evans✏️

One of the most common and challenging things that frontend engineers run across is CSS naming conventions. With the popularity of the Block Element Modifier (BEM) method, many became used to organizing their styles in a maintainable pattern.

The upcoming implementation of@scope in Chrome may take the gains of BEM further by allowing block-level scoping of styles within a stylesheet. This may make styling easier to maintain, while providing tighter control of the CSS cascade that impacts any frontend application.

In this post, we will demonstrate how to use the@scope feature in Chrome and how to use it to replace BEM in frontend projects. We'll walk through several examples, all of which you can check out in thesample project on GitHub to follow along.

Jump ahead:

What is the CSS@scope?

In the upcomingChrome 118 release, the@scope feature creates block-level scoping of CSS styles. This gives developers more control over CSS styles, as we can now specifically define the scope for sections of a view directly in CSS files.

Consider the following example HTML:

<mainclassName="sample-page"><h1>With Scope</h1><sectionclassName="first-section"><p>some text</p><p>            some text and then a<ahref="/">back link</a></p></section><sectionclassName="second-section"><h2>Dog Picture</h2><div><p>second section paragraph text</p></div><imgsrc={'./DOG_1.jpg'}alt="dog"/></section></main>
Enter fullscreen modeExit fullscreen mode

Within this HTML, we can style the elements within thesecond-section styled area with the following:

.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;}}
Enter fullscreen modeExit fullscreen mode

With@scope, there is also the ability to create a “donut” scope, where the start and end sections are defined for a set of styles and the elements within them. With the same HTML above, a donut scope can define styles from the start area ofsample-page up to where thesecond-section styled area occurs:

/* 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;}}
Enter fullscreen modeExit fullscreen mode

The great part about this is that it functions very similarly to what one would do with BEM styling — but with less code.

Browser support for CSS@scope

The CSS@scope has still yet to be released as of 2 October 2023, so you’ll have to turn on the experimental web features flag to use it. To do this, first open a tab in Chrome and go tochrome://flags/, then search and enable theExperimental Web Platform features flag:Chrome experimental features flag enabled Once the experimental flag is set on Chrome, adding@scope to stylesheets should work in any Chrome session.

What is BEM?

TheBlock Element Modifier (BEM) method of styling is a way to group styles within an HTML view that can be easily navigated.

Consider a large HTML page that has many elements with different styles. After setting a few initial style names, it can become difficult to maintain the styling as the page scales. BEM attempts to alleviate that by structuring your style names around what is actually being styled.

Ablock would be a containing HTML element. Consider something like this HTML:

<mainclassName="sample-page"><h1className="sample-page__title">With BEM</h1><sectionclassName="sample-page__first-section"><pclassName="sample-page__first-section--first_line">            some text</p><pclassName="sample-page__first-section--second-line">            some text and then a{' '}<aclassName="sample-page__first-section--second-line-link"href="/">                back link</a></p></section></main>
Enter fullscreen modeExit fullscreen mode

In this HTML:

  • Block = thesample-page style is theblock as it wraps a group of elements
  • Element = When styling the<h1> element that is considered anelement and as such an additional__ is added to the style name, creatingsample-page__title. The same can be said for thesample-page__first-section
  • Modifier = When styling the<p> element within the<section> element, the style name has an additional--first-line, creatingsample-page__first-section--first-line, so:
    • The (1) block issample-page
    • The (2) element isfirst-section and
    • The (3) modifier isfirst-line

BEM scales well, especially if you use SASS to wrap your styles in groups with an& operator to create something like:

.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;}}}
Enter fullscreen modeExit fullscreen mode

The challenge is that in a very large project, this produce very large CSS or SASS files that can still be difficult to manage at scale. You can replace BEM styling with@scope and make style definitions smaller and more manageable. We’ll demonstrate how this works in the next section.

Refactoring BEM to use@scope

The best way to showcase the advantages of using@scope would be in an application with one of the leading frameworks or libraries, like React. In thesample application on GitHub, there is a project in thereact-example folder that has a page styled first with BEM and then refactored version using@scope.

You can run the application and click theWithBEM orWithScope buttons to see the implementations specifically. The components and stylesheets are named accordingly, with the prefixesWithBEM orWithScope in thepages andstyles folders, respectively.

Starting with the BEM styled componentWithBEMPage.tsx, we first see HTML styled in the BEM method:

<mainclassName="sample-page"><h1className="sample-page__title">WithBEM</h1><sectionclassName="sample-page__first-section"><pclassName="sample-page__first-section--first_line">sometext</p><pclassName="sample-page__first-section--second-line">sometextandthena{''}<aclassName="sample-page__first-section--second-line-link"href="/">backlink</a></p></section><sectionclassName="sample-page__second-section"><h2className="sample-page__second-section--title">DogPicture</h2><divclassName="sample-page__second-section--div"><pclassName="sample-page__second-section--div-paragraph">secondsectionparagraphtext</p></div><imgclassName="sample-page__second-section--image"src={'./DOG_1.jpg'}alt="dog"/></section></main>
Enter fullscreen modeExit fullscreen mode

In the componentWithScopePage.tsx, we see how clean the refactor is with the following:

<mainclassName="sample-page"><h1>WithScope</h1><sectionclassName="first-section"><p>sometext</p><p>sometextandthena<ahref="/">backlink</a></p></section><sectionclassName="second-section"><h2>DogPicture</h2><div><p>secondsectionparagraphtext</p></div><imgsrc={'./DOG_1.jpg'}alt="dog"/></section></main>
Enter fullscreen modeExit fullscreen mode

To refactor from BEM into@scope, you only have to find the groups of styles and then appropriately add your scoped styles. Let’s first consider the title section. In the originalWithBEMPage.tsx file, there were different styles defined for each section. In the@scope version, there is a more succinct style definition for the 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;}}
Enter fullscreen modeExit fullscreen mode

Similarly, within the first section of content, the original BEM styles are as follows:

.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;}
Enter fullscreen modeExit fullscreen mode

Refactoring this first section with@scope, we now have a more concise 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;}}
Enter fullscreen modeExit fullscreen mode

The other nice side effect of this is that the HTML view is smaller and easier to read. Considering before:

<sectionclassName="sample-page__first-section"><pclassName="sample-page__first-section--first_line">        some text</p><pclassName="sample-page__first-section--second-line">        some text and then a{' '}<aclassName="sample-page__first-section--second-line-link"href="/">            back link</a></p></section>
Enter fullscreen modeExit fullscreen mode

Then after:

<sectionclassName="first-section"><p>some text</p><p>        some text and then a<ahref="/">back link</a></p></section>
Enter fullscreen modeExit fullscreen mode

Walking through the two sample components, one can apply the refactor to each section. Ultimately noting how it makes the styling cleaner and easier to read.

Additional benefits of@scope vs. BEM

In addition to the advantages of refactoring BEM into@scope, the use of@scope also allows for better control ofthe CSS cascade. The CSS cascade is an algorithm that defines how web browsers handle styling conditions from elements on a composed HTML page.

When working with any frontend project, developers may have to accommodate side effects from the cascade when styles produce odd results. Using@scope, one can potentially control the side effects of the cascade by tightly scoping the elements.

In the sample GitHub project, the folderhtml-css has two basic HTML files that have an example of a page impacted by the cascade. These examples were modified from those found in the Bram.us post,A Quick Introduction to CSS Scope.

The fileno_scope.html has styles and a few elements defined as follows:

<!DOCTYPE html><html><head><title>Plain HTML</title><style>.light{background:#ccc;}.dark{background:#333;}.lighta{color:red;}.darka{color:yellow;}div{padding:2rem;}div>div{margin:0002rem;}p{margin:002rem0;}</style></head><body><divclass="light"><p><ahref="#">First Level</a></p><divclass="dark"><p><ahref="#">Second Level</a></p><divclass="light"><p><ahref="#">Third Level</a></p></div></div></div></body></html>
Enter fullscreen modeExit fullscreen mode

The result is the following:CSS @scope demo The issue here is that, with the CSS that was defined, one would expectThird Level to be in red text — not yellow. This is a side effect of the CSS cascade, as the page styling is interpreted based on the appearance order, and thus theThird Level is taken to be yellow, instead of red. Taking a diagram from the original Bram.us post, one can see the order that the CSS cascade evaluates selectors and styles:A visualization of the cascade with @scope proximity injected and highlighted A visualization of the cascade with@scope proximity injected and highlighted. Without use of the@scope, the CSS cascade would go fromSpecificity directly toOrder of Appearance. With the@scope, the CSS cascade takes the@scope elements into account first. You can see this in effect by adding@scope specifically for the.light and.dark styles in the example.

First, modify the original HTML and CSS to be the following:

<!DOCTYPE html><html><head><title>Plain HTML</title><style>.light{background:#ccc;}.dark{background:#333;}div{padding:2rem;}div>div{margin:0002rem;}p{margin:002rem0;}@scope(.light){:scope{background:white;}a{color:red;}}@scope(.dark){:scope{background:black;}a{color:yellow;}}</style></head><body><divclass="light"><p><ahref="#">First Level</a></p><divclass="dark"><p><ahref="#">Second Level</a></p><divclass="light"><p><ahref="#">Third Level</a></p></div></div></div></body></html>
Enter fullscreen modeExit fullscreen mode

The result is output that looks like this:Our final @scope output

Conclusion

In this post, we explored ways that you can refactor BEM-styled applications into using the new@scope feature coming out with Chrome. We walked through how@scope works and then refactored a simple page from BEM to@scope.

The new@scope feature could potentially be a big win for frontend developers. However, other browsers will also have to implement support, which may take time. Until then, it is definitely an interesting feature and could be very helpful for styling frontend projects.


Is your frontend hogging your users' CPU?

As web frontends get increasingly complex, resource-greedy features demand more and more from the browser. If you’re interested in monitoring and tracking client-side CPU usage, memory usage, and more for all of your users in production,try LogRocket.

LogRocket Signup

LogRocket is like a DVR for web and mobile apps, recording everything that happens in your web app, mobile app, or website. Instead of guessing why problems happen, you can aggregate and report on key frontend performance metrics, replay user sessions along with application state, log network requests, and automatically surface all errors.

Modernize how you debug web and mobile apps —Start monitoring for free.

Top comments(1)

Subscribe
pic
Create template

Templates let you quickly answer FAQs or store snippets for re-use.

Dismiss
CollapseExpand
 
jodoesgit profile image
Jo
Crunchy poi dog, professional grumpy pants, eternal learner.
  • Location
    WA, USA
  • Education
    Wildcard
  • Pronouns
    he/she/they (don't give a hoot)
  • Work
    Future She-EO of So-and-Such
  • Joined

Do you know if this feature is explicit to Chrome? It seems lovely, it clears up a lot of the brainpower needed over creating/utilizing those wordy variables. I'm sure this will be adopted long-term. But I'm not sure if using @scope now will end up neutering your work, if you're only working towards the compatibility of a singular browser. It's good to put a pin in, if that's how things currently sit. At least, to the best of my knowledge.

Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment'spermalink.

For further actions, you may consider blocking this person and/orreporting abuse

Rather than spending hours/days trying to reproduce an elusive bug, you can see the reproduction in seconds with LogRocket.Try it yourself — get in touch today.

More fromLogRocket

DEV Community

We're a place where coders share, stay up-to-date and grow their careers.

Log in Create account

[8]ページ先頭

©2009-2025 Movatter.jp