Skeleton
StabilnyElement zastępczy, który podczas ładowania pokazuje kształt treści, dzięki czemu strona spokojnie się układa, zamiast skakać.
Instalacja
$ pnpm dlx shadcn@latest add https://www.prfct.dev/r/skeleton.jsonUżycie
import { Skeleton } from "@/components/ui/skeleton"<Skeleton className="h-4 w-48" />Szkielet to kształt i nic więcej. Nadaj mu rozmiar i zaokrąglenie treści, którą zastępuje — rounded-full dla awatarów, rounded-lg dla przycisków — a resztę zostaw układowi.
Przykłady
Karta
Odwzoruj strukturę prawdziwego komponentu. Użycie Card i jego części gwarantuje, że szkielet zajmie dokładnie tyle miejsca, ile zajmie załadowana karta.
Lista
Różnicuj długość linii. Identyczne paski wyglądają jak deseń, a nie jak treść, przez co podmiana na prawdziwy tekst razi bardziej.
Tabela
Zostaw prawdziwy nagłówek i zastąp tylko komórki. Ludzie mogą czytać nazwy kolumn, zanim dotrą dane.
Stan ładowania
Podmieniaj szkielety na treść w tym samym miejscu. Na czas ładowania oznacz obszar atrybutem aria-busy i dodaj wizualnie ukryty komunikat, żeby użytkownicy czytników ekranu wiedzieli, że coś jest w drodze.
<div aria-busy={loading} aria-live="polite">
{loading && <span className="sr-only">Loading team members…</span>}
{loading ? <MembersSkeleton /> : <MembersList />}
</div>Wytyczne
Kiedy używać
- Przy pierwszym ładowaniu treści o znanym układzie: kart, list, tabel, profili.
- Przy treści ładowanej częściami, żeby każda sekcja mogła pojawić się niezależnie od pozostałych.
Kiedy nie używać
- Przy oczekiwaniu krótszym niż około sekunda — szkielet tylko mignie, a strona wyda się wolniejsza. Pokaż go z opóźnieniem albo nie pokazuj niczego.
- Przy akcjach takich jak zapisywanie czy wysyłanie — użyj stanu
loadingkomponentu Button albo komponentu Spinner. - Przy zadaniach, których postęp da się zmierzyć — użyj komponentu Progress.
- Przy treści, której układu nie da się przewidzieć. Błędny szkielet jest gorszy niż żaden.
Odwzoruj układ, nie piksele
Szkielet powinien zajmować tyle samo miejsca co treść, którą zastępuje, żeby nic się nie przesunęło, gdy dotrą dane. Nie potrzebuje każdego szczegółu: wystarczy awatar, linia na nazwę i krótsza linia na metadane.
Dostępność
- Każdy szkielet renderuje
aria-hidden="true": elementy zastępcze nic nie znaczą dla czytnika ekranu. Zamiast tego opisz stan ładowania na kontenerze — atrybutemaria-busy="true"i wizualnie ukrytym komunikatem. - Gdy treść się pojawi, usuń
aria-busy. Dziękiaria-live="polite"na kontenerze nowa treść zostanie odczytana bez przenoszenia fokusu. - Połysk animuje wyłącznie
translatena pseudoelemencie, więc nigdy nie wymusza przeliczenia układu. Przyprefers-reduced-motionprzesuwający się refleks znika, a szkielet pozostaje nieruchomą plamą koloru. - Wypełnienia szkieletów używają tokenu
mutedi nigdy nie przekazują informacji, więc nie dotyczą ich wymagania kontrastu — mimo to pozostają widoczne w obu motywach i w trybie wysokiego kontrastu.
Dokumentacja API
Skeleton
Renderuje <div> z data-slot="skeleton" i aria-hidden="true". Przyjmuje wszystkie propsy elementu div.