Build Status Badges That Behave the Same in Server Components and Client Components, With No Specificity Fights
Zero-Specificity Badges Across the Next.js App Router
The classic CSS-in-JS specificity trap: you write a Badge with a shared "loud" style for the success/warning/danger tones, then a consumer tries to override the font weight on one instance and finds their style loses. Salty CSS handles this with anyOfVariants — the shared rule is wrapped in :where() so it carries zero specificity and any later rule for the same property wins automatically.
The component
// /components/badge.css.ts import { styled } from "@salty-css/react/styled"; export const Badge = styled("span", { base: { display: "inline-block", padding: "0.125rem 0.5rem", borderRadius: "999px", fontSize: "0.75rem", }, variants: { tone: { success: { background: "#16a34a", color: "white" }, warning: { background: "#eab308", color: "black" }, danger: { background: "#dc2626", color: "white" }, neutral: { background: "#e5e7eb", color: "#111" }, }, }, // Apply the same "loud weight" whenever the tone is brand-loud. // anyOfVariants emits :where(…) so this rule has zero specificity and // always loses to a per-instance override. anyOfVariants: [ { tone: "success", css: { fontWeight: 700, letterSpacing: "0.02em" } }, { tone: "warning", css: { fontWeight: 700, letterSpacing: "0.02em" } }, { tone: "danger", css: { fontWeight: 700, letterSpacing: "0.02em" } }, ], });
Why this beats the obvious alternatives
- Repeating
fontWeight: 700inside eachvariants.tonebranch works, but every new tone has to remember to copy it — and merging two of those into a compound override later is awkward. - Adding it to
baseand trying to "un-bold" the neutral tone via{ fontWeight: 400 }also works — until a parent style sheet (Tailwind reset, design-system base) wins on specificity.anyOfVariantsducks that fight entirely.
Per-instance override
Because the shared rule lives behind :where(), the override below wins without !important or extra class chaining:
// Loud red badge, but this one needs to read as quiet emphasis. <Badge tone="danger" style=> Optional </Badge>
The style prop is an inline declaration, which always wins; but even a more disciplined override — a className rule keyed off data-quiet="true" — would win, because the shared rule is :where(.badge-…).
What it produces
For the four tones you get one base class, four tone branches, and one :where(...) rule covering the three "loud" tones at once. All of it is in saltygen/index.css after the build; nothing about specificity is computed at render time.
See also
anyOfVariantsin the styled API reference- Variants — the broader picture of variant composition.
- Overrides — when to reach for
priorityinstead.