Paradise CodeSoftware Studio
Back to articles
Product ArchitectureUpdated 13 min read

Designing Design Systems with Tailwind and HeroUI Without Class Chaos

From tokens and variants to a shared component package—how to turn Tailwind and HeroUI into a product system, not scattered utilities.

design systemTailwindHeroUIUItokens

Ali Mortazavi

Founder, Paradise Code

A design system starts before components

If you start with Button and skip tokens, you build a UI kit—not a system. Lock foundations first: spacing scale, type, radius, shadow, semantic color (primary/danger/success), and interaction states. Tailwind expresses these as theme tokens so raw classes do not leak across features.

HeroUI accelerates accessible interactions, but without a product token layer every page invents its own palette. A system is intentional constraint.

Make tokens semantic, not decorative

Instead of `blue-600` in features, define `brand`, `surface`, `muted`, `danger`. Decorative color binds to brand; semantic color binds to UI role. When brand changes, you change tokens—not hundreds of classes.

For light/dark or customer themes, map the same meanings. If every component encodes its own color conditions, theming is already lost.

Variants and composition instead of copied classes

A `cva`/variant pattern for size, appearance, and state cuts long repeated `className` strings. Component APIs should stay short: `variant`, `size`, `isDisabled`. If callers must pass ten Tailwind classes, encapsulation failed.

Allow a limited escape hatch (controlled `className`) but confront it in review with “why wasn’t the system enough?” Every exception either creates a new token or visual debt.

Treat HeroUI as a core, not a shell to bypass

Use the accessible behaviors—focus, keyboard, dialog—and align appearance with your tokens. Fully rewriting components for taste raises maintenance cost and makes library upgrades painful.

Where HeroUI does not cover a product pattern, build a primitive and register it in the design-system inventory. Living systems move with a clear catalog, not orphan components in features.

Consumption docs for engineers and designers

Storybook or an equivalent should show states, Do/Don't, and spacing—not only a pretty gallery. For each component: when to use it, default variant, and what is forbidden.

Designers and engineers need one naming language. If Figma says “Primary / Large” and code says `variant="solid" size="lg"`, write the mapping table explicitly.

Change governance without bureaucracy

Foundational token changes need a dedicated PR and screenshots of key paths. New variants need a real use case, not “maybe later.”

Run a short visual audit each quarter: fold repeated raw classes back into tokens/components. That cleanup is cheaper than next year’s UI rewrite.

Frequently asked questions

Is Tailwind alone enough without a UI library?

For a tiny product yes, but you pay accessibility and interaction consistency yourself. HeroUI reduces that cost when consumed token-first.

How much free className should we allow?

Little, and controlled. If a pattern repeats, add a variant or token—do not copy classes.

When should we start a design system?

As soon as more than one surface—or two related apps—exists. Delay means paying visual debt with interest.

Insights

Need these ideas implemented in your product?

Paradise Code supports you from consult to full delivery.

Request collaboration