Skip to main content
Technology & EngineeringUi Components Services455 lines

Daisyui

"daisyUI: Tailwind CSS component library, themes, responsive utilities, component classes, customization, semantic color names"

Quick Summary32 lines
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 lines
Paste into your CLAUDE.md or agent config

daisyUI 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

  1. Use semantic color names (primary, secondary, accent, base-100) instead of Tailwind color values. This ensures your components respect the active theme.
  2. 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).
  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.

Anti-Patterns

  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.
  2. Overriding daisyUI styles with !important -- instead, extend the theme configuration or use more specific Tailwind utilities that naturally win in the cascade.
  3. 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.
  4. 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.
  5. 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.
  6. Neglecting to include all themes you reference in the Tailwind config. If a theme is set via data-theme but 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

Get CLI access →