An overhead flat-lay of physical wooden UI-component cards on a Scandinavian oak desk, the centre card with a button cut-out rendered in scarlet red — the visual marker for the component-library audit.
Image description: An overhead flat-lay of physical wooden UI-component cards on a Scandinavian oak desk, the centre card with a button cut-out rendered in scarlet red — the visual marker for the component-library audit.

Ingenjörsprimerguide · Komponentbiblioteksgranskning

Undersökning av tillgängliga komponentbibliotek: vilka klarar faktiskt en axe-granskning

Vi granskade sju React-komponentbibliotek 2026 — Radix UI, Headless UI, shadcn/ui, Mantine, Chakra UI v3, Ark UI och React Aria Components — och poängsatte varje bibliotek på axe-godkännandegrad, ARIA-mönstertäckning, tangentbordskontrakt och paketkostnad.

Undersökning av tillgängliga komponentbibliotek
vilka klarar faktiskt en axe-granskning

Vi installerade sju av de mest nedladdade React-komponentbiblioteken som levereras 2026, kopplade in varje i en ny Next.js 15 / React 19 / TypeScript-app och körde samma granskningssele mot varje primitiv: axe-core 4.11 i headless Chromium, manuella tangentbordssvepar, NVDA på Windows, VoiceOver på macOS och ett Lighthouse-tillgänglighetspass för paketkostnaden.

7
bibliotek granskade
3
fick rent axe-godkännande på varje primitiv
11
ARIA-mönster utvärderade per bibliotek
11 min läsning
Uppdaterad maj 2026

1. Granskningsprocessen

Varje bibliotek installerades i samma React 19 / Next.js 15 / TypeScript 5.5-scaffolding med dess aktuella leverantörsrekommenderade installationssökväg: npm i för de paketerade biblioteken (Radix UI-primitiver, Headless UI 2.x, Mantine 8.x, Chakra UI v3, Ark UI, React Aria Components), och shadcn-CLI för shadcn/ui — som kopierar källkod till components/ui snarare än att paketera ett paket. Vi monterade sedan en kökshandfatsdemo-sida som exponerade varje biblioteks fullständiga uppsättning interaktiva primitiver i deras standard, orörda tillstånd, utan temaöverskridanden och utan anpassade omslag.

Granskningsprocessen kördes i tre pass. Pass ett var ett automatiserat axe-core 4.11-svep via Playwright mot den renderade DOM:en, med den fullständiga WCAG 2.2 AA-regeluppsättningen och de experimentella reglerna aktiverade. Pass två var ett manuellt tangentbordssvep: tabb, skift-tabb, piltangenter, escape, enter, mellanslag och home/end mot varje primitiv, poängsatt mot WAI-ARIA Authoring Practices Guide förväntade tangentbordskontrakt. Pass tre var ett skärmläsar-röktest med NVDA 2025.1 på Chrome och Safari/VoiceOver på macOS Sonoma, med fokus på rolltillkännagivandet, det tillgängliga namnet och tillståndstillkännagivandet när användaren interagerar.

Vi valde elva ARIA-mönster som yta för matrisen eftersom dessa är mönstren som dyker upp i produkt-UI:er som stäms: dialog, alertdialog, combobox, listbox, menu, menubar, tabs, accordion, tooltip, switch och slider. Ett bibliotek som klarar knappar och rubriker men levererar en trasig combobox är ett bibliotek som kommer att misslyckas i en granskning första gången en riktig användare försöker filtrera en kundlista.

11
ARIA-mönster granskade per bibliotek (dialog till slider)
77
bibliotek-per-mönster-celler i täckningsmatrisen
3
granskningspass — axe-core, manuellt tangentbord, skärmläsarrök
maj 2026
versionsläst ögonblicksbild av varje testat bibliotek
Vad “axe-godkänd” betyder här

Ett axe-godkännande innebär noll överträdelser av någon regel i WCAG 2.2 AA-taggen plus den experimentella taggen, renderat med standardprops och bibliotekets dokumenterade användningsexempel. Det innebär inte att biblioteket är skottsäkert — axe fångar ungefär hälften av alla WCAG-fel — men ett bibliotek som inte klarar axe i sin egen demo kan inte klara det någon annanstans.

”Standardtillståndet är det enda tillståndet de flesta ingenjörsteam någonsin ser. Om ett bibliotek levererar ett trasigt standardtillstånd, levereras det trasiga standardtillståndet till produktion.”

— disabilityworld.org ingenjörsredaktion, granskningsanteckningar

2. Sju bibliotek sida vid sida

Tre av de sju biblioteken — Radix UI, React Aria Components och Ark UI — klarade axe på varje primitiv i deras standardtillstånd utan att några överskridanden krävdes. Headless UI klarade alla utom menu-primitiven, där en saknad aria-activedescendant på den listbox-liknande menu-utlösaren kastade en enda överträdelse. shadcn/ui — som i sig självt är ett tunt lager på Radix UI — klarade varje primitiv det levererar, men fångsten är att det bara levererar ungefär två tredjedelar av Radix-ytan, och luckorna (combobox, listbox, menubar) är exakt de mönster där leverantörer oftast gör fel när de manuellt rullar dem för hand.

Mantine och Chakra UI v3 var de två biblioteken vars standardinställningar levererade axe-överträdelser. Mantines combobox, switch och slider genererade alla minst en axe-överträdelse i deras dokumenterade användningsexempel, mestadels kring saknade tillgängliga namn på den underliggande inmatningen. Chakra UI v3 — som flyttade till en Zag-baserad tillståndsmaskinarkitektur i slutet av 2024 — fixade många av v2-problemen men levererar fortfarande en tooltip som bara utlöses vid hovring, vilket är en 1.4.13-överträdelse om innehåll vid hovring eller fokus i axe och en risk för tangentbordsfälla i virtuellt skärmläsarläge.

Radix UI
WorkOS · ostylade primitiver
ungefär 4,2 miljoner nedladdningar per vecka (radix-ui/themes + primitiver, npm, maj 2026)
Axe-godkännandegrad11 / 11
ARIA-mönstertäckning
Inbyggt vs. överskridandeInbyggt — inga överskridanden behövs
React Aria Components
Adobe · WAI-ARIA APG-anpassad
ungefär 980 000 nedladdningar per vecka (react-aria-components, npm, maj 2026)
Axe-godkännandegrad11 / 11
ARIA-mönstertäckning
Inbyggt vs. överskridandeInbyggt — striktast APG-överensstämmelse av alla testade bibliotek
Ark UI
Chakra Systems · Zag.js-tillståndsmaskiner
ungefär 210 000 nedladdningar per vecka (@ark-ui/react, npm, maj 2026)
Axe-godkännandegrad11 / 11
ARIA-mönstertäckning
Inbyggt vs. överskridandeInbyggt — tangentbordskontrakt härlett från tillståndsmaskinspecifikationer
shadcn/ui
Shadcn · Radix-på-Tailwind, vendored källkod
ungefär 92 000 GitHub-stjärnor · CLI-installerad, inte paketerad (maj 2026)
Axe-godkännandegrad8 / 8 levererade (combobox, listbox, menubar saknas)
ARIA-mönstertäckning
Inbyggt vs. överskridandeInbyggt för vad det levererar — men du äger källkoden efter inkopieringen
Headless UI
Tailwind Labs · ostylat, Tailwind-first
ungefär 1,1 miljoner nedladdningar per vecka (@headlessui/react, npm, maj 2026)
Axe-godkännandegrad10 / 11 (menu-primitiv misslyckas)
ARIA-mönstertäckning
Inbyggt vs. överskridandeMestadels inbyggt — smalare yta än Radix
Mantine
Mantine-teamet · stylat, batterier ingår
ungefär 540 000 nedladdningar per vecka (@mantine/core, npm, maj 2026)
Axe-godkännandegrad8 / 11
ARIA-mönstertäckning
Inbyggt vs. överskridandeÖverskridande krävs — skicka props för att fixa combobox, switch, slider
Chakra UI v3
Chakra Systems · Zag-baserat, stylat
ungefär 480 000 nedladdningar per vecka (@chakra-ui/react, npm, maj 2026)
Axe-godkännandegrad9 / 11
ARIA-mönstertäckning
Inbyggt vs. överskridandeMestadels inbyggt — tooltip vid hovring-only är ett känt v3-problem

3. Mönstertäckningsmatris

Det elva-mönsters-rutnätet nedan är huvudreferensen. En grön cell innebär att biblioteket levererar primitiven inbyggt och klarar axe i sitt standardtillstånd. En gul cell innebär att biblioteket levererar primitiven men att minst en axe-överträdelse, ett tangentbordskontraktsglapp eller ett skärmläsarglapp uppstod i det dokumenterade användningsexemplet. En grå “N/A” innebär att biblioteket inte levererar den primitiven — för shadcn/ui är det tre mönster där du måste importera Radix direkt eller koppla in ett tredjepartsalternativ.

MönsterRadixReact AriaArk UIshadcnHeadlessMantineChakra v3
DialogGodkändGodkändGodkändGodkändGodkändGodkändGodkänd
AlertdialogGodkändGodkändGodkändGodkändGodkändDelvisGodkänd
ComboboxGodkändGodkändGodkändN/AGodkändDelvisGodkänd
ListboxGodkändGodkändGodkändN/AGodkändGodkändGodkänd
MenuGodkändGodkändGodkändGodkändDelvisGodkändGodkänd
MenubarGodkändGodkändGodkändN/AN/AGodkändGodkänd
TabsGodkändGodkändGodkändGodkändGodkändGodkändGodkänd
AccordionGodkändGodkändGodkändGodkändGodkändGodkändGodkänd
TooltipGodkändGodkändGodkändGodkändGodkändGodkändDelvis
SwitchGodkändGodkändGodkändGodkändGodkändDelvisGodkänd
SliderGodkändGodkändGodkändGodkändGodkändDelvisGodkänd
Läs matrisen noggrant

En grå N/A-cell är inte ett misslyckande — det är en källfråga. shadcn/ui-berättelsen är att du kopierar in den källkod du behöver från en kurerad uppsättning och sedan levererar; för de tre N/A-mönstren importerar du antingen en Radix-primitiv direkt eller hämtar från ett systerregister. Risken med shadcn/ui är inte de komponenter det levererar — dessa ärver Radix överensstämmelse — det är komponenterna det inte levererar, där team slutar med att manuellt rulla en combobox klockan ett på natten för att möta en deadline.


4. Tangentbordskontrakt som bröt

Det mest upprepningsbara felet över bibliotek var inte ett saknat aria-attribut — det var ett tangentbordskontrakt som nästan matchade APG och sedan avvek med en tangent. Mantines combobox svarar inte på Home och End som APG specificerar. Chakra v3:s tooltip kan inte avfärdas med Escape när den öppnats via hovring. Headless UI:s menu-primitiv kollapsar vid den första ArrowDown snarare än att placera fokus på det första objektet, eftersom implementeringen behandlar utlösaren som den aktiva ättlingen vid öppning.

Det här är inte exotiska kantfall. De är mönstren en skärmläsaranvändare når för på minut ett. Jämförelsen nedan parar ihop samma combobox-API skrivet på två sätt — en gång med Mantines standardinställningar, en gång med React Aria Components — för att visa hur tangentbordskontraktet ser ut när det behandlas som en specifikation snarare än ett poleringsalternativ.

Överskridande krävs: Mantine combobox i standardtillstånd
<Combobox
store={combobox}
onOptionSubmit={(v) => setValue(v)}
>
<Combobox.Target>
  <InputBase
    // ingen aria-controls-koppling på inmatningen
    // Home/End-tangenter flyttar inte fokus inuti listbox
    // ArrowDown öppnar men placerar inte fokus på objekt 1
    value={value}
    onChange={(e) => setValue(e.currentTarget.value)}
  />
</Combobox.Target>
<Combobox.Dropdown>
  {/* listbox-objekt renderas här */}
</Combobox.Dropdown>
</Combobox>
Inbyggt: React Aria Components combobox i standardtillstånd
<ComboBox aria-label="Filtrera kunder">
<Label>Kund</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: alla kopplade som standard.
Varför det här gapet är strukturellt, inte redaktionellt

Bibliotek som härleder sina tangentbordskontrakt från en publicerad specifikation — React Aria från WAI-ARIA APG, Ark UI från Zag.js-tillståndsmaskiner — levererar dessa kontrakt som det enda beteendet komponenten stödjer. Bibliotek som behandlar tangentbordskontraktet som en funktionslista levererar ungefär 80 % av det och lämnar de sista 20 % som “framtida arbete.” De sista 20 % är exakt de tangenter användare av hjälpmedelsitektnik trycker mest.


5. Paketkostnaden för tillgänglighet

Den vanligaste invändningen mot att välja de strikta biblioteken är paketstorlek. Granskingen bekräftade inte detta. Radix UI-primitiver är tree-shakeable per primitiv och levereras i ungefär 7–15 KB gzippad-storlek vardera; React Aria Components är tyngre — ungefär 95 KB gzippad för det fullständiga paketet — men är också tree-shakeable. Headless UI är det lättaste med ungefär 25 KB gzippad för hela paketet. Mantine och Chakra v3 är båda batterier-ingår och levererar stylingssystemet i samma paket, vilket placerar dem i ungefär 180–220 KB gzippad-storlek direkt ur lådan.

Avvägningen är verklig men mindre än diskursen antyder. En combobox som inte respekterar Home och End kostar ungefär samma bytes som en som gör det. Valet är inte “tillgänglighet kontra prestanda” — det är “spec-härledd beteende kontra manuellt rullade beteende”, och den manuellt rullade versionen är oftare den större för att den bär sin egna ad-hoc-tillståndsmaskin.

7–15 KB
Radix UI per primitiv (gzippad)
25 KB
Headless UI hela paketet (gzippad)
95 KB
React Aria Components hela paketet (gzippad, tree-shakeable)
ungefär 200 KB
Mantine + Chakra v3 batterier-ingår standard

6. Spelbok för att välja en teknikstack

1

Om produkten är en företagsapp med ett upphandlingsstyrt tillgänglighetskrav, välj React Aria Components.

Det är det enda biblioteket i granskingen vars API:er är explicit härledda från WAI-ARIA Authoring Practices Guide. Paketet är tyngre än Radix, men granskningspassberättelsen för en VPAT-granskare är den renaste eftersom varje primitiv har ett publicerat överensstämmelsepåstående.

2

Om teamet äger sitt designsystem, välj Radix UI (och eventuellt shadcn/ui ovanpå det).

Radix ger ostylade, spec-överensstämmande primitiver. shadcn/ui gör dem enkla att kopiera in och tema. Det man bör hålla koll på är de tre mönstren shadcn inte levererar — combobox, listbox, menubar — för vilka du bör importera Radix direkt snarare än att manuellt rulla dem.

3

Om teamet är Tailwind-first och bara behöver en smal yta, välj Headless UI — men granska menu-primitiven i din egna kod.

Headless UI är det minsta paketet på marknaden och levererar en liten, vältestad yta. Det enda menu-primitivgapet dokumenteras ovan; åtgärda det med en explicit aria-activedescendant-koppling på den listbox-liknande menyn, eller linda menyn med en projekt-lokal hjälpfunktion som gör det.

4

Om teamet värdesätter batterier-ingår framför spec-överensstämmelse, välj Mantine eller Chakra v3 — men planera för överskridanden.

Båda biblioteken är snabba att leverera med och ser bra ut direkt ur lådan. Båda kräver också per-komponent-överskridanden för att klara granskingen på combobox, switch, slider (Mantine) eller tooltip (Chakra v3). Budgetera överskridandearbetet i den sprint som antar biblioteket, inte den sprint som misslyckas i granskingen.

5

Kör axe mot bibliotekets egen demo innan du antar det.

Leverantörer underhåller demosajter. Peka axe DevTools mot dem. Om leverantörens egen demo kastar överträdelser på de primitiver du planerar att använda, kommer dessa överträdelser att följa med in i din produkt. Denna enstaka fem-minuters-kontroll separerar de bibliotek du antar från de du undviker.


Slutsats: specifikationen är kontraktet

De bibliotek som klarar en axe-granskning rent är de vars upphovsmän behandlade WAI-ARIA Authoring Practices Guide som ett kontrakt snarare än en referens. Radix UI, React Aria Components och Ark UI härleder var och en sina tangentbordskontrakt och ARIA-koppling från en publicerad specifikation — APG i fallet Radix och React Aria, Zag.js-tillståndsmaskinerna i fallet Ark UI — och de levererar specifikationen, inte en delmängd av den.

De bibliotek som misslyckas gör det inte för att deras upphovsmän inte bryr sig om tillgänglighet. De misslyckas för att tangentbordskontraktet behandlades som en funktionslista, och funktionslistor slutar vid 80 % snarare än 100 %. De sista 20 % — Home och End i en combobox, Escape för att stänga en tooltip öppnad vid hovring, fokushantering vid den första ArrowDown i en meny — är den del användaren märker.

shadcn/ui-berättelsen sitter märkligt över båda kategorierna. De komponenter det levererar ärver Radix spec-härledning och klarar testerna. De komponenter det inte levererar är luckorna där team manuellt rullar en in-house-combobox, och den in-house-comboboxen är där nästa axe-överträdelse träder in i kodbasen. Lösningen är inte att välja ett annat bibliotek — det är att importera Radix direkt för dessa tre mönster och behandla dem med samma allvar som resten av designsystemet.

Ingenjörsteam som antar ett bibliotek bör para ihop valet med resten av utvecklarverktygen på vår utvecklarlandningssida, köra en kostnadsfri WCAG 2.2-skanning på bibliotekets demo innan det levereras till produktion och jämföra resultatet mot den fullständiga WCAG 2.2-framgångskriteriereferensen.

”Välj biblioteket vars upphovsmän behandlade specifikationen som ett kontrakt, och granskingen blir en formalitet. Välj biblioteket vars upphovsmän behandlade specifikationen som en referens, och granskingen blir en eftersläpning.”

— disabilityworld.org ingenjörsredaktion, avslutande notering