• Aplikacje webowe
  • Bootstrap spinner - jak wdrożyć ładowanie i dostępność

Bootstrap spinner - jak wdrożyć ładowanie i dostępność

Bruno Krawczyk 22 sierpnia 2026
Widok aplikacji "Cyfrowa Kancelaria" z rozliczeniem roboczym. Na ekranie widać pozycje rozliczenia, w tym "Doradztwo prawne" z wartością netto 500,00 i brutto 615,00. W dolnej części ekranu widoczny jest przycisk "Wystaw dokument rozliczenia", który mo...

Spis treści

Gdy użytkownik klika przycisk, a aplikacja przez kilka sekund czeka na odpowiedź API, pusty ekran szybko wygląda jak błąd. Komponent znany jako bootstrap spinner pozwala jasno pokazać, że operacja nadal trwa. Pokażę, jak używać go w Bootstrapie, kiedy wybrać obracający się obrys, jak połączyć wskaźnik z JavaScriptem oraz o czym pamiętać przy dostępności.

Najważniejsze informacje o wskaźniku ładowania

  • Dwa warianty to spinner z obramowaniem oraz animowany spinner typu grow.
  • Sam komponent działa dzięki HTML i CSS, ale jego widoczność trzeba przełączać kodem aplikacji.
  • Do komunikatów dla czytników ekranu użyj role="status" oraz klasy visually-hidden.
  • W przyciskach najlepiej stosować mały spinner i czasowo zablokować przycisk.
  • Spinner informuje o oczekiwaniu, ale nie pokazuje rzeczywistego postępu zadania.

Różne warianty wskaźników ładowania, w tym bootstrap spinner, stacked spinner, dotted spinner i bubble spinner, prezentowane na białych kartach.

Czym jest spinner w Bootstrapie i jak działa

Spinner to wizualny sygnał, że aplikacja wykonuje operację, na przykład pobiera dane, zapisuje formularz albo czeka na odpowiedź z usługi Azure. W Bootstrapie jest to element z gotową animacją CSS, więc do samego wyświetlenia nie potrzebujesz dodatkowej biblioteki JavaScript.

Najprostszy wariant wykorzystuje klasę spinner-border. Wewnątrz umieszczam tekst ukryty wizualnie, ale dostępny dla czytników ekranu.

Ładowanie...

To ważne rozróżnienie. Bootstrap odpowiada za wygląd i animację, natomiast logika aplikacji decyduje, kiedy spinner ma się pojawić i zniknąć. W praktyce najczęściej kontroluję go klasą d-none, która ukrywa element za pomocą display: none.

Dwa warianty animacji do różnych sytuacji

Klasa spinner-border pokazuje obracający się pierścień. To mój domyślny wybór przy ładowaniu tabel, list i całych sekcji, bo jest czytelny, neutralny i nie zajmuje dużo miejsca.

Drugi wariant to spinner-grow. Element nie obraca się, tylko cyklicznie zwiększa i zmniejsza swoją skalę. Dobrze pasuje do krótkich komunikatów lub sytuacji, w których animacja ma być bardziej widoczna.

Wariant Klasa Najlepsze zastosowanie
Obramowanie spinner-border Ładowanie danych, tabel i widoków
Rozrastający się element spinner-grow Krótki komunikat lub mały komponent
Ładowanie...

Oba warianty dziedziczą kolor z właściwości currentColor, dlatego można je łatwo zmienić klasami tekstowymi, na przykład text-primary, text-success albo text-danger. Nie używałbym jednak samego koloru jako komunikatu. Użytkownik powinien otrzymać również tekst albo semantyczną informację dla technologii asystujących.

Jak pokazać spinner podczas żądania API

Najczęstszy scenariusz wygląda tak: użytkownik klika przycisk, aplikacja wysyła żądanie, przycisk zostaje zablokowany, a spinner zastępuje jego zawartość. Po zakończeniu operacji przywracam pierwotny tekst niezależnie od tego, czy żądanie zakończyło się sukcesem, czy błędem.



const button = document.querySelector("#loadButton");
const spinner = button.querySelector(".spinner-border");
const label = button.querySelector(".button-label");
const loadingLabel = button.querySelector(".loading-label");
const result = document.querySelector("#result");

button.addEventListener("click", async () => {
  button.disabled = true;
  spinner.classList.remove("d-none");
  label.textContent = "Pobieranie...";
  loadingLabel.textContent = "Pobieranie danych...";

  try {
    const response = await fetch("/api/orders");

    if (!response.ok) {
      throw new Error("Nie udało się pobrać danych");
    }

    const orders = await response.json();
    result.textContent = `Liczba zamówień: ${orders.length}`;
  } catch (error) {
    result.textContent = "Wystąpił błąd. Spróbuj ponownie.";
  } finally {
    button.disabled = false;
    spinner.classList.add("d-none");
    label.textContent = "Pobierz dane";
  }
});

Blokada przycisku jest równie istotna jak sama animacja. Bez niej użytkownik może wysłać kilka identycznych żądań, a backend otrzyma powielone operacje. W aplikacjach .NET ma to szczególne znaczenie przy zapisie formularzy, tworzeniu płatności lub uruchamianiu kosztownych procesów w tle.

Przeczytaj również: ng command not found? Jak naprawić Angular CLI

Spinner dla całej sekcji

Gdy ładowanie dotyczy większego fragmentu interfejsu, umieszczam wskaźnik w kontenerze i oznaczam ten obszar jako zajęty. Atrybut aria-busy="true" przekazuje technologiom asystującym, że zawartość jest właśnie aktualizowana.

Ładowanie zamówień...

Po zakończeniu pobierania ustawiam aria-busy="false" i zastępuję spinner właściwą zawartością. To lepszy wzorzec niż zasłanianie całej strony, chyba że użytkownik rzeczywiście nie może wykonać żadnej innej czynności.

Rozmiar, kolor i pozycjonowanie bez własnego CSS

Bootstrap daje kilka prostych narzędzi do dopasowania wskaźnika. Mały wariant uzyskasz przez spinner-border-sm lub spinner-grow-sm. Używam ich głównie w przyciskach, polach formularza i wierszach tabeli.


  Ładowanie...

Do wyśrodkowania wystarczą klasy narzędziowe. Przy pojedynczym komponencie sprawdzi się text-center, a przy bardziej rozbudowanym układzie flex.

Ładowanie...

Standardowy rozmiar jest dobrym punktem wyjścia, ale nie warto powiększać spinnera bez powodu. Bardzo duża animacja przy krótkim żądaniu wygląda ciężko i może sugerować poważną awarię. Jeżeli potrzebujesz własnych wymiarów, możesz użyć zmiennych CSS albo zwykłego width i height, lecz dla większości ekranów gotowe klasy są wystarczające.

Dostępność i ograniczenie ruchu

Sam obracający się pierścień nie jest komunikatem dla osoby, która nie widzi animacji. Dlatego dodaję role="status" oraz tekst w klasie visually-hidden. Tekst powinien opisywać konkretną operację, na przykład „Zapisywanie profilu” zamiast ogólnego „Loading”.

Zapisywanie profilu...

Nie używaj roli progressbar, jeśli nie znasz rzeczywistego postępu. Spinner oznacza oczekiwanie o nieokreślonym czasie, a pasek postępu sugeruje konkretną wartość, na przykład 65 procent. W tym drugim przypadku lepszy będzie element lub odpowiednio opisany komponent z wartościami ARIA.

Bootstrap uwzględnia preferencję prefers-reduced-motion, więc animacja może zostać spowolniona dla osób, które ograniczają ruch w systemie. To rozsądne ustawienie domyślne, ale i tak testuję interfejs bez patrzenia na ekran oraz z klawiaturą. Spinner nie powinien blokować dostępu do komunikatu o błędzie ani odbierać fokusu użytkownikowi.

Błędy, przez które spinner przestaje pomagać

Najczęstszy problem nie polega na złej klasie CSS, tylko na nieprzemyślanym cyklu życia komponentu. Wskaźnik pojawia się, ale nie znika po wyjątku, albo użytkownik widzi animację tak krótko, że interfejs zaczyna migotać.

  • Brak bloku finally sprawia, że po błędzie przycisk może pozostać zablokowany.
  • Spinner bez komunikatu jest nieczytelny dla części użytkowników.
  • Ukrywanie całej strony utrudnia pracę, gdy ładowana jest tylko jedna tabela.
  • Brak limitu czasu powoduje wieczne oczekiwanie przy niedostępnym API.
  • Wielokrotne żądania pojawiają się, gdy przycisk nie jest blokowany na czas operacji.

Jeśli odpowiedź może trwać dłużej niż kilka sekund, sam spinner zaczyna być zbyt ubogą informacją. W takim przypadku dodaj komunikat, możliwość anulowania albo prawdziwy pasek postępu. Moim zdaniem animacja powinna uspokajać użytkownika, a nie maskować brak kontroli nad stanem aplikacji.

Mały komponent, który dobrze komunikuje stan aplikacji

Spinner w Bootstrapie najlepiej traktować jako element większego przepływu, a nie ozdobę dodaną do przycisku. Powinien mieć jasno określony moment startu, zakończenie obsługiwane także po błędzie, opis dla czytników ekranu i rozmiar dopasowany do miejsca użycia.

Do prostego ładowania danych wystarczy spinner-border, mały wariant sprawdzi się w przycisku, a aria-busy pomoże opisać aktualizowaną sekcję. Gdy operacja ma znany procent wykonania, wybierz pasek postępu zamiast udawać, że nieokreślone oczekiwanie jest pełną informacją.

Dobrze wdrożony wskaźnik nie przyspieszy API, ale sprawi, że aplikacja będzie odbierana jako przewidywalna i responsywna. Właśnie ta drobna różnica często decyduje o tym, czy użytkownik kliknie ponownie, czy spokojnie poczeka na wynik.

FAQ - Najczęstsze pytania

spinner-border pokazuje obracający się pierścień i dobrze sprawdza się przy ładowaniu tabel, list oraz całych widoków. spinner-grow zwiększa i zmniejsza swoją skalę, dlatego pasuje do krótkich komunikatów i małych komponentów.

Po kliknięciu przycisku należy go zablokować przez ustawienie disabled, usunąć klasę d-none ze spinnera i zmienić etykietę na komunikat o pobieraniu. Po sukcesie lub błędzie trzeba przywrócić stan początkowy w bloku finally, ponownie odblokować przycisk i ukryć spinner.

Spinner powinien mieć role="status" oraz opisowy tekst w elemencie z klasą visually-hidden, na przykład „Zapisywanie profilu”. Przy ładowaniu większej sekcji warto dodatkowo użyć aria-busy="true", a po zakończeniu ustawić aria-busy="false".

Spinner informuje o oczekiwaniu, ale nie pokazuje rzeczywistego postępu. Jeśli znasz procent wykonania zadania, użyj elementu progress albo komponentu z odpowiednimi wartościami ARIA; przy nieokreślonym czasie trwania lepszy jest spinner.

Oceń artykuł

Ocena: 0.00 Liczba głosów: 0

Tagi

javascript
aria
api
bootstrap
spinner
Autor Bruno Krawczyk
Bruno Krawczyk
Mam na imię Bruno i od 8 lat zgłębiam tajniki programowania w ekosystemie .NET, chmury Azure oraz sztucznej inteligencji. Moja przygoda z technologią zaczęła się od ciekawości, jak złożone systemy mogą ułatwiać codzienne życie i rozwiązywać realne problemy. Dziś moją misją jest dzielenie się tą wiedzą, starając się przybliżyć nawet najbardziej skomplikowane zagadnienia w sposób zrozumiały i przystępny dla każdego. W moich artykułach na kursdotnet.pl skupiam się na praktycznych aspektach, analizuję najnowsze trendy i weryfikuję informacje, aby dostarczyć Wam treści, które są nie tylko dokładne i aktualne, ale przede wszystkim użyteczne w Waszej własnej ścieżce rozwoju technologicznego.

Udostępnij artykuł

Napisz komentarz