Przejdź do treści

Skeleton

Stabilny

Element 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.json

Uż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.

FakturaKlientStatusKwota

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.

Ładowanie członków zespołu…
<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 loading komponentu 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.

Dobrze.Proste kształty w prawdziwym układzie. Nic się nie przesuwa, gdy pojawia się treść.
Źle.Ogólny blok nie pasuje do treści i po załadowaniu powoduje skok układu.

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 — atrybutem aria-busy="true" i wizualnie ukrytym komunikatem.
  • Gdy treść się pojawi, usuń aria-busy. Dzięki aria-live="polite" na kontenerze nowa treść zostanie odczytana bez przenoszenia fokusu.
  • Połysk animuje wyłącznie translate na pseudoelemencie, więc nigdy nie wymusza przeliczenia układu. Przy prefers-reduced-motion przesuwający się refleks znika, a szkielet pozostaje nieruchomą plamą koloru.
  • Wypełnienia szkieletów używają tokenu muted i 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>data-slot="skeleton"aria-hidden="true". Przyjmuje wszystkie propsy elementu div.

PropTypDomyślnie
className

Rozmiar i kształt. Ustaw szerokość, wysokość i zaokrąglenie zgodne z zastępowaną treścią.

stringBrak wartości domyślnej
style

Przydaje się przy wyliczanych szerokościach, np. różnej długości linii na liście.

CSSPropertiesBrak wartości domyślnej