Przegląd dostępnych bibliotek komponentów
które naprawdę przechodzą audyt axe
Zainstalowaliśmy siedem spośród najczęściej pobieranych bibliotek komponentów React wydanych w 2026 roku, podłączyliśmy każdą do świeżej aplikacji Next.js 15 / React 19 / TypeScript i uruchomiliśmy ten sam zestaw audytowy dla każdego komponentu bazowego: axe-core 4.11 w bezgłowym Chromium, ręczne przejścia klawiaturą, NVDA na Windows, VoiceOver na macOS oraz przejście Lighthouse-accessibility dla kosztu rozmiaru pakietu.
1. Zestaw audytowy
Każda biblioteka była instalowana w tym samym szkielecie React 19 / Next.js 15 / TypeScript 5.5 przy użyciu bieżącej ścieżki instalacji zalecanej przez dostawcę: npm i dla pakietowanych bibliotek (Radix UI primitives, Headless UI 2.x, Mantine 8.x, Chakra UI v3, Ark UI, React Aria Components) oraz interfejsu CLI shadcn dla shadcn/ui — który kopiuje źródło do components/ui zamiast dostarczać pakiet. Następnie zamontowano stronę demonstracyjną eksponującą pełny zestaw interaktywnych komponentów bazowych każdej biblioteki w ich domyślnym, niemodyfikowanym stanie, bez nadpisywania motywów i bez niestandardowych wrapperów.
Zestaw audytowy działał w trzech przejściach. Pierwsze przejście to automatyczny skan axe-core 4.11 przez Playwright względem wyrenderowanego DOM, z pełnym zestawem reguł WCAG 2.2 AA i włączonymi regułami eksperymentalnymi. Drugie przejście to ręczne przejście klawiaturą: tab, shift-tab, klawisze strzałek, escape, enter, spacja oraz home/end względem każdego komponentu bazowego, oceniane względem oczekiwanego kontraktu klawiaturowego WAI-ARIA Authoring Practices Guide. Trzecie przejście to dymny test czytnikiem ekranu z NVDA 2025.1 na Chrome i Safari/VoiceOver na macOS Sonoma, sprawdzający ogłoszenie roli, dostępną nazwę i ogłoszenie stanu przy interakcji użytkownika.
Wybrano jedenaście wzorców ARIA jako obszar macierzy, ponieważ to wzorce pojawiające się w interfejsach produktowych, które są pozywane: dialog, alert dialog, combobox, listbox, menu, menubar, tabs, accordion, tooltip, switch i slider. Biblioteka, która dobrze radzi sobie z przyciskami i nagłówkami, ale dostarcza zepsuty combobox, to biblioteka, która nie przejdzie audytu za pierwszym razem, gdy prawdziwy użytkownik spróbuje przefiltrować listę klientów.
Zaliczenie axe oznacza zero naruszeń którejkolwiek reguły w tagu WCAG 2.2 AA oraz tagu eksperymentalnym, wyrenderowane z domyślnymi właściwościami i udokumentowanym przykładem użycia biblioteki. Nie oznacza, że biblioteka jest niezawodna — axe wykrywa około połowy wszystkich błędów WCAG — ale biblioteka, która nie może zaliczyć axe we własnym demo, nie zaliczy go nigdzie indziej.
„Stan domyślny to jedyny stan, który większość zespołów inżynierskich kiedykolwiek widzi. Jeśli biblioteka dostarcza zepsuty stan domyślny, zepsuty stan domyślny trafia do produkcji.“
2. Siedem bibliotek obok siebie
Trzy z siedmiu bibliotek — Radix UI, React Aria Components i Ark UI — zaliczyły axe na każdym komponencie bazowym w stanie domyślnym bez konieczności nadpisywania. Headless UI zaliczyło na wszystkich poza komponentem menu, gdzie brakujący aria-activedescendant na wyzwalaczu menu w stylu listbox wygenerował jedno naruszenie. shadcn/ui — będąca cienką warstwą na Radix UI — zaliczyła na każdym dostarczanym komponencie bazowym, ale zastrzeżenie jest takie, że dostarcza tylko około dwóch trzecich powierzchni Radix, a luki (combobox, listbox, menubar) to dokładnie wzorce, w których dostawcy najczęściej popełniają błędy dostępności, gdy ręcznie je tworzą.
Mantine i Chakra UI v3 to dwie biblioteki, w których domyślne ustawienia generowały naruszenia axe. Combobox, switch i slider Mantine generowały co najmniej jedno naruszenie axe w udokumentowanym przykładzie użycia, głównie wokół brakujących dostępnych nazw na podległym elemencie wejściowym. Chakra UI v3 — która w architekturze opartej na maszynach stanów Zag przeszła na koniec 2024 roku — naprawiła wiele problemów z v2, ale nadal dostarcza tooltip uruchamiany tylko przy najechaniu kursorem, co jest naruszeniem reguły 1.4.13 Treść na wskazanie lub fokus w axe i ryzykiem pułapki klawiaturowej w trybie wirtualnym czytnika ekranu.
3. Macierz pokrycia wzorców
Siatka jedenastu wzorców poniżej to główny materiał referencyjny. Zielona komórka oznacza, że biblioteka dostarcza komponent bazowy natywnie i zalicza axe w stanie domyślnym. Żółta komórka oznacza, że biblioteka dostarcza komponent bazowy, ale w udokumentowanym przykładzie użycia wystąpiło co najmniej jedno naruszenie axe, luka w kontrakcie klawiaturowym lub luka w obsłudze czytnika ekranu. Szare „N/A“ oznacza, że biblioteka nie dostarcza danego komponentu bazowego — w przypadku shadcn/ui to trzy wzorce, dla których należałoby importować Radix bezpośrednio lub podłączyć rozwiązanie zewnętrzne.
| Wzorzec | Radix | React Aria | Ark UI | shadcn | Headless | Mantine | Chakra v3 |
|---|---|---|---|---|---|---|---|
| Dialog | Zalicza | Zalicza | Zalicza | Zalicza | Zalicza | Zalicza | Zalicza |
| Alert dialog | Zalicza | Zalicza | Zalicza | Zalicza | Zalicza | Częściowo | Zalicza |
| Combobox | Zalicza | Zalicza | Zalicza | N/A | Zalicza | Częściowo | Zalicza |
| Listbox | Zalicza | Zalicza | Zalicza | N/A | Zalicza | Zalicza | Zalicza |
| Menu | Zalicza | Zalicza | Zalicza | Zalicza | Częściowo | Zalicza | Zalicza |
| Menubar | Zalicza | Zalicza | Zalicza | N/A | N/A | Zalicza | Zalicza |
| Tabs | Zalicza | Zalicza | Zalicza | Zalicza | Zalicza | Zalicza | Zalicza |
| Accordion | Zalicza | Zalicza | Zalicza | Zalicza | Zalicza | Zalicza | Zalicza |
| Tooltip | Zalicza | Zalicza | Zalicza | Zalicza | Zalicza | Zalicza | Częściowo |
| Switch | Zalicza | Zalicza | Zalicza | Zalicza | Zalicza | Częściowo | Zalicza |
| Slider | Zalicza | Zalicza | Zalicza | Zalicza | Zalicza | Częściowo | Zalicza |
Szara komórka N/A to nie błąd — to pytanie o pozyskanie. Zasada shadcn/ui polega na tym, że kopiuje się potrzebny kod źródłowy z wyselekcjonowanego zestawu, a następnie dostarcza; dla trzech wzorców N/A albo importuje się komponent bazowy Radix bezpośrednio, albo pobiera z siostrzanego rejestru. Ryzyko w shadcn/ui nie leży w dostarczanych komponentach — te dziedziczą zgodność Radix — lecz w komponentach, których nie dostarcza, gdzie zespoły kończą na ręcznym tworzeniu comboboxa o 1:00 w nocy, żeby zdążyć na deadline.
4. Kontrakty klawiaturowe, które się psują
Najbardziej powtarzalnym błędem w bibliotekach nie był brakujący atrybut aria — był to kontrakt klawiaturowy, który prawie zgadzał się z APG, a potem rozmijał się o jeden klawisz. Combobox Mantine nie reaguje na Home i End zgodnie ze specyfikacją APG. Tooltip Chakra v3 nie może być odrzucony przez Escape, gdy był otwierany przy najechaniu. Komponent menu Headless UI zwija się po pierwszym ArrowDown zamiast umieszczać fokus na pierwszym elemencie, ponieważ implementacja traktuje wyzwalacz jako aktywnego potomka przy otwieraniu.
To nie są egzotyczne przypadki brzegowe. To wzorce, po które użytkownik czytnika ekranu sięga w pierwszej minucie. Poniższe porównanie zestawia ten sam interfejs API comboboxa napisany na dwa sposoby — raz z domyślnymi ustawieniami Mantine, raz z React Aria Components — aby pokazać, jak wygląda kontrakt klawiaturowy, gdy jest traktowany jako specyfikacja, a nie jako element dopracowania.
<Combobox
store={combobox}
onOptionSubmit={(v) => setValue(v)}
>
<Combobox.Target>
<InputBase
// brak połączenia aria-controls z polem wejściowym
// klawisze Home/End nie przenoszą fokusa do listboxa
// ArrowDown otwiera, ale nie umieszcza fokusa na elemencie 1
value={value}
onChange={(e) => setValue(e.currentTarget.value)}
/>
</Combobox.Target>
<Combobox.Dropdown>
{/* elementy listboxa są tutaj renderowane */}
</Combobox.Dropdown>
</Combobox><ComboBox aria-label="Filtruj klientów">
<Label>Klient</Label>
<Input />
<Popover>
<ListBox>
<ListBoxItem>Alpha</ListBoxItem>
<ListBoxItem>Bravo</ListBoxItem>
<ListBoxItem>Charlie</ListBoxItem>
</ListBox>
</Popover>
</ComboBox>
// aria-controls, aria-activedescendant,
// aria-expanded, role=combobox, Home/End,
// PageUp/PageDown, Escape: wszystkie podłączone domyślnie.Biblioteki wywodzące swoje kontrakty klawiaturowe z opublikowanej specyfikacji — React Aria z WAI-ARIA APG, Ark UI z maszyn stanów Zag.js — dostarczają te kontrakty jako jedyne możliwe zachowanie komponentu. Biblioteki traktujące kontrakt klawiaturowy jak listę funkcji dostarczają około 80% i zostawiają ostatnie 20% jako „przyszłą pracę“. Te ostatnie 20% to dokładnie klawisze, które użytkownicy technologii wspomagających naciskają najczęściej.
5. Koszt dostępności w rozmiarze pakietu
Najczęstszym zarzutem wobec wyboru ścisłych bibliotek jest rozmiar pakietu. Audyt nie potwierdził tego zarzutu. Komponenty bazowe Radix UI są tree-shakeable per-komponent i mają ok. 7–15 KB skompresowanych gzipem każdy; React Aria Components jest cięższy — ok. 95 KB skompresowanych gzipem dla całego pakietu — ale też tree-shakeable. Headless UI jest najlżejszy: ok. 25 KB skompresowanych gzipem dla całego pakietu. Mantine i Chakra v3 to biblioteki z bateriami w zestawie i dostarczają system stylów w tym samym pakiecie, przez co domyślnie mieszczą się w zakresie ok. 180–220 KB skompresowanych gzipem.
Kompromis jest realny, ale mniejszy niż sugeruje dyskurs. Combobox niezgodny z klawiszami Home i End kosztuje mniej więcej tyle samo bajtów co zgodny. Wybór to nie „dostępność kontra wydajność“ — to „zachowanie wywodzone ze specyfikacji kontra zachowanie ręcznie wbudowane“, a wersja ręcznie wbudowana jest częściej tą większą, bo niesie własną ad-hoc maszynę stanów.
6. Playbook wyboru stacku
Jeśli produkt to aplikacja korporacyjna z wymogiem dostępności wynikającym z zamówień publicznych, należy wybrać React Aria Components.
To jedyna audytowana biblioteka, której interfejsy API są jawnie wywodzone z WAI-ARIA Authoring Practices Guide. Pakiet jest cięższy niż Radix, ale historia zaliczenia audytu przed recenzentem VPAT jest najczystsza, bo każdy komponent bazowy ma opublikowane twierdzenie o zgodności.
Jeśli zespół jest właścicielem własnego systemu projektowego, należy wybrać Radix UI (i opcjonalnie shadcn/ui na jego bazie).
Radix dostarcza niestylowane, zgodne ze specyfikacją komponenty bazowe. shadcn/ui ułatwia ich kopiowanie i motywowanie. Na co uważać: trzy wzorce niedostarczane przez shadcn — combobox, listbox, menubar — dla których należy importować Radix bezpośrednio, a nie tworzyć ręcznie.
Jeśli zespół jest zorientowany na Tailwind i potrzebuje wąskiej powierzchni, należy wybrać Headless UI — ale trzeba audytować komponent menu we własnym kodzie.
Headless UI to najmniejszy pakiet w stawce i dostarcza małą, dobrze przetestowaną powierzchnię. Jedna luka w komponencie menu jest opisana powyżej; należy ją naprawić przez jawne połączenie aria-activedescendant na menu w stylu listbox lub owinięcie menu projektem-lokalnym pomocnikiem, który to robi.
Jeśli zespół ceni „baterie w zestawie“ ponad zgodność ze specyfikacją, należy wybrać Mantine lub Chakra v3 — ale zaplanować nadpisania.
Obie biblioteki pozwalają szybko dostarczać i wyglądają dobrze od razu. Obie jednak wymagają nadpisań per-komponent, aby zaliczyć audyt dla combobox, switch, slider (Mantine) lub tooltip (Chakra v3). Pracę nad nadpisaniami należy zabudżetować w sprint, który adoptuję bibliotekę, a nie w sprint, który nie zalicza audytu.
Należy uruchomić axe na własnym demo biblioteki przed jej adoptowaniem.
Dostawcy utrzymują strony demonstracyjne. Należy skierować axe DevTools na nie. Jeśli własne demo dostawcy generuje naruszenia na komponentach bazowych, które planuje się używać, te naruszenia trafią do produktu. To pojedyncze pięciominutowe sprawdzenie oddziela biblioteki, które się adoptuje, od tych, których się unika.
Podsumowanie: specyfikacja to kontrakt
Biblioteki, które czysto zaliczają audyt axe, to biblioteki, których autorzy traktowali WAI-ARIA Authoring Practices Guide jako kontrakt, a nie referencję. Radix UI, React Aria Components i Ark UI wywodzą swoje kontrakty klawiaturowe i połączenia ARIA z opublikowanej specyfikacji — APG w przypadku Radix i React Aria, maszyny stanów Zag.js w przypadku Ark UI — i dostarczają specyfikację, nie jej podzbiór.
Biblioteki, które nie zaliczają, nie robią tego dlatego, że ich autorom nie zależy na dostępności. Nie zaliczają, ponieważ kontrakt klawiaturowy był traktowany jak lista funkcji, a listy funkcji kończą na 80% zamiast 100%. Ostatnie 20% — Home i End w comboboxie, Escape do odrzucenia tooltipu otwartego przy najechaniu, zarządzanie fokusem przy pierwszym ArrowDown w menu — to część, którą użytkownik zauważa.
Historia shadcn/ui leży dziwnie pomiędzy obiema kategoriami. Dostarczane przez nią komponenty dziedziczą wywodzenie ze specyfikacji Radix i zaliczają. Niedostarczane przez nią komponenty to luki, gdzie zespoły tworzą ręcznie wewnętrzny combobox, i ten wewnętrzny combobox jest miejscem, gdzie do codebase wchodzi następne naruszenie axe. Rozwiązaniem nie jest wybranie innej biblioteki — to importowanie Radix bezpośrednio dla tych trzech wzorców i traktowanie ich z taką samą powagą jak reszty systemu projektowego.
Zespoły inżynierskie adoptujące bibliotekę powinny połączyć ten wybór z pozostałymi narzędziami deweloperskimi w naszym centrum dla deweloperów, uruchomić bezpłatny skan WCAG 2.2 na demo biblioteki przed wdrożeniem na produkcję i porównać wynik z pełnym materiałem referencyjnym kryteriów sukcesu WCAG 2.2.
„Wybierz bibliotekę, której autorzy traktowali specyfikację jako kontrakt, a audyt stanie się formalnością. Wybierz bibliotekę, której autorzy traktowali specyfikację jako referencję, a audyt stanie się zaległościami.“