Skip to main content

Class Merge

Resolve two utilities that set the same property so the last one wins.

View as Markdown

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

  • @layer ranks a class everywhere it is used, so demoting c:white would demote it on every element, not only the one you override.
  • !important wins 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.