DEV Community

Cathy Lai
Cathy Lai

Posted on

Real World Tailwind CSS: The "Gatekeeper" Architecture: A Senior Developer's Guide (Part 1/2)

So, armed with the knowledge of Tailwind and what it has to offer, how should we actually start our next large-scale project? To answer that question, it helps to look at how mature design-system teams structure frontend development in large organizations.

The Blueprint: Component Authors vs. Feature Developers

After a quick research, I've found that many successful design systems, including examples such as Shopify Polaris, are built around a simple idea: not everyone on the team is responsible for styling decisions.

Instead, styling responsibilities are often separated into two distinct roles:

  1. The Component Authors (The Gatekeepers): This core team builds foundational UI primitives. They write the Tailwind classes, manage accessibility, dark mode, component states, and design-token compliance.
  2. The Feature Developers (The Assemblers): This team builds product features, workflows, and dashboards. Their primary focus is business logic and page composition rather than choosing colors, typography, or spacing scales.

Many organizations reinforce this boundary through a combination of lint rules, code review standards, internal tooling, and shared team conventions.

✔ Allowed: layout and composition utilities (flex, grid, gap-4)

❌ Discouraged: introducing new visual styles such as bg-indigo-600 or text-lg directly inside feature pages when an approved component already exists

Code Example: The Standard Corporate Button

To see this boundary in practice, let's look at how a standard corporate button might be built and consumed.

1. What the Component Author Builds

The Component Authors create a reusable primitive inside @/components/ui/button.tsx. The visual styling is centralized behind a clean TypeScript API:

// Managed by the Design System Team (@/components/ui/button.tsx)
import React from 'react';

interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
  variant?: 'primary' | 'secondary';
  className?: string; // Intended for layout adjustments when needed
}

export function Button({
  variant = 'primary',
  className = '',
  children,
  ...props
}: ButtonProps) {
  const baseStyles =
    "px-4 py-2 rounded-lg font-medium text-sm transition-all focus:outline-none focus:ring-2 focus:ring-offset-2";

  const variants = {
    primary: "bg-indigo-600 text-white hover:bg-indigo-700 focus:ring-indigo-500",
    secondary: "bg-slate-100 text-slate-700 hover:bg-slate-200 focus:ring-slate-500"
  };

  return (
    <button className={`${baseStyles} ${variants[variant]} ${className}`} {...props}>
      {children}
    </button>
  );
}
Enter fullscreen mode Exit fullscreen mode

2. How the Feature Developer Uses It

When a Feature Developer builds a dashboard page, they primarily assemble existing building blocks and focus on feature logic:

// Inside @/app/dashboard/page.tsx
import { Button } from "@/components/ui/button";

export default function Dashboard() {
  return (
    <div>
      ...

      <Button variant="primary" className="mt-4">
        Save Changes
      </Button>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

The visual design remains centralized in the component, while the page controls placement and composition.

Scenarios

Deleting a Button

The developer who originally created a page has moved on, and another developer needs to remove a button.

Because the visual styling is already encapsulated inside a shared component, there is no need to search through multiple CSS files looking for button-specific styles. The page simply stops using the component.

This reduces the risk of obsolete styling logic lingering in the codebase and makes long-term maintenance easier.

Colour or Theme Change

Suppose the company decides to move from Indigo to Violet as its primary brand color.

In many design-system architectures, the update happens in a single shared location (or design-token layer). The change then propagates consistently across the application without requiring developers to update individual pages.

A Bigger Lesson

The important idea is not Tailwind itself. The key architectural concept is the boundary between component authors and feature developers. Tailwind is simply one effective tool for implementing that separation while keeping styles close to the components that own them.

In the Next Article...

This approach covers most day-to-day product development. But what happens when the marketing team wants a one-off campaign page with a glowing, animated gradient button that doesn't fit the design system? Do we extend the component library? Create a special exception? Relax the rules?

In Part 2, we'll explore practical ways to handle architectural exceptions without turning the codebase into a free-for-all.

Top comments (0)