In the world of CSS architecture, two methodologies have emerged as dominant approaches: BEM (Block Element Modifier) and Utility-First CSS. While BEM has been a reliable standard for many years, the industry has seen a noticeable shift toward utility-first frameworks like Tailwind CSS. This article explores both approaches, explains why modern libraries are embracing utility-first, dissects the factors that propelled Tailwind to its current popularity.
#What is BEM?
BEM is a naming convention that structures CSS into Blocks, Elements, and Modifiers. Its goal is to create reusable, self-documenting components with low specificity and clear boundaries.
- Block: Standalone component (e.g.,
card,button). - Element: Part of a block (e.g.,
card__title,button__icon). - Modifier: Variation of a block or element (e.g.,
card--featured,button--large).
<div class="card card--featured">
<h2 class="card__title">Featured Product</h2>
<p class="card__description">...</p>
<button class="card__button card__button--primary">Buy Now</button>
</div>
.card {
border-radius: 8px;
padding: 16px;
}
.card--featured {
border: 2px solid gold;
}
.card__title {
font-size: 1.5rem;
}
.card__button--primary {
background: blue;
color: white;
}
Pros:
- Explicit component structure.
- No specificity conflicts.
- Reusable blocks.
Cons:
- Verbose class names.
- Manual naming discipline.
- CSS file can grow large.
- Unused CSS can accumulate over time.
#What is Utility-First CSS?
Utility-first CSS, popularized by Tailwind CSS, is an approach where you compose styles by applying many single-purpose utility classes directly in your HTML. Each utility does one thing (e.g., p-4 for padding, text-center for text alignment). This method enforces design constraints and minimizes context switching.
<div class="rounded-lg p-4 border-2 border-gold-500 shadow-lg">
<h2 class="text-2xl font-bold mt-2">Featured Product</h2>
<p class="text-gray-600 mt-1">...</p>
<button class="mt-4 px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700">Buy Now</button>
</div>
Pros:
- Rapid development (no switching between HTML and CSS).
- No naming overhead.
- Consistent design system enforced.
- Optimized bundle size (purge unused styles).
- Easy maintenance (styles live with markup).
Cons:
- HTML becomes cluttered with many classes.
- Learning curve for utility names.
- Perceived violation of separation of concerns.
#Why Modern Libraries Are Moving Toward Utility-First
Several trends have accelerated the adoption of utility-first CSS, especially within modern frameworks and component libraries:
#1. Developer Experience (DX)
Utility-first eliminates the constant back-and-forth between HTML and CSS files. Developers can style elements directly where they are used, which speeds up iteration. This aligns with the component-based architecture of frameworks like React, Vue, and Svelte, where encapsulation is valued.
#2. Design System Enforcement
Utilities are generated from a limited set of design tokens (spacing, colors, typography). This makes it nearly impossible to deviate from the system, ensuring consistency across the application. BEM relies on developers to adhere to design tokens manually.
#3. Bundle Size Optimization
Tools like Tailwind use PurgeCSS (or built-in content scanning) to remove unused CSS. The final CSS contains only the utilities used in your codebase, often resulting in a much smaller file than handcrafted CSS that accumulates unused styles over time.
#4. Scalability Without Complexity
Utility-first scales well because you never have to worry about naming conflicts, specificity wars, or the dreaded !important. Since each utility does exactly one thing, there’s no cascade to debug. This is especially valuable in large, long-lived projects.
#5. Component Reusability at a Higher Level
Instead of reusing CSS classes, teams reuse components (e.g., React components). Utility classes become the implementation detail of those components, abstracted away from the consumer. This makes styling changes isolated and predictable.
#What Made Tailwind CSS Successful?
Tailwind CSS is the most prominent utility-first framework. Its success can be attributed to several key factors:
#1. A Well-Designed API
Tailwind provides intuitive, consistent utility names (e.g., p-4 for padding, text-lg for font size). It uses a spacing scale and color palette that are easy to memorize and extend.
#2. Configuration-First
Tailwind is highly customizable. You can define your own design tokens, breakpoints, and variants without fighting the framework. This makes it suitable for both small projects and large design systems.
#3. Just-In-Time (JIT) Engine
The JIT engine (now default) generates utilities on-demand, enabling:
- Unlimited variants (e.g.,
md:hover:bg-red-500). - Smaller CSS bundles (no pre-generated bloat).
- Faster builds.
#4. Strong Ecosystem & Tooling
Tailwind integrates seamlessly with popular build tools (Webpack, Vite) and frameworks. Plugins like @tailwindcss/forms, @tailwindcss/typography, and the official VS Code extension enhance productivity.
#5. Excellent Documentation
Tailwind’s documentation is comprehensive, visually rich, and beginner-friendly. It includes interactive examples, search, and clear explanations—a major reason for its rapid adoption.
#6. Community and Adoption
Tailwind has built a massive community. It’s used by companies like GitHub, Shopify, and Vercel. This network effect creates trust and a wealth of third-party resources, components, and tutorials.
#7. Philosophy Alignment with Modern Frontend
Modern frontend development emphasizes component encapsulation and tooling. Tailwind’s utility-first approach fits naturally with component frameworks, where you style components using utilities rather than maintaining separate CSS files.
#Design Tokens & Figma as Source of Truth for Utility-First
One of the most powerful advantages of utility-first CSS—especially when implemented with Tailwind—is the ability to derive the entire design system from a single source of truth. This source of truth is often a design tool like Figma, combined with design tokens.
#What Are Design Tokens?
Design tokens are named entities that store visual design attributes—such as colors, spacing, typography, shadows, and breakpoints—in a platform-agnostic format. They act as the single source of truth for design decisions, ensuring consistency across all platforms and tools.
Example of design tokens in JSON:
{
"color": {
"primary": {
"500": "#3B82F6",
"600": "#2563EB"
},
"gray": {
"600": "#4B5563"
}
},
"spacing": {
"1": "4px",
"2": "8px",
"4": "16px"
},
"fontSize": {
"lg": "1.125rem",
"2xl": "1.5rem"
}
}
#Figma as the Design Source
Figma is the industry-standard design tool. By leveraging plugins like Figma Tokens, Style Dictionary, or Tailwind CSS Figma Kit, teams can:
- Define color palettes, typography scales, spacing units, and other design decisions directly in Figma.
- Export these decisions as design tokens (JSON, YAML, etc.).
- Automatically generate the Tailwind configuration (or any utility-first framework config) from those tokens.
#Generating Utility Classes from Figma Tokens
A typical workflow:
- Design in Figma using styles (colors, text styles, effects) that are named according to the token system.
- Use a plugin (e.g., Figma Tokens) to sync tokens to a GitHub repository or a local file.
- Transform tokens into a Tailwind configuration using tools like Style Dictionary or a custom script.
- Tailwind generates utilities exactly matching the design system.
Example transformation (simplified):
// tokens.json (exported from Figma)
{
"colors": {
"primary": { "500": "#3B82F6", "600": "#2563EB" },
"gray": { "600": "#4B5563" }
},
"spacing": { "1": "4px", "2": "8px", "4": "16px" }
}
// tailwind.config.js (auto-generated)
module.exports = {
theme: {
extend: {
colors: {
primary: { 500: '#3B82F6', 600: '#2563EB' },
gray: { 600: '#4B5563' }
},
spacing: {
1: '4px',
2: '8px',
4: '16px'
}
}
}
};
Now, designers and developers share the exact same values. Any change in Figma can be propagated to code via an automated pipeline, ensuring pixel-perfect consistency and eliminating manual transcription errors.
#Benefits of This Workflow
- Single Source of Truth: No more “the design says blue, but the code uses a different hex.”
- Automated Consistency: Updates in Figma automatically sync to code, reducing design‑development friction.
- Scalability: Adding a new brand color or spacing scale is done once in Figma and instantly available as utilities.
- Documentation: The design system is automatically documented in both design and development environments.
#How Utility-First Shapes a Consistent Design System and Simplifies Maintenance for Companies
Adopting utility-first CSS goes beyond developer preference—it fundamentally changes how teams maintain and scale a design system over time.
#1. Design Decisions Become Constraints
With utility-first, every style decision is constrained by the underlying design tokens. Developers cannot arbitrarily use margin-left: 17px because no utility exists for that value—they must use the predefined spacing scale (e.g., ml-4). This forces adherence to the design system and eliminates stylistic drift.
#2. No Orphaned CSS
In traditional CSS (including BEM), styles often accumulate over time. Even when a component is deleted, its CSS remains in the stylesheet, leading to bloat and confusion. With utility-first, styles are applied directly in the markup. When you delete an HTML element or a component, all its associated utility classes disappear with it—no leftover CSS to maintain.
#3. Refactoring Is Safe and Predictable
Since utilities are atomic and independent, modifying a utility’s definition (e.g., changing bg-blue-600 from one hex to another) updates every instance across the entire application. There’s no risk of accidentally affecting other selectors because of cascade or specificity.
#4. Onboarding and Cross-Team Collaboration
New developers can quickly grasp the design system by looking at the utility classes used in components. The design tokens are self‑documenting and visible directly in the code. Additionally, designers can review the actual utility classes used in production if the team adopts a component‑driven approach (e.g., Storybook with Tailwind), creating a shared language.
#5. Reduced Maintenance Overhead
In companies where multiple teams contribute to the same codebase, utility-first reduces the cognitive load of understanding custom CSS classes. There are no “magic” class names like card__inner-wrapper; everything is composed of predictable utilities. This lowers the barrier to contribution and reduces the need for extensive CSS documentation.
#6. Enables Design System as Code
The design tokens exported from Figma become the foundation of the utility-first configuration. This means the design system lives as code, can be versioned, tested, and even subjected to CI/CD pipelines. Any change to tokens triggers a build that validates and deploys the updated utilities, ensuring the entire organization stays in sync.
#Reducing Unused CSS: DevTools Coverage and the Utility-First Advantage
One of the biggest performance challenges in traditional CSS is unused CSS. Studies show that many production websites ship stylesheets where 60–80% of the rules are never applied. This waste increases download time, parse time, and memory usage.
#How Utility-First Eliminates Unused CSS
Utility-first frameworks like Tailwind solve this by generating CSS only for the utilities actually used in your markup. With tools like PurgeCSS (or Tailwind’s built-in content scanning), the final CSS bundle contains exactly what your application uses—nothing more.
For a typical Tailwind project, the output CSS is often less than 10 KB after compression, even for large applications. Compare this to a traditional CSS codebase that can balloon to hundreds of kilobytes.
#How to Check Unused CSS with DevTools Coverage
Modern browsers provide a Coverage tool that shows which CSS rules are used on a page. Here’s how to use it:
- Open Chrome DevTools (F12).
- Go to the More tools menu (three dots) and select Coverage.
- Click the record button (circle) and reload the page.
- The tool displays a table of loaded resources, showing the percentage of unused bytes for each CSS file.
# Steps to analyze unused CSS
1. Open DevTools → More tools → Coverage
2. Click the record button (circle) → reload page
3. Inspect the CSS files listed; the red portion indicates unused bytes
For a typical BEM-based site, you might see 50–80% unused CSS. For a properly purged utility-first site, unused CSS is usually 0–5% .
#Impact on Performance
- Smaller network payload: Faster downloads, especially on slow connections.
- Faster parsing: Browsers parse CSS in a blocking manner; smaller CSS means less blocking.
- Reduced memory footprint: Fewer rules to store and apply.
- Better performance scores: Tools like Lighthouse reward smaller, optimized CSS.
#Impact on LCP and TTFB
The performance benefits of utility-first CSS directly influence two critical Core Web Vitals: Largest Contentful Paint (LCP) and Time to First Byte (TTFB) .
#What Are LCP and TTFB?
- LCP (Largest Contentful Paint): Measures the time it takes for the largest visible element (e.g., hero image, heading) to render. A good LCP is ≤ 2.5 seconds.
- TTFB (Time to First Byte): Measures the time between the browser requesting a page and receiving the first byte of the response. It reflects server responsiveness and network latency.
#How Utility-First Improves LCP
- Smaller CSS bundles reduce the amount of data the browser must download and parse before rendering.
- Fewer blocking resources: CSS is render-blocking. A smaller CSS file means the browser can parse and apply styles faster, unblocking the rendering of the largest contentful element.
- Critical CSS strategies: Because utility classes are predictable, you can easily extract critical CSS for above-the-fold content, further improving LCP.
#How Utility-First Improves TTFB
While TTFB is primarily server-driven, CSS bundle size can indirectly affect it when using server-side rendering (SSR) or edge caching. A smaller CSS file may be:
- Cached more efficiently at CDN edges.
- Transferred faster if the server needs to send it as part of the initial HTML (e.g., inlined critical CSS).
- Less processing overhead on the server if the CSS is generated from a static configuration rather than being composed dynamically.
#Real-World Example
A company switched from a handcrafted BEM CSS (200 KB minified) to Tailwind with purging (12 KB). Their LCP improved from 3.2 seconds to 1.8 seconds on mobile 3G. TTFB decreased by 15% due to reduced response size when CSS was inlined.
<!-- Inlining critical CSS for utility-first -->
<style>
/* Tailwind utilities used above the fold */
.text-center {
text-align: center;
}
.bg-blue-600 {
background-color: #2563eb;
}
/* ... only a few utilities */
</style>
#Comparison at a Glance
| Aspect | BEM | Utility-First (Tailwind) |
|---|---|---|
| Styling approach | Semantic class names per component | Composable utility classes |
| HTML readability | Cleaner but verbose names | Cluttered but explicit |
| Development speed | Slower (context switching) | Faster (inline styling) |
| Design consistency | Relies on CSS variables / manual tokens | Enforced by design system (tokens) |
| Bundle size | Often grows over time, unused CSS accumulates | Purged, minimal (usually < 20 KB) |
| Unused CSS | High risk; requires periodic audits | Automatically eliminated by purging |
| Learning curve | Low (naming convention) | Medium (utility API) |
| Naming overhead | High (invent component names) | None |
| Tooling | Any preprocessor | Tailwind-specific (but well integrated) |
| Source of truth | Manual / CSS variables | Design tokens (Figma → config) |
| Maintenance | Orphaned CSS risk, cascade issues | Delete markup → styles gone, predictable |
| LCP / TTFB impact | Larger CSS can hurt LCP and TTFB | Small CSS improves LCP, helps TTFB |
| DevTools Coverage | Typically 40–80% unused | Typically 0–5% unused |
#Conclusion
The shift toward utility-first CSS, exemplified by Tailwind’s success, is driven by a desire for better developer experience, enforced design consistency, and efficient bundle management. By coupling utility-first with design tokens exported from Figma, organizations can achieve a seamless handoff between design and development, ensuring every pixel aligns with the original vision.
Beyond workflow benefits, utility-first directly addresses performance pain points: it eliminates unused CSS (as visible in DevTools Coverage), reduces bundle sizes, and improves critical metrics like LCP and TTFB. For companies aiming to maintain a scalable, consistent, and high‑performing design system, utility-first offers a compelling, future‑proof foundation.
Ultimately, the choice between BEM and utility-first depends on your team’s context, but the momentum—and the performance wins—are increasingly favoring the utility-first approach.