{"id":947,"date":"2025-06-19T15:00:00","date_gmt":"2025-06-19T15:00:00","guid":{"rendered":"http:\/\/www.computercoursesonline.com\/?p=947"},"modified":"2025-06-19T21:03:10","modified_gmt":"2025-06-19T21:03:10","slug":"css-cascade-layers-vs-bem-vs-utility-classes-specificity-control","status":"publish","type":"post","link":"http:\/\/www.computercoursesonline.com\/index.php\/2025\/06\/19\/css-cascade-layers-vs-bem-vs-utility-classes-specificity-control\/","title":{"rendered":"CSS Cascade Layers Vs. BEM Vs. Utility Classes: Specificity Control"},"content":{"rendered":"

CSS Cascade Layers Vs. BEM Vs. Utility Classes: Specificity Control<\/title><\/p>\n<article>\n<header>\n<h1>CSS Cascade Layers Vs. BEM Vs. Utility Classes: Specificity Control<\/h1>\n<address>Victor Ayomipo<\/address>\n<p> 2025-06-19T15:00:00+00:00<br \/>\n 2025-06-19T20:32:57+00:00<br \/>\n <\/header>\n<p>CSS is wild, really wild. And tricky. But let\u2019s talk specifically about <strong>specificity<\/strong>.<\/p>\n<p>When writing CSS, it\u2019s close to impossible that you haven\u2019t faced the frustration of styles not applying as expected — that\u2019s specificity. You applied a style, it worked, and later, you try to override it with a different style and\u2026 nothing, it just ignores you. Again, specificity.<\/p>\n<p>Sure, there\u2019s the option of resorting to <code>!important<\/code> flags, but like all developers before us, it\u2019s always <a href=\"https:\/\/cssguidelin.es\/#important\">risky and discouraged<\/a>. It\u2019s way better to fully understand specificity than go down that route because otherwise you wind up fighting your own important styles.<\/p>\n<h2 id=\"specificity-101\">Specificity 101<\/h2>\n<p>Lots of developers understand the concept of specificity in different ways.<\/p>\n<blockquote><p>The core idea of specificity is that the CSS Cascade algorithm used by browsers determines which style declaration is applied when two or more rules match the same element.<\/p><\/blockquote>\n<p>Think about it. As a project expands, so do the specificity challenges. Let\u2019s say Developer A adds <code>.cart-button<\/code>, then maybe the button style looks good to be used on the sidebar, but with a little tweak. Then, later, Developer B adds <code>.cart-button .sidebar<\/code>, and from there, any future changes applied to <code>.cart-button<\/code> might get overridden by <code>.cart-button .sidebar<\/code>, and just like that, the specificity war begins.<\/p>\n<figure class=\"\n \n break-out article__image\n \n \n \"><\/p>\n<p> <a href=\"https:\/\/files.smashing.media\/articles\/css-cascade-layers-bem-utility-classes-specificity-control\/1-specificity-tension.png\"><\/p>\n<p> <img loading=\"lazy\" width=\"800\" height=\"500\" src=\"data:image\/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==\" alt=\"Specifity tension represented by a pile of different elements\" class=\"lazyload\" data-src=\"https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_80\/w_400\/https:\/\/files.smashing.media\/articles\/css-cascade-layers-bem-utility-classes-specificity-control\/1-specificity-tension.png\"><\/p>\n<p> <\/a><figcaption class=\"op-vertical-bottom\">\n (<a href=\"https:\/\/files.smashing.media\/articles\/css-cascade-layers-bem-utility-classes-specificity-control\/1-specificity-tension.png\">Large preview<\/a>)<br \/>\n <\/figcaption><\/figure>\n<p>I\u2019ve written CSS long enough to witness different strategies that developers have used to manage the specificity battles that come with CSS.<\/p>\n<pre><code class=\"language-css\">\/* Traditional approach *\/\n#header .nav li a.active { color: blue; }\n\n\/* BEM approach *\/\n.header__nav-item--active { color: blue; }\n\n\/* Utility classes approach *\/\n.text-blue { color: blue; }\n\n\/* Cascade Layers approach *\/\n@layer components {\n .nav-link.active { color: blue; }\n}\n<\/code><\/pre>\n<p>All these methods reflect different strategies on how to control or at least maintain CSS specificity:<\/p>\n<ul>\n<li><strong>BEM<\/strong>: tries to simplify specificity by being explicit.<\/li>\n<li><strong>Utility-first CSS<\/strong>: tries to bypass specificity by keeping it all atomic.<\/li>\n<li><strong>CSS Cascade Layers<\/strong>: manage specificity by organizing styles in layered groups.<\/li>\n<\/ul>\n<p>We\u2019re going to put all three side by side and look at how they handle specificity.<\/p>\n<figure class=\"\n \n break-out article__image\n \n \n \"><\/p>\n<p> <a href=\"https:\/\/files.smashing.media\/articles\/css-cascade-layers-bem-utility-classes-specificity-control\/2-different-strategies-control-css-specificity.png\"><\/p>\n<p> <img loading=\"lazy\" width=\"800\" height=\"500\" src=\"data:image\/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==\" alt=\"A chart which ilustrates different strategies on how to control or at least maintain CSS specificity\" class=\"lazyload\" data-src=\"https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_80\/w_400\/https:\/\/files.smashing.media\/articles\/css-cascade-layers-bem-utility-classes-specificity-control\/2-different-strategies-control-css-specificity.png\"><\/p>\n<p> <\/a><figcaption class=\"op-vertical-bottom\">\n (<a href=\"https:\/\/files.smashing.media\/articles\/css-cascade-layers-bem-utility-classes-specificity-control\/2-different-strategies-control-css-specificity.png\">Large preview<\/a>)<br \/>\n <\/figcaption><\/figure>\n<div data-audience=\"non-subscriber\" data-remove=\"true\" class=\"feature-panel-container\">\n<aside class=\"feature-panel\">\n<div class=\"feature-panel-left-col\">\n<div class=\"feature-panel-description\">\n<p>Meet <strong><a data-instant href=\"https:\/\/www.smashingconf.com\/online-workshops\/\">Smashing Workshops<\/a><\/strong> on <strong>front-end, design & UX<\/strong>, with practical takeaways, live sessions, <strong>video recordings<\/strong> and a friendly Q&A. With Brad Frost, St\u00e9ph Walter and <a href=\"https:\/\/smashingconf.com\/online-workshops\/workshops\">so many others<\/a>.<\/p>\n<p><a data-instant href=\"smashing-workshops\" class=\"btn btn--green btn--large\">Jump to the workshops \u21ac<\/a><\/div>\n<\/div>\n<div class=\"feature-panel-right-col\"><a data-instant href=\"smashing-workshops\" class=\"feature-panel-image-link\"><\/p>\n<div class=\"feature-panel-image\">\n<img loading=\"lazy\" class=\"feature-panel-image-img lazyload\" src=\"data:image\/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==\" alt=\"Feature Panel\" width=\"257\" height=\"355\" data-src=\"\/images\/smashing-cat\/cat-scubadiving-panel.svg\"><\/p>\n<\/div>\n<p><\/a>\n<\/div>\n<\/aside>\n<\/div>\n<h2 id=\"my-relationship-with-specificity\">My Relationship With Specificity<\/h2>\n<p>I actually used to think that I got the whole picture of CSS specificity. Like the usual inline greater than ID greater than class greater than tag. But, reading <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/CSS\/CSS_cascade\/Cascade\">the MDN docs on how the CSS Cascade truly works<\/a> was an eye-opener.<\/p>\n<p>There\u2019s a code I worked on in an old codebase provided by a client, which looked something like this:<\/p>\n<pre><code class=\"language-css\">\/* Legacy code *\/\n#main-content .product-grid button.add-to-cart {\n background-color: #3a86ff;\n color: white;\n padding: 10px 15px;\n border-radius: 4px;\n}\n\n\/* 100 lines of other code here *\/\n\n\/* My new CSS *\/\n.btn-primary {\n background-color: #4361ee; \/* New brand color *\/\n color: white;\n padding: 12px 20px;\n border-radius: 4px;\n box-shadow: 0 2px 5px rgba(0,0,0,0.1);\n}\n<\/code><\/pre>\n<p>Looking at this code, no way that the <code>.btn-primary<\/code> class stands a chance against whatever specificity chain of selectors was previously written. As far as specification goes, CSS gives the first selector a specificity score of <code>1, 2, 1<\/code>: one point for the ID, two points for the two classes, and one point for the element selector. Meanwhile, the second selector is scored as <code>0, 1, 0<\/code> since it only consists of a single class selector.<\/p>\n<p>Sure, I had some options:<\/p>\n<ul>\n<li>I could <strong>use <code>!important<\/code> on the properties<\/strong> in <code>.btn-primary<\/code> to override the ones declared in the stronger selector, but the moment that happens, be prepared to use it everywhere. So, I\u2019d rather avoid it.<\/li>\n<li>I could try <strong>going more specific<\/strong>, but personally, that\u2019s just being cruel to the next developer (who might even be me).<\/li>\n<li>I could <strong>change the styles of the existing code<\/strong>, but that\u2019s adding to the specificity problem:<\/li>\n<\/ul>\n<pre><code class=\"language-css\">#main-content .product-grid .btn-primary {\n \/* edit styles directly *\/\n}\n<\/code><\/pre>\n<p>Eventually, I ended up writing the whole CSS from scratch.<\/p>\n<figure class=\"\n \n break-out article__image\n \n \n \"><\/p>\n<p> <a href=\"https:\/\/files.smashing.media\/articles\/css-cascade-layers-bem-utility-classes-specificity-control\/3-legacy-modern-button.png\"><\/p>\n<p> <img loading=\"lazy\" width=\"800\" height=\"500\" src=\"data:image\/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==\" alt=\"Legacy button vs modern button\" class=\"lazyload\" data-src=\"https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_80\/w_400\/https:\/\/files.smashing.media\/articles\/css-cascade-layers-bem-utility-classes-specificity-control\/3-legacy-modern-button.png\"><\/p>\n<p> <\/a><figcaption class=\"op-vertical-bottom\">\n (<a href=\"https:\/\/files.smashing.media\/articles\/css-cascade-layers-bem-utility-classes-specificity-control\/3-legacy-modern-button.png\">Large preview<\/a>)<br \/>\n <\/figcaption><\/figure>\n<p>When <em>nesting<\/em> was introduced, I tried it to control specificity that way:<\/p>\n<div class=\"break-out\">\n<pre><code class=\"language-css\">.profile-widget {\n \/\/ ... other styles\n .header {\n \/\/ ... header styles\n .user-avatar {\n border: 2px solid blue;\n &.is-admin {\n border-color: gold; \/\/ This becomes .profile-widget .header .user-avatar.is-admin\n }\n }\n }\n}\n<\/code><\/pre>\n<\/div>\n<p>And just like that, I have unintentionally created high-specificity rules. That\u2019s how easily and naturally we can drift toward specificity complexities.<\/p>\n<p>So, to save myself a lot of these issues, I have one principle I always abide by: <a href=\"https:\/\/css-tricks.com\/strategies-keeping-css-specificity-low\/\"><strong>keep specificity as low as possible<\/strong><\/a>. And if the selector complexity is becoming a complex chain, I rethink the whole thing.<\/p>\n<div class=\"partners__lead-place\"><\/div>\n<h2 id=\"bem-the-og-system\">BEM: The OG System<\/h2>\n<p>The Block-Element-Modifier (BEM, for short) has been around the block (pun intended) for a long time. It is a methodological system for writing CSS that forces you to make every style hierarchy explicit.<\/p>\n<pre><code class=\"language-css\">\/* Block *\/\n.panel {}\n\n\/* Element that depends on the Block *\/\n.panel__header {}\n.panel__content {}\n.panel__footer {}\n\n\/* Modifier that changes the style of the Block *\/\n.panel--highlighted {}\n.panel__button--secondary {}\n<\/code><\/pre>\n<p>When I first experienced BEM, I thought it was amazing, despite contrary opinions that it looked ugly. I had no problems with the double hyphens or underscores because they made my CSS predictable and simplified.<\/p>\n<figure class=\"\n \n break-out article__image\n \n \n \"><\/p>\n<p> <a href=\"https:\/\/files.smashing.media\/articles\/css-cascade-layers-bem-utility-classes-specificity-control\/4-bem.png\"><\/p>\n<p> <img loading=\"lazy\" width=\"800\" height=\"500\" src=\"data:image\/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==\" alt=\"Illustration for BEM methodological system\" class=\"lazyload\" data-src=\"https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_80\/w_400\/https:\/\/files.smashing.media\/articles\/css-cascade-layers-bem-utility-classes-specificity-control\/4-bem.png\"><\/p>\n<p> <\/a><figcaption class=\"op-vertical-bottom\">\n (<a href=\"https:\/\/files.smashing.media\/articles\/css-cascade-layers-bem-utility-classes-specificity-control\/4-bem.png\">Large preview<\/a>)<br \/>\n <\/figcaption><\/figure>\n<h3 id=\"how-bem-handles-specificity\">How BEM Handles Specificity<\/h3>\n<p>Take a look at these examples. Without BEM:<\/p>\n<pre><code class=\"language-css\">\/* Specificity: 0, 3, 0 *\/\n.site-header .main-nav .nav-link {\n color: #472EFE;\n text-decoration: none;\n}\n\n\/* Specificity: 0, 2, 0 *\/\n.nav-link.special {\n color: #FF5733;\n}\n<\/code><\/pre>\n<p>With BEM:<\/p>\n<pre><code class=\"language-css\">\/* Specificity: 0, 1, 0 *\/\n.main-nav__link {\n color: #472EFE;\n text-decoration: none;\n}\n\n\/* Specificity: 0, 1, 0 *\/\n.main-nav__link--special {\n color: #FF5733;\n}\n<\/code><\/pre>\n<p>You see how BEM makes the code look predictable as all selectors are created equal, thus making the code easier to maintain and extend. And if I want to add a button to <code>.main-nav<\/code>, I just add <code>.main-nav__btn<\/code>, and if I need a disabled button (modifier), <code>.main-nav__btn--disabled<\/code>. Specificity is low, as I don\u2019t have to increase it or fight the cascade; I just write a new class.<\/p>\n<p>BEM\u2019s naming principle made sure components lived in isolation, which, for a part of CSS, the specificity part, it worked, i.e, <code>.card__title<\/code> class will never accidentally clash with a <code>.menu__title<\/code> class.<\/p>\n<h3 id=\"where-bem-falls-short\">Where BEM Falls Short<\/h3>\n<p>I like the idea of BEM, but it is not perfect, and a lot of people noticed it:<\/p>\n<ul>\n<li>The class names can get <em>really<\/em> long.<\/li>\n<\/ul>\n<div class=\"break-out\">\n<pre><code class=\"language-html\"><div class=\"product-carousel__slide--featured product-carousel__slide--on-sale\">\n <!-- yikes -->\n<\/div>\n<\/code><\/pre>\n<\/div>\n<ul>\n<li><strong>Reusability might not be prioritized<\/strong>, which somewhat contradicts the native CSS ideology. Should a button inside a card be <code>.card__button<\/code> or reuse a global <code>.button<\/code> class? With the former, styles are being duplicated, and with the latter, the BEM strict model is being broken.<\/li>\n<li>One of the core pains in software development starts becoming a reality — <strong>naming things<\/strong>. <a href=\"https:\/\/css-tricks.com\/naming-things-is-only-getting-harder\/\">I\u2019m sure you know the frustration of that already.<\/a><\/li>\n<\/ul>\n<p>BEM is good, but sometimes you may need to be flexible with it. A <strong>hybrid system<\/strong> (maybe using BEM for core components but simpler classes elsewhere) can still keep specificity as low as needed.<\/p>\n<pre><code class=\"language-css\">\/* Base button without BEM *\/\n.button {\n \/* Button styles *\/\n}\n\n\/* Component-specific button with BEM *\/\n.card__footer .button {\n \/* Minor overrides *\/\n}\n<\/code><\/pre>\n<h2 id=\"utility-classes-specificity-by-avoidance\">Utility Classes: Specificity By Avoidance<\/h2>\n<p>This is also called <a href=\"https:\/\/css-tricks.com\/lets-define-exactly-atomic-css\/\">Atomic CSS<\/a>. And in its entirety, it <em>avoids specificity.<\/em><\/p>\n<div class=\"break-out\">\n<pre><code class=\"language-html\"><button class=\"bg-red-300 hover:bg-red-500 text-white py-2 px-4 rounded\">\n A button\n<\/button>\n<\/code><\/pre>\n<\/div>\n<blockquote><p>The idea behind utility-first classes is that every utility class has the same specificity, which is one class selector. Each class is a tiny CSS property with a single purpose.<\/p><\/blockquote>\n<p><code>p-2<\/code>? Padding, nothing more. <code>text-red<\/code>? Color red for text. <code>text-center<\/code>? Text alignment. It\u2019s like how LEGOs work, but for styling. You stack classes on top of each other until you get your desired appearance.<\/p>\n<figure class=\"\n \n break-out article__image\n \n \n \"><\/p>\n<p> <a href=\"https:\/\/files.smashing.media\/articles\/css-cascade-layers-bem-utility-classes-specificity-control\/5-specificity-by-avoidance.png\"><\/p>\n<p> <img loading=\"lazy\" width=\"800\" height=\"500\" src=\"data:image\/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==\" alt=\"An illustration with a title: Avoiding specifity - one utility at a time\" class=\"lazyload\" data-src=\"https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_80\/w_400\/https:\/\/files.smashing.media\/articles\/css-cascade-layers-bem-utility-classes-specificity-control\/5-specificity-by-avoidance.png\"><\/p>\n<p> <\/a><figcaption class=\"op-vertical-bottom\">\n (<a href=\"https:\/\/files.smashing.media\/articles\/css-cascade-layers-bem-utility-classes-specificity-control\/5-specificity-by-avoidance.png\">Large preview<\/a>)<br \/>\n <\/figcaption><\/figure>\n<h3 id=\"how-utility-classes-handle-specificity\">How Utility Classes Handle Specificity<\/h3>\n<p>Utility classes do not solve specificity, but rather, they take the BEM ideology of low specificity to the extreme. Almost all utility classes have the same lowest possible specificity level of (<code>0<\/code>, <code>1<\/code>, <code>0<\/code>). And because of this, overrides become easy; if more padding is needed, bump <code>.p-2<\/code> to <code>.p-4<\/code>.<\/p>\n<p>Another example:<\/p>\n<pre><code class=\"language-html\"><button class=\"bg-orange-300 hover:bg-orange-700\">\n This can be hovered\n<\/button>\n<\/code><\/pre>\n<p>If another class, <code>hover:bg-red-500<\/code>, is added, the order matters for CSS to determine which to use. So, even though the utility classes avoid specificity, the other parts of the CSS Cascade come in, which is the order of appearance, with the last matching selector declared being the winner.<\/p>\n<h3 id=\"utility-class-trade-offs\">Utility Class Trade-Offs<\/h3>\n<p>The most common issue with utility classes is that <strong>they make the code look ugly<\/strong>. And frankly, I agree. But being able to picture what a component looks like without seeing it rendered is just priceless.<\/p>\n<p>There\u2019s also the argument of reusability, that you repeat yourself every single time. But once one finds a repetition happening, just turn that part into a reusable component. It also has its genuine <strong>limitations<\/strong> when it comes to specificity:<\/p>\n<ul>\n<li>If your brand color changes, which is a global change, and you\u2019re deep in the codebase, you can\u2019t just change one and have others follow like native CSS.<\/li>\n<li>The parent-child relationship that happens naturally in native CSS is out the window due to how atomic utility classes behave.<\/li>\n<li>Some argue the HTML part should be left as markup and the CSS part for styling. Because now, there\u2019s more markup to scan, and if you decide to clean up:<\/li>\n<\/ul>\n<div class=\"break-out\">\n<pre><code class=\"language-html\"><!-- Too long -->\n<div class=\"p-4 bg-yellow-100 border border-yellow-300 text-yellow-800 rounded\">\n\n<!-- Better? -->\n<div class=\"alert-warning\">\n<\/code><\/pre>\n<\/div>\n<p>Just like that, we\u2019ve ended up writing CSS. Circle of life.<\/p>\n<p>In my experience with utility classes, they work best for:<\/p>\n<ul>\n<li><strong>Speed<\/strong><br \/>\nWriting the markup, styling it, and seeing the result swiftly.<\/li>\n<li><strong>Predictability<\/strong><br \/>\nA utility class does exactly what it says it does.<\/li>\n<\/ul>\n<h2 id=\"cascade-layers-specificity-by-design\">Cascade Layers: Specificity By Design<\/h2>\n<p>Now, this is where it gets interesting. BEM offers structure, utility classes gain speed, and CSS Cascade Layers give us something paramount: <strong>control<\/strong>.<\/p>\n<p>Anyways, Cascade Layers (<code>@layers<\/code>) groups styles and declares what order the groups should be, regardless of the specificity scores of those rules.<\/p>\n<p>Looking at a set of independent rulesets:<\/p>\n<pre><code class=\"language-css\">button {\n background-color: orange; \/* Specificity: 0, 0, 1 *\/\n}\n\n.button {\n background-color: blue; \/\/* Specificity: 0, 1, 0*\/\n}\n\n#button {\n background-color: red; \/* Specificity: 1, 0, 0 *\/\n}\n\n\/* No matter what, the button is red *\/\n<\/code><\/pre>\n<p>But with <code>@layer<\/code>, let\u2019s say, I want to prioritize the <code>.button<\/code> class selector. I can shape how the specificity order should go:<\/p>\n<pre><code class=\"language-css\">@layer utilities, defaults, components;\n\n@layer defaults {\n button {\n background-color: orange; \/* Specificity: 0, 0, 1 *\/\n }\n}\n\n@layer components {\n .button {\n background-color: blue; \/\/* Specificity: 0, 1, 0*\/\n }\n}\n\n@layer utilities {\n #button {\n background-color: red; \/* Specificity: 1, 0, 0 *\/\n }\n}\n<\/code><\/pre>\n<p>Due to how <code>@layer<\/code> works, <code>.button<\/code> would win because the <code>components<\/code> layer is the highest priority, even though <code>#button<\/code> has higher specificity. Thus, before CSS could even check the usual specificity rules, the layer order would first be respected.<\/p>\n<p>You just have to respect the folks over at W3C, because now one can purposely override an ID selector with a simple class, without even using <code>!important<\/code>. Fascinating.<\/p>\n<h3 id=\"cascade-layers-nuances\">Cascade Layers Nuances<\/h3>\n<p>Here are some things that are worth calling out when we\u2019re talking about CSS Cascade Layers:<\/p>\n<ul>\n<li>Specificity is still part of the game.<\/li>\n<li><code>!important<\/code> acts differently than expected in <code>@layer<\/code> (they work in reverse!).<\/li>\n<li><code>@layers<\/code> aren\u2019t selector-specific but rather style-property-specific.<\/li>\n<\/ul>\n<div class=\"break-out\">\n<pre><code class=\"language-css\">@layer base {\n .button {\n background-color: blue;\n color: white;\n }\n}\n\n@layer theme {\n .button {\n background-color: red;\n \/* No color property here, so white from base layer still applies *\/\n }\n}\n<\/code><\/pre>\n<\/div>\n<ul>\n<li><code>@layer<\/code> can easily be abused. I\u2019m sure there\u2019s a developer out there with over 20+ layer declarations that\u2019s grown into a monstrosity.<\/li>\n<\/ul>\n<h3 id=\"comparing-all-three\">Comparing All Three<\/h3>\n<p>Now, for the TL;DR folks out there, here\u2019s a side-by-side comparison of the three: BEM, utility classes, and CSS Cascade Layers.<\/p>\n<table class=\"tablesaw break-out\">\n<thead>\n<tr>\n<th>Feature<\/th>\n<th>BEM<\/th>\n<th>Utility Classes<\/th>\n<th>Cascade Layers<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>Core Idea<\/td>\n<td>Namespace components<\/td>\n<td>Single purpose classes<\/td>\n<td>Control cascade order<\/td>\n<\/tr>\n<tr>\n<td>Specificity Control<\/td>\n<td>Low and flat<\/td>\n<td>Avoids entirely<\/td>\n<td>Absolute control due to Layer supremacy<\/td>\n<\/tr>\n<tr>\n<td>Code Readability<\/td>\n<td>Clear structure due to naming<\/td>\n<td>Unclear if unfamiliar with the class names<\/td>\n<td>Clear if layer structure is followed<\/td>\n<\/tr>\n<tr>\n<td>HTML Verbosity<\/td>\n<td>Moderate class names (can get long)<\/td>\n<td>Many small classes that adds up quickly<\/td>\n<td>No direct impact, stays only in CSS<\/td>\n<\/tr>\n<tr>\n<td>CSS Organization<\/td>\n<td>By component<\/td>\n<td>By property<\/td>\n<td>By priority order<\/td>\n<\/tr>\n<tr>\n<td>Learning Curve<\/td>\n<td>Requires understanding conventions<\/td>\n<td>Requires knowing the utility names<\/td>\n<td>Easy to pick up, but requires a deep understanding of CSS<\/td>\n<\/tr>\n<tr>\n<td>Tools Dependency<\/td>\n<td>Pure CSS<\/td>\n<td>Often depends of third-party e.g Tailwind<\/td>\n<td>Native CSS<\/td>\n<\/tr>\n<tr>\n<td>Refactoring Ease<\/td>\n<td>High<\/td>\n<td>Medium<\/td>\n<td>Low<\/td>\n<\/tr>\n<tr>\n<td>Best Use Case<\/td>\n<td>Design Systems<\/td>\n<td>Fast builds<\/td>\n<td>Legacy code or third-party codes that need overrides<\/td>\n<\/tr>\n<tr>\n<td>Browser Support<\/td>\n<td>All<\/td>\n<td>All<\/td>\n<td>All (except IE)<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Among the three, each has its sweet spot:<\/p>\n<ul>\n<li><strong>BEM<\/strong> is best when:\n<ul>\n<li>There\u2019s a clear design system that needs to be consistent,<\/li>\n<li>There\u2019s a team with different philosophies about CSS (BEM can be the middle ground), and<\/li>\n<li>Styles are less likely to leak between components.<\/li>\n<\/ul>\n<\/li>\n<li><strong>Utility classes<\/strong> work best when:\n<ul>\n<li>You need to build fast, like prototypes or MVPs, and<\/li>\n<li>Using a component-based JavaScript framework like React.<\/li>\n<\/ul>\n<\/li>\n<li><strong>Cascade Layers<\/strong> are most effective when:\n<ul>\n<li>Working on legacy codebases where you need full specificity control,<\/li>\n<li>You need to integrate third-party libraries or styles from different sources, and<\/li>\n<li>Working on a large, complex application or projects with long-term maintenance.<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p>If I had to choose or rank them, I\u2019d go for utility classes with Cascade Layers over using BEM. But that\u2019s just me!<\/p>\n<div class=\"partners__lead-place\"><\/div>\n<h2 id=\"where-they-intersect-how-they-can-work-together\">Where They Intersect (How They Can Work Together)<\/h2>\n<p>Among the three, Cascade Layers should be seen as an orchestrator, as it can work with the other two strategies. <code>@layer<\/code> is a fundamental tenet of the CSS Cascade\u2019s architecture, unlike BEM and utility classes, which are methodologies for controlling the Cascade\u2019s behavior.<\/p>\n<pre><code class=\"language-css\">\/* Cascade Layers + BEM *\/\n@layer components {\n .card__title {\n font-size: 1.5rem;\n font-weight: bold;\n }\n}\n\n\/* Cascade Layers + Utility Classes *\/\n@layer utilities {\n .text-xl {\n font-size: 1.25rem;\n }\n .font-bold {\n font-weight: 700;\n }\n}\n<\/code><\/pre>\n<p>On the other hand, using BEM with utility classes would just end up clashing:<\/p>\n<div class=\"break-out\">\n<pre><code class=\"language-javascript\"><!-- This feels wrong -->\n<div class=\"card__container p-4 flex items-center\">\n <p class=\"card__title text-xl font-bold\">Something seems wrong<\/p>\n<\/div>\n<\/code><\/pre>\n<\/div>\n<p>I\u2019m putting all my cards on the table: I\u2019m a utility-first developer. And most utility class frameworks use <code>@layer<\/code> behind the scenes (e.g., <a href=\"https:\/\/tailwindcss.com\/blog\/tailwindcss-v4#designed-for-the-modern-web\">Tailwind<\/a>). So, those two are already together in the bag.<\/p>\n<p>But, do I dislike BEM? Not at all! I\u2019ve used it a lot and still would, if necessary. I just find naming things to be an exhausting exercise.<\/p>\n<p>That said, we\u2019re all different, and you might have opposing thoughts about what you think feels best. It truly doesn\u2019t matter, and that\u2019s the beauty of this web development space. <em>Multiple routes can lead to the same destination<\/em>.<\/p>\n<h2 id=\"conclusion\">Conclusion<\/h2>\n<p>So, when it comes to comparing BEM, utility classes, and CSS Cascade Layers, is there a true \u201cwinning\u201d approach for controlling specificity in the Cascade?<\/p>\n<p>First of all, CSS Cascade Layers are arguably the most powerful CSS feature that we\u2019ve gotten in years. They shouldn\u2019t be confused with BEM or utility classes, which are strategies rather than part of the CSS feature set.<\/p>\n<p>That\u2019s why I like the idea of combining either BEM with Cascade Layers or utility classes with Cascade Layers. Either way, the idea is to <strong>keep specificity low and leverage Cascade Layers to set priorities on those styles<\/strong>.<\/p>\n<div class=\"signature\">\n <img src=\"data:image\/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==\" alt=\"Smashing Editorial\" width=\"35\" height=\"46\" loading=\"lazy\" class=\"lazyload\" data-src=\"https:\/\/www.smashingmagazine.com\/images\/logo\/logo--red.png\"><br \/>\n <span>(gg, yk)<\/span>\n<\/div>\n<\/article>\n","protected":false},"excerpt":{"rendered":"<p>CSS Cascade Layers Vs. BEM Vs. Utility Classes: Specificity Control CSS Cascade Layers Vs. BEM Vs. Utility Classes: Specificity Control Victor Ayomipo 2025-06-19T15:00:00+00:00 2025-06-19T20:32:57+00:00 CSS is wild, really wild. And tricky. But let\u2019s talk specifically about specificity. When writing CSS, it\u2019s close to impossible that you haven\u2019t faced the frustration of styles not applying as…<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[11],"tags":[],"_links":{"self":[{"href":"http:\/\/www.computercoursesonline.com\/index.php\/wp-json\/wp\/v2\/posts\/947"}],"collection":[{"href":"http:\/\/www.computercoursesonline.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/www.computercoursesonline.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/www.computercoursesonline.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/www.computercoursesonline.com\/index.php\/wp-json\/wp\/v2\/comments?post=947"}],"version-history":[{"count":1,"href":"http:\/\/www.computercoursesonline.com\/index.php\/wp-json\/wp\/v2\/posts\/947\/revisions"}],"predecessor-version":[{"id":948,"href":"http:\/\/www.computercoursesonline.com\/index.php\/wp-json\/wp\/v2\/posts\/947\/revisions\/948"}],"wp:attachment":[{"href":"http:\/\/www.computercoursesonline.com\/index.php\/wp-json\/wp\/v2\/media?parent=947"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/www.computercoursesonline.com\/index.php\/wp-json\/wp\/v2\/categories?post=947"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/www.computercoursesonline.com\/index.php\/wp-json\/wp\/v2\/tags?post=947"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}