RRenDSv0.13.0

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:

  1. Within RenDS, later layers in that list win over earlier ones. utilities beats components beats base beats tokens beats reset.
  2. Unlayered CSS (your application styles, written without @layer) beats every RenDS layer. You don't need !important to override a button — just write .my-btn { background: red; } and it wins.
  3. !important reverses the layer order. Useful nowhere, almost ever. Don't reach for it.

Visual

Layer order (lowest priority → highest)

1
@layer reset
Browser normalization. Box-sizing, margin reset, native form element basics.
lowest
2
@layer tokens
Primitive, semantic, and component tokens (--color-*, --space-*, --ren-*).
low
3
@layer base
Classless typography, headings, lists, layout primitives.
medium
4
@layer components
All .ren-* selectors. The bulk of RenDS lives here.
high
5
@layer utilities
High-specificity utility overrides — sr-only, visibility helpers.
highest in RenDS
(unlayered) — your CSS
Anything you write without @layer. Wins over every layer above.
WINS ALL
!
!important
Reverses layer order. Bottom-tier styles win. Avoid.
last resort

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

LayerSource filesContains
reset base/reset.css Box-sizing, margin/padding zeroing, native form element baseline.
tokens tokens/primitives/*.css
tokens/semantic/*.css
tokens/component/*.css
All custom properties: --blue-500, --color-text, --space-4, --ren-btn-bg, etc.
base base/typography.css
base/layouts.css
base/primitive-zero.css
Classless HTML (h1h6, 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-btn looks defensive but breaks the moment a teammate writes html.dark body .my-btn. Unlayered CSS wins by mechanism, not by specificity.
  • Don't unwrap RenDS' layer declarations. Removing @layer from rends/index.css moves 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 components are tagged components in the inspector.
  • Unlayered rules are tagged (implicit outer layer).
  • If your override isn't winning, check the badge — if your CSS is in @layer components by accident (e.g. via an aggressive bundler), it's at the same priority as RenDS instead of above it.