Skip to content

Composition Over Abstraction

When developers see repetition, they instinctively reach for abstraction.

In CSS, that usually means:

  • creating new class names
  • extracting shared rules
  • building layers of indirection

This feels tidy.

At scale, it often isn’t.


CSS abstractions tend to age poorly:

  • their original intent fades
  • edge cases pile up
  • overrides appear
  • names stop matching behavior

Eventually, developers are afraid to touch them.

The abstraction becomes a liability.


Atomic UI favors composition instead.

You assemble UI at the point of use:

  • spacing
  • layout
  • color
  • state

Everything that matters is visible where it’s applied.

There’s no guessing what a class does.


With composition:

  • HTML expresses layout
  • classes express intent
  • CSS stays predictable

You don’t have to jump between files to understand UI.

That dramatically improves maintainability.


Composition avoids:

  • deep inheritance chains
  • magical overrides
  • specificity battles

Each utility does one thing.

Together, they describe the interface.


This is not an absolutist rule.

Abstraction is still useful for:

  • repeated animations
  • complex interactions
  • shared component behavior
  • non-visual logic

The key is intentional abstraction, not reflexive abstraction.


Tailwind allows @apply.

That doesn’t mean you should reach for it immediately.

Overusing @apply:

  • recreates traditional CSS problems
  • hides decisions again
  • increases coupling

Compose first. Abstract later — if needed.


Compare a utility-composed UI to an abstracted version.

Notice:

  • readability
  • flexibility
  • ease of change

Once composition feels natural, the next question is how it adapts across screens and states.

Next up: Responsive and State Modifiers