Cascade Layers
CSS cascade layers refer to the order in which CSS rules are applied to an HTML element.
When multiple CSS rules apply to the same element, the browser uses the cascade to determine which rule should take precedence. See the MDN article (opens in a new tab) to learn more.
Panda takes advantage of the cascade to provide a more efficient and flexible way to organize styles. This allows you to define styles in a modular way, using CSS rules that are scoped to specific components or elements.
Layer Types
Panda supports five types of cascade layers out of the box:
@layer reset- The reset layer is used to reset the default styles of HTML elements. This is used whenpreflight: trueis set in the config. You can also use this layer to add your own reset styles.
The generated CSS for the reset layer looks like this:
@layer reset {
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
/* ... */
}@layer base- The base layer contains global styles defined in theglobalStyleskey in the config. You can also use this layer to add your own global styles.
The generated CSS for the base layer looks like this:
@layer base {
a {
color: #000;
text-decoration: none;
}
/* ... */
}@layer recipes- The recipes layer contains styles for recipes created within the config (aka config recipes). You can also use this layer to add your own component styles.
The generated CSS for the recipes layer looks like this:
@layer recipes {
.button {
/* ... */
}
.button--variant-primary {
/* ... */
}
/* ... */
}@layer tokens- The tokens layer contains css variables for tokens and semantic tokens. You can also use this layer to add your own design tokens.
The generated CSS for the tokens layer looks like this:
@layer tokens {
:root {
--color-primary: #000;
--color-secondary: #fff;
--color-tertiary: #ccc;
--shadow-sm: 0 0 0 1px rgba(0, 0, 0, 0.05);
}
/* ... */
}@layer utilities- Styles that are scoped to a specific utility class. These styles are only applied to elements that have the utility class applied.
Layer Order
The cascade layers are applied in the following order:
@layer utilities(Highest priority)@layer recipes@layer tokens@layer base@layer reset(Lowest priority)
This means that styles defined in the @layer utilities will take precedence over styles defined in the
@layer recipes. This is useful when you want to override the default styles of a component.
Layer CSS
The generated CSS in Panda is organized into layers. This allows you to define styles in a modular way, using CSS rules that are scoped to specific components or elements.
Here's what the first line of the generated CSS looks like:
@layer reset, base, tokens, recipes, utilities;Adding this line to the top of your CSS file will determine the order in which the layers are applied. This is the most exciting feature of CSS cascade layers.
Customize layers
Panda lets you customize the cascade layers, so your project can coexist with other solutions. Learn more about customizing layers here.
Migrating alongside unlayered CSS
Per the CSS spec, any unlayered rule beats every layered rule, regardless of selector specificity or load order. Most existing CSS is unlayered: plain stylesheets, most CSS-in-JS libraries. So while you migrate to Panda one component at a time, your legacy styles win over Panda's by default. If a converted component still shows its old styles, this is almost always why, not a specificity bug.
Two ways to fix it, depending on which side you want to win:
- Let Panda's new styles win. Turn on the polyfill below. Panda's CSS then competes as unlayered CSS against your legacy styles instead of automatically losing to them.
- Keep legacy styles as the fallback. Leave both as they are, and delete each component's legacy override once it's fully on Panda. Layering alone won't make Panda's version win.
If the other tool also ships its own @layer rules, the fix is different: rename Panda's layers with
layers so the names don't collide, then declare both tools' @layer statements
in one explicit order at the top of your CSS. That declaration, not import order, decides priority between two
layered sources.
Polyfills
Need older browsers, or want unlayered CSS unable to override Panda? Turn on the built-in polyfill:
export default defineConfig({
// ...
polyfill: true
})Or pass --polyfill to panda / panda cssgen. Emit replaces @layer with :not(#\#) specificity boosts (same idea
as @csstools/postcss-cascade-layers). Keep @layer reset, base, …; in your entry CSS so PostCSS / Vite / webpack can
find the stylesheet root — hosts strip that Panda order line when polyfill is on.