To introduce cascade layers into existing CSS, first decide where the old rules belong. Normal unlayered rules outrank every named layer, so new layered overrides will work only after competing legacy rules move into a lower-priority layer.
This guide is for someone editing an authored web page on a Mac and checking it in a desktop browser. The concrete task is to place an existing stylesheet below new component styles while preserving an understandable migration path. The examples use local files, synthetic labels, and a simple CSS shape.
Why do new layered styles lose to existing rules?
For competing normal declarations in author stylesheets, unlayered CSS takes precedence over CSS inside any explicit layer. A more specific selector or a later position inside a layer cannot overcome that boundary. The CSS specification demonstrates this behavior with an unlayered rule overriding a more specific layered rule. CSS Cascading and Inheritance Level 5.
Consider this CSS fragment, targeting an element with the class tile:
.tile {
background-color: #b8bdc5;
}
@layer components {
.tile {
background-color: #246a73;
}
}
The background remains gray. Naming the layer overrides, moving its block farther down the file, or increasing its selector specificity does not change the normal unlayered rule’s precedence. Layer names have no built-in meaning; their declared order establishes their relationship. MDN: Cascade layers.
The following decision table assumes competing stylesheet declarations in the same author origin and context, with no inline styles, animations, or transitions affecting the property. Read each row from lower to higher precedence.
| Declaration type | Lower → higher precedence |
|---|---|
| Normal | First declared layer → later declared layers → unlayered rules |
!important |
Unlayered rules → later declared layers → first declared layer |
Layer precedence is resolved before selector specificity. Within the winning layer, specificity still matters; source order can resolve otherwise tied declarations. The reversal for important declarations means a migration must consider normal and important rules separately. MDN: Cascade layers.
Which migration boundary should you choose?
Start with one element and one property that the new CSS needs to control. Inspect the current winner, identify its stylesheet, and decide whether you can change that file or how the page loads it. This establishes the smallest useful migration boundary.
| Your situation | Practical next step | Consequence |
|---|---|---|
| Existing CSS must retain precedence | Leave it unlayered and add lower-priority layered defaults | New layers supply values where stronger rules do not compete |
| You can edit existing CSS | Put eligible legacy rules inside @layer legacy |
Later layers can override normal legacy declarations |
| You can change the stylesheet entry point | Import the existing file with layer(legacy) |
The imported stylesheet joins that layer without rewriting its selectors |
| You cannot change competing legacy CSS or its loading | Keep the necessary override unlayered temporarily | You still need to resolve specificity and other cascade factors |
These choices follow the three layer mechanisms documented by CSS: a statement declares layer names, a block assigns rules to a layer, and a layered import assigns an imported stylesheet to a layer. A statement alone does not move existing rules. CSS Cascading and Inheritance Level 5: Declaring Cascade Layers.
For a small stylesheet, wrapping its ordinary rules may be convenient. For an existing external file, a layered import creates a clear boundary. Do not put existing @import rules inside an @layer block: imports are top-level rules with placement restrictions. MDN: @import.
The recommended migration below imports the legacy file into a named layer. This changes its precedence relative to other stylesheets, so check any other loaded CSS before applying the same change beyond the sample page.
How do you build a complete local example on a Mac?
Create a folder containing the following three plain-text files. Use a code editor and preserve the .html and .css extensions. The files require no packages, scripts, fonts, or downloaded assets. Their names are local sample resources.
Save this complete document as sample.html:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Layer sample</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<main id="sample">
<h1>Shape sample</h1>
<p>Tile A</p>
<div class="tile" aria-label="Sample shape"></div>
</main>
</body>
</html>
The document links only to styles.css. HTML uses rel="stylesheet" and href to associate the external stylesheet with the page. The current HTML Standard does not define a layer attribute for the link element; assign the resource to a layer through CSS. HTML Standard: The link element.
Save this complete stylesheet as legacy.css. It represents existing rules whose selectors remain unchanged:
body {
margin: 2rem;
font-family: system-ui, sans-serif;
color: #26313a;
background-color: #f4f5f7;
}
#sample .tile {
width: 6rem;
height: 6rem;
background-color: #b8bdc5;
border-radius: 0;
}
Save this complete entry stylesheet as styles.css:
@layer legacy, components;
@import url("legacy.css") layer(legacy);
@layer components {
.tile {
background-color: #246a73;
border-radius: 50%;
}
}
Open sample.html in a current desktop Safari or Firefox. The expected result is a teal circle beneath the label Tile A. The legacy layer supplies the dimensions, while the later components layer supplies the background and rounded shape. Its single-class selector wins over the legacy ID selector because the declarations compete across layers. MDN: @layer.
The entry file establishes layer order first, keeps the import before the block, and then adds component rules. This follows the documented import syntax and placement rules. A layer statement may precede imports; a layer block may not. MDN: @import.
What should you change in an existing page?
Replace the page’s direct stylesheet reference with the new entry stylesheet when adopting this arrangement. If the page still loads legacy.css directly as well as importing it into a layer, the direct load supplies unlayered declarations that can defeat the migration.
Use the sample to predict the failure: add a separate direct stylesheet link to legacy.css, and its normal declarations can restore the gray square. Removing that direct load leaves the legacy rules inside their intended layer. This prediction follows the specification’s placement of unlayered rules after explicit layers in normal precedence. CSS Cascading and Inheritance Module Level 5: Layer Ordering.
Keep all three files together for this example. When adapting the approach, preserve the existing resource conditions, such as media restrictions, and check that every referenced stylesheet loads before diagnosing a cascade conflict.
How can you verify the migration in Firefox on macOS?
Open Firefox’s developer toolbox with Command–Option–I and select Inspector. Select the sample shape in the document tree, then examine the Rules view. Mozilla documents the Mac toolbox shortcut and the Inspector’s CSS inspection controls. All keyboard shortcuts, Examine and edit CSS.
Check the sample one property at a time:
- Find
background-color. The component declaration should win over the legacy gray value. - Find
border-radius. The component value should produce the circle. - Check
widthandheight. Their values should still come from the legacy file. - Temporarily disable the component background declaration. The legacy gray should become visible while the shape remains rounded.
Firefox crosses out overridden declarations and provides checkboxes for temporarily disabling them. It also links rules to their source files. Use those controls to identify the competing declaration rather than treating every unexpected result as a selector problem. Mozilla: Examine and edit CSS.
For a real page, repeat this check for the states you changed: narrower windows, relevant media conditions, and component variants. Save lasting edits in the source files and reload; temporary inspection changes are useful for diagnosis.
What can change unexpectedly during migration?
Important declarations reverse the intended order
With @layer legacy, components, an important declaration in legacy beats a competing important declaration in components. Moving important legacy rules into an early layer can therefore make them harder to override. All important layered stylesheet declarations also outrank competing important unlayered stylesheet declarations in the same author origin and context. MDN: @layer.
Before moving a file, search it for !important and inspect each relevant conflict. Where you control both declarations, determine whether importance is still needed after introducing layers. Do not add importance to every new component rule: that changes which precedence system you are using.
Inline styles need separate attention. Normal inline styles take precedence over normal author stylesheet rules, and important inline styles take precedence over other important author declarations. Moving a stylesheet into a layer does not move an element’s style attribute. MDN: Introduction to the CSS cascade.
A later statement cannot reorder existing layers
Layers take their order from their first declaration. Reopening a named layer adds rules without moving it. Put the intended order before the page first creates those layers, including creation through imported stylesheets. CSS Cascading and Inheritance Module Level 5.
For gradual migration, keep the initial order short and explicit. Move a coherent group of rules, inspect its winners, and then continue. Moving one rule while leaving a competing unlayered copy behind usually preserves the very precedence boundary you intended to remove.
Which browser support limits matter on a Mac?
MDN marks @layer as widely available across browsers since March 2022. WebKit documents cascade-layer support in Safari 15.4. These dates describe the feature’s availability; they do not establish that every Mac has a supporting browser installed. Check the versions your page actually needs to serve. MDN: @layer, WebKit: New WebKit Features in Safari 15.4.
A browser that does not understand a layer block does not apply its enclosed styles as ordinary unlayered rules. Unsupported syntax is discarded according to CSS parsing rules. Consequently, wrapping all essential CSS in layers is not a fallback for older browsers. MDN: CSS error handling.
If those browsers remain a requirement, retain a deliberately unlayered stylesheet for that supported path and resolve its conflicts through selectors and source order. Avoid loading an unlayered duplicate alongside the layered version in supporting browsers: it changes the cascade you just designed.
Begin with the smallest migration that gives the new component control over its intended properties. Establish layer order, assign the competing legacy rules to a layer, remove duplicate unlayered loading, and inspect the resulting winners. That sequence makes each precedence change visible before you migrate more CSS.
Sources
- CSS Cascading and Inheritance Level 5 — W3C.
- CSS Cascading and Inheritance Module Level 5 — CSS Working Group.
- HTML Standard — WHATWG.
@layerCSS at-rule — MDN Web Docs.@importCSS at-rule — MDN Web Docs.- Cascade layers — MDN Web Docs.
- Introduction to the CSS cascade — MDN Web Docs.
- CSS error handling — MDN Web Docs.
- All keyboard shortcuts — Mozilla.
- Examine and edit CSS — Mozilla.
- New WebKit Features in Safari 15.4 — WebKit.