{"id":925,"date":"2025-09-10T10:00:00","date_gmt":"2025-09-10T10:00:00","guid":{"rendered":"https:\/\/kerrprogroup.com\/?p=925"},"modified":"2025-09-20T02:36:56","modified_gmt":"2025-09-20T02:36:56","slug":"integrating-css-cascade-layers-to-an-existing-project","status":"publish","type":"post","link":"https:\/\/kerrprogroup.com\/index.php\/2025\/09\/10\/integrating-css-cascade-layers-to-an-existing-project\/","title":{"rendered":"Integrating CSS Cascade Layers To An Existing Project"},"content":{"rendered":"

Integrating CSS Cascade Layers To An Existing Project<\/title><\/p>\n<article>\n<header>\n<h1>Integrating CSS Cascade Layers To An Existing Project<\/h1>\n<address>Victor Ayomipo<\/address>\n<p> 2025-09-10T10:00:00+00:00<br \/>\n 2025-09-20T02:02:56+00:00<br \/>\n <\/header>\n<p>You can always get a fantastic overview of things in Stephenie Eckles\u2019 article, \u201c<a href=\"https:\/\/www.smashingmagazine.com\/2022\/01\/introduction-css-cascade-layers\/\">Getting Started With CSS Cascade Layers<\/a>\u201d. But let\u2019s talk about the experience of integrating cascade layers into real-world code, the good, the bad, and the spaghetti.<\/p>\n<p>I could have created a sample project for a classic walkthrough, but nah, that\u2019s not how things work in the real world. I want to get our hands dirty, like inheriting code with styles that work and no one knows why.<\/p>\n<p>Finding projects without cascade layers was easy. The tricky part was finding one that was messy enough to have specificity and organisation issues, but broad enough to illustrate different parts of cascade layers integration.<\/p>\n<p>Ladies and gentlemen, I present you with this <a href=\"https:\/\/github.com\/Drix10\/discord-bot-web\">Discord bot website<\/a> by <a href=\"https:\/\/github.com\/Drix10\">Drishtant Ghosh<\/a>. I\u2019m deeply grateful to Drishtant for allowing me to use his work as an example. This project is a typical landing page with a navigation bar, a hero section, a few buttons, and a mobile menu.<\/p>\n<figure class=\"\n \n break-out article__image\n \n \n \"><\/p>\n<p> <a href=\"https:\/\/files.smashing.media\/articles\/integrating-css-cascade-layers-existing-project\/1-discord-bot-landing-page.png\"><\/p>\n<p> <img decoding=\"async\" loading=\"lazy\" width=\"800\" src=\"data:image\/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==\" alt=\"Discord Bot landing page, including a circular logo centered above a heading, text blub, then a row of three buttons.\" class=\"lazyload\" data-src=\"https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_80\/w_400\/https:\/\/files.smashing.media\/articles\/integrating-css-cascade-layers-existing-project\/1-discord-bot-landing-page.png\"><\/p>\n<p> <\/a><figcaption class=\"op-vertical-bottom\">\n (<a href=\"https:\/\/files.smashing.media\/articles\/integrating-css-cascade-layers-existing-project\/1-discord-bot-landing-page.png\">Large preview<\/a>)<br \/>\n <\/figcaption><\/figure>\n<p>You see how it looks perfect on the outside. Things get interesting, however, when we look at the CSS styles under the hood.<\/p>\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\u00a0\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 decoding=\"async\" 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=\"understanding-the-project\">Understanding The Project<\/h2>\n<p>Before we start throwing <code>@layers<\/code> around, let\u2019s get a firm understanding of what we\u2019re working with. I <a href=\"https:\/\/codepen.io\/vayospot\/pen\/bNdoYdP\">cloned<\/a> the GitHub repo, and since our focus is working with CSS Cascade Layers, I\u2019ll focus only on the main page, which consists of three files: <code>index.html<\/code>, <code>index.css<\/code>, and <code>index.js<\/code>.<\/p>\n<p><strong>Note<\/strong>: <em>I didn\u2019t include other pages of this project as it\u2019d make this tutorial too verbose. However, you can refactor the other pages as an experiment.<\/em><\/p>\n<p>The <code>index.css<\/code> file is over 450 lines of code, and skimming through it, I can see some red flags right off the bat:<\/p>\n<ul>\n<li>There\u2019s a lot of code repetition with the same selectors pointing to the same HTML element.<\/li>\n<li>There are quite a few <code>#id<\/code> selectors, which one might argue shouldn\u2019t be used in CSS (and I am one of those people).<\/li>\n<li><code>#botLogo<\/code> is defined twice and over 70 lines apart.<\/li>\n<li>The <code>!important<\/code> keyword is used liberally throughout the code.<\/li>\n<\/ul>\n<p>And yet the site works. There is nothing \u201ctechnically\u201d wrong here, which is another reason CSS is a big, beautiful monster \u2014 errors are silent!<\/p>\n<h2 id=\"planning-the-layer-structure\">Planning The Layer Structure<\/h2>\n<p>Now, some might be thinking, <em>\u201cCan\u2019t we simply move all of the styles into a single layer, like <code>@layer legacy<\/code> and call it a day?\u201d<\/em><\/p>\n<p>You could\u2026 but I don\u2019t think you should.<\/p>\n<p>Think about it: If more layers are added after the <code>legacy<\/code> layer, they <em>should<\/em> override the styles contained in the <code>legacy<\/code> layer because the specificity of layers is organized by priority, where the layers declared later carry higher priority.<\/p>\n<pre><code class=\"language-css\">\/* new is more specific *\/\n@layer legacy, new;\n\n\/* legacy is more specific *\/\n@layer new, legacy;\n<\/code><\/pre>\n<p>That said, we must remember that the site\u2019s existing styles make liberal use of the <code>!important<\/code> keyword. And when that happens, the order of cascade layers gets reversed. So, even though the layers are outlined like this:<\/p>\n<pre><code class=\"language-css\">@layer legacy, new;\n<\/code><\/pre>\n<p>\u2026any styles with an <code>!important<\/code> declaration suddenly shake things up. In this case, the priority order becomes:<\/p>\n<ol>\n<li><code>!important<\/code> styles in the <code>legacy<\/code> layer (most powerful),<\/li>\n<li><code>!important<\/code> styles in the <code>new<\/code> layer,<\/li>\n<li>Normal styles in the <code>new<\/code> layer,<\/li>\n<li>Normal styles in the <code>legacy<\/code> layer (least powerful).<\/li>\n<\/ol>\n<p>I just wanted to clear that part up. Let\u2019s continue.<\/p>\n<p>We know that cascade layers handle specificity by creating an explicit order where each layer has a clear responsibility, and later layers always win.<\/p>\n<p>So, I decided to split things up into five distinct layers:<\/p>\n<ul>\n<li><strong><code>reset<\/code><\/strong>: Browser default resets like <code>box-sizing<\/code>, margins, and paddings.<\/li>\n<li><strong><code>base<\/code><\/strong>: Default styles of HTML elements, like <code>body<\/code>, <code>h1<\/code>, <code>p<\/code>, <code>a<\/code>, etc., including default typography and colours.<\/li>\n<li><strong><code>layout<\/code><\/strong>: Major page structure stuff for controlling how elements are positioned.<\/li>\n<li><strong><code>components<\/code><\/strong>: Reusable UI segments, like buttons, cards, and menus.<\/li>\n<li><strong><code>utilities<\/code><\/strong>: Single helper modifiers that do just one thing and do it well.<\/li>\n<\/ul>\n<p>This is merely how I like to break things out and organize styles. Zell Liew, for example, <a href=\"https:\/\/css-tricks.com\/composition-in-css\/\">has a different set of four buckets<\/a> that could be defined as layers.<\/p>\n<p>There\u2019s also the concept of dividing things up even further into <strong>sublayers<\/strong>:<\/p>\n<pre><code class=\"language-css\">@layer components {\n \/* sub-layers *\/\n @layer buttons, cards, menus;\n}\n\n\/* or this: *\/\n@layer components.buttons, components.cards, components.menus;\n<\/code><\/pre>\n<p>That might come in handy, but I also don\u2019t want to overly abstract things. That might be a better strategy for a project that\u2019s scoped to a well-defined design system.<\/p>\n<p>Another thing we could leverage is <strong>unlayered styles<\/strong> and the fact that any styles not contained in a cascade layer get the highest priority:<\/p>\n<pre><code class=\"language-css\">@layer legacy { a { color: red !important; } }\n@layer reset { a { color: orange !important; } }\n@layer base { a { color: yellow !important; } }\n\n\/* unlayered *\/\na { color: green !important; } \/* highest priority *\/\n<\/code><\/pre>\n<p>But I like the idea of keeping all styles organized in explicit layers because it keeps things <strong>modular<\/strong> and <strong>maintainable<\/strong>, at least in this context.<\/p>\n<p>Let\u2019s move on to adding cascade layers to this project.<\/p>\n<div class=\"partners__lead-place\"><\/div>\n<h2 id=\"integrating-cascade-layers\">Integrating Cascade Layers<\/h2>\n<p>We need to define the layer order at the top of the file:<\/p>\n<pre><code class=\"language-css\">@layer reset, base, layout, components, utilities;\n<\/code><\/pre>\n<p>This makes it easy to tell which layer takes precedence over which (they get more priority from left to right), and now we can think in terms of layer responsibility instead of selector weight. Moving forward, I\u2019ll proceed through the stylesheet from top to bottom.<\/p>\n<p>First, I noticed that the <a href=\"https:\/\/fonts.google.com\/specimen\/Poppins?query=poppins\">Poppins font<\/a> was imported in both the HTML and CSS files, so I removed the CSS import and left the one in <code>index.html<\/code>, as that\u2019s generally recommended for quickly loading fonts.<\/p>\n<p>Next is the universal selector (<code>*<\/code>) styles, which include <a href=\"https:\/\/css-tricks.com\/box-sizing\/\">classic reset styles<\/a> that are perfect for <code>@layer reset<\/code>:<\/p>\n<pre><code class=\"language-css\">@layer reset {\n * {\n margin: 0;\n padding: 0;\n box-sizing: border-box;\n }\n}\n<\/code><\/pre>\n<p>With that out of the way, the <code>body<\/code> selector is next. I\u2019m putting this into <code>@layer base<\/code> because it contains core styles for the project, like backgrounds and fonts:<\/p>\n<div class=\"break-out\">\n<pre><code class=\"language-css\">@layer base {\n body {\n background-image: url(\"bg.svg\"); \/* Renamed to bg.svg for clarity *\/\n font-family: \"Poppins\", sans-serif;\n \/* ... other styles *\/\n }\n}\n<\/code><\/pre>\n<\/div>\n<p>The way I\u2019m tackling this is that styles in the <code>base<\/code> layer should generally affect the whole document. So far, no page breaks or anything.<\/p>\n<h3 id=\"swapping-ids-for-classes\">Swapping IDs For Classes<\/h3>\n<p>Following the <code>body<\/code> element selector is the page loader, which is defined as an ID selector, <code>#loader<\/code>.<\/p>\n<blockquote><p>I\u2019m a firm believer in using class selectors over ID selectors as much as possible. It keeps specificity low by default, which prevents specificity battles and <a href=\"https:\/\/css-tricks.com\/the-difference-between-id-and-class\/\">makes the code a lot more maintainable<\/a>.<\/p><\/blockquote>\n<p>So, I went into the <code>index.html<\/code> file and refactored elements with <code>id=\"loader\"<\/code> to <code>class=\"loader\"<\/code>. In the process, I saw another element with <code>id=\"page\"<\/code> and changed that at the same time.<\/p>\n<p>While still in the <code>index.html<\/code> file, I noticed a few <code>div<\/code> elements missing closing tags. It is astounding how permissive browsers are with that. Anyways, I cleaned those up and moved the <code><\/code> tag out of the <code>.heading<\/code> element to be a direct child of <code>body<\/code>. Let\u2019s not make it any tougher to load our scripts.<\/p>\n<p>Now that we\u2019ve levelled the specificity playing field by moving IDs to classes, we can drop them into the <code>components<\/code> layer since a loader is indeed a reusable component:<\/p>\n<pre><code class=\"language-css\">@layer components {\n .loader {\n width: 100%;\n height: 100vh;\n \/* ... *\/\n }\n .loader .loading {\n \/* ... *\/\n }\n .loader .loading span {\n \/* ... *\/\n }\n .loader .loading span:before {\n \/* ... *\/\n }\n}\n<\/code><\/pre>\n<h3 id=\"animations\">Animations<\/h3>\n<p>Next are keyframes, and this was a bit tricky, but I eventually chose to isolate animations in their own new fifth layer and updated the layer order to include it:<\/p>\n<div class=\"break-out\">\n<pre><code class=\"language-css\">@layer reset, base, layout, components, utilities, animations;\n<\/code><\/pre>\n<\/div>\n<p>But why place <code>animations<\/code> as the last layer? Because animations are generally the last to run and shouldn\u2019t be affected by style conflicts.<\/p>\n<p>I searched the project\u2019s styles for <code>@keyframes<\/code> and dumped them into the new layer:<\/p>\n<pre><code class=\"language-css\">@layer animations {\n @keyframes loading {\n \/* ... *\/\n }\n @keyframes loading2 {\n \/* ... *\/\n }\n @keyframes pageShow {\n \/* ... *\/\n }\n}\n<\/code><\/pre>\n<p>This gives a clear distinction of static styles from dynamic ones while also enforcing reusability.<\/p>\n<h3 id=\"layouts\">Layouts<\/h3>\n<p>The <code>#page<\/code> selector also has the same issue as <code>#id<\/code>, and since we fixed it in the HTML earlier, we can modify it to <code>.page<\/code> and drop it in the <code>layout<\/code> layer, as its main purpose is to control the initial visibility of the content:<\/p>\n<pre><code class=\"language-css\">@layer layout {\n .page {\n display: none;\n }\n}\n<\/code><\/pre>\n<h3 id=\"custom-scrollbars\">Custom Scrollbars<\/h3>\n<p>Where do we put these? Scrollbars are global elements that persist across the site. This might be a gray area, but I\u2019d say it fits perfectly in <code>@layer base<\/code> since it\u2019s a global, default feature.<\/p>\n<pre><code class=\"language-css\">@layer base {\n \/* ... *\/\n ::-webkit-scrollbar {\n width: 8px;\n }\n ::-webkit-scrollbar-track {\n background: #0e0e0f;\n }\n ::-webkit-scrollbar-thumb {\n background: #5865f2;\n border-radius: 100px;\n }\n ::-webkit-scrollbar-thumb:hover {\n background: #202225;\n }\n}\n<\/code><\/pre>\n<p>I also removed the <code>!important<\/code> keywords as I came across them.<\/p>\n<h3 id=\"navigation\">Navigation<\/h3>\n<p>The <code>nav<\/code> element is pretty straightforward, as it is the main structure container that defines the position and dimensions of the navigation bar. It should definitely go in the <code>layout<\/code> layer:<\/p>\n<pre><code class=\"language-css\">@layer layout {\n \/* ... *\/\n nav {\n display: flex;\n height: 55px;\n width: 100%;\n padding: 0 50px; \/* Consistent horizontal padding *\/\n \/* ... *\/\n }\n}\n<\/code><\/pre>\n<h3 id=\"logo\">Logo<\/h3>\n<p>We have three style blocks that are tied to the logo: <code>nav .logo<\/code>, <code>.logo img<\/code>, and <code>#botLogo<\/code>. These names are redundant and could benefit from inheritance component reusability.<\/p>\n<p>Here\u2019s how I\u2019m approaching it:<\/p>\n<ol>\n<li>The <code>nav .logo<\/code> is overly specific since the logo can be reused in other places. I dropped the <code>nav<\/code> so that the selector is just <code>.logo<\/code>. There was also an <code>!important<\/code> keyword in there, so I removed it.<\/li>\n<li>I updated <code>.logo<\/code> to be a Flexbox container to help position <code>.logo img<\/code>, which was previously set with less flexible absolute positioning.<\/li>\n<li>The <code>#botLogo<\/code> ID is declared twice, so I merged the two rulesets into one and lowered its specificity by making it a <code>.botLogo<\/code> class. And, of course, I updated the HTML to replace the ID with the class.<\/li>\n<li>The <code>.logo img<\/code> selector becomes <code>.botLogo<\/code>, making it the base class for styling all instances of the logo.<\/li>\n<\/ol>\n<p>Now, we\u2019re left with this:<\/p>\n<pre><code class=\"language-css\">\/* initially .logo img *\/\n.botLogo {\n border-radius: 50%;\n height: 40px;\n border: 2px solid #5865f2;\n}\n\n\/* initially #botLogo *\/\n.botLogo {\n border-radius: 50%;\n width: 180px;\n \/* ... *\/\n}\n<\/code><\/pre>\n<p>The difference is that one is used in the navigation and the other in the hero section heading. We can transform the second <code>.botLogo<\/code> by slightly increasing the specificity with a <code>.heading .botLogo<\/code> selector. We may as well clean up any duplicated styles as we go.<\/p>\n<p>Let\u2019s place the entire code in the <code>components<\/code> layer as we\u2019ve successfully turned the logo into a reusable component:<\/p>\n<div class=\"break-out\">\n<pre><code class=\"language-css\">@layer components {\n \/* ... *\/\n .logo {\n font-size: 30px;\n font-weight: bold;\n color: #fff;\n display: flex;\n align-items: center;\n gap: 10px;\n }\n .botLogo {\n aspect-ratio: 1; \/* maintains square dimensions with width *\/\n border-radius: 50%;\n width: 40px;\n border: 2px solid #5865f2;\n }\n .heading .botLogo {\n width: 180px;\n height: 180px;\n background-color: #5865f2;\n box-shadow: 0px 0px 8px 2px rgba(88, 101, 242, 0.5);\n \/* ... *\/\n }\n}\n<\/code><\/pre>\n<\/div>\n<p>This was a bit of work! But now the logo is properly set up as a component that fits perfectly in the new layer architecture.<\/p>\n<div class=\"partners__lead-place\"><\/div>\n<h3 id=\"navigation-list\">Navigation List<\/h3>\n<p>This is a typical navigation pattern. Take an unordered list (<code><\/p>\n<ul><\/ul>\n<p><\/code>) and turn it into a flexible container that displays all of the list items horizontally on the same row (with wrapping allowed). It\u2019s a type of navigation that can be reused, which belongs in the <code>components<\/code> layer. But there\u2019s a little refactoring to do before we add it.<\/p>\n<p>There\u2019s already a <code>.mainMenu<\/code> class, so let\u2019s lean into that. We\u2019ll swap out any <code>nav ul<\/code> selectors with that class. Again, it keeps specificity low while making it clearer what that element does.<\/p>\n<pre><code class=\"language-css\">@layer components {\n \/* ... *\/\n .mainMenu {\n display: flex;\n flex-wrap: wrap;\n list-style: none;\n }\n .mainMenu li {\n margin: 0 4px;\n }\n .mainMenu li a {\n color: #fff;\n text-decoration: none;\n font-size: 16px;\n \/* ... *\/\n }\n .mainMenu li a:where(.active, .hover) {\n color: #fff;\n background: #1d1e21;\n }\n .mainMenu li a.active:hover {\n background-color: #5865f2;\n }\n}\n<\/code><\/pre>\n<p>There are also two buttons in the code that are used to toggle the navigation between \u201copen\u201d and \u201cclosed\u201d states when the navigation is collapsed on smaller screens. It\u2019s tied specifically to the <code>.mainMenu<\/code> component, so we\u2019ll keep everything together in the <code>components<\/code> layer. We can combine and simplify the selectors in the process for cleaner, more readable styles:<\/p>\n<pre><code class=\"language-css\">@layer components {\n \/* ... *\/\n nav:is(.openMenu, .closeMenu) {\n font-size: 25px;\n display: none;\n cursor: pointer;\n color: #fff;\n }\n}\n<\/code><\/pre>\n<p>I also noticed that several other selectors in the CSS were not used anywhere in the HTML. So, I removed those styles to keep things trim. There are <a href=\"https:\/\/css-tricks.com\/how-do-you-remove-unused-css-from-a-site\/\">automated ways to go about this<\/a>, too.<\/p>\n<h3 id=\"media-queries\">Media Queries<\/h3>\n<p>Should media queries have a dedicated layer (<code>@layer responsive<\/code>), or should they be in the same layer as their target elements? I really struggled with that question while refactoring the styles for this project. I did some research and testing, and my verdict is the latter, that <strong>media queries ought to be in the same layer as the elements they affect<\/strong>.<\/p>\n<p>My reasoning is that keeping them together:<\/p>\n<ul>\n<li>Maintains responsive styles with their base element styles,<\/li>\n<li>Makes overrides predictable, and<\/li>\n<li>Flows well with component-based architecture common in modern web development.<\/li>\n<\/ul>\n<p>However, it also means <strong>responsive logic<\/strong> is scattered across layers. But it beats the one with a gap between the layer where elements are styled and the layer where their responsive behaviors are managed. That\u2019s a deal-breaker for me because it\u2019s way too easy to update styles in one layer and forget to update their corresponding responsive style in the responsive layer.<\/p>\n<p>The other big point is that media queries in the same layer have <strong>the same priority<\/strong> as their elements. This is consistent with my overall goal of keeping the CSS Cascade simple and predictable, free of style conflicts.<\/p>\n<p>Plus, the <a href=\"https:\/\/css-tricks.com\/tag\/nesting\/\">CSS nesting syntax<\/a> makes the relationship between media queries and elements super clear. Here\u2019s an abbreviated example of how things look when we nest media queries in the <code>components<\/code> layer:<\/p>\n<pre><code class=\"language-css\">@layer components {\n .mainMenu {\n display: flex;\n flex-wrap: wrap;\n list-style: none;\n }\n @media (max-width: 900px) {\n .mainMenu {\n width: 100%;\n text-align: center;\n height: 100vh;\n display: none;\n }\n }\n}\n<\/code><\/pre>\n<p>This also allows me to nest a component\u2019s child element styles (e.g., <code>nav .openMenu<\/code> and <code>nav .closeMenu<\/code>).<\/p>\n<pre><code class=\"language-css\">@layer components {\n nav {\n &.openMenu {\n display: none;\n \n @media (max-width: 900px) {\n &.openMenu {\n display: block;\n }\n }\n }\n }\n}\n<\/code><\/pre>\n<h3 id=\"typography-buttons\">Typography & Buttons<\/h3>\n<p>The <code>.title<\/code> and <code>.subtitle<\/code> can be seen as typography components, so they and their responsive associates go into \u2014 you guessed it \u2014 the <code>components<\/code> layer:<\/p>\n<pre><code class=\"language-css\">@layer components {\n .title {\n font-size: 40px;\n font-weight: 700;\n \/* etc. *\/\n }\n .subtitle {\n color: rgba(255, 255, 255, 0.75);\n font-size: 15px;\n \/* etc.. *\/\n }\n @media (max-width: 420px) {\n .title {\n font-size: 30px;\n }\n .subtitle {\n font-size: 12px;\n }\n }\n}\n<\/code><\/pre>\n<p>What about buttons? Like many website\u2019s this one has a class, <code>.btn<\/code>, for that component, so we can chuck those in there as well:<\/p>\n<pre><code class=\"language-css\">@layer components {\n .btn {\n color: #fff;\n background-color: #1d1e21;\n font-size: 18px;\n \/* etc. *\/\n }\n .btn-primary {\n background-color: #5865f2;\n }\n .btn-secondary {\n transition: all 0.3s ease-in-out;\n }\n .btn-primary:hover {\n background-color: #5865f2;\n box-shadow: 0px 0px 8px 2px rgba(88, 101, 242, 0.5);\n \/* etc. *\/\n }\n .btn-secondary:hover {\n background-color: #1d1e21;\n background-color: rgba(88, 101, 242, 0.7);\n }\n @media (max-width: 420px) {\n .btn {\n font-size: 14px;\n margin: 2px;\n padding: 8px 13px;\n }\n }\n @media (max-width: 335px) {\n .btn {\n display: flex;\n flex-direction: column;\n }\n }\n}\n<\/code><\/pre>\n<h3 id=\"the-final-layer\">The Final Layer<\/h3>\n<p>We haven\u2019t touched the <code>utilities<\/code> layer yet! I\u2019ve reserved this layer for helper classes that are designed for specific purposes, like hiding content \u2014 or, in this case, there\u2019s a <code>.noselect<\/code> class that fits right in. It has a single reusable purpose: to disable selection on an element.<\/p>\n<p>So, that\u2019s going to be the only style rule in our <code>utilities<\/code> layer:<\/p>\n<pre><code class=\"language-css\">@layer utilities {\n .noselect {\n -webkit-touch-callout: none;\n -webkit-user-select: none;\n -khtml-user-select: none;\n -webkit-user-drag: none;\n -moz-user-select: none;\n -ms-user-select: none;\n user-select: none;\n }\n}\n<\/code><\/pre>\n<p>And that\u2019s it! We\u2019ve completely refactored the CSS of a real-world project to use CSS Cascade Layers. You can compare <a href=\"https:\/\/codepen.io\/vayospot\/pen\/bNdoYdP\">where we started<\/a> with the <a href=\"https:\/\/codepen.io\/vayospot\/pen\/XJbeVdB\">final code<\/a>.<\/p>\n<h2 id=\"it-wasn-t-all-easy\">It Wasn\u2019t All Easy<\/h2>\n<p>That\u2019s not to say that working with Cascade Layers was challenging, but there were some sticky points in the process that forced me to pause and carefully think through what I was doing.<\/p>\n<p>I kept some notes as I worked:<\/p>\n<ul>\n<li><strong>It\u2019s tough to determine where to start with an existing project.<\/strong><br \/>\nHowever, by defining the layers first and setting their priority levels, I had a framework for deciding how and where to move specific styles, even though I was not totally familiar with the existing CSS. That helped me avoid situations where I might second-guess myself or define extra, unnecessary layers.<\/li>\n<li><strong>Browser support is still a thing!<\/strong><br \/>\nI mean, Cascade Layers enjoy 94% support coverage as I\u2019m writing this, but you might be one of those sites that needs to accommodate legacy browsers that are unable to support layered styles.<\/li>\n<li><strong>It wasn\u2019t clear where media queries fit into the process.<\/strong><br \/>\nMedia queries put me on the spot to find where they work best: nested in the same layers as their selectors, or in a completely separate layer? I went with the former, as you know.<\/li>\n<li><strong>The <code>!important<\/code> keyword is a juggling act.<\/strong><br \/>\nThey invert the entire layering priority system, and this project was littered with instances. Once you start chipping away at those, the existing CSS architecture erodes and requires a balance between refactoring the code and fixing what\u2019s already there to know exactly how styles cascade.<\/li>\n<\/ul>\n<blockquote class=\"pull-quote\">\n<p>\n <a class=\"pull-quote__link\" aria-label=\"Share on Twitter\" href=\"https:\/\/twitter.com\/share?text=%0aOverall,%20refactoring%20a%20codebase%20for%20CSS%20Cascade%20Layers%20is%20a%20bit%20daunting%20at%20first%20glance.%20The%20important%20thing,%20though,%20is%20to%20acknowledge%20that%20it%20isn%e2%80%99t%20really%20the%20layers%20that%20complicate%20things,%20but%20the%20existing%20codebase.%0a&url=https:\/\/smashingmagazine.com%2f2025%2f09%2fintegrating-css-cascade-layers-existing-project%2f\"><\/p>\n<p>Overall, refactoring a codebase for CSS Cascade Layers is a bit daunting at first glance. The important thing, though, is to acknowledge that it isn\u2019t really the layers that complicate things, but the existing codebase.<\/p>\n<p> <\/a>\n <\/p>\n<div class=\"pull-quote__quotation\">\n<div class=\"pull-quote__bg\">\n <span class=\"pull-quote__symbol\">\u201c<\/span><\/div>\n<\/p><\/div>\n<\/blockquote>\n<p>It\u2019s tough to completely overhaul someone\u2019s existing approach for a new one, even if the new approach is elegant.<\/p>\n<h2 id=\"where-cascade-layers-helped-and-didn-t\">Where Cascade Layers Helped (And Didn\u2019t)<\/h2>\n<p>Establishing layers improved the code, no doubt. I\u2019m sure there are some <strong>performance benchmarks<\/strong> in there since we were able to remove unused and conflicting styles, but the real win is in <strong>a more maintainable set of styles<\/strong>. It\u2019s easier to find what you need, know what specific style rules are doing, and where to insert new styles moving forward.<\/p>\n<p>At the same time, I wouldn\u2019t say that Cascade Layers are a silver bullet solution. Remember, CSS is intrinsically tied to the HTML structure it queries. If the HTML you\u2019re working with is unstructured and suffers from <code>div<\/code>-itus, then you can safely bet that the effort to untangle that mess is higher and involves rewriting markup at the same time.<\/p>\n<blockquote class=\"pull-quote\">\n<p>\n <a class=\"pull-quote__link\" aria-label=\"Share on Twitter\" href=\"https:\/\/twitter.com\/share?text=%0aRefactoring%20CSS%20for%20cascade%20layers%20is%20most%20certainly%20worth%20the%20maintenance%20enhancements%20alone.%0a&url=https:\/\/smashingmagazine.com%2f2025%2f09%2fintegrating-css-cascade-layers-existing-project%2f\"><\/p>\n<p>Refactoring CSS for cascade layers is most certainly worth the maintenance enhancements alone.<\/p>\n<p> <\/a>\n <\/p>\n<div class=\"pull-quote__quotation\">\n<div class=\"pull-quote__bg\">\n <span class=\"pull-quote__symbol\">\u201c<\/span><\/div>\n<\/p><\/div>\n<\/blockquote>\n<p>It may be \u201ceasier\u201d to start from scratch and define layers as you work from the ground up because there\u2019s less inherited overhead and technical debt to sort through. But if you have to start from an existing codebase, you might need to de-tangle the complexity of your styles first to determine exactly how much refactoring you\u2019re looking at.<\/p>\n<div class=\"signature\">\n <img decoding=\"async\" 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>Integrating CSS Cascade Layers To An Existing Project Integrating CSS Cascade Layers To An Existing Project Victor Ayomipo 2025-09-10T10:00:00+00:00 2025-09-20T02:02:56+00:00 You can always get a fantastic overview of things in Stephenie Eckles\u2019 article, \u201cGetting Started With CSS Cascade Layers\u201d. But let\u2019s talk about the experience of integrating cascade layers into real-world code, the good, the bad, and the spaghetti. I could have created a sample project for a classic walkthrough,…<\/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":"https:\/\/kerrprogroup.com\/index.php\/wp-json\/wp\/v2\/posts\/925"}],"collection":[{"href":"https:\/\/kerrprogroup.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/kerrprogroup.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/kerrprogroup.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/kerrprogroup.com\/index.php\/wp-json\/wp\/v2\/comments?post=925"}],"version-history":[{"count":1,"href":"https:\/\/kerrprogroup.com\/index.php\/wp-json\/wp\/v2\/posts\/925\/revisions"}],"predecessor-version":[{"id":926,"href":"https:\/\/kerrprogroup.com\/index.php\/wp-json\/wp\/v2\/posts\/925\/revisions\/926"}],"wp:attachment":[{"href":"https:\/\/kerrprogroup.com\/index.php\/wp-json\/wp\/v2\/media?parent=925"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/kerrprogroup.com\/index.php\/wp-json\/wp\/v2\/categories?post=925"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/kerrprogroup.com\/index.php\/wp-json\/wp\/v2\/tags?post=925"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}