DaisyUI - Kompletny Przewodnik po Komponentach dla Tailwind CSS
Czym jest DaisyUI?
DaisyUI to najpopularniejszy plugin do Tailwind CSS, który dodaje gotowe klasy komponentów do Twojego projektu. Zamiast pisać dziesiątki klas utility jak bg-blue-500 hover:bg-blue-600 text-white font-bold py-2 px-4 rounded, piszesz po prostu btn btn-primary. To najlepsza rzecz, jaka mogła się przydarzyć Tailwind CSS.
DaisyUI nie zastępuje Tailwind - rozszerza go. Nadal możesz używać wszystkich klas utility Tailwind razem z semantycznymi klasami DaisyUI. Dzięki temu masz elastyczność Tailwind z wygodą gotowych komponentów, a wszystko to bez żadnego JavaScript - DaisyUI to czysty CSS.
Dlaczego DaisyUI?
Kluczowe zalety DaisyUI
- Semantyczne klasy -
btnzamiastbg-blue-500 hover:bg-blue-600... - 35+ tematów - Łatwa zmiana wyglądu całej aplikacji
- Zero JavaScript - Czysty CSS, działa z każdym frameworkiem
- Pełna kompatybilność - Działa z React, Vue, Svelte, Angular, vanilla HTML
- Mniejszy bundle - Mniej klas = mniejszy CSS
- 100% darmowy - MIT License, open source
DaisyUI vs inne rozwiązania
| Cecha | DaisyUI | Tailwind UI | Headless UI | Bootstrap |
|---|---|---|---|---|
| Cena | Darmowy | $299+ | Darmowy | Darmowy |
| JavaScript | Nie wymaga | Tak | Tak | Tak |
| Tailwind | Natywny | Natywny | Natywny | Osobny |
| Komponenty | 50+ | 500+ | 10 | 25+ |
| Tematy | 35+ | Brak | Brak | Ograniczone |
| Customizacja | Pełna | Pełna | Pełna | Ograniczona |
| Bundle size | Minimalny | Średni | Minimalny | Duży |
Instalacja i konfiguracja
Instalacja npm
# npm
npm install -D daisyui@latest
# pnpm
pnpm add -D daisyui@latest
# yarn
yarn add -D daisyui@latest
# bun
bun add -D daisyui@latestKonfiguracja Tailwind CSS v3
// tailwind.config.js
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
"./src/**/*.{js,ts,jsx,tsx,html}",
"./components/**/*.{js,ts,jsx,tsx}",
],
theme: {
extend: {},
},
plugins: [
require("daisyui"),
],
daisyui: {
themes: ["light", "dark", "cupcake", "cyberpunk", "valentine", "forest"],
darkTheme: "dark",
base: true,
styled: true,
utils: true,
prefix: "",
logs: true,
themeRoot: ":root",
},
}Konfiguracja Tailwind CSS v4
// tailwind.config.js (v4)
import daisyui from "daisyui"
export default {
plugins: [daisyui],
daisyui: {
themes: ["light", "dark"],
},
}Weryfikacja instalacji
<!-- Sprawdź czy DaisyUI działa -->
<button class="btn btn-primary">
Jeśli ten przycisk jest stylizowany, DaisyUI działa!
</button>Komponenty DaisyUI
Przyciski (Button)
<!-- Podstawowe warianty -->
<button class="btn">Default</button>
<button class="btn btn-neutral">Neutral</button>
<button class="btn btn-primary">Primary</button>
<button class="btn btn-secondary">Secondary</button>
<button class="btn btn-accent">Accent</button>
<button class="btn btn-ghost">Ghost</button>
<button class="btn btn-link">Link</button>
<!-- Stany -->
<button class="btn btn-info">Info</button>
<button class="btn btn-success">Success</button>
<button class="btn btn-warning">Warning</button>
<button class="btn btn-error">Error</button>
<!-- Rozmiary -->
<button class="btn btn-lg">Large</button>
<button class="btn btn-md">Medium (default)</button>
<button class="btn btn-sm">Small</button>
<button class="btn btn-xs">Extra small</button>
<!-- Modyfikatory -->
<button class="btn btn-wide">Wide</button>
<button class="btn btn-block">Block (full width)</button>
<button class="btn btn-circle">○</button>
<button class="btn btn-square">□</button>
<!-- Stany interaktywne -->
<button class="btn btn-outline btn-primary">Outline</button>
<button class="btn btn-active">Active</button>
<button class="btn btn-disabled">Disabled</button>
<button class="btn loading">Loading...</button>
<!-- Z ikoną -->
<button class="btn btn-primary gap-2">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
<path d="M10 12a2 2 0 100-4 2 2 0 000 4z" />
</svg>
Settings
</button>Karty (Card)
<!-- Podstawowa karta -->
<div class="card w-96 bg-base-100 shadow-xl">
<figure>
<img src="https://picsum.photos/400/200" alt="Image" />
</figure>
<div class="card-body">
<h2 class="card-title">Tytuł karty</h2>
<p>Opis karty z dodatkowym tekstem wyjaśniającym zawartość.</p>
<div class="card-actions justify-end">
<button class="btn btn-primary">Kup teraz</button>
</div>
</div>
</div>
<!-- Karta kompaktowa -->
<div class="card card-compact w-96 bg-base-100 shadow-xl">
<figure><img src="image.jpg" alt="Photo" /></figure>
<div class="card-body">
<h2 class="card-title">Kompaktowa!</h2>
<p>Mniej paddingu</p>
<div class="card-actions justify-end">
<button class="btn btn-primary btn-sm">Akcja</button>
</div>
</div>
</div>
<!-- Karta z badge -->
<div class="card w-96 bg-base-100 shadow-xl">
<div class="card-body">
<h2 class="card-title">
Produkt
<div class="badge badge-secondary">NEW</div>
</h2>
<p>Opis produktu</p>
<div class="card-actions justify-end">
<div class="badge badge-outline">Fashion</div>
<div class="badge badge-outline">Products</div>
</div>
</div>
</div>
<!-- Karta pozioma -->
<div class="card card-side bg-base-100 shadow-xl">
<figure><img src="image.jpg" alt="Movie" class="w-48" /></figure>
<div class="card-body">
<h2 class="card-title">Film</h2>
<p>Opis filmu</p>
<div class="card-actions justify-end">
<button class="btn btn-primary">Oglądaj</button>
</div>
</div>
</div>
<!-- Karta z overlay -->
<div class="card w-96 bg-base-100 shadow-xl image-full">
<figure><img src="background.jpg" alt="Background" /></figure>
<div class="card-body">
<h2 class="card-title">Tytuł</h2>
<p>Tekst na tle obrazu</p>
<div class="card-actions justify-end">
<button class="btn btn-primary">Akcja</button>
</div>
</div>
</div>Modal
<!-- Modal z przyciskiem -->
<button class="btn" onclick="my_modal_1.showModal()">Otwórz Modal</button>
<dialog id="my_modal_1" class="modal">
<div class="modal-box">
<h3 class="font-bold text-lg">Witaj!</h3>
<p class="py-4">Naciśnij ESC lub kliknij przycisk, aby zamknąć</p>
<div class="modal-action">
<form method="dialog">
<button class="btn">Zamknij</button>
</form>
</div>
</div>
</dialog>
<!-- Modal z backdrop do zamknięcia -->
<button class="btn" onclick="my_modal_2.showModal()">Modal z backdrop</button>
<dialog id="my_modal_2" class="modal">
<div class="modal-box">
<h3 class="font-bold text-lg">Modal</h3>
<p class="py-4">Kliknij poza modal aby zamknąć</p>
</div>
<form method="dialog" class="modal-backdrop">
<button>close</button>
</form>
</dialog>
<!-- Modal z akcjami -->
<dialog id="my_modal_3" class="modal">
<div class="modal-box">
<form method="dialog">
<button class="btn btn-sm btn-circle btn-ghost absolute right-2 top-2">✕</button>
</form>
<h3 class="font-bold text-lg">Potwierdź akcję</h3>
<p class="py-4">Czy na pewno chcesz kontynuować?</p>
<div class="modal-action">
<form method="dialog">
<button class="btn btn-ghost">Anuluj</button>
<button class="btn btn-primary">Potwierdź</button>
</form>
</div>
</div>
</dialog>Nawigacja (Navbar)
<!-- Prosty navbar -->
<div class="navbar bg-base-100">
<a class="btn btn-ghost text-xl">daisyUI</a>
</div>
<!-- Navbar z menu -->
<div class="navbar bg-base-100">
<div class="flex-1">
<a class="btn btn-ghost text-xl">Logo</a>
</div>
<div class="flex-none">
<ul class="menu menu-horizontal px-1">
<li><a>Link 1</a></li>
<li><a>Link 2</a></li>
</ul>
</div>
</div>
<!-- Navbar z wyszukiwaniem i awatarem -->
<div class="navbar bg-base-100">
<div class="flex-1">
<a class="btn btn-ghost text-xl">Logo</a>
</div>
<div class="flex-none gap-2">
<div class="form-control">
<input type="text" placeholder="Szukaj" class="input input-bordered w-24 md:w-auto" />
</div>
<div class="dropdown dropdown-end">
<div tabindex="0" role="button" class="btn btn-ghost btn-circle avatar">
<div class="w-10 rounded-full">
<img alt="Avatar" src="avatar.jpg" />
</div>
</div>
<ul tabindex="0" class="mt-3 z-[1] p-2 shadow menu menu-sm dropdown-content bg-base-100 rounded-box w-52">
<li><a>Profil</a></li>
<li><a>Ustawienia</a></li>
<li><a>Wyloguj</a></li>
</ul>
</div>
</div>
</div>
<!-- Navbar z ikonami -->
<div class="navbar bg-neutral text-neutral-content">
<div class="flex-1">
<a class="btn btn-ghost text-xl">daisyUI</a>
</div>
<div class="flex-none">
<button class="btn btn-square btn-ghost">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" class="inline-block w-5 h-5 stroke-current">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 12h.01M12 12h.01M19 12h.01M6 12a1 1 0 11-2 0 1 1 0 012 0zm7 0a1 1 0 11-2 0 1 1 0 012 0zm7 0a1 1 0 11-2 0 1 1 0 012 0z"></path>
</svg>
</button>
</div>
</div>Formularze
<!-- Input -->
<div class="form-control w-full max-w-xs">
<label class="label">
<span class="label-text">Email</span>
<span class="label-text-alt">Wymagane</span>
</label>
<input type="email" placeholder="email@example.com" class="input input-bordered w-full max-w-xs" />
<label class="label">
<span class="label-text-alt">Wprowadź poprawny email</span>
</label>
</div>
<!-- Input z wariantami -->
<input type="text" placeholder="Default" class="input input-bordered w-full max-w-xs" />
<input type="text" placeholder="Primary" class="input input-bordered input-primary w-full max-w-xs" />
<input type="text" placeholder="Secondary" class="input input-bordered input-secondary w-full max-w-xs" />
<input type="text" placeholder="Accent" class="input input-bordered input-accent w-full max-w-xs" />
<input type="text" placeholder="Info" class="input input-bordered input-info w-full max-w-xs" />
<input type="text" placeholder="Success" class="input input-bordered input-success w-full max-w-xs" />
<input type="text" placeholder="Warning" class="input input-bordered input-warning w-full max-w-xs" />
<input type="text" placeholder="Error" class="input input-bordered input-error w-full max-w-xs" />
<!-- Rozmiary -->
<input type="text" placeholder="Large" class="input input-bordered input-lg" />
<input type="text" placeholder="Medium" class="input input-bordered input-md" />
<input type="text" placeholder="Small" class="input input-bordered input-sm" />
<input type="text" placeholder="XS" class="input input-bordered input-xs" />
<!-- Select -->
<select class="select select-bordered w-full max-w-xs">
<option disabled selected>Wybierz opcję</option>
<option>Opcja 1</option>
<option>Opcja 2</option>
<option>Opcja 3</option>
</select>
<!-- Textarea -->
<textarea class="textarea textarea-bordered" placeholder="Twoja wiadomość"></textarea>
<!-- Checkbox -->
<div class="form-control">
<label class="label cursor-pointer">
<span class="label-text">Zapamiętaj mnie</span>
<input type="checkbox" checked class="checkbox checkbox-primary" />
</label>
</div>
<!-- Radio -->
<div class="form-control">
<label class="label cursor-pointer">
<span class="label-text">Opcja A</span>
<input type="radio" name="radio-1" class="radio radio-primary" checked />
</label>
<label class="label cursor-pointer">
<span class="label-text">Opcja B</span>
<input type="radio" name="radio-1" class="radio radio-primary" />
</label>
</div>
<!-- Toggle -->
<input type="checkbox" class="toggle toggle-primary" checked />
<!-- Range -->
<input type="range" min="0" max="100" value="40" class="range range-primary" />
<!-- Rating -->
<div class="rating">
<input type="radio" name="rating-1" class="mask mask-star" />
<input type="radio" name="rating-1" class="mask mask-star" checked />
<input type="radio" name="rating-1" class="mask mask-star" />
<input type="radio" name="rating-1" class="mask mask-star" />
<input type="radio" name="rating-1" class="mask mask-star" />
</div>
<!-- File input -->
<input type="file" class="file-input file-input-bordered w-full max-w-xs" />Tabele
<!-- Podstawowa tabela -->
<div class="overflow-x-auto">
<table class="table">
<thead>
<tr>
<th></th>
<th>Imię</th>
<th>Stanowisko</th>
<th>Email</th>
</tr>
</thead>
<tbody>
<tr>
<th>1</th>
<td>Jan Kowalski</td>
<td>Developer</td>
<td>jan@example.com</td>
</tr>
<tr>
<th>2</th>
<td>Anna Nowak</td>
<td>Designer</td>
<td>anna@example.com</td>
</tr>
<tr>
<th>3</th>
<td>Piotr Wiśniewski</td>
<td>Manager</td>
<td>piotr@example.com</td>
</tr>
</tbody>
</table>
</div>
<!-- Tabela zebra -->
<table class="table table-zebra">
<!-- ... -->
</table>
<!-- Tabela z hover -->
<table class="table">
<tbody>
<tr class="hover">
<td>Row with hover effect</td>
</tr>
</tbody>
</table>
<!-- Tabela aktywna -->
<table class="table">
<tbody>
<tr class="active">
<td>Active row (highlighted)</td>
</tr>
</tbody>
</table>
<!-- Kompaktowa tabela -->
<table class="table table-xs">
<!-- Mniejszy padding -->
</table>Alerty i powiadomienia
<!-- Alert podstawowy -->
<div class="alert">
<span>12 nieprzeczytanych wiadomości.</span>
</div>
<!-- Alert info -->
<div class="alert alert-info">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" class="stroke-current shrink-0 w-6 h-6">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>
<span>Nowa aktualizacja oprogramowania jest dostępna.</span>
</div>
<!-- Alert success -->
<div class="alert alert-success">
<svg xmlns="http://www.w3.org/2000/svg" class="stroke-current shrink-0 h-6 w-6" fill="none" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
<span>Twoje zamówienie zostało potwierdzone!</span>
</div>
<!-- Alert warning -->
<div class="alert alert-warning">
<svg xmlns="http://www.w3.org/2000/svg" class="stroke-current shrink-0 h-6 w-6" fill="none" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
</svg>
<span>Uwaga: Twoja sesja wygasa za 5 minut!</span>
</div>
<!-- Alert error -->
<div class="alert alert-error">
<svg xmlns="http://www.w3.org/2000/svg" class="stroke-current shrink-0 h-6 w-6" fill="none" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
<span>Błąd! Coś poszło nie tak.</span>
</div>
<!-- Alert z akcjami -->
<div class="alert">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" class="stroke-info shrink-0 w-6 h-6">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>
<span>Mamy nowe warunki użytkowania.</span>
<div>
<button class="btn btn-sm">Odmów</button>
<button class="btn btn-sm btn-primary">Akceptuj</button>
</div>
</div>
<!-- Toast -->
<div class="toast toast-end">
<div class="alert alert-info">
<span>Nowa wiadomość.</span>
</div>
</div>Badge i Indicator
<!-- Badge -->
<span class="badge">default</span>
<span class="badge badge-neutral">neutral</span>
<span class="badge badge-primary">primary</span>
<span class="badge badge-secondary">secondary</span>
<span class="badge badge-accent">accent</span>
<span class="badge badge-ghost">ghost</span>
<!-- Badge outline -->
<span class="badge badge-outline">outline</span>
<span class="badge badge-outline badge-primary">primary</span>
<!-- Badge rozmiary -->
<span class="badge badge-lg">Large</span>
<span class="badge badge-md">Medium</span>
<span class="badge badge-sm">Small</span>
<span class="badge badge-xs">Tiny</span>
<!-- Badge w przycisku -->
<button class="btn gap-2">
Inbox
<span class="badge badge-sm">+99</span>
</button>
<!-- Indicator -->
<div class="indicator">
<span class="indicator-item badge badge-primary">new</span>
<div class="grid w-32 h-32 bg-base-300 place-items-center">content</div>
</div>
<!-- Indicator na awatarze -->
<div class="indicator">
<span class="indicator-item badge badge-secondary">online</span>
<div class="avatar">
<div class="w-12 rounded">
<img src="avatar.jpg" alt="Avatar" />
</div>
</div>
</div>Collapse i Accordion
<!-- Collapse -->
<div class="collapse bg-base-200">
<input type="checkbox" />
<div class="collapse-title text-xl font-medium">
Kliknij, aby rozwinąć
</div>
<div class="collapse-content">
<p>Ukryta treść pojawia się po kliknięciu.</p>
</div>
</div>
<!-- Collapse z arrow -->
<div class="collapse collapse-arrow bg-base-200">
<input type="checkbox" />
<div class="collapse-title text-xl font-medium">
Kliknij, aby rozwinąć
</div>
<div class="collapse-content">
<p>Treść z ikoną strzałki.</p>
</div>
</div>
<!-- Collapse z plus -->
<div class="collapse collapse-plus bg-base-200">
<input type="checkbox" />
<div class="collapse-title text-xl font-medium">
Kliknij, aby rozwinąć
</div>
<div class="collapse-content">
<p>Treść z ikoną plus/minus.</p>
</div>
</div>
<!-- Accordion (radio - tylko jeden otwarty) -->
<div class="join join-vertical w-full">
<div class="collapse collapse-arrow join-item border border-base-300">
<input type="radio" name="my-accordion-1" checked />
<div class="collapse-title text-xl font-medium">
Pytanie 1
</div>
<div class="collapse-content">
<p>Odpowiedź na pytanie 1.</p>
</div>
</div>
<div class="collapse collapse-arrow join-item border border-base-300">
<input type="radio" name="my-accordion-1" />
<div class="collapse-title text-xl font-medium">
Pytanie 2
</div>
<div class="collapse-content">
<p>Odpowiedź na pytanie 2.</p>
</div>
</div>
<div class="collapse collapse-arrow join-item border border-base-300">
<input type="radio" name="my-accordion-1" />
<div class="collapse-title text-xl font-medium">
Pytanie 3
</div>
<div class="collapse-content">
<p>Odpowiedź na pytanie 3.</p>
</div>
</div>
</div>Tabs
<!-- Podstawowe tabs -->
<div role="tablist" class="tabs tabs-bordered">
<a role="tab" class="tab">Tab 1</a>
<a role="tab" class="tab tab-active">Tab 2</a>
<a role="tab" class="tab">Tab 3</a>
</div>
<!-- Tabs lifted -->
<div role="tablist" class="tabs tabs-lifted">
<a role="tab" class="tab">Tab 1</a>
<a role="tab" class="tab tab-active">Tab 2</a>
<a role="tab" class="tab">Tab 3</a>
</div>
<!-- Tabs boxed -->
<div role="tablist" class="tabs tabs-boxed">
<a role="tab" class="tab">Tab 1</a>
<a role="tab" class="tab tab-active">Tab 2</a>
<a role="tab" class="tab">Tab 3</a>
</div>
<!-- Tabs z zawartością (radio) -->
<div role="tablist" class="tabs tabs-lifted">
<input type="radio" name="my_tabs_1" role="tab" class="tab" aria-label="Tab 1" />
<div role="tabpanel" class="tab-content bg-base-100 border-base-300 rounded-box p-6">
Zawartość Tab 1
</div>
<input type="radio" name="my_tabs_1" role="tab" class="tab" aria-label="Tab 2" checked />
<div role="tabpanel" class="tab-content bg-base-100 border-base-300 rounded-box p-6">
Zawartość Tab 2
</div>
<input type="radio" name="my_tabs_1" role="tab" class="tab" aria-label="Tab 3" />
<div role="tabpanel" class="tab-content bg-base-100 border-base-300 rounded-box p-6">
Zawartość Tab 3
</div>
</div>Progress i Loading
<!-- Progress bar -->
<progress class="progress w-56" value="0" max="100"></progress>
<progress class="progress w-56" value="25" max="100"></progress>
<progress class="progress w-56" value="50" max="100"></progress>
<progress class="progress w-56" value="75" max="100"></progress>
<progress class="progress w-56" value="100" max="100"></progress>
<!-- Progress z kolorami -->
<progress class="progress progress-primary w-56" value="40" max="100"></progress>
<progress class="progress progress-secondary w-56" value="60" max="100"></progress>
<progress class="progress progress-accent w-56" value="80" max="100"></progress>
<progress class="progress progress-info w-56" value="45" max="100"></progress>
<progress class="progress progress-success w-56" value="70" max="100"></progress>
<progress class="progress progress-warning w-56" value="55" max="100"></progress>
<progress class="progress progress-error w-56" value="30" max="100"></progress>
<!-- Progress nieokreślony (animowany) -->
<progress class="progress w-56"></progress>
<!-- Radial progress -->
<div class="radial-progress" style="--value:70;">70%</div>
<div class="radial-progress" style="--value:70; --size:12rem; --thickness: 2px;">70%</div>
<div class="radial-progress bg-primary text-primary-content border-4 border-primary" style="--value:70;">70%</div>
<!-- Loading spinner -->
<span class="loading loading-spinner loading-xs"></span>
<span class="loading loading-spinner loading-sm"></span>
<span class="loading loading-spinner loading-md"></span>
<span class="loading loading-spinner loading-lg"></span>
<!-- Loading dots -->
<span class="loading loading-dots loading-lg"></span>
<!-- Loading ring -->
<span class="loading loading-ring loading-lg"></span>
<!-- Loading ball -->
<span class="loading loading-ball loading-lg"></span>
<!-- Loading bars -->
<span class="loading loading-bars loading-lg"></span>
<!-- Loading infinity -->
<span class="loading loading-infinity loading-lg"></span>Avatar
<!-- Podstawowy avatar -->
<div class="avatar">
<div class="w-24 rounded">
<img src="avatar.jpg" alt="Avatar" />
</div>
</div>
<!-- Avatar zaokrąglony -->
<div class="avatar">
<div class="w-24 rounded-full">
<img src="avatar.jpg" alt="Avatar" />
</div>
</div>
<!-- Avatar z mask -->
<div class="avatar">
<div class="w-24 mask mask-squircle">
<img src="avatar.jpg" alt="Avatar" />
</div>
</div>
<div class="avatar">
<div class="w-24 mask mask-hexagon">
<img src="avatar.jpg" alt="Avatar" />
</div>
</div>
<!-- Avatar z ringiem -->
<div class="avatar">
<div class="w-24 rounded-full ring ring-primary ring-offset-base-100 ring-offset-2">
<img src="avatar.jpg" alt="Avatar" />
</div>
</div>
<!-- Avatar z online indicator -->
<div class="avatar online">
<div class="w-24 rounded-full">
<img src="avatar.jpg" alt="Avatar" />
</div>
</div>
<div class="avatar offline">
<div class="w-24 rounded-full">
<img src="avatar.jpg" alt="Avatar" />
</div>
</div>
<!-- Avatar placeholder -->
<div class="avatar placeholder">
<div class="bg-neutral text-neutral-content rounded-full w-24">
<span class="text-3xl">JK</span>
</div>
</div>
<!-- Avatar group -->
<div class="avatar-group -space-x-6 rtl:space-x-reverse">
<div class="avatar">
<div class="w-12">
<img src="avatar1.jpg" alt="Avatar 1" />
</div>
</div>
<div class="avatar">
<div class="w-12">
<img src="avatar2.jpg" alt="Avatar 2" />
</div>
</div>
<div class="avatar">
<div class="w-12">
<img src="avatar3.jpg" alt="Avatar 3" />
</div>
</div>
<div class="avatar placeholder">
<div class="w-12 bg-neutral text-neutral-content">
<span>+99</span>
</div>
</div>
</div>System tematów
Wbudowane tematy
DaisyUI oferuje 35+ gotowych tematów:
// tailwind.config.js
module.exports = {
daisyui: {
themes: [
"light",
"dark",
"cupcake",
"bumblebee",
"emerald",
"corporate",
"synthwave",
"retro",
"cyberpunk",
"valentine",
"halloween",
"garden",
"forest",
"aqua",
"lofi",
"pastel",
"fantasy",
"wireframe",
"black",
"luxury",
"dracula",
"cmyk",
"autumn",
"business",
"acid",
"lemonade",
"night",
"coffee",
"winter",
"dim",
"nord",
"sunset",
],
},
}Zmiana tematu
<!-- W HTML -->
<html data-theme="cyberpunk">
<!-- Lub na konkretnym elemencie -->
<div data-theme="dark" class="bg-base-200 p-4">
Ten div używa dark theme
</div>Theme switcher w JavaScript
// React/Next.js przykład
import { useState, useEffect } from 'react'
export function ThemeSwitcher() {
const [theme, setTheme] = useState('light')
useEffect(() => {
document.documentElement.setAttribute('data-theme', theme)
localStorage.setItem('theme', theme)
}, [theme])
useEffect(() => {
const savedTheme = localStorage.getItem('theme')
if (savedTheme) {
setTheme(savedTheme)
}
}, [])
return (
<select
className="select select-bordered"
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="synthwave">Synthwave</option>
</select>
)
}Tworzenie własnego tematu
// tailwind.config.js
module.exports = {
daisyui: {
themes: [
{
mytheme: {
"primary": "#a991f7",
"primary-content": "#ffffff",
"secondary": "#f6d860",
"secondary-content": "#000000",
"accent": "#37cdbe",
"accent-content": "#000000",
"neutral": "#3d4451",
"neutral-content": "#ffffff",
"base-100": "#ffffff",
"base-200": "#f2f2f2",
"base-300": "#e5e5e5",
"base-content": "#1f2937",
"info": "#3abff8",
"success": "#36d399",
"warning": "#fbbd23",
"error": "#f87272",
// Opcjonalne customizacje
"--rounded-box": "1rem",
"--rounded-btn": "0.5rem",
"--rounded-badge": "1.9rem",
"--animation-btn": "0.25s",
"--animation-input": "0.2s",
"--btn-focus-scale": "0.95",
"--border-btn": "1px",
"--tab-border": "1px",
"--tab-radius": "0.5rem",
},
},
"light",
"dark",
],
},
}Kolory w tematach
<!-- Kolory podstawowe -->
<div class="bg-primary text-primary-content">Primary</div>
<div class="bg-secondary text-secondary-content">Secondary</div>
<div class="bg-accent text-accent-content">Accent</div>
<div class="bg-neutral text-neutral-content">Neutral</div>
<!-- Kolory bazowe (tło) -->
<div class="bg-base-100">Base 100 (main bg)</div>
<div class="bg-base-200">Base 200 (slightly darker)</div>
<div class="bg-base-300">Base 300 (darker)</div>
<div class="text-base-content">Base content (text color)</div>
<!-- Kolory stanu -->
<div class="bg-info text-info-content">Info</div>
<div class="bg-success text-success-content">Success</div>
<div class="bg-warning text-warning-content">Warning</div>
<div class="bg-error text-error-content">Error</div>Integracja z frameworkami
React / Next.js
// components/Button.tsx
interface ButtonProps {
variant?: 'primary' | 'secondary' | 'accent' | 'ghost'
size?: 'lg' | 'md' | 'sm' | 'xs'
children: React.ReactNode
onClick?: () => void
loading?: boolean
disabled?: boolean
}
export function Button({
variant = 'primary',
size = 'md',
children,
onClick,
loading = false,
disabled = false,
}: ButtonProps) {
return (
<button
className={`btn btn-${variant} btn-${size} ${loading ? 'loading' : ''}`}
onClick={onClick}
disabled={disabled || loading}
>
{children}
</button>
)
}
// Użycie
<Button variant="primary" size="lg" onClick={() => alert('Clicked!')}>
Kliknij mnie
</Button>// components/Card.tsx
interface CardProps {
title: string
description?: string
image?: string
actions?: React.ReactNode
compact?: boolean
}
export function Card({ title, description, image, actions, compact = false }: CardProps) {
return (
<div className={`card bg-base-100 shadow-xl ${compact ? 'card-compact' : ''}`}>
{image && (
<figure>
<img src={image} alt={title} />
</figure>
)}
<div className="card-body">
<h2 className="card-title">{title}</h2>
{description && <p>{description}</p>}
{actions && <div className="card-actions justify-end">{actions}</div>}
</div>
</div>
)
}
// Użycie
<Card
title="Produkt"
description="Opis produktu"
image="/product.jpg"
actions={<Button variant="primary">Kup</Button>}
/>// components/Modal.tsx
import { useRef, useEffect } from 'react'
interface ModalProps {
isOpen: boolean
onClose: () => void
title: string
children: React.ReactNode
actions?: React.ReactNode
}
export function Modal({ isOpen, onClose, title, children, actions }: ModalProps) {
const dialogRef = useRef<HTMLDialogElement>(null)
useEffect(() => {
if (isOpen) {
dialogRef.current?.showModal()
} else {
dialogRef.current?.close()
}
}, [isOpen])
return (
<dialog ref={dialogRef} className="modal" onClose={onClose}>
<div className="modal-box">
<button
className="btn btn-sm btn-circle btn-ghost absolute right-2 top-2"
onClick={onClose}
>
✕
</button>
<h3 className="font-bold text-lg">{title}</h3>
<div className="py-4">{children}</div>
{actions && <div className="modal-action">{actions}</div>}
</div>
<form method="dialog" className="modal-backdrop">
<button onClick={onClose}>close</button>
</form>
</dialog>
)
}Vue 3
<!-- components/Button.vue -->
<script setup lang="ts">
interface Props {
variant?: 'primary' | 'secondary' | 'accent' | 'ghost'
size?: 'lg' | 'md' | 'sm' | 'xs'
loading?: boolean
disabled?: boolean
}
const props = withDefaults(defineProps<Props>(), {
variant: 'primary',
size: 'md',
loading: false,
disabled: false,
})
const emit = defineEmits(['click'])
</script>
<template>
<button
:class="[
'btn',
`btn-${variant}`,
`btn-${size}`,
{ loading: loading }
]"
:disabled="disabled || loading"
@click="emit('click')"
>
<slot />
</button>
</template><!-- components/ThemeSwitcher.vue -->
<script setup lang="ts">
import { ref, onMounted, watch } from 'vue'
const themes = ['light', 'dark', 'cupcake', 'cyberpunk', 'synthwave']
const currentTheme = ref('light')
onMounted(() => {
const saved = localStorage.getItem('theme')
if (saved) {
currentTheme.value = saved
document.documentElement.setAttribute('data-theme', saved)
}
})
watch(currentTheme, (newTheme) => {
document.documentElement.setAttribute('data-theme', newTheme)
localStorage.setItem('theme', newTheme)
})
</script>
<template>
<select v-model="currentTheme" class="select select-bordered">
<option v-for="theme in themes" :key="theme" :value="theme">
{{ theme }}
</option>
</select>
</template>Svelte
<!-- Button.svelte -->
<script lang="ts">
export let variant: 'primary' | 'secondary' | 'accent' | 'ghost' = 'primary'
export let size: 'lg' | 'md' | 'sm' | 'xs' = 'md'
export let loading = false
export let disabled = false
</script>
<button
class="btn btn-{variant} btn-{size}"
class:loading
disabled={disabled || loading}
on:click
>
<slot />
</button><!-- ThemeSwitcher.svelte -->
<script lang="ts">
import { onMount } from 'svelte'
const themes = ['light', 'dark', 'cupcake', 'cyberpunk', 'synthwave']
let currentTheme = 'light'
onMount(() => {
const saved = localStorage.getItem('theme')
if (saved) {
currentTheme = saved
document.documentElement.setAttribute('data-theme', saved)
}
})
function handleChange(event: Event) {
const target = event.target as HTMLSelectElement
currentTheme = target.value
document.documentElement.setAttribute('data-theme', currentTheme)
localStorage.setItem('theme', currentTheme)
}
</script>
<select class="select select-bordered" value={currentTheme} on:change={handleChange}>
{#each themes as theme}
<option value={theme}>{theme}</option>
{/each}
</select>Responsywność
Breakpoints z Tailwind
<!-- Karty responsywne -->
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
<div class="card bg-base-100 shadow-xl">
<div class="card-body">
<h2 class="card-title">Card 1</h2>
</div>
</div>
<div class="card bg-base-100 shadow-xl">
<div class="card-body">
<h2 class="card-title">Card 2</h2>
</div>
</div>
<div class="card bg-base-100 shadow-xl">
<div class="card-body">
<h2 class="card-title">Card 3</h2>
</div>
</div>
</div>
<!-- Przyciski responsywne -->
<button class="btn btn-sm md:btn-md lg:btn-lg">
Responsywny przycisk
</button>
<!-- Drawer responsywny -->
<div class="drawer lg:drawer-open">
<input id="my-drawer" type="checkbox" class="drawer-toggle" />
<div class="drawer-content">
<!-- Page content -->
<label for="my-drawer" class="btn btn-primary drawer-button lg:hidden">
Open drawer
</label>
</div>
<div class="drawer-side">
<label for="my-drawer" aria-label="close sidebar" class="drawer-overlay"></label>
<ul class="menu p-4 w-80 min-h-full bg-base-200">
<li><a>Sidebar Item 1</a></li>
<li><a>Sidebar Item 2</a></li>
</ul>
</div>
</div>Dostępność (Accessibility)
ARIA attributes
<!-- Przycisk z ARIA -->
<button class="btn btn-primary" aria-label="Zapisz dokument">
<svg>...</svg>
Zapisz
</button>
<!-- Modal z ARIA -->
<dialog id="modal" class="modal" aria-labelledby="modal-title" aria-describedby="modal-desc">
<div class="modal-box">
<h3 id="modal-title" class="font-bold text-lg">Tytuł</h3>
<p id="modal-desc" class="py-4">Opis modala</p>
</div>
</dialog>
<!-- Tabs z rolami -->
<div role="tablist" class="tabs tabs-boxed" aria-label="Navigation tabs">
<button role="tab" class="tab" aria-selected="false">Tab 1</button>
<button role="tab" class="tab tab-active" aria-selected="true">Tab 2</button>
<button role="tab" class="tab" aria-selected="false">Tab 3</button>
</div>
<!-- Alert z role -->
<div role="alert" class="alert alert-warning">
<span>Uwaga: Sprawdź swoje dane</span>
</div>
<!-- Progress z ARIA -->
<progress
class="progress progress-primary w-56"
value="70"
max="100"
aria-label="Postęp ładowania"
aria-valuenow="70"
aria-valuemin="0"
aria-valuemax="100"
></progress>Fokus i nawigacja klawiaturą
<!-- Skip link -->
<a href="#main-content" class="btn btn-link sr-only focus:not-sr-only">
Przejdź do głównej treści
</a>
<!-- Focus ring -->
<button class="btn btn-primary focus:ring focus:ring-primary focus:ring-offset-2">
Widoczny focus
</button>
<!-- Focusable card -->
<div class="card bg-base-100 shadow-xl" tabindex="0" role="article">
<div class="card-body">
<h2 class="card-title">Focusable Card</h2>
<p>Można nawigować Tab-em</p>
</div>
</div>Optymalizacja wydajności
Purge nieużywanych klas
// tailwind.config.js
module.exports = {
content: [
"./src/**/*.{js,ts,jsx,tsx,html}",
],
theme: {
extend: {},
},
plugins: [require("daisyui")],
daisyui: {
themes: ["light", "dark"], // Tylko używane tematy
styled: true,
base: true,
utils: true,
logs: false, // Wyłącz logi w produkcji
},
}Lazy loading komponentów
// React - dynamiczny import modala
import dynamic from 'next/dynamic'
const Modal = dynamic(() => import('./Modal'), {
loading: () => <span className="loading loading-spinner" />,
ssr: false
})Minimalizacja CSS
# Budowanie z minifikacją
NODE_ENV=production npx tailwindcss -i ./src/input.css -o ./dist/output.css --minifyBest practices
Struktura projektu
src/
├── components/
│ ├── ui/ # Podstawowe komponenty DaisyUI
│ │ ├── Button.tsx
│ │ ├── Card.tsx
│ │ ├── Modal.tsx
│ │ └── Input.tsx
│ ├── layout/ # Layouty
│ │ ├── Navbar.tsx
│ │ ├── Sidebar.tsx
│ │ └── Footer.tsx
│ └── features/ # Komponenty biznesowe
├── styles/
│ └── globals.css # Import Tailwind
└── lib/
└── theme.ts # Konfiguracja tematówKonsystencja w projekcie
// lib/theme.ts - Centralizacja kolorów i wariantów
export const buttonVariants = {
primary: 'btn-primary',
secondary: 'btn-secondary',
danger: 'btn-error',
success: 'btn-success',
} as const
export const sizeVariants = {
small: 'btn-sm',
medium: 'btn-md',
large: 'btn-lg',
} as const
// Użycie
<button className={`btn ${buttonVariants.primary} ${sizeVariants.medium}`}>
Przycisk
</button>Łączenie z Tailwind utilities
<!-- DaisyUI + Tailwind utilities -->
<button class="btn btn-primary hover:scale-105 transition-transform">
Animowany przycisk
</button>
<div class="card bg-base-100 shadow-xl hover:shadow-2xl transition-shadow">
<div class="card-body">
<h2 class="card-title text-gradient">Gradient title</h2>
</div>
</div>
<input
class="input input-bordered focus:input-primary w-full"
placeholder="Focus zmienia kolor"
/>FAQ - Najczęściej zadawane pytania
Czy DaisyUI zwiększa rozmiar bundle?
Nie znacząco. DaisyUI to czysty CSS, który jest purgowany przez Tailwind w produkcji. Używane komponenty dodają minimalny overhead. W praktyce często zmniejsza bundle, bo używasz krótszych klas (btn zamiast 10 utility classes).
Czy mogę używać DaisyUI z komponentami JavaScript?
Tak! DaisyUI to tylko CSS - nie wymaga żadnego JavaScript. Możesz dodać własną logikę lub użyć bibliotek jak Headless UI dla bardziej złożonych interakcji (dropdown, modal z focus trap itp.).
Jak customizować istniejące komponenty?
Masz trzy opcje:
- Dodaj klasy Tailwind obok klas DaisyUI
- Stwórz własny temat z custom colors
- Nadpisz style w CSS używając selektorów
Czy DaisyUI jest kompatybilny z Tailwind v4?
Tak! DaisyUI wspiera Tailwind v4. Sprawdź dokumentację dla szczegółów konfiguracji.
Jak zaimplementować dark mode?
DaisyUI ma wbudowane wsparcie przez tematy. Ustaw data-theme="dark" na <html> i używaj tematu "dark" lub dowolnego ciemnego tematu jak "dracula", "night", "forest".
Podsumowanie
DaisyUI to idealne rozwiązanie dla projektów używających Tailwind CSS, oferujące:
- Semantyczne klasy - Czytelniejszy kod, szybsze pisanie
- 35+ tematów - Łatwa zmiana wyglądu całej aplikacji
- 50+ komponentów - Wszystko czego potrzebujesz do UI
- Zero JavaScript - Działa z każdym frameworkiem
- Pełna kompatybilność - Z Tailwind utilities i responsive design
- 100% darmowy - Open source, MIT License
Jeśli używasz Tailwind CSS i chcesz przyspieszyć development bez rezygnacji z elastyczności, DaisyUI jest doskonałym wyborem.
DaisyUI - A complete guide to components for Tailwind CSS
What is DaisyUI?
DaisyUI is the most popular Tailwind CSS plugin that adds ready-made component classes to your project. Instead of writing dozens of utility classes like bg-blue-500 hover:bg-blue-600 text-white font-bold py-2 px-4 rounded, you simply write btn btn-primary. It is the best thing that could have happened to Tailwind CSS.
DaisyUI does not replace Tailwind - it extends it. You can still use all Tailwind utility classes alongside DaisyUI semantic classes. This gives you the flexibility of Tailwind with the convenience of ready-made components, and all of it without any JavaScript - DaisyUI is pure CSS.
Why DaisyUI?
Key advantages of DaisyUI
- Semantic classes -
btninstead ofbg-blue-500 hover:bg-blue-600... - 35+ themes - Easy appearance change for your entire application
- Zero JavaScript - Pure CSS, works with any framework
- Full compatibility - Works with React, Vue, Svelte, Angular, vanilla HTML
- Smaller bundle - Fewer classes = smaller CSS
- 100% free - MIT License, open source
DaisyUI vs other solutions
| Feature | DaisyUI | Tailwind UI | Headless UI | Bootstrap |
|---|---|---|---|---|
| Price | Free | $299+ | Free | Free |
| JavaScript | Not required | Yes | Yes | Yes |
| Tailwind | Native | Native | Native | Separate |
| Components | 50+ | 500+ | 10 | 25+ |
| Themes | 35+ | None | None | Limited |
| Customization | Full | Full | Full | Limited |
| Bundle size | Minimal | Medium | Minimal | Large |
Installation and configuration
npm installation
# npm
npm install -D daisyui@latest
# pnpm
pnpm add -D daisyui@latest
# yarn
yarn add -D daisyui@latest
# bun
bun add -D daisyui@latestTailwind CSS v3 configuration
// tailwind.config.js
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
"./src/**/*.{js,ts,jsx,tsx,html}",
"./components/**/*.{js,ts,jsx,tsx}",
],
theme: {
extend: {},
},
plugins: [
require("daisyui"),
],
daisyui: {
themes: ["light", "dark", "cupcake", "cyberpunk", "valentine", "forest"],
darkTheme: "dark",
base: true,
styled: true,
utils: true,
prefix: "",
logs: true,
themeRoot: ":root",
},
}Tailwind CSS v4 configuration
// tailwind.config.js (v4)
import daisyui from "daisyui"
export default {
plugins: [daisyui],
daisyui: {
themes: ["light", "dark"],
},
}Verifying the installation
<!-- Check if DaisyUI is working -->
<button class="btn btn-primary">
If this button is styled, DaisyUI is working!
</button>DaisyUI components
Buttons (Button)
<!-- Basic variants -->
<button class="btn">Default</button>
<button class="btn btn-neutral">Neutral</button>
<button class="btn btn-primary">Primary</button>
<button class="btn btn-secondary">Secondary</button>
<button class="btn btn-accent">Accent</button>
<button class="btn btn-ghost">Ghost</button>
<button class="btn btn-link">Link</button>
<!-- States -->
<button class="btn btn-info">Info</button>
<button class="btn btn-success">Success</button>
<button class="btn btn-warning">Warning</button>
<button class="btn btn-error">Error</button>
<!-- Sizes -->
<button class="btn btn-lg">Large</button>
<button class="btn btn-md">Medium (default)</button>
<button class="btn btn-sm">Small</button>
<button class="btn btn-xs">Extra small</button>
<!-- Modifiers -->
<button class="btn btn-wide">Wide</button>
<button class="btn btn-block">Block (full width)</button>
<button class="btn btn-circle">○</button>
<button class="btn btn-square">□</button>
<!-- Interactive states -->
<button class="btn btn-outline btn-primary">Outline</button>
<button class="btn btn-active">Active</button>
<button class="btn btn-disabled">Disabled</button>
<button class="btn loading">Loading...</button>
<!-- With icon -->
<button class="btn btn-primary gap-2">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
<path d="M10 12a2 2 0 100-4 2 2 0 000 4z" />
</svg>
Settings
</button>Cards (Card)
<!-- Basic card -->
<div class="card w-96 bg-base-100 shadow-xl">
<figure>
<img src="https://picsum.photos/400/200" alt="Image" />
</figure>
<div class="card-body">
<h2 class="card-title">Card title</h2>
<p>Card description with additional text explaining the content.</p>
<div class="card-actions justify-end">
<button class="btn btn-primary">Buy now</button>
</div>
</div>
</div>
<!-- Compact card -->
<div class="card card-compact w-96 bg-base-100 shadow-xl">
<figure><img src="image.jpg" alt="Photo" /></figure>
<div class="card-body">
<h2 class="card-title">Compact!</h2>
<p>Less padding</p>
<div class="card-actions justify-end">
<button class="btn btn-primary btn-sm">Action</button>
</div>
</div>
</div>
<!-- Card with badge -->
<div class="card w-96 bg-base-100 shadow-xl">
<div class="card-body">
<h2 class="card-title">
Product
<div class="badge badge-secondary">NEW</div>
</h2>
<p>Product description</p>
<div class="card-actions justify-end">
<div class="badge badge-outline">Fashion</div>
<div class="badge badge-outline">Products</div>
</div>
</div>
</div>
<!-- Horizontal card -->
<div class="card card-side bg-base-100 shadow-xl">
<figure><img src="image.jpg" alt="Movie" class="w-48" /></figure>
<div class="card-body">
<h2 class="card-title">Movie</h2>
<p>Movie description</p>
<div class="card-actions justify-end">
<button class="btn btn-primary">Watch</button>
</div>
</div>
</div>
<!-- Card with overlay -->
<div class="card w-96 bg-base-100 shadow-xl image-full">
<figure><img src="background.jpg" alt="Background" /></figure>
<div class="card-body">
<h2 class="card-title">Title</h2>
<p>Text over the background image</p>
<div class="card-actions justify-end">
<button class="btn btn-primary">Action</button>
</div>
</div>
</div>Modal
<!-- Modal with button -->
<button class="btn" onclick="my_modal_1.showModal()">Open Modal</button>
<dialog id="my_modal_1" class="modal">
<div class="modal-box">
<h3 class="font-bold text-lg">Hello!</h3>
<p class="py-4">Press ESC or click the button to close</p>
<div class="modal-action">
<form method="dialog">
<button class="btn">Close</button>
</form>
</div>
</div>
</dialog>
<!-- Modal with backdrop to close -->
<button class="btn" onclick="my_modal_2.showModal()">Modal with backdrop</button>
<dialog id="my_modal_2" class="modal">
<div class="modal-box">
<h3 class="font-bold text-lg">Modal</h3>
<p class="py-4">Click outside the modal to close</p>
</div>
<form method="dialog" class="modal-backdrop">
<button>close</button>
</form>
</dialog>
<!-- Modal with actions -->
<dialog id="my_modal_3" class="modal">
<div class="modal-box">
<form method="dialog">
<button class="btn btn-sm btn-circle btn-ghost absolute right-2 top-2">✕</button>
</form>
<h3 class="font-bold text-lg">Confirm action</h3>
<p class="py-4">Are you sure you want to continue?</p>
<div class="modal-action">
<form method="dialog">
<button class="btn btn-ghost">Cancel</button>
<button class="btn btn-primary">Confirm</button>
</form>
</div>
</div>
</dialog>Navigation (Navbar)
<!-- Simple navbar -->
<div class="navbar bg-base-100">
<a class="btn btn-ghost text-xl">daisyUI</a>
</div>
<!-- Navbar with menu -->
<div class="navbar bg-base-100">
<div class="flex-1">
<a class="btn btn-ghost text-xl">Logo</a>
</div>
<div class="flex-none">
<ul class="menu menu-horizontal px-1">
<li><a>Link 1</a></li>
<li><a>Link 2</a></li>
</ul>
</div>
</div>
<!-- Navbar with search and avatar -->
<div class="navbar bg-base-100">
<div class="flex-1">
<a class="btn btn-ghost text-xl">Logo</a>
</div>
<div class="flex-none gap-2">
<div class="form-control">
<input type="text" placeholder="Search" class="input input-bordered w-24 md:w-auto" />
</div>
<div class="dropdown dropdown-end">
<div tabindex="0" role="button" class="btn btn-ghost btn-circle avatar">
<div class="w-10 rounded-full">
<img alt="Avatar" src="avatar.jpg" />
</div>
</div>
<ul tabindex="0" class="mt-3 z-[1] p-2 shadow menu menu-sm dropdown-content bg-base-100 rounded-box w-52">
<li><a>Profile</a></li>
<li><a>Settings</a></li>
<li><a>Logout</a></li>
</ul>
</div>
</div>
</div>
<!-- Navbar with icons -->
<div class="navbar bg-neutral text-neutral-content">
<div class="flex-1">
<a class="btn btn-ghost text-xl">daisyUI</a>
</div>
<div class="flex-none">
<button class="btn btn-square btn-ghost">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" class="inline-block w-5 h-5 stroke-current">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 12h.01M12 12h.01M19 12h.01M6 12a1 1 0 11-2 0 1 1 0 012 0zm7 0a1 1 0 11-2 0 1 1 0 012 0zm7 0a1 1 0 11-2 0 1 1 0 012 0z"></path>
</svg>
</button>
</div>
</div>Forms
<!-- Input -->
<div class="form-control w-full max-w-xs">
<label class="label">
<span class="label-text">Email</span>
<span class="label-text-alt">Required</span>
</label>
<input type="email" placeholder="email@example.com" class="input input-bordered w-full max-w-xs" />
<label class="label">
<span class="label-text-alt">Enter a valid email address</span>
</label>
</div>
<!-- Input with variants -->
<input type="text" placeholder="Default" class="input input-bordered w-full max-w-xs" />
<input type="text" placeholder="Primary" class="input input-bordered input-primary w-full max-w-xs" />
<input type="text" placeholder="Secondary" class="input input-bordered input-secondary w-full max-w-xs" />
<input type="text" placeholder="Accent" class="input input-bordered input-accent w-full max-w-xs" />
<input type="text" placeholder="Info" class="input input-bordered input-info w-full max-w-xs" />
<input type="text" placeholder="Success" class="input input-bordered input-success w-full max-w-xs" />
<input type="text" placeholder="Warning" class="input input-bordered input-warning w-full max-w-xs" />
<input type="text" placeholder="Error" class="input input-bordered input-error w-full max-w-xs" />
<!-- Sizes -->
<input type="text" placeholder="Large" class="input input-bordered input-lg" />
<input type="text" placeholder="Medium" class="input input-bordered input-md" />
<input type="text" placeholder="Small" class="input input-bordered input-sm" />
<input type="text" placeholder="XS" class="input input-bordered input-xs" />
<!-- Select -->
<select class="select select-bordered w-full max-w-xs">
<option disabled selected>Choose an option</option>
<option>Option 1</option>
<option>Option 2</option>
<option>Option 3</option>
</select>
<!-- Textarea -->
<textarea class="textarea textarea-bordered" placeholder="Your message"></textarea>
<!-- Checkbox -->
<div class="form-control">
<label class="label cursor-pointer">
<span class="label-text">Remember me</span>
<input type="checkbox" checked class="checkbox checkbox-primary" />
</label>
</div>
<!-- Radio -->
<div class="form-control">
<label class="label cursor-pointer">
<span class="label-text">Option A</span>
<input type="radio" name="radio-1" class="radio radio-primary" checked />
</label>
<label class="label cursor-pointer">
<span class="label-text">Option B</span>
<input type="radio" name="radio-1" class="radio radio-primary" />
</label>
</div>
<!-- Toggle -->
<input type="checkbox" class="toggle toggle-primary" checked />
<!-- Range -->
<input type="range" min="0" max="100" value="40" class="range range-primary" />
<!-- Rating -->
<div class="rating">
<input type="radio" name="rating-1" class="mask mask-star" />
<input type="radio" name="rating-1" class="mask mask-star" checked />
<input type="radio" name="rating-1" class="mask mask-star" />
<input type="radio" name="rating-1" class="mask mask-star" />
<input type="radio" name="rating-1" class="mask mask-star" />
</div>
<!-- File input -->
<input type="file" class="file-input file-input-bordered w-full max-w-xs" />Tables
<!-- Basic table -->
<div class="overflow-x-auto">
<table class="table">
<thead>
<tr>
<th></th>
<th>Name</th>
<th>Position</th>
<th>Email</th>
</tr>
</thead>
<tbody>
<tr>
<th>1</th>
<td>John Smith</td>
<td>Developer</td>
<td>john@example.com</td>
</tr>
<tr>
<th>2</th>
<td>Anna Johnson</td>
<td>Designer</td>
<td>anna@example.com</td>
</tr>
<tr>
<th>3</th>
<td>Peter Williams</td>
<td>Manager</td>
<td>peter@example.com</td>
</tr>
</tbody>
</table>
</div>
<!-- Zebra table -->
<table class="table table-zebra">
<!-- ... -->
</table>
<!-- Table with hover -->
<table class="table">
<tbody>
<tr class="hover">
<td>Row with hover effect</td>
</tr>
</tbody>
</table>
<!-- Active table -->
<table class="table">
<tbody>
<tr class="active">
<td>Active row (highlighted)</td>
</tr>
</tbody>
</table>
<!-- Compact table -->
<table class="table table-xs">
<!-- Less padding -->
</table>Alerts and notifications
<!-- Basic alert -->
<div class="alert">
<span>12 unread messages.</span>
</div>
<!-- Info alert -->
<div class="alert alert-info">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" class="stroke-current shrink-0 w-6 h-6">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>
<span>A new software update is available.</span>
</div>
<!-- Success alert -->
<div class="alert alert-success">
<svg xmlns="http://www.w3.org/2000/svg" class="stroke-current shrink-0 h-6 w-6" fill="none" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
<span>Your order has been confirmed!</span>
</div>
<!-- Warning alert -->
<div class="alert alert-warning">
<svg xmlns="http://www.w3.org/2000/svg" class="stroke-current shrink-0 h-6 w-6" fill="none" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
</svg>
<span>Warning: Your session expires in 5 minutes!</span>
</div>
<!-- Error alert -->
<div class="alert alert-error">
<svg xmlns="http://www.w3.org/2000/svg" class="stroke-current shrink-0 h-6 w-6" fill="none" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
<span>Error! Something went wrong.</span>
</div>
<!-- Alert with actions -->
<div class="alert">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" class="stroke-info shrink-0 w-6 h-6">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>
<span>We have new terms of service.</span>
<div>
<button class="btn btn-sm">Decline</button>
<button class="btn btn-sm btn-primary">Accept</button>
</div>
</div>
<!-- Toast -->
<div class="toast toast-end">
<div class="alert alert-info">
<span>New message.</span>
</div>
</div>Badge and Indicator
<!-- Badge -->
<span class="badge">default</span>
<span class="badge badge-neutral">neutral</span>
<span class="badge badge-primary">primary</span>
<span class="badge badge-secondary">secondary</span>
<span class="badge badge-accent">accent</span>
<span class="badge badge-ghost">ghost</span>
<!-- Badge outline -->
<span class="badge badge-outline">outline</span>
<span class="badge badge-outline badge-primary">primary</span>
<!-- Badge sizes -->
<span class="badge badge-lg">Large</span>
<span class="badge badge-md">Medium</span>
<span class="badge badge-sm">Small</span>
<span class="badge badge-xs">Tiny</span>
<!-- Badge in button -->
<button class="btn gap-2">
Inbox
<span class="badge badge-sm">+99</span>
</button>
<!-- Indicator -->
<div class="indicator">
<span class="indicator-item badge badge-primary">new</span>
<div class="grid w-32 h-32 bg-base-300 place-items-center">content</div>
</div>
<!-- Indicator on avatar -->
<div class="indicator">
<span class="indicator-item badge badge-secondary">online</span>
<div class="avatar">
<div class="w-12 rounded">
<img src="avatar.jpg" alt="Avatar" />
</div>
</div>
</div>Collapse and Accordion
<!-- Collapse -->
<div class="collapse bg-base-200">
<input type="checkbox" />
<div class="collapse-title text-xl font-medium">
Click to expand
</div>
<div class="collapse-content">
<p>Hidden content appears after clicking.</p>
</div>
</div>
<!-- Collapse with arrow -->
<div class="collapse collapse-arrow bg-base-200">
<input type="checkbox" />
<div class="collapse-title text-xl font-medium">
Click to expand
</div>
<div class="collapse-content">
<p>Content with arrow icon.</p>
</div>
</div>
<!-- Collapse with plus -->
<div class="collapse collapse-plus bg-base-200">
<input type="checkbox" />
<div class="collapse-title text-xl font-medium">
Click to expand
</div>
<div class="collapse-content">
<p>Content with plus/minus icon.</p>
</div>
</div>
<!-- Accordion (radio - only one open at a time) -->
<div class="join join-vertical w-full">
<div class="collapse collapse-arrow join-item border border-base-300">
<input type="radio" name="my-accordion-1" checked />
<div class="collapse-title text-xl font-medium">
Question 1
</div>
<div class="collapse-content">
<p>Answer to question 1.</p>
</div>
</div>
<div class="collapse collapse-arrow join-item border border-base-300">
<input type="radio" name="my-accordion-1" />
<div class="collapse-title text-xl font-medium">
Question 2
</div>
<div class="collapse-content">
<p>Answer to question 2.</p>
</div>
</div>
<div class="collapse collapse-arrow join-item border border-base-300">
<input type="radio" name="my-accordion-1" />
<div class="collapse-title text-xl font-medium">
Question 3
</div>
<div class="collapse-content">
<p>Answer to question 3.</p>
</div>
</div>
</div>Tabs
<!-- Basic tabs -->
<div role="tablist" class="tabs tabs-bordered">
<a role="tab" class="tab">Tab 1</a>
<a role="tab" class="tab tab-active">Tab 2</a>
<a role="tab" class="tab">Tab 3</a>
</div>
<!-- Tabs lifted -->
<div role="tablist" class="tabs tabs-lifted">
<a role="tab" class="tab">Tab 1</a>
<a role="tab" class="tab tab-active">Tab 2</a>
<a role="tab" class="tab">Tab 3</a>
</div>
<!-- Tabs boxed -->
<div role="tablist" class="tabs tabs-boxed">
<a role="tab" class="tab">Tab 1</a>
<a role="tab" class="tab tab-active">Tab 2</a>
<a role="tab" class="tab">Tab 3</a>
</div>
<!-- Tabs with content (radio) -->
<div role="tablist" class="tabs tabs-lifted">
<input type="radio" name="my_tabs_1" role="tab" class="tab" aria-label="Tab 1" />
<div role="tabpanel" class="tab-content bg-base-100 border-base-300 rounded-box p-6">
Tab 1 content
</div>
<input type="radio" name="my_tabs_1" role="tab" class="tab" aria-label="Tab 2" checked />
<div role="tabpanel" class="tab-content bg-base-100 border-base-300 rounded-box p-6">
Tab 2 content
</div>
<input type="radio" name="my_tabs_1" role="tab" class="tab" aria-label="Tab 3" />
<div role="tabpanel" class="tab-content bg-base-100 border-base-300 rounded-box p-6">
Tab 3 content
</div>
</div>Progress and Loading
<!-- Progress bar -->
<progress class="progress w-56" value="0" max="100"></progress>
<progress class="progress w-56" value="25" max="100"></progress>
<progress class="progress w-56" value="50" max="100"></progress>
<progress class="progress w-56" value="75" max="100"></progress>
<progress class="progress w-56" value="100" max="100"></progress>
<!-- Progress with colors -->
<progress class="progress progress-primary w-56" value="40" max="100"></progress>
<progress class="progress progress-secondary w-56" value="60" max="100"></progress>
<progress class="progress progress-accent w-56" value="80" max="100"></progress>
<progress class="progress progress-info w-56" value="45" max="100"></progress>
<progress class="progress progress-success w-56" value="70" max="100"></progress>
<progress class="progress progress-warning w-56" value="55" max="100"></progress>
<progress class="progress progress-error w-56" value="30" max="100"></progress>
<!-- Indeterminate progress (animated) -->
<progress class="progress w-56"></progress>
<!-- Radial progress -->
<div class="radial-progress" style="--value:70;">70%</div>
<div class="radial-progress" style="--value:70; --size:12rem; --thickness: 2px;">70%</div>
<div class="radial-progress bg-primary text-primary-content border-4 border-primary" style="--value:70;">70%</div>
<!-- Loading spinner -->
<span class="loading loading-spinner loading-xs"></span>
<span class="loading loading-spinner loading-sm"></span>
<span class="loading loading-spinner loading-md"></span>
<span class="loading loading-spinner loading-lg"></span>
<!-- Loading dots -->
<span class="loading loading-dots loading-lg"></span>
<!-- Loading ring -->
<span class="loading loading-ring loading-lg"></span>
<!-- Loading ball -->
<span class="loading loading-ball loading-lg"></span>
<!-- Loading bars -->
<span class="loading loading-bars loading-lg"></span>
<!-- Loading infinity -->
<span class="loading loading-infinity loading-lg"></span>Avatar
<!-- Basic avatar -->
<div class="avatar">
<div class="w-24 rounded">
<img src="avatar.jpg" alt="Avatar" />
</div>
</div>
<!-- Rounded avatar -->
<div class="avatar">
<div class="w-24 rounded-full">
<img src="avatar.jpg" alt="Avatar" />
</div>
</div>
<!-- Avatar with mask -->
<div class="avatar">
<div class="w-24 mask mask-squircle">
<img src="avatar.jpg" alt="Avatar" />
</div>
</div>
<div class="avatar">
<div class="w-24 mask mask-hexagon">
<img src="avatar.jpg" alt="Avatar" />
</div>
</div>
<!-- Avatar with ring -->
<div class="avatar">
<div class="w-24 rounded-full ring ring-primary ring-offset-base-100 ring-offset-2">
<img src="avatar.jpg" alt="Avatar" />
</div>
</div>
<!-- Avatar with online indicator -->
<div class="avatar online">
<div class="w-24 rounded-full">
<img src="avatar.jpg" alt="Avatar" />
</div>
</div>
<div class="avatar offline">
<div class="w-24 rounded-full">
<img src="avatar.jpg" alt="Avatar" />
</div>
</div>
<!-- Avatar placeholder -->
<div class="avatar placeholder">
<div class="bg-neutral text-neutral-content rounded-full w-24">
<span class="text-3xl">JK</span>
</div>
</div>
<!-- Avatar group -->
<div class="avatar-group -space-x-6 rtl:space-x-reverse">
<div class="avatar">
<div class="w-12">
<img src="avatar1.jpg" alt="Avatar 1" />
</div>
</div>
<div class="avatar">
<div class="w-12">
<img src="avatar2.jpg" alt="Avatar 2" />
</div>
</div>
<div class="avatar">
<div class="w-12">
<img src="avatar3.jpg" alt="Avatar 3" />
</div>
</div>
<div class="avatar placeholder">
<div class="w-12 bg-neutral text-neutral-content">
<span>+99</span>
</div>
</div>
</div>Theme system
Built-in themes
DaisyUI offers 35+ ready-made themes:
// tailwind.config.js
module.exports = {
daisyui: {
themes: [
"light",
"dark",
"cupcake",
"bumblebee",
"emerald",
"corporate",
"synthwave",
"retro",
"cyberpunk",
"valentine",
"halloween",
"garden",
"forest",
"aqua",
"lofi",
"pastel",
"fantasy",
"wireframe",
"black",
"luxury",
"dracula",
"cmyk",
"autumn",
"business",
"acid",
"lemonade",
"night",
"coffee",
"winter",
"dim",
"nord",
"sunset",
],
},
}Changing the theme
<!-- In HTML -->
<html data-theme="cyberpunk">
<!-- Or on a specific element -->
<div data-theme="dark" class="bg-base-200 p-4">
This div uses the dark theme
</div>Theme switcher in JavaScript
// React/Next.js example
import { useState, useEffect } from 'react'
export function ThemeSwitcher() {
const [theme, setTheme] = useState('light')
useEffect(() => {
document.documentElement.setAttribute('data-theme', theme)
localStorage.setItem('theme', theme)
}, [theme])
useEffect(() => {
const savedTheme = localStorage.getItem('theme')
if (savedTheme) {
setTheme(savedTheme)
}
}, [])
return (
<select
className="select select-bordered"
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="synthwave">Synthwave</option>
</select>
)
}Creating a custom theme
// tailwind.config.js
module.exports = {
daisyui: {
themes: [
{
mytheme: {
"primary": "#a991f7",
"primary-content": "#ffffff",
"secondary": "#f6d860",
"secondary-content": "#000000",
"accent": "#37cdbe",
"accent-content": "#000000",
"neutral": "#3d4451",
"neutral-content": "#ffffff",
"base-100": "#ffffff",
"base-200": "#f2f2f2",
"base-300": "#e5e5e5",
"base-content": "#1f2937",
"info": "#3abff8",
"success": "#36d399",
"warning": "#fbbd23",
"error": "#f87272",
// Optional customizations
"--rounded-box": "1rem",
"--rounded-btn": "0.5rem",
"--rounded-badge": "1.9rem",
"--animation-btn": "0.25s",
"--animation-input": "0.2s",
"--btn-focus-scale": "0.95",
"--border-btn": "1px",
"--tab-border": "1px",
"--tab-radius": "0.5rem",
},
},
"light",
"dark",
],
},
}Colors in themes
<!-- Primary colors -->
<div class="bg-primary text-primary-content">Primary</div>
<div class="bg-secondary text-secondary-content">Secondary</div>
<div class="bg-accent text-accent-content">Accent</div>
<div class="bg-neutral text-neutral-content">Neutral</div>
<!-- Base colors (background) -->
<div class="bg-base-100">Base 100 (main bg)</div>
<div class="bg-base-200">Base 200 (slightly darker)</div>
<div class="bg-base-300">Base 300 (darker)</div>
<div class="text-base-content">Base content (text color)</div>
<!-- State colors -->
<div class="bg-info text-info-content">Info</div>
<div class="bg-success text-success-content">Success</div>
<div class="bg-warning text-warning-content">Warning</div>
<div class="bg-error text-error-content">Error</div>Framework integration
React / Next.js
// components/Button.tsx
interface ButtonProps {
variant?: 'primary' | 'secondary' | 'accent' | 'ghost'
size?: 'lg' | 'md' | 'sm' | 'xs'
children: React.ReactNode
onClick?: () => void
loading?: boolean
disabled?: boolean
}
export function Button({
variant = 'primary',
size = 'md',
children,
onClick,
loading = false,
disabled = false,
}: ButtonProps) {
return (
<button
className={`btn btn-${variant} btn-${size} ${loading ? 'loading' : ''}`}
onClick={onClick}
disabled={disabled || loading}
>
{children}
</button>
)
}
// Usage
<Button variant="primary" size="lg" onClick={() => alert('Clicked!')}>
Click me
</Button>// components/Card.tsx
interface CardProps {
title: string
description?: string
image?: string
actions?: React.ReactNode
compact?: boolean
}
export function Card({ title, description, image, actions, compact = false }: CardProps) {
return (
<div className={`card bg-base-100 shadow-xl ${compact ? 'card-compact' : ''}`}>
{image && (
<figure>
<img src={image} alt={title} />
</figure>
)}
<div className="card-body">
<h2 className="card-title">{title}</h2>
{description && <p>{description}</p>}
{actions && <div className="card-actions justify-end">{actions}</div>}
</div>
</div>
)
}
// Usage
<Card
title="Product"
description="Product description"
image="/product.jpg"
actions={<Button variant="primary">Buy</Button>}
/>// components/Modal.tsx
import { useRef, useEffect } from 'react'
interface ModalProps {
isOpen: boolean
onClose: () => void
title: string
children: React.ReactNode
actions?: React.ReactNode
}
export function Modal({ isOpen, onClose, title, children, actions }: ModalProps) {
const dialogRef = useRef<HTMLDialogElement>(null)
useEffect(() => {
if (isOpen) {
dialogRef.current?.showModal()
} else {
dialogRef.current?.close()
}
}, [isOpen])
return (
<dialog ref={dialogRef} className="modal" onClose={onClose}>
<div className="modal-box">
<button
className="btn btn-sm btn-circle btn-ghost absolute right-2 top-2"
onClick={onClose}
>
✕
</button>
<h3 className="font-bold text-lg">{title}</h3>
<div className="py-4">{children}</div>
{actions && <div className="modal-action">{actions}</div>}
</div>
<form method="dialog" className="modal-backdrop">
<button onClick={onClose}>close</button>
</form>
</dialog>
)
}Vue 3
<!-- components/Button.vue -->
<script setup lang="ts">
interface Props {
variant?: 'primary' | 'secondary' | 'accent' | 'ghost'
size?: 'lg' | 'md' | 'sm' | 'xs'
loading?: boolean
disabled?: boolean
}
const props = withDefaults(defineProps<Props>(), {
variant: 'primary',
size: 'md',
loading: false,
disabled: false,
})
const emit = defineEmits(['click'])
</script>
<template>
<button
:class="[
'btn',
`btn-${variant}`,
`btn-${size}`,
{ loading: loading }
]"
:disabled="disabled || loading"
@click="emit('click')"
>
<slot />
</button>
</template><!-- components/ThemeSwitcher.vue -->
<script setup lang="ts">
import { ref, onMounted, watch } from 'vue'
const themes = ['light', 'dark', 'cupcake', 'cyberpunk', 'synthwave']
const currentTheme = ref('light')
onMounted(() => {
const saved = localStorage.getItem('theme')
if (saved) {
currentTheme.value = saved
document.documentElement.setAttribute('data-theme', saved)
}
})
watch(currentTheme, (newTheme) => {
document.documentElement.setAttribute('data-theme', newTheme)
localStorage.setItem('theme', newTheme)
})
</script>
<template>
<select v-model="currentTheme" class="select select-bordered">
<option v-for="theme in themes" :key="theme" :value="theme">
{{ theme }}
</option>
</select>
</template>Svelte
<!-- Button.svelte -->
<script lang="ts">
export let variant: 'primary' | 'secondary' | 'accent' | 'ghost' = 'primary'
export let size: 'lg' | 'md' | 'sm' | 'xs' = 'md'
export let loading = false
export let disabled = false
</script>
<button
class="btn btn-{variant} btn-{size}"
class:loading
disabled={disabled || loading}
on:click
>
<slot />
</button><!-- ThemeSwitcher.svelte -->
<script lang="ts">
import { onMount } from 'svelte'
const themes = ['light', 'dark', 'cupcake', 'cyberpunk', 'synthwave']
let currentTheme = 'light'
onMount(() => {
const saved = localStorage.getItem('theme')
if (saved) {
currentTheme = saved
document.documentElement.setAttribute('data-theme', saved)
}
})
function handleChange(event: Event) {
const target = event.target as HTMLSelectElement
currentTheme = target.value
document.documentElement.setAttribute('data-theme', currentTheme)
localStorage.setItem('theme', currentTheme)
}
</script>
<select class="select select-bordered" value={currentTheme} on:change={handleChange}>
{#each themes as theme}
<option value={theme}>{theme}</option>
{/each}
</select>Responsiveness
Breakpoints with Tailwind
<!-- Responsive cards -->
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
<div class="card bg-base-100 shadow-xl">
<div class="card-body">
<h2 class="card-title">Card 1</h2>
</div>
</div>
<div class="card bg-base-100 shadow-xl">
<div class="card-body">
<h2 class="card-title">Card 2</h2>
</div>
</div>
<div class="card bg-base-100 shadow-xl">
<div class="card-body">
<h2 class="card-title">Card 3</h2>
</div>
</div>
</div>
<!-- Responsive buttons -->
<button class="btn btn-sm md:btn-md lg:btn-lg">
Responsive button
</button>
<!-- Responsive drawer -->
<div class="drawer lg:drawer-open">
<input id="my-drawer" type="checkbox" class="drawer-toggle" />
<div class="drawer-content">
<!-- Page content -->
<label for="my-drawer" class="btn btn-primary drawer-button lg:hidden">
Open drawer
</label>
</div>
<div class="drawer-side">
<label for="my-drawer" aria-label="close sidebar" class="drawer-overlay"></label>
<ul class="menu p-4 w-80 min-h-full bg-base-200">
<li><a>Sidebar Item 1</a></li>
<li><a>Sidebar Item 2</a></li>
</ul>
</div>
</div>Accessibility
ARIA attributes
<!-- Button with ARIA -->
<button class="btn btn-primary" aria-label="Save document">
<svg>...</svg>
Save
</button>
<!-- Modal with ARIA -->
<dialog id="modal" class="modal" aria-labelledby="modal-title" aria-describedby="modal-desc">
<div class="modal-box">
<h3 id="modal-title" class="font-bold text-lg">Title</h3>
<p id="modal-desc" class="py-4">Modal description</p>
</div>
</dialog>
<!-- Tabs with roles -->
<div role="tablist" class="tabs tabs-boxed" aria-label="Navigation tabs">
<button role="tab" class="tab" aria-selected="false">Tab 1</button>
<button role="tab" class="tab tab-active" aria-selected="true">Tab 2</button>
<button role="tab" class="tab" aria-selected="false">Tab 3</button>
</div>
<!-- Alert with role -->
<div role="alert" class="alert alert-warning">
<span>Warning: Check your data</span>
</div>
<!-- Progress with ARIA -->
<progress
class="progress progress-primary w-56"
value="70"
max="100"
aria-label="Loading progress"
aria-valuenow="70"
aria-valuemin="0"
aria-valuemax="100"
></progress>Focus and keyboard navigation
<!-- Skip link -->
<a href="#main-content" class="btn btn-link sr-only focus:not-sr-only">
Skip to main content
</a>
<!-- Focus ring -->
<button class="btn btn-primary focus:ring focus:ring-primary focus:ring-offset-2">
Visible focus
</button>
<!-- Focusable card -->
<div class="card bg-base-100 shadow-xl" tabindex="0" role="article">
<div class="card-body">
<h2 class="card-title">Focusable Card</h2>
<p>Can be navigated with Tab key</p>
</div>
</div>Performance optimization
Purging unused classes
// tailwind.config.js
module.exports = {
content: [
"./src/**/*.{js,ts,jsx,tsx,html}",
],
theme: {
extend: {},
},
plugins: [require("daisyui")],
daisyui: {
themes: ["light", "dark"], // Only used themes
styled: true,
base: true,
utils: true,
logs: false, // Disable logs in production
},
}Lazy loading components
// React - dynamic modal import
import dynamic from 'next/dynamic'
const Modal = dynamic(() => import('./Modal'), {
loading: () => <span className="loading loading-spinner" />,
ssr: false
})CSS minification
# Build with minification
NODE_ENV=production npx tailwindcss -i ./src/input.css -o ./dist/output.css --minifyBest practices
Project structure
src/
├── components/
│ ├── ui/ # Base DaisyUI components
│ │ ├── Button.tsx
│ │ ├── Card.tsx
│ │ ├── Modal.tsx
│ │ └── Input.tsx
│ ├── layout/ # Layouts
│ │ ├── Navbar.tsx
│ │ ├── Sidebar.tsx
│ │ └── Footer.tsx
│ └── features/ # Business components
├── styles/
│ └── globals.css # Tailwind import
└── lib/
└── theme.ts # Theme configurationConsistency in your project
// lib/theme.ts - Centralizing colors and variants
export const buttonVariants = {
primary: 'btn-primary',
secondary: 'btn-secondary',
danger: 'btn-error',
success: 'btn-success',
} as const
export const sizeVariants = {
small: 'btn-sm',
medium: 'btn-md',
large: 'btn-lg',
} as const
// Usage
<button className={`btn ${buttonVariants.primary} ${sizeVariants.medium}`}>
Button
</button>Combining with Tailwind utilities
<!-- DaisyUI + Tailwind utilities -->
<button class="btn btn-primary hover:scale-105 transition-transform">
Animated button
</button>
<div class="card bg-base-100 shadow-xl hover:shadow-2xl transition-shadow">
<div class="card-body">
<h2 class="card-title text-gradient">Gradient title</h2>
</div>
</div>
<input
class="input input-bordered focus:input-primary w-full"
placeholder="Focus changes the color"
/>FAQ - Frequently asked questions
Does DaisyUI increase bundle size?
Not significantly. DaisyUI is pure CSS that gets purged by Tailwind in production. The components you use add minimal overhead. In practice it often reduces the bundle, because you use shorter classes (btn instead of 10 utility classes).
Can I use DaisyUI with JavaScript components?
Yes! DaisyUI is CSS only - it does not require any JavaScript. You can add your own logic or use libraries like Headless UI for more complex interactions (dropdown, modal with focus trap, etc.).
How do I customize existing components?
You have three options:
- Add Tailwind classes alongside DaisyUI classes
- Create a custom theme with custom colors
- Override styles in CSS using selectors
Is DaisyUI compatible with Tailwind v4?
Yes! DaisyUI supports Tailwind v4. Check the documentation for configuration details.
How do I implement dark mode?
DaisyUI has built-in support through themes. Set data-theme="dark" on <html> and use the "dark" theme or any other dark theme such as "dracula", "night", "forest".
Summary
DaisyUI is the ideal solution for projects using Tailwind CSS, offering:
- Semantic classes - More readable code, faster writing
- 35+ themes - Easy appearance change for your entire application
- 50+ components - Everything you need for UI
- Zero JavaScript - Works with any framework
- Full compatibility - With Tailwind utilities and responsive design
- 100% free - Open source, MIT License
If you use Tailwind CSS and want to speed up development without sacrificing flexibility, DaisyUI is an excellent choice.