CONCEPTS

Composition laws

The four laws a style array obeys, and how two of them make the merge a monoid, a category with one object.

A classStyle array merges its styles property by property. Written as an operation, where ⊕\oplus merges the style on its right into the one on its left, that merge obeys four laws. Each one is something you can rely on when you restructure styles.

Associativity

(A⊕B)⊕C=A⊕(B⊕C)(A \oplus B) \oplus C = A \oplus (B \oplus C)

Grouping does not change the result. Arrays can be nested, so these are the same:

<div classStyle={[[styles.a, styles.b], styles.c]} />
<div classStyle={[styles.a, [styles.b, styles.c]]} />

That is what lets you pull part of an array into a variable, or a prop, and put it back anywhere in the same position.

Identity

A⊕I=I⊕A=AA \oplus I = I \oplus A = A

false, null and undefined merge into nothing, so each of them is the identity II. That is why a condition can sit in the array:

<div classStyle={[styles.base, isActive && styles.active]} />

When isActive is false, the array is styles.base alone.

Idempotency

A⊕A=AA \oplus A = A

Merging a style with itself changes nothing, so a style that reaches an element twice, through a shared base and a prop, say, does no harm.

No commutativity

A⊕B≠B⊕AA \oplus B \neq B \oplus A

Order is the one thing that does matter. When two styles set the same property, the one on the right wins, so swapping them swaps the winner. Styles that set different properties never compete, and their order makes no difference.

A shorthand against its longhand is the exception: the longhand wins in either order. See Specificity.

What the laws add up to

Together, associativity and identity make the merge a monoid: any array, however it is nested or wherever its conditions fall, reduces to one style. The compiler does that reduction at build time, so the element receives a single class list:

parse(A⊕B⊕C)→"classA classB classC"parse(A \oplus B \oplus C) \to \text{"classA classB classC"}

Here classA and the rest are not each style's classes laid side by side. The reduction is a merge, so a property overridden from the right leaves no class behind:

const styles = css.create({
  a: { color: 'red', padding: 4 },
  b: { color: 'blue' },
});

<div classStyle={[styles.a, styles.b]} />

The element receives two classes, padding: 4px and color: blue; the class for color: red is not among them. Two classes for the same property never reach one element, so the cascade is never left to pick the winner.

On this page