Dubem WorkDubem Work

28 Tue Apr 2026

How to Reduce CSS Size in a Project (Without Breaking UI)

Practical ways to shrink CSS bundles: Tailwind discipline, choosing tree-shakeable UI libs, safe purging, avoiding accidental global CSS, and setting team/AI conventions.

Big CSS bundles rarely come from one dramatic mistake. They’re usually the sum of small choices: styling “just this one thing” with arbitrary values, adopting a UI library that ships a huge stylesheet, or accidentally making most CSS global so every route downloads everything.

The goal isn’t “no CSS.” The goal is ship only the CSS your users need for the routes they visit, and do it in a way that doesn’t silently break production UI.


Avoid Tailwind arbitrary classes (they add noise and complicate purging)

Tailwind stays small when your class names are static and reusable. Arbitrary values (bg-[#0f172a], w-[372px], shadow-[...]) tend to push you toward one-off styling and can force you to loosen purge rules when classes become dynamic.

What to do instead:

  • Prefer design tokens in tailwind.config (theme.extend) for colors, spacing, radii, shadows, font sizes.
  • Prefer composed utilities for repeated patterns:
    • shared static class strings (keep them literal, not generated)
    • small wrapper components (<Button>, <Card>, <Badge>) that encode a consistent style
    • Tailwind layers like @layer components for semantic classes
  • If you must use an arbitrary value, keep it rare, static, and reviewed.

A useful rule: if you see the same arbitrary value twice, it probably wants to be a token.


Avoid UI libraries that can’t tree-shake or purge CSS well

JavaScript tree-shaking is common. CSS tree-shaking is not guaranteed. Many UI libraries ship a single big stylesheet (or inject global styles) that you can’t easily split by route or prune by usage.

Before you adopt a UI library, verify:

  • It supports per-component imports (for both JS and CSS), or a build step that outputs “used-only” CSS.
  • It documents how to work with purge (Tailwind, PurgeCSS, or the framework’s native purge).
  • It doesn’t require a giant global CSS import just to render a few components.

If you can’t answer “how does this library ensure unused CSS isn’t shipped?”, you’ll usually pay for it in bundle size.


Use a CSS purge plugin, but treat it like a risky refactor

Purging is one of the highest-impact optimizations for CSS size—and one of the easiest ways to ship broken UI if your purge config misses a template source or dynamic class pattern.

Best practices:

  • Run purge for production builds and ensure the tool scans all rendered templates (pages, components, content, emails, CMS templates).
  • Prefer static class strings over generated class names.
  • If you must safelist, do it narrowly (explicit list over broad regex).

How to validate you didn’t break anything:

  • If you have screenshot tests, run them and review diffs for key routes.
  • If you don’t, do a manual pass that specifically checks:
    • responsive variants (sm:, md:), hover/focus/active states
    • dark mode variants
    • modals, toasts, dropdowns, empty states, error states
    • rarely visited routes (settings/admin/onboarding)

Purging should reduce CSS. It shouldn’t reduce trust.


Watch out for global CSS exports (they prevent page-level splitting)

One very common reason CSS stays large: your app ends up in a state where most styles are global, so every route loads nearly everything.

This often happens when:

  • a root entry imports a “main.css” that imports lots of component/page CSS
  • a UI library requires a global stylesheet
  • framework limitations or dynamic routing patterns prevent clean CSS chunking

What to do:

  • Keep global CSS minimal: tokens (CSS variables), tiny reset, base typography—nothing route-specific.
  • Co-locate route-specific styling with the route/module so your bundler has a chance to split by page.
  • Audit imports: if a stylesheet is imported by the root layout/app entry, it’s effectively global.

If you want small CSS, global CSS should be a deliberate choice, not an accident.


Keep best practices consistent across your team (and AI agents)

CSS bloat is often a process problem: different people (and agents) produce different styling patterns, which increases unique utilities, safelists, and global overrides.

Team conventions that keep CSS lean:

  • Define a “styling decision tree”:
    • utilities first → tokens second → components/layers for repetition → arbitrary values last
  • Add lightweight PR checks:
    • flag new global CSS imports
    • flag broad purge safelists
    • track CSS output size (even just “before vs after” from build stats)
  • Teach your AI agents the same rules:
    • default to tokens and reusable patterns
    • avoid arbitrary classes unless justified
    • never expand purge safelists casually
    • don’t introduce global CSS without a clear reason

If the rules aren’t shared, the CSS will grow back—quietly.


Quick checklist

  • Reduce arbitrary Tailwind values; add tokens.
  • Choose UI libraries with proven CSS purging/tree-shaking.
  • Purge in production builds, scan all templates, safelist narrowly.
  • Keep global CSS minimal; avoid importing route styles globally.
  • Standardize conventions for humans and AI agents; enforce with PR checks.
Dubem Izuorah

Written by

Dubem Izuorah

Design Engineer

With over 10 years of experience in design and software engineering, I build tools for startups across various industries, with a special focus on marketing tools that support businesses around the world. Lately I'm focused on the Human ↔ AI work loop, helping people collaborate with AI to do great work.