Używamy cookies, żeby zwiększyć Twoje doświadczenia na stronie
CodeWorlds
Powrót do kolekcji
Przewodnik11 min czytania

daisyUI, komponenty i motywy dla Tailwind CSS

daisyUI dodaje do Tailwind CSS nazwane klasy komponentów i 35 motywów. Konfiguracja w CSS, własny motyw, integracja z React, ograniczenia i porównanie z shadcn.

daisyUI, czyli Tailwind bez ściany klas

Tailwind rozwiązał problem nazywania klas, tworząc przy okazji nowy: przycisk opisany dwunastoma klasami użytkowymi wygląda w kodzie jak awaria. daisyUI dokłada warstwę nazw komponentów, więc ten sam przycisk to btn btn-primary, a pod spodem nadal siedzi ten sam Tailwind.

To wtyczka, nie framework. Nie dostajesz komponentów Reacta ani logiki, tylko klasy CSS, więc działa tak samo w Next.js, Vue, Svelte i w zwykłym HTML.

Instalacja i konfiguracja w CSS

Piąta wersja porzuciła plik konfiguracyjny JavaScriptu na rzecz konfiguracji wprost w arkuszu stylów, zgodnie z kierunkiem, w którym poszedł sam Tailwind.

Code
Bash
pnpm add -D daisyui
Code
CSS
@import "tailwindcss";
@plugin "daisyui" {
  themes: light --default, dark --prefersdark, cupcake;
}

Ta zmiana upraszcza więcej, niż wygląda. Motywy, kolory i zmienne żyją w jednym pliku razem z resztą stylów, a nie w konfiguracji obok, więc nie trzeba pilnować spójności między dwoma miejscami.

Znacznik przy nazwie motywu określa jego rolę. Jeden motyw jest domyślny, drugi włącza się przy systemowym ustawieniu ciemnym. Reszta jest dostępna do przełączania ręcznego.

Komponenty i modyfikatory

Nazwa komponentu to klasa bazowa, a wygląd zmieniasz modyfikatorami. Układ jest przewidywalny i po kilku komponentach zgadujesz nazwy poprawnie.

Code
HTML
<button class="btn btn-primary">Zapisz</button>
<button class="btn btn-primary btn-soft">Anuluj</button>
<button class="btn btn-error btn-outline btn-sm">Usuń</button>

<div class="card bg-base-100 shadow-sm">
  <div class="card-body">
    <h2 class="card-title">Tytuł</h2>
    <p>Treść karty.</p>
  </div>
</div>

Piąta wersja dołożyła style miękki i przerywany do przycisków, plakietek i alertów, więc zestaw wariantów pokrywa większość potrzeb bez pisania własnych. Zmniejszono też domyślną wysokość przycisków, pól i przełączników, co zbliżyło wygląd do współczesnych interfejsów, ale przy migracji ze starszej wersji wymaga przejrzenia formularzy.

Wszystkie modyfikatory są responsywne, więc lg:btn-lg działa bez dodatkowej konfiguracji. To drobiazg, który usuwa najczęstszy powód pisania własnych klas.

Ważna zasada: klasy Tailwinda mają pierwszeństwo, więc btn bg-red-500 nadpisze kolor z komponentu. Nie musisz wybierać między jednym a drugim, tylko traktujesz komponenty jako punkt wyjścia i dostrajasz je klasami użytkowymi.

Motywy, czyli największa zaleta

Biblioteka dostarcza trzydzieści pięć gotowych motywów, a przełączenie polega na zmianie jednego atrybutu na elemencie nadrzędnym.

Code
HTML
<html data-theme="dark">

Działa to, bo komponenty nie odwołują się do konkretnych kolorów, tylko do nazw semantycznych: podstawowy, drugorzędny, akcent, tło, tekst na tle. Motyw definiuje wartości tych nazw, a komponenty zmieniają wygląd bez dotykania znaczników.

Własny motyw definiujesz w tym samym pliku CSS, podając kolory w dowolnym formacie. Czwarta wersja Tailwinda przeszła na zmienne CSS i funkcję mieszania kolorów, więc nie ma już przekształcania wartości i nie musisz podawać kolorów w konkretnej przestrzeni.

Code
CSS
@plugin "daisyui/theme" {
  name: "firma";
  default: true;
  --color-primary: oklch(55% 0.2 250);
  --color-primary-content: oklch(98% 0.01 250);
  --color-base-100: oklch(98% 0 0);
  --color-base-content: oklch(21% 0.01 250);
  --radius-box: 0.5rem;
}

Pary kolorów mają tu istotne znaczenie praktyczne. Do każdego koloru tła należy kolor treści na nim, dzięki czemu tekst na przycisku ma kontrast po każdej zmianie palety. Jest jednak haczyk, na którym potyka się większość poradników przepisanych z czwartej wersji: piąta usunęła automatyczne wyliczanie koloru treści z koloru tła. Zmienne --color-primary-content i --color-base-content musisz podać sam, bo w nowym motywie nikt ich za Ciebie nie policzy.

Inaczej jest przy dostrajaniu motywu wbudowanego. Jeśli w bloku podasz nazwę istniejącego motywu i zmienisz w nim dwa kolory, reszta wartości zostanie odziedziczona z oryginału, więc pary zachowają kontrast bez wypisywania całej listy. Nowy motyw od zera to komplet zmiennych, dostrojenie gotowego to kilka linii.

Przełączanie motywu w praktyce

Sam atrybut to za mało, bo trzeba jeszcze zapamiętać wybór i uniknąć mignięcia złym motywem przy pierwszym renderowaniu. Ten drugi problem jest najczęściej zgłaszany i ma jedno rozwiązanie: ustawić atrybut, zanim strona się wyświetli.

Code
HTML
<script>
  const zapisany = localStorage.getItem('motyw')
  const ciemny = window.matchMedia('(prefers-color-scheme: dark)').matches
  document.documentElement.dataset.theme = zapisany ?? (ciemny ? 'dark' : 'light')
</script>

Ten skrypt musi znaleźć się w nagłówku dokumentu i wykonać synchronicznie. Ustawienie motywu w efekcie Reacta jest za późno, bo przeglądarka zdąży narysować stronę w motywie domyślnym i użytkownik zobaczy błysk.

Przy renderowaniu po stronie serwera dochodzi drugi aspekt: serwer nie zna preferencji użytkownika, więc znacznik wysłany z serwera i ten po nawodnieniu muszą się zgadzać. Standardowe wyjście to ustawienie atrybutu skryptem przed nawodnieniem i niewłączanie motywu do stanu komponentu.

Sam przełącznik to zwykły przycisk zapisujący wybór i zmieniający atrybut. Warto dać trzy opcje zamiast dwóch: jasny, ciemny i zgodny z systemem, bo część użytkowników oczekuje, że aplikacja pójdzie za ustawieniem urządzenia.

Zmienne, które warto znać

Poza kolorami motyw ustawia kilka zmiennych sterujących kształtem interfejsu, a ich zmiana daje najwięcej przy najmniejszym nakładzie.

Promień zaokrąglenia jest osobny dla pól, przycisków i kontenerów, więc da się zrobić interfejs z ostrymi kartami i okrągłymi przyciskami bez pisania własnych klas. Skala rozmiaru pozwala zmienić gęstość całego interfejsu jedną wartością, co bywa potrzebne w panelach z dużą ilością danych.

Grubość obramowania i efekt naciśnięcia to pozostałe dwie zmienne warte uwagi. Ustawione raz w motywie działają wszędzie, więc nie trzeba wracać do poszczególnych komponentów.

Praktyczna rada: zmieniaj te wartości w motywie, nigdy w komponentach. Kilka nadpisań rozsianych po projekcie to dokładnie ten stan, którego system motywów miał zapobiec, a wraca on zwykle przy pierwszej prośbie o drugi motyw.

Praca z Reactem

Ponieważ to same klasy CSS, komponenty Reacta piszesz sam i opakowujesz w nie klasy. To bywa uznawane za wadę, a bywa zaletą, zależnie od tego, czego szukasz.

Code
TypeScript
type Props = {
  wariant?: 'primary' | 'ghost' | 'error'
  rozmiar?: 'sm' | 'md' | 'lg'
} & React.ButtonHTMLAttributes<HTMLButtonElement>

export function Przycisk({ wariant = 'primary', rozmiar = 'md', className, ...reszta }: Props) {
  return (
    <button
      className={`btn btn-${wariant} btn-${rozmiar} ${className ?? ''}`}
      {...reszta}
    />
  )
}

Jedna pułapka wymaga uwagi. Tailwind skanuje kod w poszukiwaniu pełnych nazw klas, więc łańcuch budowany z fragmentów może nie trafić do wyjściowego arkusza. Bezpieczniej jest wypisać pełne nazwy w mapie, zamiast sklejać je z kawałków.

Druga sprawa to dostępność. Klasy dają wygląd, ale nie zachowanie, więc okno modalne, lista rozwijana i zakładki wymagają obsługi klawiatury i atrybutów po Twojej stronie. Część komponentów opiera się na natywnych elementach HTML, co pomaga, ale nie zwalnia z przetestowania.

Formularze i elementy interaktywne

Formularze to obszar, w którym biblioteka oszczędza najwięcej kodu, bo pola, etykiety i komunikaty walidacji mają gotowe klasy o spójnych rozmiarach.

Code
HTML
<fieldset class="fieldset">
  <legend class="fieldset-legend">Adres e-mail</legend>
  <input type="email" class="input w-full" placeholder="jan@example.com" required />
  <p class="label">Uzyjemy go wylacznie do potwierdzenia zamowienia.</p>
</fieldset>

Warto zauważyć, że biblioteka opiera się na natywnych mechanizmach przeglądarki wszędzie tam, gdzie to możliwe. Walidacja korzysta z atrybutów HTML, więc komunikat o błędnym adresie pojawia się bez JavaScriptu, a stan pola odzwierciedla się w stylach automatycznie.

To samo podejście widać w komponentach rozwijanych i oknach modalnych, gdzie wykorzystywane są elementy natywne zamiast pól ukrytych i klas przełączanych skryptem. Zysk jest podwójny: mniej kodu i lepsze zachowanie domyślne przy nawigacji klawiaturą.

Granica tego podejścia leży przy komponentach, których przeglądarka nie ma. Pole z podpowiedziami, wielopoziomowe menu albo tabela z sortowaniem wymagają JavaScriptu i to Ty go dostarczasz. Biblioteka daje wygląd, resztę składasz sam albo bierzesz z osobnej biblioteki zachowań.

Rozmiar wyjściowego arkusza

Częste pytanie brzmi, ile ta warstwa kosztuje w kilobajtach. Odpowiedź jest korzystna, bo generowane są wyłącznie klasy faktycznie użyte w kodzie, tak samo jak w samym Tailwindzie.

Motywy działają inaczej: każdy włączony motyw dokłada zestaw zmiennych do arkusza, niezależnie od tego, czy ktoś go użyje. Da się to zmierzyć, bo biblioteka publikuje arkusz z kompletem motywów. Wszystkie trzydzieści pięć waży 38 kB zminifikowanego CSS i 6,4 kB po spakowaniu gzipem, czyli mniej więcej kilobajt na motyw. Włączenie kompletu w projekcie, który korzysta z dwóch, dokłada zatem około trzydziestu sześciu kilobajtów w źródle i kilka kilobajtów na łączu.

Rozsądna praktyka to wymienienie w konfiguracji wyłącznie tych motywów, które są w interfejsie do wyboru. Jeśli aplikacja ma jasny i ciemny, wpisz dwa, a resztę zostaw wyłączoną. Pozostałe motywy warto natomiast włączyć na czas pracy nad wyglądem, bo są dobrym punktem odniesienia przy dobieraniu własnej palety.

daisyUI kontra alternatywy

NarzędzieMocna stronaSłabośćKiedy wybrać
daisyUIMotywy, krótki kod, działa w każdym frameworkuBrak logiki i dostępności w komplecieSzybki projekt, wiele motywów, dowolny framework
shadcn/uiKod komponentów u Ciebie, dostępność w komplecieTylko React, więcej kodu do utrzymaniaAplikacja w Reakcie z wymogami dostępności
HeroUIGotowe komponenty z zachowaniem i animacjamiPrzywiązanie do bibliotekiProjekt, w którym liczy się czas do wdrożenia
FlowbiteDuży zestaw gotowych blokówCzęść zawartości płatnaPanele administracyjne i strony marketingowe

Podstawowa różnica wobec shadcn polega na tym, gdzie mieszka kod komponentu. Tutaj komponent to klasa CSS z biblioteki, więc aktualizacja przychodzi z paczką. Tam komponent kopiujesz do projektu i od tej chwili jest Twój, razem z obowiązkiem utrzymania.

Zapis „działa w każdym frameworku" wymaga doprecyzowania, bo brzmi mocniej, niż znaczy. Klasy nie wiedzą nic o frameworku, więc te same nazwy wkleisz w Svelte, w Vue i w SolidJS, gdzie komponent uruchamia się raz i model reaktywności nie przypomina Reactowego. Nie dostajesz jednak żadnych komponentów do zaimportowania, więc opakowanie klas i cała obsługa klawiatury spada na Ciebie w każdym z tych miejsc osobno. Przewagą jest to, że przy zmianie frameworka warstwa wyglądu zostaje, a nie to, że masz mniej pracy.

Wybór zależy od tego, czy potrzebujesz sterowania szczegółami. Do panelu, gdzie liczy się tempo i spójność, klasy z biblioteki wygrywają. Do produktu z własnym systemem projektowym i wymogami dostępności prędzej czy później i tak dojdziesz do własnych komponentów.

Spójność w większym zespole

Biblioteka klas rozwiązuje problem wyglądu, ale nie problem dyscypliny. W projekcie, nad którym pracuje kilka osób, ta sama karta prędzej czy później powstanie w trzech odmianach, bo każdy doda własne klasy użytkowe.

Najprostsze zabezpieczenie to opakowanie najczęstszych układów we własne komponenty i traktowanie klas biblioteki jako szczegółu implementacyjnego. Wtedy zmiana wyglądu karty w całej aplikacji to jedno miejsce, a nie wyszukiwanie po repozytorium.

Drugie to reguła w przeglądzie kodu: nowe klasy kolorów zapisane na sztywno nie wchodzą. Kolor pochodzi z motywu albo z nazwy semantycznej, a nie z palety Tailwinda, bo inaczej drugi motyw przestaje działać w połowie interfejsu.

Trzecie to lista komponentów, których używamy, spisana w repozytorium. Biblioteka daje kilkadziesiąt możliwości, z których projekt potrzebuje kilkunastu, a reszta to miejsce na niepotrzebną różnorodność.

Kiedy to nie jest właściwy wybór

Projekt z gotowym systemem projektowym i biblioteką komponentów nie zyska nic, a dołoży kolejną warstwę klas mieszającą się z istniejącymi.

Aplikacja z wysokimi wymogami dostępności potrzebuje komponentów z obsługą klawiatury i czytników ekranu w komplecie. Same klasy tego nie dają, więc trzeba to napisać albo wziąć bibliotekę zachowań.

Trzeci przypadek to zespół, który nie używa Tailwinda. Wtyczka jest jego rozszerzeniem, więc bez niego nie ma o czym mówić.

Czwarty to produkt, w którym wygląd jest wyróżnikiem. Gotowe komponenty przyspieszają pracę i jednocześnie sprawiają, że interfejs przypomina setki innych zbudowanych z tego samego zestawu. Do panelu wewnętrznego to nie ma znaczenia, do strony sprzedażowej bywa problemem, choć własny motyw z odmienną paletą i innym promieniem zaokrągleń zmienia odbiór bardziej, niż można przypuszczać.

Typowe błędy

Pierwszy to sklejanie nazw klas z fragmentów. Tailwind nie znajdzie takiej klasy przy skanowaniu i styl nie trafi do wyjścia, a błąd pojawi się dopiero w wersji produkcyjnej.

Drugi to nadpisywanie kolorów komponentów klasami zamiast zmianą motywu. Efekt jest ten sam na jednej stronie, ale przy przełączeniu motywu wszystko się rozjeżdża, bo wartości zapisane na sztywno nie reagują.

Trzeci to zakładanie, że komponent jest dostępny. Okno modalne bez pułapki na ognisko i bez zamykania klawiszem ucieczki wygląda dobrze i nie działa dla części użytkowników.

Czwarty to mieszanie wielu bibliotek komponentów naraz. Dwa systemy klas w jednym projekcie prowadzą do konfliktów specyficzności, których nikt później nie chce rozplątywać.

Piąty to migracja ze starszej wersji bez przejrzenia formularzy. Zmiana domyślnych rozmiarów pól w piątej wersji jest niewielka na jednym elemencie, a widoczna w gęstym formularzu.

FAQ

Czy daisyUI działa z Tailwind CSS 4?

Tak, piąta wersja biblioteki jest przeznaczona właśnie dla czwartej wersji Tailwinda. Konfigurację przeniesiono z pliku JavaScriptu do arkusza stylów, a kolory korzystają ze zmiennych CSS, więc można podawać je w dowolnym formacie.

daisyUI czy shadcn/ui?

daisyUI wybierzesz, gdy chcesz gotowe klasy, wiele motywów i pracę w dowolnym frameworku. shadcn/ui wybierzesz, gdy pracujesz w Reakcie, potrzebujesz dostępności w komplecie i chcesz mieć kod komponentów w swoim repozytorium.

Czy komponenty są dostępne dla czytników ekranu?

Częściowo, bo część opiera się na natywnych elementach HTML. Biblioteka dostarcza jednak wygląd, a nie zachowanie, więc pułapkę na ognisko, obsługę klawiatury i atrybuty pomocnicze trzeba dodać samodzielnie albo sięgnąć po bibliotekę zachowań.

Ile motywów jest w komplecie?

Trzydzieści pięć gotowych, plus możliwość zdefiniowania własnych w tym samym pliku CSS. Przełączanie polega na zmianie jednego atrybutu, a komponenty reagują automatycznie, bo odwołują się do nazw semantycznych, a nie do konkretnych kolorów.

Czy to spowalnia stronę?

Nie, bo to zwykły CSS generowany razem z Tailwindem, a nieużywane klasy nie trafiają do wyjścia. Wtyczka nie dokłada JavaScriptu, więc rozmiar paczki po stronie przeglądarki nie rośnie.

Dokumentacja stoi na stronie projektu, a opis zmian w piątej wersji znajdziesz w notatkach wydania.