Yo Kirupa folks, I’m refactoring a little design system at work and I’m trying to keep component styles predictable while still letting state variants (loading/disabled/error) stack without surprises.
.card { --bg: white; background: var(--bg); }
.card.is-loading { opacity: .6; }
.card .button { --bg: #222; }
.button { background: var(--bg); }
Right now a nested .button inside .card “inherits” the card’s CSS variable setup in ways that feel accidental, and small changes cause random-looking visual regressions, so what’s your go-to CSS architecture pattern to keep variants scoped and composable without a specificity arms race?
custom properties inherit by design, so --bg is basically shared state whether you wanted it or not.
i’d namespace per component and only override the nested component’s var on purpose:
.card { --card-bg: white; background: var(--card-bg); }
.button { --button-bg: #222; background: var(--button-bg); }
.card .button { --button-bg: #111; } /* only when you mean it */
that’s been the least annoying pattern for me. generic vars feel nice for about five minutes, then you spend an afternoon chasing weird “why did this button turn white” bugs.
the CSS variable goblin is doing exactly what it’s supposed to do here — custom properties inherit, so a nested .button is gonna see whatever --bg is in scope unless you separate “theme tokens” from “component tokens.”
What’s worked for me is keeping the global stuff like --bg and --fg at the page/theme level, then giving each component its own token names and reading those first. So the button looks at --button-bg before it falls back to the shared theme token.
:root {
--bg: #fff;
--fg: #111;
}
.card {
--card-bg: var(--bg);
background: var(--card-bg);
}
.card.is-loading {
opacity: 0.6;
}
.button {
background: var(--button-bg, var(--bg, #222));
color: var(--button-fg, var(--fg, #fff));
}
Then if a card is supposed to theme a child on purpose, do it explicitly:
.card .button {
--button-bg: #222;
}
That way a card changing --bg doesn’t accidentally reskin every nested thing like some cursed RPG aura effect.