Architecture Decisions

ADR-0005: Tailwind CSS v4 Adoption

Decision to adopt Tailwind CSS v4 with CSS-first configuration for faster builds and better theming

Status

Accepted

Date: 2025-02-03

Context

Our design system requires a CSS framework that enables:

  • Rapid UI development with utility classes
  • Design consistency across all apps
  • Theming (light/dark mode, per-app branding)
  • Type safety for design tokens
  • Tree-shaking to minimize bundle size
  • Custom design tokens that integrate with shadcn/ui

Tailwind CSS v3 had been our standard, but v4 was released with major improvements:

  • Faster builds via Rust-based engine (Oxide)
  • CSS-first configuration (no more tailwind.config.js)
  • Better DX with @theme directive
  • Improved tree-shaking and smaller bundles
  • Native CSS variables for design tokens

We needed to decide: stick with v3 or adopt v4?

Decision

We adopted Tailwind CSS v4 across all apps with CSS-native configuration.

Key changes from v3:

  1. No tailwind.config.js — Configuration via CSS @theme directive
  2. PostCSS-based — Use @tailwindcss/postcss plugin
  3. CSS variables for themes — Design tokens defined in :root

Configuration approach:

// postcss.config.mjs
export default {
  plugins: {
    "@tailwindcss/postcss": {},
  },
};
/* globals.css */
@import "@hn-monorepo/ui/styles/base.css";

@theme inline {
  --radius-sm: calc(var(--radius) - 4px);
  --radius-lg: var(--radius);
  --color-primary: hsl(var(--primary));
  --color-background: hsl(var(--background));
}

:root {
  /* Light mode */
  --background: 36 100% 97%;
  --primary: 146 19% 49%;
}

.dark {
  /* Dark mode */
  --background: 120 25% 12%;
  --primary: 146 19% 49%;
}

Benefits for our use case:

  • Each app can define its own theme tokens (CalNexus = warm/organic, Lexilink = modern/tech)
  • Shared UI components inherit theme from the consuming app
  • No build-time config conflicts between apps
  • Type-safe design tokens via CSS custom properties

Consequences

Positive

  • Faster builds: Rust-based Oxide engine is ~10x faster than v3
  • Smaller bundles: Better tree-shaking reduces CSS by ~30%
  • Better theming: CSS variables make light/dark mode seamless
  • Simpler config: No JS config files, just CSS
  • Per-app themes: Each app can customize without affecting others
  • Future-proof: v4 is the future of Tailwind, v3 will be deprecated
  • Native CSS features: Leverages modern CSS instead of PostCSS hacks

Negative

  • Breaking changes: Migration from v3 required updating all config
  • Ecosystem lag: Some plugins/tools do not support v4 yet
  • Less documentation: v4 is newer, fewer resources available
  • Learning curve: Team had to learn new @theme syntax
  • Risk of early adoption: Potential bugs in new engine

Neutral

  • Different mental model: CSS-first vs JS-first configuration
  • Migration effort: One-time cost to convert existing apps
  • Plugin compatibility: Most popular plugins work, some niche ones do not

Alternatives Considered

Alternative 1: Stay on Tailwind v3

  • Description: Keep using proven v3 with JS config
  • Pros: Stable, battle-tested; huge ecosystem of plugins; extensive documentation
  • Cons: Slower builds (JS-based engine); larger bundles; v3 will be deprecated eventually
  • Why not chosen: v4’s performance and theming benefits outweigh migration cost

Alternative 2: CSS Modules

  • Description: Write custom CSS with modules for scoping
  • Pros: No framework dependency; complete control over CSS
  • Cons: Much slower development velocity; inconsistent design system; no built-in theming
  • Why not chosen: Tailwind’s utility-first approach is much faster

Alternative 3: Vanilla Extract

  • Description: Zero-runtime CSS-in-JS with TypeScript
  • Pros: Type-safe styles; great theming via contracts; zero runtime cost
  • Cons: Smaller ecosystem than Tailwind; more verbose than utilities
  • Why not chosen: Tailwind’s ecosystem and DX are superior

Alternative 4: Panda CSS

  • Description: Type-safe, utility-first CSS-in-JS (like Chakra + Tailwind)
  • Pros: Type-safe utilities; great theming; modern architecture
  • Cons: Very new, small community; less mature than Tailwind
  • Why not chosen: Too new, Tailwind is more proven

References

Notes

  • Shared base styles: @hn-monorepo/ui/styles/base.css contains core Tailwind setup
  • Per-app themes: Each app imports base styles, then defines its own @theme and :root tokens
  • shadcn/ui compatibility: Works perfectly with shadcn — CSS variables map directly to components
  • Design tokens: We use HSL color format (e.g., 146 19% 49%) for easy manipulation
  • Font tokens: --font-display and --font-body define typography system
  • Radius system: Computed radius tokens (--radius-sm, --radius-lg) based on base --radius
HanseNexus 2026