Daisyui
"daisyUI: Tailwind CSS component library, themes, responsive utilities, component classes, customization, semantic color names"
daisyUI is a component library for Tailwind CSS that provides semantic class names for common UI components. Instead of writing `bg-blue-600 text-white px-4 py-2 rounded-lg font-medium hover:bg-blue-700` for every button, you write `btn btn-primary`. It adds no JavaScript runtime -- everything is pure CSS built on top of Tailwind. daisyUI provides a powerful theming system with semantic color names, making it trivial to support multiple themes including dark mode. ## Key Points - Pure CSS component classes on top of Tailwind - Zero JavaScript runtime -- works with any framework or plain HTML - Semantic color names (primary, secondary, accent) instead of fixed color values - 30+ built-in themes with a single data attribute swap - Fully customizable -- override any component style with Tailwind utilities 1. **Use semantic color names** (`primary`, `secondary`, `accent`, `base-100`) instead of Tailwind color values. This ensures your components respect the active theme. 3. **Use `data-theme` on nested elements** to scope themes. A section of your page can use a different theme from the rest. 4. **Use the `*-content` color tokens** (e.g., `primary-content`) for text/icons on colored backgrounds. daisyUI calculates readable contrast colors automatically. 5. **Use the `swap` component** for toggle animations (hamburger to X icon, sun to moon). It handles the animation states with pure CSS. 6. **Use `join` class** to group buttons, inputs, and selects side-by-side with connected borders. 7. **Test multiple themes during development** to ensure your custom styles and layout work with any theme, not just the default. 1. **Hardcoding Tailwind color classes** (like `bg-blue-600`) alongside daisyUI components. These will not change when the theme changes. Use `bg-primary`, `text-base-content`, etc. ## Quick Example ```bash npm install -D daisyui@latest ``` ```html <!-- Set theme on the html element --> <html data-theme="light"> <!-- Or change dynamically --> <html data-theme="cyberpunk"> ```
skilldb get ui-components-services-skills/DaisyuiFull skill: 455 linesdaisyUI Skill
Core Philosophy
daisyUI is a component library for Tailwind CSS that provides semantic class names for common UI components. Instead of writing bg-blue-600 text-white px-4 py-2 rounded-lg font-medium hover:bg-blue-700 for every button, you write btn btn-primary. It adds no JavaScript runtime -- everything is pure CSS built on top of Tailwind. daisyUI provides a powerful theming system with semantic color names, making it trivial to support multiple themes including dark mode.
Key principles:
- Pure CSS component classes on top of Tailwind
- Zero JavaScript runtime -- works with any framework or plain HTML
- Semantic color names (primary, secondary, accent) instead of fixed color values
- 30+ built-in themes with a single data attribute swap
- Fully customizable -- override any component style with Tailwind utilities
Setup
Installation
npm install -D daisyui@latest
Tailwind Configuration
// tailwind.config.ts
import type { Config } from "tailwindcss";
import daisyui from "daisyui";
const config: Config = {
content: ["./src/**/*.{js,ts,jsx,tsx,mdx}"],
theme: {
extend: {},
},
plugins: [daisyui],
daisyui: {
themes: ["light", "dark", "cupcake", "cyberpunk", "retro"],
darkTheme: "dark",
base: true, // apply background color and foreground color
styled: true, // include daisyUI component styles
utils: true, // include daisyUI responsive utilities
logs: true, // show daisyUI info in console during build
},
};
export default config;
Theme Activation
<!-- Set theme on the html element -->
<html data-theme="light">
<!-- Or change dynamically -->
<html data-theme="cyberpunk">
// React theme switcher
import { useState, useEffect } from "react";
export function ThemeSwitcher() {
const [theme, setTheme] = useState("light");
useEffect(() => {
document.documentElement.setAttribute("data-theme", theme);
}, [theme]);
return (
<select
className="select select-bordered select-sm"
value={theme}
onChange={(e) => setTheme(e.target.value)}
>
<option value="light">Light</option>
<option value="dark">Dark</option>
<option value="cupcake">Cupcake</option>
<option value="cyberpunk">Cyberpunk</option>
<option value="retro">Retro</option>
</select>
);
}
Key Techniques
Buttons
export function ButtonShowcase() {
return (
<div className="flex flex-wrap gap-2">
{/* Variants */}
<button className="btn">Default</button>
<button className="btn btn-primary">Primary</button>
<button className="btn btn-secondary">Secondary</button>
<button className="btn btn-accent">Accent</button>
<button className="btn btn-ghost">Ghost</button>
<button className="btn btn-link">Link</button>
<button className="btn btn-outline btn-primary">Outlined</button>
{/* Sizes */}
<button className="btn btn-lg">Large</button>
<button className="btn btn-md">Medium</button>
<button className="btn btn-sm">Small</button>
<button className="btn btn-xs">Tiny</button>
{/* States */}
<button className="btn btn-primary loading">Loading</button>
<button className="btn btn-disabled">Disabled</button>
{/* With icon */}
<button className="btn btn-primary gap-2">
<svg xmlns="http://www.w3.org/2000/svg" className="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
<path fillRule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm1-11a1 1 0 10-2 0v2H7a1 1 0 100 2h2v2a1 1 0 102 0v-2h2a1 1 0 100-2h-2V7z" />
</svg>
Add Item
</button>
</div>
);
}
Cards
interface ProjectCardProps {
title: string;
description: string;
tags: string[];
imageUrl?: string;
}
export function ProjectCard({ title, description, tags, imageUrl }: ProjectCardProps) {
return (
<div className="card bg-base-100 shadow-xl">
{imageUrl && (
<figure>
<img src={imageUrl} alt={title} className="h-48 w-full object-cover" />
</figure>
)}
<div className="card-body">
<h2 className="card-title">
{title}
<div className="badge badge-secondary">NEW</div>
</h2>
<p>{description}</p>
<div className="card-actions justify-end">
{tags.map((tag) => (
<div key={tag} className="badge badge-outline">{tag}</div>
))}
</div>
</div>
</div>
);
}
// Card variants
function CardVariants() {
return (
<div className="grid gap-4">
{/* Compact card */}
<div className="card card-compact bg-base-100 shadow-md">
<div className="card-body">
<h3 className="card-title text-sm">Compact</h3>
<p className="text-xs">Less padding for dense layouts.</p>
</div>
</div>
{/* Side image card */}
<div className="card card-side bg-base-100 shadow-md">
<figure className="w-32">
<img src="/thumb.jpg" alt="Thumbnail" className="h-full object-cover" />
</figure>
<div className="card-body">
<h3 className="card-title">Horizontal Layout</h3>
<p>Image on the side.</p>
</div>
</div>
{/* Glass card */}
<div className="card glass">
<div className="card-body">
<h3 className="card-title">Glass Effect</h3>
<p>Frosted glass background.</p>
</div>
</div>
</div>
);
}
Form Controls
export function SettingsForm() {
return (
<form className="space-y-4 max-w-md">
{/* Text input with label */}
<div className="form-control w-full">
<label className="label">
<span className="label-text">Display Name</span>
<span className="label-text-alt">Required</span>
</label>
<input type="text" placeholder="Enter your name" className="input input-bordered w-full" />
<label className="label">
<span className="label-text-alt text-error">Name cannot be empty</span>
</label>
</div>
{/* Select */}
<div className="form-control w-full">
<label className="label">
<span className="label-text">Country</span>
</label>
<select className="select select-bordered w-full">
<option disabled selected>Pick one</option>
<option>United States</option>
<option>United Kingdom</option>
<option>Germany</option>
</select>
</div>
{/* Textarea */}
<div className="form-control w-full">
<label className="label">
<span className="label-text">Bio</span>
</label>
<textarea className="textarea textarea-bordered h-24" placeholder="Tell us about yourself" />
</div>
{/* Toggle */}
<div className="form-control">
<label className="label cursor-pointer justify-start gap-3">
<input type="checkbox" className="toggle toggle-primary" />
<span className="label-text">Enable notifications</span>
</label>
</div>
{/* Checkbox */}
<div className="form-control">
<label className="label cursor-pointer justify-start gap-3">
<input type="checkbox" className="checkbox checkbox-primary" />
<span className="label-text">I agree to the terms</span>
</label>
</div>
{/* Range slider */}
<div className="form-control w-full">
<label className="label">
<span className="label-text">Volume</span>
</label>
<input type="range" min="0" max="100" className="range range-primary range-sm" />
</div>
<button type="submit" className="btn btn-primary w-full">Save Settings</button>
</form>
);
}
Navigation Components
export function AppNavbar() {
return (
<div className="navbar bg-base-100 shadow-sm">
<div className="navbar-start">
<div className="dropdown">
<label tabIndex={0} className="btn btn-ghost lg:hidden">
<svg xmlns="http://www.w3.org/2000/svg" className="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M4 6h16M4 12h8m-8 6h16" />
</svg>
</label>
<ul tabIndex={0} className="menu menu-sm dropdown-content mt-3 z-[1] p-2 shadow bg-base-100 rounded-box w-52">
<li><a>Dashboard</a></li>
<li><a>Projects</a></li>
<li><a>Team</a></li>
</ul>
</div>
<a className="btn btn-ghost text-xl">MyApp</a>
</div>
<div className="navbar-center hidden lg:flex">
<ul className="menu menu-horizontal px-1">
<li><a>Dashboard</a></li>
<li><a>Projects</a></li>
<li><a>Team</a></li>
</ul>
</div>
<div className="navbar-end">
<a className="btn btn-primary btn-sm">Get Started</a>
</div>
</div>
);
}
// Breadcrumbs
function AppBreadcrumbs() {
return (
<div className="breadcrumbs text-sm">
<ul>
<li><a>Home</a></li>
<li><a>Projects</a></li>
<li>Current Project</li>
</ul>
</div>
);
}
// Tabs
function AppTabs() {
return (
<div className="tabs tabs-bordered">
<a className="tab">Overview</a>
<a className="tab tab-active">Settings</a>
<a className="tab">Members</a>
</div>
);
}
Custom Theme
// tailwind.config.ts
daisyui: {
themes: [
{
"my-brand": {
"primary": "#4f46e5", // indigo
"primary-content": "#ffffff", // text on primary
"secondary": "#7c3aed", // violet
"secondary-content": "#ffffff",
"accent": "#06b6d4", // cyan
"accent-content": "#ffffff",
"neutral": "#1f2937",
"neutral-content": "#f3f4f6",
"base-100": "#ffffff", // page background
"base-200": "#f9fafb", // slightly darker
"base-300": "#f3f4f6", // cards, surfaces
"base-content": "#111827", // text on base
"info": "#3b82f6",
"success": "#22c55e",
"warning": "#f59e0b",
"error": "#ef4444",
"--rounded-box": "0.75rem",
"--rounded-btn": "0.5rem",
"--rounded-badge": "1rem",
"--animation-btn": "0.25s",
"--animation-input": "0.2s",
"--btn-focus-scale": "0.95",
},
},
"dark", // also include the built-in dark theme
],
},
Responsive Utilities and Indicators
// Stat display
function StatsPanel() {
return (
<div className="stats shadow">
<div className="stat">
<div className="stat-figure text-primary">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" className="inline-block w-8 h-8 stroke-current">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M13 10V3L4 14h7v7l9-11h-7z" />
</svg>
</div>
<div className="stat-title">Total Page Views</div>
<div className="stat-value text-primary">25.6K</div>
<div className="stat-desc">21% more than last month</div>
</div>
<div className="stat">
<div className="stat-title">Active Users</div>
<div className="stat-value">4,200</div>
<div className="stat-desc text-success">+12% this week</div>
</div>
</div>
);
}
// Alert variants
function AlertExamples() {
return (
<div className="space-y-2">
<div className="alert alert-info">
<span>New update available for your account.</span>
</div>
<div className="alert alert-success">
<span>Your purchase has been confirmed.</span>
</div>
<div className="alert alert-warning">
<span>Warning: your storage is almost full.</span>
</div>
<div className="alert alert-error">
<span>Error: could not process your request.</span>
</div>
</div>
);
}
// Modal using the dialog element
function DaisyModal() {
return (
<>
<button className="btn" onClick={() => (document.getElementById("my_modal") as HTMLDialogElement)?.showModal()}>
Open Modal
</button>
<dialog id="my_modal" className="modal">
<div className="modal-box">
<h3 className="font-bold text-lg">Confirm Action</h3>
<p className="py-4">Are you sure you want to proceed?</p>
<div className="modal-action">
<form method="dialog">
<button className="btn btn-ghost">Cancel</button>
<button className="btn btn-primary">Confirm</button>
</form>
</div>
</div>
<form method="dialog" className="modal-backdrop">
<button>close</button>
</form>
</dialog>
</>
);
}
Best Practices
- Use semantic color names (
primary,secondary,accent,base-100) instead of Tailwind color values. This ensures your components respect the active theme. - Combine daisyUI classes with Tailwind utilities freely. Use daisyUI for the component structure (
btn,card,input) and Tailwind for layout, spacing, and fine-tuning (mt-4,flex,gap-2). - Use
data-themeon nested elements to scope themes. A section of your page can use a different theme from the rest. - Use the
*-contentcolor tokens (e.g.,primary-content) for text/icons on colored backgrounds. daisyUI calculates readable contrast colors automatically. - Use the
swapcomponent for toggle animations (hamburger to X icon, sun to moon). It handles the animation states with pure CSS. - Use
joinclass to group buttons, inputs, and selects side-by-side with connected borders. - Test multiple themes during development to ensure your custom styles and layout work with any theme, not just the default.
Anti-Patterns
- Hardcoding Tailwind color classes (like
bg-blue-600) alongside daisyUI components. These will not change when the theme changes. Usebg-primary,text-base-content, etc. - Overriding daisyUI styles with
!important-- instead, extend the theme configuration or use more specific Tailwind utilities that naturally win in the cascade. - Using daisyUI's modal with complex React state management when you need programmatic control. daisyUI modals use the native
<dialog>element. For complex flows, pair with a headless library like Headless UI or Radix. - Assuming daisyUI handles accessibility automatically -- it provides good defaults but interactive components still need proper ARIA attributes, keyboard handling, and focus management that CSS alone cannot provide.
- Mixing multiple component libraries (e.g., daisyUI + shadcn/ui) in the same project. Their class names and design tokens can conflict. Choose one as your primary system.
- Neglecting to include all themes you reference in the Tailwind config. If a theme is set via
data-themebut not listed in the config, it will fall back to default styles with missing variables.
Install this skill directly: skilldb add ui-components-services-skills
Related Skills
Ark UI
"Ark UI: state machine-driven components, headless primitives, Select, Combobox, DatePicker, Toast, styling with any CSS framework"
Headless UI
"Headless UI: unstyled accessible components for Tailwind, Dialog, Combobox, Listbox, Menu, Transition, render props, React/Vue"
Kobalte
Kobalte: accessible, unstyled UI component library for SolidJS with WAI-ARIA compliance and composable compound component APIs
Melt UI
Melt UI: headless, accessible UI component builders for Svelte using the builder pattern with full styling freedom
Park UI
Park UI: pre-designed styled components built on Ark UI primitives, available for React, Vue, and Solid with Panda CSS or Tailwind CSS styling
Radix UI
"Radix UI: accessible primitives, Dialog, Dropdown, Popover, Tabs, Toast, composition pattern, styling with className, controlled/uncontrolled"