CSS

An example-based guide to CSS Cascade Layers

Dane Grant
4 min read
Dane - An example based guide to CSS Cascade Layers
This article was written over 18 months ago and may contain information that is out of date. Some content may still be relevant, but please refer to official documentation for the latest information.

CSS is actually good now! If you’ve been a web developer for a while, you’ll know this hasn’t always been the case. Over the past few years, a lot of really amazing features have been added that now support all the major browsers. Cascading and selector specificity have always been a pain point when writing stylesheets. CSS cascade layers is a new feature that provides us with a lot more power and flexibility for tackling this problem. We no longer need to resort to tricky specificity hacks or order-of-appearance magic.

Cascade layers are really easy to get started with. I think the best way to understand how and when they are useful is by walking through some practical examples.

In this post, we’ll cover:

  • What CSS cascade layers are and how they work
  • Real-world examples of using layers to manage style priorities
  • How Tailwind CSS leverages cascade layers

What are CSS Cascade Layers?

Imagine CSS cascade layers as drawers in a filing cabinet, each holding a set of styles. The drawer at the top represents the highest priority, so when you open the cabinet, you first access the styles in that drawer. If a style isn't found there, you move down to the next drawer until you find what you need.

Traditionally, CSS styles cascade by specificity (i.e., more specific selectors win) and source order (styles declared later in the file override earlier ones). Cascade layers add a new, structured way to manage styles within a single origin—giving you control over which layer takes precedence without worrying about specificity.

This is useful when you need to control the order of styles from different sources, like:

  • Resets (e.g., Normalize)
  • Third-party libraries (e.g., Tailwind CSS)
  • Themes and overrides

You define cascade layers using the @layer rule, assigning styles to a specific layer. The order in which layers are defined determines their priority in the cascade. Styles in later layers override those in earlier layers, regardless of specificity or order within the file.

Here’s a quick example:

@layer base {
  p { color: blue; }
}

@layer theme {
  p { color: darkblue; }
}

In this example, since the theme layer comes after base, it overrides the paragraph text color to dark blue—even though both declarations have the same specificity.

How Do CSS Layers Work?

Cascade layers allow you to assign rules to specific named layers, and then control the order of those layers. This means that:

  • Layers declared later take priority over earlier ones.
  • You don’t need to increase selector specificity to override styles from another layer—just place it in a higher-priority layer.
  • Styles outside of any layer will always take precedence over layered styles unless explicitly ordered.

Let’s break it down with a more detailed example.

audio {
  display: flex;
}

@layer reset {
  audio[controls] {
    display: block;
  }
}

In this example:

  • The unlayered audio rule takes precedence because it’s not part of the reset layer, even though the audio[controls] rule has higher specificity.
  • Without the cascade layers feature, specificity and order-of-appearance would normally decide the winner, but now, we have clear control by defining styles in or outside of a layer.

Use Case: Overriding Styles with Layers

Cascade layers become especially useful when working with frameworks and third-party libraries. Say you’re using a CSS framework that defines a keyframe animation, but you want to override it in your custom styles. Normally, you might have to rely on specificity or carefully place your custom rules at the end. With layers, this is simplified:

@layer framework, custom;

@layer framework {
  @keyframes slide-left {
    from { margin-left: 0; }
    to { margin-left: -100%; }
  }
}

@layer custom {
  @keyframes slide-left {
    from { translate: 0; }
    to { translate: -100% 0; }
  }
}

There’s some new syntax in this example. Multiple layers can be defined at once. This declares up front the order of the layers. With the first line defined, we could even switch the order of the framework and custom layers to achieve the same result.

Here, the custom layer comes after framework, so the translate animation takes precedence, no matter where these rules appear in the file.

Cascade Layers in Tailwind CSS

Tailwind CSS, a utility-first CSS framework, uses cascade layers starting with version 3. Tailwind organizes its layers in a way that gives you flexibility and control over third-party utilities, customizations, and overrides.

In Tailwind, the framework styles are divided into distinct layers like base, components, and utilities. These layers can be reordered or combined with your custom layers.

Here's an example:

@layer base {
  /* Base styles like resets or typography */
  h1 { font-size: 2rem; }
}

@layer components {
  /* Component-level styles */
  .btn { background-color: blue; }
}

@layer utilities {
  /* Utility classes (e.g., margin, padding) */
  .mt-4 { margin-top: 1rem; }
}

Tailwind assigns these layers in a way that utilities take precedence over components, and components override base styles. You can use Tailwind’s @layer directive to extend or override any of these layers with your custom rules.

For example, if you want to add a custom button style that overrides Tailwind’s built-in btn component, you can do it like this:

@layer components {
  .btn { background-color: green; }
}

Practical Example: Layering Resets and Overrides

Let’s say you’re building a design system with both Tailwind and your own custom styles. You want a reset layer, some basic framework styles, and custom overrides.

@layer reset {
  * { box-sizing: border-box; }
}

@layer framework {
  p { color: gray; }
}

@layer custom {
  p { color: black; }
}

In this setup:

  • The reset layer applies basic resets (like box-sizing).
  • The framework layer provides default styles for elements like paragraphs.
  • Your custom layer overrides the paragraph color to black.

By controlling the layer order, you ensure that your custom styles override both the framework and reset layers, without messing with specificity.

Conclusion

CSS cascade layers are a powerful tool that helps you organize your styles in a way that’s scalable, easy to manage, and doesn’t rely on specificity hacks or the appearance order of rules. When used with frameworks like Tailwind CSS, you can create clean, structured styles that are easy to override and customize, giving you full control of your project’s styling hierarchy. It really shines for managing complex projects and integrating with third-party CSS libraries.

About the author

Dane Grant

Dane Grant

Senior Software Engineer

JavaScript developer building things on web, native, and server environments. Free time filled with reading, cooking, dog, sports, comedy, and music.

Keep reading

View all posts →

CSS Container Queries, what are they?

In this blog post, we take a look at container queries, a new feature that makes designing websites easier and more flexible. ...

Jesús Padrón6 mins
CSS

Understanding the Difference Between `:focus` and `:focus-visible` in CSS

Understanding the Difference Between :focus and :focus visible in CSS I have learned my fair share about the importance of keyboard accessibility, so I know that visual indication of the focused element is very important. But the well known :focus pseudo class is not always the best fit for this job. That's where :focus visible comes in. Let's look at the differences between these two pseudo classes and explore the best practices for using them effectively. What is the :focus Pseudo Class? The :focus pseudo class is a CSS selector that applies styles to any element that receives focus, regardless of how that focus was triggered. This includes focus events from keyboard navigation, mouse clicks, and touch interactions. Example Usage of :focus In this example, the button will display a blue outline whenever it is focused, whether the user clicks on it with a mouse, taps it on a touchscreen, or navigates to it using the keyboard. What is the :focus visible Pseudo Class? The :focus visible pseudo class is more specialized. It only applies styles to an element when the browser determines that the focus should be visible. This typically occurs when the user navigates via the keyboard or assistive technologies rather than through mouse or touch input. Example Usage of :focus visible Here, the button will only show a blue outline when focused through keyboard navigation or another input method that usually requires visible focus indicators. Key Differences Between :focus and :focus visible :focus Behavior: Applies to any element that receives focus, regardless of the input method. Use Cases: Ensures that all interactions with the element are visually indicated, whether by mouse, keyboard, or touch. :focus visible Behavior: Applies styles only when the focus should be visible, such as using a keyboard or assistive technology. Use Cases: Ideal for scenarios where you want to provide focus indicators only to keyboard and assistive technology users while avoiding unnecessary outlines for mouse and touch users, typically required by design. Accessibility Implications :focus Pros: Guarantees that all users can see when an element is focused, which is critical for accessibility. Cons: Can lead to a suboptimal experience for mouse users, as focus styles may appear unnecessarily during mouse interactions. :focus visible Pros: Enhances user experience by showing focus indicators only when necessary, thus keeping the interface clean for mouse and touch users. Tailors the experience for keyboard and assistive technology users, providing them with clear visual cues. Cons: Additional considerations may be required to ensure that focus indicators are not accidentally omitted, especially in older browsers that do not support :focus visible. There may be cases where you want to show focus indicators for all users, regardless of input method. Best Practices for Using :focus and :focus visible To achieve the best accessibility and user experience, combining both :focus and :focus visible in your CSS is often a good idea. Combining :focus and :focus visible Here is a Stackblitz example of what such styling could look like for you to try out and play with. Additional Tips Test with Keyboard and Assistive Technology: Ensure that your web application is navigable using a keyboard (Tab, Shift + Tab, etc.) and that focus indicators are visible for those who rely on them. It's never a bad idea to include accessibility testing in your e2e testing suite. Provide Clear Focus Indicators: Make sure that focus indicators are prominent and easy to see. A subtle or hard to spot focus indicator can severely impact accessibility for users who rely on keyboard navigation. Conclusion The :focus visible pseudo class offers a more refined way to manage focus indicators, improving accessibility and user experience, particularly for keyboard and assistive technology users. By understanding the differences between :focus and :focus visible, and applying best practices in your CSS, you can create more accessible and user friendly web applications. Remember, accessibility should never be an afterthought. By thoughtfully applying focus styles, you ensure that all users, regardless of how they interact with your site, can easily navigate and interact....

Jan Kaiser3 mins
AccessibilityCSSPlaywright

How to Truncate Strings Easily with CSS

Learn how to truncate text in CSS, focusing on single-line and multi-line truncation using properties like overflow, text-overflow, and -webkit-line-clamp. It highlights CSS’s simplicity and responsiveness compared to JavaScript-based truncation....

Mark Shenouda4 mins
CSS

:where functional pseudo-selectors :is valuable in CSS

Simplify your CSS and add dynamic styling without JS or pesky !important tags using CSS functional pseudo-selectors...

Dan Spratling4 mins
CSS