Foundations
Cascade Layers
RenDS uses native CSS Cascade Layers (@layer) so its component styles never fight your code on specificity. Any CSS you write outside a layer automatically beats RenDS — no !important needed. This page explains the layer order, what lives in each, and how to integrate with legacy CSS or other design systems.
Summary
The short version
RenDS declares this in rends/index.css:
@layer reset, tokens, base, components, utilities;
Three rules to remember:
- Within RenDS, later layers in that list win over earlier ones.
utilitiesbeatscomponentsbeatsbasebeatstokensbeatsreset. - Unlayered CSS (your application styles, written without
@layer) beats every RenDS layer. You don't need!importantto override a button — just write.my-btn { background: red; }and it wins. !importantreverses the layer order. Useful nowhere, almost ever. Don't reach for it.
Visual
Layer order (lowest priority → highest)
.ren-* selectors. The bulk of RenDS lives here.@layer. Wins over every layer above.This is plain modern CSS — no preprocessor, no PostCSS plugin. The browser handles it. Supported in every evergreen browser since 2022.
Reference
What lives in each layer
| Layer | Source files | Contains |
|---|---|---|
reset |
base/reset.css |
Box-sizing, margin/padding zeroing, native form element baseline. |
tokens |
tokens/primitives/*.csstokens/semantic/*.csstokens/component/*.css |
All custom properties: --blue-500, --color-text, --space-4, --ren-btn-bg, etc. |
base |
base/typography.cssbase/layouts.cssbase/primitive-zero.css |
Classless HTML (h1–h6, p, ul, blockquote, etc.) and layout primitives (.ren-stack, .ren-grid, …). |
components |
components/**/*.css |
Every .ren-* selector. .ren-btn, .ren-dialog, .ren-field, all 53 components. |
utilities |
base/utilities.css |
Single-purpose helpers: .ren-sr-only, .ren-hidden, .ren-truncate. |
Patterns
Overriding RenDS styles
Recipe 1 — Just write CSS (the answer 90% of the time)
Your application CSS is unlayered by default. It wins over RenDS automatically.
/* app.css — loaded after rends/index.css */
.my-checkout-btn {
background: var(--color-success); /* override .ren-btn-primary bg */
border-radius: 0;
}
No !important, no specificity bumping, no :where() tricks. Just plain class selectors.
Recipe 2 — Override a token, not a selector (best for global theme tweaks)
Most RenDS styles consume tokens. Change the token and every component using it follows.
:root {
/* Make every button slightly rounder */
--ren-btn-radius: 12px;
/* Switch the accent color across the whole DS */
--color-accent: oklch(0.72 0.19 145);
}
Recipe 3 — A layered override (for vendored or shared stylesheets)
If you ship CSS that other apps consume, layer it so consumers can still override you without !important.
/* my-design-pack.css */
@layer my-pack; /* declared AFTER components / utilities,
so it wins over RenDS */
@layer my-pack {
.my-card { background: var(--color-surface-raised); }
}
Now consumers of my-design-pack.css can override .my-card from their unlayered CSS and still win.
Integration
Mixing RenDS with other CSS
Existing legacy CSS
Already have a big bundle of .btn-blue / .card-old / etc.? Two paths:
- Leave it unlayered. Your legacy CSS wins over RenDS automatically. RenDS only kicks in where you opt in with
.ren-*classes. No collision. - Layer it explicitly if you want RenDS to win in some places:
@layer legacy; @import "./rends/index.css"; /* declares reset → utilities */ @import "./old-app.css" layer(legacy); /* Layer order is now: legacy, reset, tokens, base, components, utilities. RenDS wins over legacy because RenDS layers come after. */
Another design system (anything CSS-based)
Same approach: layer the other DS so the order is explicit. Whichever layer you declare later wins.
@import "./other-ds.css" layer(other-ds);
@import "./rends/index.css"; /* unwraps to reset, tokens, base, components, utilities */
/* RenDS components win over other-ds. Want the reverse? Move @layer other-ds
to be declared after the RenDS layers. */
Inline / scoped styles
style="…" attributes are not in any layer; they have specificity higher than any class but still lose to !important in the highest layer. For a one-off tweak inside a single component instance, inline style is fine.
Avoid
Anti-patterns
Don't reach for !important
Cascade Layers exist to make !important obsolete. If you need to override RenDS, write unlayered CSS or override a token. If you find yourself wanting !important, you're fighting a specificity battle that the layer system has already resolved for you.
- Don't increase specificity to win.
html body .my-btnlooks defensive but breaks the moment a teammate writeshtml.dark body .my-btn. Unlayered CSS wins by mechanism, not by specificity. - Don't unwrap RenDS' layer declarations. Removing
@layerfromrends/index.cssmoves every RenDS rule out of its layer and into the unlayered pool — meaning your CSS no longer wins automatically. - Don't redeclare component selectors in your CSS just to bump specificity. Override the token (
--ren-btn-bg) or write a fresh class.
Devtools
Debugging cascade in DevTools
Chrome, Firefox, and Safari all show the active layer next to each rule in the inspector. Look for the @layer name badge in the Styles panel:
- Rules from
@layer componentsare taggedcomponentsin the inspector. - Unlayered rules are tagged
(implicit outer layer). - If your override isn't winning, check the badge — if your CSS is in
@layer componentsby accident (e.g. via an aggressive bundler), it's at the same priority as RenDS instead of above it.