Contents

Two approaches suit most new projects: Tailwind — utility classes right in the markup, tied to design tokens — and CSS modules — plain CSS with automatic name isolation per component. Tailwind is faster for typical interfaces and keeps styles from sprawling; CSS modules are more familiar and handier for complex, unusual components. Runtime CSS-in-JS is chosen less often now because of its performance cost.

Here are the problems styles run into, how the approaches differ and how to choose.

What goes wrong with styles in large projects?

In a small project any way of writing CSS works. Problems appear when several people work on the interface and the project is a few years old: styles conflict, nobody dares delete old CSS and the same spacing and colours exist as dozens of different values.

  • name conflicts — a new class accidentally changes another page;
  • dead CSS — styles outlive deleted components and the file keeps growing;
  • inconsistency — twenty shades of grey and spacing by eye;
  • specificity — battles of !important and nested selectors;
  • fear of change — nobody knows what breaks if a shared style is edited.

How do styling approaches differ?

The main approaches differ in where styles live, how they are isolated and how they reach the browser.

Approach How it works Pros Cons
Tailwind Utility classes in markup Fast, consistent, CSS does not grow Long class lists, takes getting used to
CSS modules A CSS file next to the component, isolated names Plain CSS, isolation, flexibility Values must be taken from variables manually
BEM A class naming convention No tooling Isolation relies on discipline
Runtime CSS-in-JS Styles generated by JavaScript Dynamic, all in one file Extra JavaScript, SSR issues
Build-time CSS-in-JS Styles extracted to CSS at build Typing and isolation at no runtime cost Smaller ecosystem

A screen showing HTML markup with style classes Photo: Unsplash

How does Tailwind work?

Tailwind provides small classes that each do one thing: set a margin, a colour, a font size. Values come from configuration — the project's design tokens — and only classes actually used end up in the final CSS.

<button class="rounded-lg bg-brand-600 px-4 py-2 text-sm font-semibold text-white
               hover:bg-brand-700 focus-visible:outline-2 disabled:opacity-50">
  Place order
</button>

Strengths:

  1. You cannot accidentally break a neighbouring component — styles are bound to the element.
  2. Spacing and colours come from a limited scale, keeping the interface consistent.
  3. Deleting a component deletes its styles too.
  4. Responsiveness and states live in the markup: md:, hover:, dark:.

Repeated class combinations go into components — a button, a card, an input — not into CSS.

When are CSS modules better?

CSS modules are handier when the team knows CSS well, components are complex and unusual — animations, intricate grids, illustrations — or the project is already written in plain CSS and SCSS. Class names become unique automatically, so conflicts are impossible, and you write familiar CSS.

  • keep values in CSS variables — these are your design tokens;
  • one module per component, with no global styles inside;
  • global styles only for resets, typography and variables;
  • use a style linter to catch hard-coded colours and spacing.

What should a team choose?

The choice depends less on technology than on having a design system and on the team's experience. More important than any tool are shared tokens — colours, fonts, spacing and radii used by designers and developers alike.

  • a new product with a typical interface and a design system — Tailwind;
  • complex animated layouts and strong CSS skills — CSS modules;
  • a large existing SCSS project — CSS modules and gradual introduction of tokens;
  • do not mix more than two approaches in one project.

More on design systems in design system: when you need one, on frontend speed in Core Web Vitals, and on accessibility in website accessibility.

In short

  • Style problems are name conflicts, dead CSS and inconsistent values.
  • Tailwind means utilities in markup, consistency and small final CSS.
  • CSS modules mean plain CSS with isolation, handy for complex components.
  • Runtime CSS-in-JS adds JavaScript and is chosen less and less.
  • More important than the tool are shared design tokens and team conventions.

FAQ

  • Doesn't Tailwind make markup unreadable?

    Long class lists do look unusual. Small components, automatic class sorting and moving repeated combinations into components rather than separate CSS classes solve it.

  • Can Tailwind and CSS modules be combined?

    Yes: utilities for typical layout, modules for complex animations and unusual components. Agree on when to use which, or the project will end up with two styles.

  • Does the choice affect site speed?

    Yes. Tailwind and CSS modules produce static CSS that caches well. Runtime CSS-in-JS adds JavaScript and slows rendering, especially on weak devices.