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
- Simplified Maintenance: Color changes require editing one file instead of understanding DaisyUI’s token system
- Future-Proof: No DaisyUI dependency means no waiting for Tailwind CSS v4 compatibility
- Performance: Eliminated framework abstraction and reduced CSS complexity
- 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.