CSS Cascade Layers: How to Control Style Priority
Learn how CSS cascade layers set style priority, how they interact with specificity and !important, and how to debug rules that do not win.
CSS cascade layers let you organize styles into named priority groups. For normal declarations in the same origin and context, later layers beat earlier layers, and unlayered author styles beat styles in named layers. The cascade compares layer priority before selector specificity, so a less-specific rule in a later layer can win over a more-specific rule in an earlier one. For !important declarations, layer priority reverses.
Layers control one stage of the cascade; they do not replace the full cascade. First check whether both declarations apply and compare their origin, importance, and context. Then compare layer priority, specificity, and later tie-breakers. The CSS Cascading and Inheritance Level 5 specification defines these rules.
1. Declare a stable layer order
Put a layer-order statement near the beginning of your stylesheet. The first appearance of each layer name establishes its position. Repeating a name later adds declarations to that layer; it does not move the layer.
@layer reset, vendor, base, components, utilities;
For normal declarations in this author stylesheet, priority proceeds from left to right: reset is lower priority than vendor, which is lower than base, and so on. Unlayered author declarations have higher normal priority than all these explicitly named layers.
Declare the order before imports and conditional rules that might create layers. This makes the intended order apparent and avoids relying on which stylesheet happens to load first. The MDN @layer reference documents the statement, block, and import forms.
2. Put rules into layers
Use a layer block to assign declarations to a layer:
@layer reset {
*, *::before, *::after {
box-sizing: border-box;
}
}
@layer base {
body {
margin: 0;
font-family: system-ui, sans-serif;
color: #202124;
}
}
@layer components {
.button {
color: white;
background: #2457d6;
border: 0;
border-radius: 0.4rem;
padding: 0.65rem 1rem;
}
}
@layer utilities {
.text-danger {
color: #b42318;
}
}
You can also put an imported stylesheet into a layer:
@import url("vendor.css") layer(vendor);
Imports must follow the syntax and placement rules for @import, so place them before ordinary style rules. A layer can be declared without a block first, which is useful for setting its order before its contents are imported or written.
3. Understand which declaration wins
For competing declarations, ask these questions in order:
- Do both declarations apply? Check the element, property, selector match, media or support conditions, and whether the declaration is active.
- Which origin and importance category applies? Origin and importance are considered before layer priority. A layer cannot defeat a declaration that wins at an earlier cascade stage.
- Are the declarations in comparable contexts? Encapsulation contexts, such as shadow-tree boundaries, can affect priority.
- Which layer has priority? For normal declarations, later layers win; unlayered author declarations outrank named author layers. For important declarations, the layer comparison reverses.
- What is the specificity? Specificity is compared only after the higher-priority cascade stages are tied.
- Do later tie-breakers decide it? Scoping proximity and order of appearance can matter after the earlier stages.
This follows the broader cascade described in the MDN cascade guide. In a common author stylesheet, a useful shorthand is: origin and importance, then layer, then specificity, then later tie-breakers.
Why a less-specific selector can win
@layer framework, app;
@layer framework {
#main .button {
color: navy;
}
}
@layer app {
.button {
color: tomato;
}
}
If both rules apply to the same element and property and are comparable in origin, importance, and context, the app layer wins. Its .button selector is less specific, but layer priority is resolved before specificity. This is why adding IDs or longer selectors is often the wrong first response to a layer conflict. See MDN’s specificity guide for where specificity fits in the cascade.
Unlayered declarations can surprise you
@layer app {
.button {
color: tomato;
}
}
.button {
color: navy;
}
For normal author declarations, the unlayered color: navy wins even though it appears earlier in the source. Unlayered author styles behave like an implicit final layer for normal declarations. To make priority easier to reason about, put application styles into named layers consistently instead of mixing layered and unlayered rules.
4. Account for !important
Important declarations reverse layer precedence. Among important declarations in the same origin and context, the earlier layer has priority over later layers, and important declarations in explicit layers outrank unlayered important author declarations.
@layer reset, components, utilities;
@layer reset {
.button {
color: black !important;
}
}
@layer utilities {
.button {
color: red !important;
}
}
Here the important declaration in reset wins because it is in the earlier layer. This reversal is deliberate. It makes important declarations in early layers useful for protecting critical foundational constraints, but it can make ad hoc overrides confusing. Avoid adding !important as a routine fix; first identify the winning cascade stage and adjust the layer or declaration design.
Origin and importance still matter before layer order. For details on important declarations across the full cascade, consult MDN’s !important reference.
5. Organize layers for a maintainable stylesheet
A practical order separates broad defaults from intentional overrides:
@layer reset, vendor, theme, base, components, utilities, overrides;
reset: broad defaults and normalization rules.vendor: third-party framework styles imported into a layer.theme: design tokens and theme-specific values.base: element-level defaults such as typography.components: reusable component rules.utilities: small, intentional helper classes.overrides: a deliberate place for exceptional normal overrides.
Use names that describe responsibility, establish the order once, and keep declarations in the appropriate layer. Treat an overrides layer as a documented exception area, not a place to move every conflict. These are organizational conventions, not special keywords: you choose the names and order.
Nested layers
Layers can be nested. A name such as framework.theme refers to a theme sublayer inside framework:
@layer framework.reset, framework.components, app;
@layer framework.components {
.card {
border-radius: 0.5rem;
}
}
Nested ordering is evaluated within the parent layer. Declarations placed directly in a parent and declarations placed in its sublayers have their own ordering details; do not treat every nested name as an independent top-level layer. Keep the hierarchy shallow unless it clarifies ownership.
Conditional layers
Conditional group rules such as media conditions can introduce layers when their rules are encountered. If stable ordering matters, declare the named order explicitly before conditional blocks, then add the conditional declarations to those names. This prevents the active conditions or source placement from accidentally defining the order you intended to control.
6. Debug a rule that does not win
- Inspect the element in browser developer tools and find the declarations for the property whose computed value is unexpected.
- Confirm both selectors match and check whether media queries or other conditions are active.
- Check whether either declaration is important and identify its origin and context.
- Identify each declaration’s layer. Check the first appearance of every layer name, and whether one declaration is unlayered.
- Apply the correct layer direction: later wins for normal declarations; earlier wins for important declarations.
- Only if the earlier cascade stages tie, compare specificity and then source order or other applicable later tie-breakers.
When inspecting computed styles, do not assume that the rule with the most specific selector or the latest file wins. Those checks happen after layer priority. The W3C cascade specification describes the complete ordering model.
7. Common problems and fixes
| Symptom | Likely cause | Fix |
|---|---|---|
| A more-specific rule loses. | It is in an earlier layer than the competing normal declaration. | Compare layer priority first. Move the rule to the intended later layer if that matches your design. |
| A normal layered rule loses to an older rule elsewhere. | The competing author rule is unlayered, so it has higher normal priority than explicit layers. | Put both styles into the planned layer system, or intentionally retain the unlayered rule as the higher-priority exception. |
| A later layer’s important rule loses. | Important declaration layer order is reversed. | Check for earlier-layer important declarations; remove unnecessary importance or put the intended important rule in the correctly prioritized layer. |
| Changing the order statement has no effect. | The layer name was already created earlier; a repeated name does not move it. | Find its first appearance, including imports and conditional rules, and declare the intended order before it. |
| The expected rule is absent from the cascade. | The selector does not match, a conditional rule is inactive, or the declaration is invalid or overridden at an earlier stage. | Verify applicability and inspect the browser’s rule and computed-style panels before changing specificity. |
| Two stylesheets seem to have an unexpected relationship. | One stylesheet is layered and another is unlayered, or the first layer creation happened in an imported file. | Declare the order centrally and assign imports to named layers with @import ... layer(name). |
8. Performance, reliability, and browser support
Layers are a cascade organization feature: they change how the browser ranks applicable declarations, not what a selector matches. Use them to reduce the need for specificity escalation and source-order tricks. Keep the layer order centralized and explicit so additions to a stylesheet do not silently change priority.
For reliability, make sure each stylesheet is actually loaded and that imports and conditional rules are active. Keep third-party styles in a named layer when you want application layers to take normal priority over them. Remember that unlayered normal author rules can still outrank every explicit author layer, which is a common source of migration surprises.
This guide does not provide a browser-by-browser compatibility matrix. Check the current compatibility data for the browsers and versions your project supports before adopting layers in a constrained environment. The W3C specification and MDN references linked above explain the behavior, but compatibility requirements depend on your support policy.
9. Capture a page to inspect its rendered CSS
When a cascade problem only appears on a deployed page, a screenshot can help record the visible result for a bug report or before-and-after review. It does not reveal the winning declaration; use browser developer tools to inspect computed styles and layers.
DIY: capture through a browser
Open the page in your browser at the viewport and state you want to document, then use the browser’s screenshot command or developer tools to save the visible result. For a full-page record, use the browser’s full-page capture option if available. Repeat with the same viewport and page state after the CSS change so the images are comparable.
Or skip the browser setup
ScreenshotNeo is a website screenshot API and MCP server for developers. One GET request returns a PNG, JPEG, WebP, or PDF. See the ScreenshotNeo API documentation for request options.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
ScreenshotNeo accepts cookie and consent banners like a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets before capture; each step can be turned off. Bot checks, blank pages, timeouts, failed loads, and cache hits cost nothing, and response headers say which page verdict and billing status applied. Its MCP server gives AI agents tools to take screenshots, get page information, and capture PDFs. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000. Every feature is available on every plan. Visit ScreenshotNeo for product details, or sign up free for 1,000 screenshots a month with no card.
FAQ
Do cascade layers increase selector specificity?
No. They add an earlier priority comparison to the cascade. Specificity still works normally after higher-priority stages, including layer precedence, are resolved.
Can I use the same layer name in multiple files?
Yes. Reusing a name adds rules to the existing layer; its position is set by its first appearance.
Does every declaration need a layer?
No, but mixing explicit layers with unlayered author rules can be surprising because unlayered normal declarations outrank named layers. Consistent layering makes priority easier to predict.
Are layer names built in?
No. Names such as reset, components, and utilities are author-chosen labels. Their order comes from where they are first declared.


