The Problem
When two utilities set the same property, the one later in the stylesheet wins, whatever their order in class.
<span className="c:white c:accent">Accent</span>
<span className="c:accent c:white">Accent</span>
Both render white, because c:white is generated after c:accent. So passing c:accent to a component that already sets c:white does nothing.
Using Merge
merge takes class strings & returns one with the losers removed, so the last class wins.
import { merge } from "yummacss/merge";
merge("c:white", "c:accent"); // "c:accent"
It ships with the yummacss package, so there is nothing extra to install.
<span className={merge("c:white", className)}>Accent</span>
Falsy arguments are skipped, so conditionals read cleanly.
<div className={merge("d:f ai:c", isOpen && "bg:silver-1", className)}></div>
What Gets Dropped
A class is dropped only when a later class sets everything it sets.
merge("px:8 p:4"); // "p:4"
merge("p:4 px:8"); // "p:4 px:8"
p:4 covers px:8, so px:8 goes. Reversed, both stay, since p:4 still sets the block axis.
merge("w:10 h:10", "w:12"); // "h:10 w:12"
Shared Prefixes
merge reads the value, so a shared prefix never collides: c:p is a cursor, c:slate-10 a color.
merge("c:p c:slate-10"); // "c:p c:slate-10"
merge("p:a p:4"); // "p:a p:4"
Variants
Classes are compared within their own variant, so a hover class never covers a base class.
merge("h:bg:silver-2", "bg:white"); // "h:bg:silver-2 bg:white"
merge("h:bg:silver-2", "h:bg:indigo-2"); // "h:bg:indigo-2"
Media queries work the same way.
merge("@lg:p:4 p:2", "@lg:p:8"); // "p:2 @lg:p:8"
Unknown Classes
Anything merge does not recognize passes through in place.
merge("card", "bg:white", "card-active"); // "card bg:white card-active"
Alternatives
@layerranks a class everywhere it is used, so demotingc:whitewould demote it on every element, not only the one you override.!importantwins everywhere the class appears, and does nothing when both classes carry it.
merge removes the losing class instead, so only what you meant reaches the DOM.