Your design system has a coupling problem

Published: (March 16, 2026 at 01:26 PM EDT)
3 min read
Source: Dev.to

Source: Dev.to

Introduction

I write bluntly, I value your time—less waffle, more value.

Pick a popular component library and find the Button component. You’ll see:

  • Structure – semantic HTML with ARIA attributes (the accessibility contract).
  • Interaction – JavaScript handling focus, keyboard events, disclosure state, scroll locking, etc.
  • Aesthetics – color values, spacing, font size, border radius, hover transitions, and variants (xl, lg, md, primary, secondary, …).

One component is doing three jobs.

The Three Pillars

PillarConcernExample
StructureSemantic HTML, ARIA roles, accessibility contractWhat element is it? What ARIA role does it have?
InteractionBehavioural logic, focus management, keyboard navigation, scroll lockingKeyboard shortcut, modal open/close
AestheticsVisual treatment – colors, spacing, typography, border radius, elevationBrand palette, hover states

These pillars constantly change in any product. Updating the color palette (aesthetics), adding new form fields (structure), or introducing a keyboard shortcut (interaction) can quickly become a breeding ground for tangled UI/UX.

Tokens

Tokens are named variables that store visual decisions. Instead of hard‑coding #2563eb in a component, you reference --color-primary. Instead of 16px, you reference --space-md. The value lives in one place; everything else points to it.

/* Example token definitions */
:root {
  --color-primary: #2563eb;
  --space-md: 1rem;
  --radius-sm: 4px;
}

My Argument

Decouple. That’s it.

Components vs. Tokens

  • Components own structure and interaction.
  • Tokens own aesthetics.

Concrete example

// Button component (structure & interaction only)
function Button({ onClick, children }) {
  return (
    <button onClick={onClick}>
      {children}
    </button>
  );
}
/* Token system – aesthetics */
:root {
  --color-primary: #2563eb;
  --button-bg: var(--color-primary);
  --button-padding: var(--space-md);
  --button-radius: var(--radius-sm);
}

/* Component hook */
.button {
  background: var(--button-bg);
  padding: var(--button-padding);
  border-radius: var(--button-radius);
}

Three layers (raw HTML, :root vars, CSS mapping) → zero entanglement.

What This Fixes

Theme change

Edit a single token:

/* Before */
--color-primary: #2563eb;

/* After */
--color-primary: #dc2626;

All components using --color-action-primary update automatically—no component files touched, no JavaScript redeployed.

Brand refresh

Change one key/value pair in the token file, and the entire UI reflects the new brand instantly.

So, Why Not CSS‑in‑JS?

CSS‑in‑JS solved specificity wars, naming collisions, and dead code, but it introduced a new coupling: visual treatment became part of the component tree. Overriding it requires understanding the wrapper; theming requires knowing the theme shape; testing requires mocking runtime values. We traded global CSS problems for component coupling—still a mess, just in a different file.

Tailwind?

Tailwind removed JavaScript coupling, which is progress. However, utility strings like:

<button class="bg-primary text-white py-2 px-4 rounded">
  Click me
</button>

still entangle aesthetics with structure. Theming means find‑and‑replace across every component; dark mode requires dark: on every class; a brand refresh touches every file.

Tailwind is utility‑first. The approach described here is token‑first: visual decisions live in a separate token layer, not in markup.

The Separation

ConcernOwnsDoesn’t touch
StructureHTML elements, ARIA roles, semantic contractsColors, spacing, JS behaviour
InteractionFocus, keyboard navigation, disclosure, scroll lockMarkup choice, visual treatment
AestheticsTokens → CSS custom properties → component hooksDOM structure, event handling

No overlap—change one column, the others remain unaffected.

Conclusion

This isn’t theoretical; it’s a practical way to build UI systems. Stop tying together concerns that belong in separate layers.

0 views
Back to Blog

Related posts

Read more »

2026 WeCoded Challenge (Glass Ceiling)

!Cover image for 2026 WeCoded Challenge Glass Ceilinghttps://media2.dev.to/dynamic/image/width=1000,height=420,fit=cover,gravity=auto,format=auto/https%3A%2F%2F...

Intro About Java Script

Introduction In today’s class I learned a short introduction to JavaScript, so I’ll share some facts about JavaScript in this blog. What Is JavaScript? JavaScr...