Skip to main content

From DaisyUI to CSS Custom Properties

css tailwind migration theming

After using DaisyUI for component styling, I decided to remove it entirely in favor of CSS custom properties. This migration reduced the CSS bundle by 27% while providing complete control over the design system.

Migration Strategy

1. CSS Custom Properties Theme

First, I defined the color palette as RGB space-separated values in src/styles/theme.css:

:root {
  --color-background: 23 23 23;        /* neutral-900 */
  --color-surface: 38 38 38;           /* neutral-800 */
  --color-text: 245 245 244;           /* stone-100 */
  --color-text-secondary: 168 162 158; /* stone-400 */
  --color-border: 64 64 64;            /* neutral-700 */
  --color-primary: 251 191 36;         /* amber-400 */
}

Using space-separated RGB values enables Tailwind’s opacity modifiers (bg-primary/50) through the <alpha-value> placeholder.

2. Tailwind Configuration

Updated tailwind.config.js to reference the CSS variables:

colors: {
  background: 'rgb(var(--color-background) / <alpha-value>)',
  surface: 'rgb(var(--color-surface) / <alpha-value>)',
  text: 'rgb(var(--color-text) / <alpha-value>)',
  primary: 'rgb(var(--color-primary) / <alpha-value>)',
  // ... other colors
}

3. Custom Button System

Replaced DaisyUI’s button classes with 50 lines of explicit CSS:

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.375rem;
  font-weight: 500;
  border-radius: 0.25rem;
  cursor: pointer;
  transition: all 150ms ease-out;
}

.btn-primary {
  background-color: transparent;
  color: rgb(var(--color-primary));
  font-weight: 600;
}

.btn-outline {
  background-color: transparent;
  color: rgb(var(--color-text));
  position: relative;
}

.btn-outline::after {
  content: '';
  position: absolute;
  bottom: -2px;
  left: 0;
  width: 0;
  height: 1px;
  background-color: rgb(var(--color-text) / 0.4);
  transition: width 0.2s ease-out;
}

.btn-outline:hover::after {
  width: 100%;
}

4. Component Updates

Updated all components from DaisyUI’s semantic classes to explicit ones:

<!-- Before -->
<div class="bg-base-100 text-base-content">...</div>

<!-- After -->
<div class="bg-background text-text">...</div>

The .btn classes remained identical, requiring only color utility changes.

Results

Bundle Size Impact:

  • Before: 156KB CSS (uncompressed)
  • After: 114KB CSS (uncompressed)
  • Reduction: 42KB (-27%)

Developer Experience:

  • Zero JavaScript for theme switching
  • Single source of truth in theme.css
  • Custom component styles without framework constraints
  • Direct color visibility during debugging

Architecture Benefits

  1. Simplified Maintenance: Color changes require editing one file instead of understanding DaisyUI’s token system
  2. Future-Proof: No DaisyUI dependency means no waiting for Tailwind CSS v4 compatibility
  3. Performance: Eliminated framework abstraction and reduced CSS complexity
  4. Control: Custom component styles without specificity battles

When This Approach Works Best

CSS custom properties make sense for:

  • Single-theme applications
  • Custom design systems
  • Performance-critical sites
  • Teams that prefer explicit control over framework abstractions

DaisyUI still has its place for rapid prototyping or multi-theme applications, but for a static portfolio with a fixed dark theme, native CSS custom properties provide better control with less overhead.