{
"$type": "site.standard.document",
"bskyPostRef": {
"cid": "bafyreif3ltz7tphzrw4tfnhjy6x2gc5gpjheux6ldlfyibuuupfvwgqxqa",
"uri": "at://did:plc:25rdn5elo5izoxrmtis34zuk/app.bsky.feed.post/3mon2epwc2zg2"
},
"coverImage": {
"$type": "blob",
"ref": {
"$link": "bafkreiayiw3nn4oxvif2nmk3brx6mp76k4p2kg35xdyxgayapblkwiazz4"
},
"mimeType": "image/webp",
"size": 83784
},
"path": "/cathylai/real-world-tailwind-css-the-gatekeeper-architecture-a-senior-developers-guide-part-12-m36",
"publishedAt": "2026-06-19T09:25:12.000Z",
"site": "https://dev.to",
"tags": [
"tailwindcss",
"buildinpublic",
"css",
"frontend"
],
"textContent": "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.\n\n## The Blueprint: Component Authors vs. Feature Developers\n\nAfter 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.\n\nInstead, styling responsibilities are often separated into two distinct roles:\n\n 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.\n 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.\n\n\n\nMany organizations reinforce this boundary through a combination of lint rules, code review standards, internal tooling, and shared team conventions.\n\n✔ Allowed: layout and composition utilities (`flex`, `grid`, `gap-4`)\n\n❌ Discouraged: introducing new visual styles such as `bg-indigo-600` or `text-lg` directly inside feature pages when an approved component already exists\n\n## Code Example: The Standard Corporate Button\n\nTo see this boundary in practice, let's look at how a standard corporate button might be built and consumed.\n\n### 1. What the Component Author Builds\n\nThe Component Authors create a reusable primitive inside `@/components/ui/button.tsx`. The visual styling is centralized behind a clean TypeScript API:\n\n\n\n // Managed by the Design System Team (@/components/ui/button.tsx)\n import React from 'react';\n\n interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {\n variant?: 'primary' | 'secondary';\n className?: string; // Intended for layout adjustments when needed\n }\n\n export function Button({\n variant = 'primary',\n className = '',\n children,\n ...props\n }: ButtonProps) {\n const baseStyles =\n \"px-4 py-2 rounded-lg font-medium text-sm transition-all focus:outline-none focus:ring-2 focus:ring-offset-2\";\n\n const variants = {\n primary: \"bg-indigo-600 text-white hover:bg-indigo-700 focus:ring-indigo-500\",\n secondary: \"bg-slate-100 text-slate-700 hover:bg-slate-200 focus:ring-slate-500\"\n };\n\n return (\n <button className={`${baseStyles} ${variants[variant]} ${className}`} {...props}>\n {children}\n </button>\n );\n }\n\n\n### 2. How the Feature Developer Uses It\n\nWhen a Feature Developer builds a dashboard page, they primarily assemble existing building blocks and focus on feature logic:\n\n\n\n // Inside @/app/dashboard/page.tsx\n import { Button } from \"@/components/ui/button\";\n\n export default function Dashboard() {\n return (\n <div>\n ...\n\n <Button variant=\"primary\" className=\"mt-4\">\n Save Changes\n </Button>\n </div>\n );\n }\n\n\nThe visual design remains centralized in the component, while the page controls placement and composition.\n\n## Scenarios\n\n### Deleting a Button\n\nThe developer who originally created a page has moved on, and another developer needs to remove a button.\n\nBecause 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.\n\nThis reduces the risk of obsolete styling logic lingering in the codebase and makes long-term maintenance easier.\n\n### Colour or Theme Change\n\nSuppose the company decides to move from Indigo to Violet as its primary brand color.\n\nIn 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.\n\n## A Bigger Lesson\n\nThe 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.\n\n## In the Next Article...\n\nThis 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?\n\nIn Part 2, we'll explore practical ways to handle architectural exceptions without turning the codebase into a free-for-all.",
"title": "Real World Tailwind CSS: The \"Gatekeeper\" Architecture: A Senior Developer's Guide (Part 1/2)"
}