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.

Ingeniørguide · Komponentbiblioteksrevision

Tilgængelige komponentbiblioteker: hvilke består faktisk en axe-revision

Vi reviderede syv React-komponentbiblioteker i 2026 — Radix UI, Headless UI, shadcn/ui, Mantine, Chakra UI v3, Ark UI og React Aria Components — og scorede hvert på axe-bestå-rate, ARIA-mønsterdækning, tastaturkontrakt og bundle-størrelses-omkostning.

Tilgængelige komponentbiblioteker
hvilke består faktisk en axe-revision

Vi installerede syv af de mest downloadede React-komponentbiblioteker i 2026, koblede hvert ind i en frisk Next.js 15 / React 19 / TypeScript-app og kørte det samme revisionsharness mod hvert primitiv: axe-core 4.11 i headless Chromium, manuelle tastatur-sweeps, NVDA på Windows, VoiceOver på macOS og et Lighthouse-tilgængeligheds-pass for bundle-størrelses-omkostningen.

7
biblioteker revideret
3
scorede et rent axe-bestå på hvert primitiv
11
ARIA-mønstre evalueret per bibliotek
11 min læsning
Opdateret maj 2026

1. Revisionsharness

Hvert bibliotek blev installeret i det samme React 19 / Next.js 15 / TypeScript 5.5-stillads ved hjælp af dets aktuelle leverandøranbefalet installationssti: npm i for de pakkede biblioteker (Radix UI primitiver, Headless UI 2.x, Mantine 8.x, Chakra UI v3, Ark UI, React Aria Components) og shadcn CLI for shadcn/ui — som kopierer kildekode ind i components/ui frem for at levere en pakke. Vi monterede derefter en kitchen-sink-demoside, der eksponerede hvert biblioteks fulde sæt af interaktive primitiver i deres standardtilstand uden temaoverskrivninger og uden brugerdefinerede wrappers.

Revisionsharness kørte i tre gennemløb. Gennemløb ét var et automatiseret axe-core 4.11-sweep gennem Playwright mod den renderede DOM, med det fulde WCAG 2.2 AA-regelsæt og de eksperimentelle regler aktiveret. Gennemløb to var et manuelt tastatur-sweep: tab, shift-tab, piletaster, escape, enter, space og home/end mod hvert primitiv, scoret mod WAI-ARIA Authoring Practices Guide’s forventede tastaturkontrakt. Gennemløb tre var en skærmlæser-smoke-test med NVDA 2025.1 på Chrome og Safari/VoiceOver på macOS Sonoma med fokus på rolleannonceringen, det tilgængelige navn og tilstandsannonceringen, når brugeren interagerer.

Vi valgte elleve ARIA-mønstre som overfladeareal for matricen, fordi disse er de mønstre, der optræder i produkt-UIs, der bliver sagsøgt: dialog, alarmdialog, combobox, listbox, menu, menubar, faneblade, accordion, tooltip, switch og slider. Et bibliotek, der klarer sig godt med knapper og overskrifter, men leverer en ødelagt combobox, er et bibliotek, der vil fejle en revision, første gang en rigtig bruger forsøger at filtrere en kundeliste.

11
ARIA-mønstre revideret per bibliotek (dialog til slider)
77
bibliotek-per-mønster-celler i dækningsmatricen
3
revisionsgennemløb — axe-core, manuelt tastatur, skærmlæser-smoke
Maj 2026
versions-låst snapshot af hvert testet bibliotek
Hvad »axe-bestå« betyder her

Et axe-bestå betyder nul overtrædelser af enhver regel i WCAG 2.2 AA-tagget plus det eksperimentelle tag, renderet med standardprops og bibliotekets dokumenterede brugseksempel. Det betyder ikke, at biblioteket er skudsikkert — axe fanger ca. halvdelen af alle WCAG-fejl — men et bibliotek, der ikke kan bestå axe i sin egen demo, kan ikke bestå nogen andre steder.

»Standardtilstanden er den eneste tilstand, de fleste ingeniørteams nogensinde ser. Hvis et bibliotek leverer en ødelagt standard, sendes den ødelagte standard til produktion.«

— disabilityworld.org ingeniørredaktion, revisionsnoter

2. Syv biblioteker side om side

Tre af de syv biblioteker — Radix UI, React Aria Components og Ark UI — bestod axe på hvert primitiv i deres standardtilstand uden påkrævede overskrivninger. Headless UI bestod på alle undtagen menu-primitivet, hvor en manglende aria-activedescendant på listbox-stil menu-udløseren kastede en enkelt overtrædelse. shadcn/ui — som selv er et tyndt lag oven på Radix UI — bestod på hvert primitiv, det leverer, men fangsten er, at det kun leverer ca. to tredjedele af Radix-overfladen, og hullerne (combobox, listbox, menubar) er præcis de mønstre, hvor leverandører oftest laver tilgængeligheds-fejl, når de ruller dem selv.

Mantine og Chakra UI v3 var de to biblioteker, hvis standarder leverede axe-overtrædelser. Mantines combobox, switch og slider genererede alle mindst én axe-overtrædelse i deres dokumenterede brugseksempel, primært omkring manglende tilgængelige navne på det underliggende input. Chakra UI v3 — som skiftede til en Zag-baseret tilstandsmaskine-arkitektur i slutningen af 2024 — rettede mange af v2-problemerne, men leverer stadig en tooltip, der kun udløses ved hover, hvilket er en 1.4.13 Indhold ved hover eller fokus-overtrædelse i axe og en tastaturfalderisk risiko i skærmlæservirtuel tilstand.

Radix UI
WorkOS · ustylet primitiver
ca. 4,2 mio. ugentlige downloads (radix-ui/themes + primitiver, npm, maj 2026)
Axe-bestå-rate11 / 11
ARIA-mønsterdækning
Nativ vs. overskrivningNativ — ingen overskrivninger nødvendige
React Aria Components
Adobe · WAI-ARIA APG-justeret
ca. 980.000 ugentlige downloads (react-aria-components, npm, maj 2026)
Axe-bestå-rate11 / 11
ARIA-mønsterdækning
Nativ vs. overskrivningNativ — strengeste APG-overensstemmelse af alle testede biblioteker
Ark UI
Chakra Systems · Zag.js tilstandsmaskiner
ca. 210.000 ugentlige downloads (@ark-ui/react, npm, maj 2026)
Axe-bestå-rate11 / 11
ARIA-mønsterdækning
Nativ vs. overskrivningNativ — tastaturkontrakt afledt af tilstandsmaskine-specifikationer
shadcn/ui
Shadcn · Radix-på-Tailwind, leveret kildekode
ca. 92.000 GitHub-stjerner · CLI-installeret, ikke pakket (maj 2026)
Axe-bestå-rate8 / 8 leveret (combobox, listbox, menubar mangler)
ARIA-mønsterdækning
Nativ vs. overskrivningNativ for hvad det leverer — men du ejer kildekoden efter kopiering
Headless UI
Tailwind Labs · ustylet, Tailwind-first
ca. 1,1 mio. ugentlige downloads (@headlessui/react, npm, maj 2026)
Axe-bestå-rate10 / 11 (menu-primitiv fejler)
ARIA-mønsterdækning
Nativ vs. overskrivningPrimært nativ — smallere overflade end Radix
Mantine
Mantine-teamet · stylet, batteries-included
ca. 540.000 ugentlige downloads (@mantine/core, npm, maj 2026)
Axe-bestå-rate8 / 11
ARIA-mønsterdækning
Nativ vs. overskrivningOverskrivning påkrævet — giv props for at rette combobox, switch, slider
Chakra UI v3
Chakra Systems · Zag-baseret, stylet
ca. 480.000 ugentlige downloads (@chakra-ui/react, npm, maj 2026)
Axe-bestå-rate9 / 11
ARIA-mønsterdækning
Nativ vs. overskrivningPrimært nativ — tooltip kun ved hover er et kendt v3-hul

3. Mønsterdækningsmatrix

Det elleve-mønsters gitter nedenfor er den overordnede reference. En grøn celle betyder, at biblioteket leverer primitivet nativt og består axe i sin standardtilstand. En gul celle betyder, at biblioteket leverer primitivet, men mindst én axe-overtrædelse, tastaturkontrakthul eller skærmlæserhul opstod i det dokumenterede brugseksempel. Et grå »N/A« betyder, at biblioteket ikke leverer dette primitiv — for shadcn/ui er det tre mønstre, hvor du skulle importere Radix direkte eller koble en tredjepart ind.

MønsterRadixReact AriaArk UIshadcnHeadlessMantineChakra v3
DialogBestårBestårBestårBestårBestårBestårBestår
AlarmdialogBestårBestårBestårBestårBestårDelvisBestår
ComboboxBestårBestårBestårN/ABestårDelvisBestår
ListboxBestårBestårBestårN/ABestårBestårBestår
MenuBestårBestårBestårBestårDelvisBestårBestår
MenubarBestårBestårBestårN/AN/ABestårBestår
FanebladeBestårBestårBestårBestårBestårBestårBestår
AccordionBestårBestårBestårBestårBestårBestårBestår
TooltipBestårBestårBestårBestårBestårBestårDelvis
SwitchBestårBestårBestårBestårBestårDelvisBestår
SliderBestårBestårBestårBestårBestårDelvisBestår
Læs matricen omhyggeligt

En grå N/A-celle er ikke en fejl — det er et kildevalgs-spørgsmål. shadcn/ui-historien er, at du kopierer den kildekode, du har brug for, ind fra et kurateret sæt og derefter leverer; for de tre N/A-mønstre importerer du enten et Radix-primitiv direkte eller henter fra et søsterregister. Risikoen i shadcn/ui er ikke de komponenter, det leverer — disse arver Radix’s overholdelse — det er de komponenter, det ikke leverer, hvor teams ender med at rulle en combobox selv kl. 1 om natten for at overholde en deadline.


4. Tastaturkontrakter der brød

Den mest gentagelige fejl på tværs af biblioteker var ikke en manglende aria-attribut — det var en tastaturkontrakt, der næsten matchede APG og derefter divergerede med én tast. Mantines combobox reagerer ikke på Home og End som APG specificerer. Chakra v3’s tooltip kan ikke afvises med Escape, når den blev åbnet ved hover. Headless UIs menu-primitiv folder sammen ved den første ArrowDown frem for at placere fokus på det første element, fordi implementeringen behandler udløseren som den aktive efterkommer ved åbning.

Disse er ikke eksotiske kanttilfælde. De er de mønstre, en skærmlæserbruger når frem til i det første minut. Sammenligningen nedenfor parrer den samme combobox-API skrevet på to måder — én med Mantines standarder, én med React Aria Components — for at vise, hvordan tastaturkontrakten ser ud, når den behandles som en specifikation frem for et polish-element.

Overskrivning påkrævet: Mantine combobox i standardtilstand
<Combobox
store={combobox}
onOptionSubmit={(v) => setValue(v)}
>
<Combobox.Target>
  <InputBase
    // ingen aria-controls-kobling på input
    // Home/End-taster flytter ikke fokus inden i listbox
    // ArrowDown åbner men placerer ikke fokus på element 1
    value={value}
    onChange={(e) => setValue(e.currentTarget.value)}
  />
</Combobox.Target>
<Combobox.Dropdown>
  {/* listbox-elementer renderes her */}
</Combobox.Dropdown>
</Combobox>
Nativ: React Aria Components combobox i standardtilstand
<ComboBox aria-label="Filtrer kunder">
<Label>Kunde</Label>
<Input />
<Popover>
  <ListBox>
    <ListBoxItem>Alfa</ListBoxItem>
    <ListBoxItem>Beta</ListBoxItem>
    <ListBoxItem>Gamma</ListBoxItem>
  </ListBox>
</Popover>
</ComboBox>
// aria-controls, aria-activedescendant,
// aria-expanded, role=combobox, Home/End,
// PageUp/PageDown, Escape: alle koblet som standard.
Hvorfor denne kløft er strukturel, ikke redaktionel

Biblioteker, der afledt deres tastaturkontrakter fra en offentliggjort specifikation — React Aria fra WAI-ARIA APG, Ark UI fra Zag.js-tilstandsmaskiner — leverer disse kontrakter som den eneste adfærd, komponenten understøtter. Biblioteker, der behandler tastaturkontrakten som en funktionsliste, leverer ca. 80% af den og lader de sidste 20% stå som »fremtidigt arbejde«. De sidste 20% er præcis de taster, hjælpeteknologibrugere trykker mest på.


5. Bundle-størrelses-omkostning ved tilgængelighed

Den mest almindelige indvending mod at vælge de strenge biblioteker er bundle-størrelse. Revisionen bekræftede ikke dette. Radix UI-primitiver er tree-shakeable per primitiv og leverer i ca. 7-15 KB gzippet-intervallet hvert; React Aria Components er tungere — ca. 95 KB gzippet for det fulde bundle — men er også tree-shakeable. Headless UI er det letteste med ca. 25 KB gzippet for hele pakken. Mantine og Chakra v3 er begge batteries-included og leverer stylesystemet i det samme bundle, hvilket placerer dem i ca. 180-220 KB gzippet-intervallet som standard.

Afvejningen er reel men mindre end diskursen antyder. En combobox, der ikke respekterer Home og End, koster ca. det samme antal bytes som én, der gør. Valget er ikke »tilgængelighed versus ydeevne« — det er »specifikationsafledt adfærd versus håndrulet adfærd«, og den håndrulet version er oftere den større, fordi den bærer sin egen ad-hoc-tilstandsmaskine.

7-15 KB
Radix UI per primitiv (gzippet)
25 KB
Headless UI fuld pakke (gzippet)
95 KB
React Aria Components fuldt bundle (gzippet, tree-shakeable)
ca. 200 KB
Mantine + Chakra v3 batteries-included standard

6. Playbook til valg af en stak

1

Hvis produktet er en virksomhedsapp med et udbuds-ledet tilgængeligheds-krav, vælg React Aria Components.

Det er det eneste reviderede bibliotek, hvis API’er eksplicit er afledt af WAI-ARIA Authoring Practices Guide. Bundlet er tungere end Radix, men revisionsbestå-historien over for en VPAT-anmelder er den reneste, fordi hvert primitiv har et offentliggjort overensstemmelseskrav.

2

Hvis teamet ejer sit designsystem, vælg Radix UI (og eventuelt shadcn/ui oven på det).

Radix giver ustylet, specifikationskonformante primitiver. shadcn/ui gør det nemt at kopiere dem ind og tematisere dem. Det, man skal holde øje med, er de tre mønstre, shadcn ikke leverer — combobox, listbox, menubar — som man bør importere Radix direkte for frem for at rulle dem selv.

3

Hvis teamet er Tailwind-first og kun har brug for en smal overflade, vælg Headless UI — men revidér menu-primitivet i din egen kode.

Headless UI er det mindste bundle i feltet og leverer en lille, velafprøvet overflade. Det ene menu-primitiv-hul er dokumenteret ovenfor; ret det med en eksplicit aria-activedescendant-kobling på listbox-stil menuen, eller wrap menuen med en projektlokal hjælper, der gør det.

4

Hvis teamet foretrækker batteries-included frem for specifikationsoverholdelse, vælg Mantine eller Chakra v3 — men planlæg for overskrivninger.

Begge biblioteker er hurtige at levere med og ser godt ud ud af boksen. Begge kræver også per-komponent-overskrivninger for at rydde revisionen på combobox, switch, slider (Mantine) eller tooltip (Chakra v3). Budgetér overskrivningsarbejdet ind i den sprint, der adopterer biblioteket, ikke den sprint, der fejler revisionen.

5

Kør axe mod bibliotekets egen demo, inden du adopterer det.

Leverandører vedligeholder demo-sites. Peg axe DevTools mod dem. Hvis leverandørens egen demo kaster overtrædelser på de primitiver, du planlægger at bruge, vil disse overtrædelser følge med ind i dit produkt. Denne enkle fem-minutters kontrol adskiller de biblioteker, man adopterer, fra dem, man undgår.


Konklusion: specifikationen er kontrakten

De biblioteker, der rydder en axe-revision rent, er de biblioteker, hvis forfattere behandlede WAI-ARIA Authoring Practices Guide som en kontrakt frem for en reference. Radix UI, React Aria Components og Ark UI afledt alle deres tastaturkontrakter og ARIA-kobling fra en offentliggjort specifikation — APG i tilfældet Radix og React Aria, Zag.js-tilstandsmaskiner i tilfældet Ark UI — og de leverer specifikationen, ikke et delsæt af den.

De biblioteker, der fejler, gør det ikke, fordi deres forfattere ikke bekymrer sig om tilgængelighed. De fejler, fordi tastaturkontrakten blev behandlet som en funktionsliste, og funktionslister ender på 80% frem for 100%. De sidste 20% — Home og End i en combobox, Escape til at afvise en hover-åbnet tooltip, fokusstyring på den første ArrowDown i en menu — er den del, brugeren lægger mærke til.

shadcn/ui-historien placerer sig mærkeligt på tværs af begge kategorier. De komponenter, det leverer, arver Radix’s specifikationsafledning og består. De komponenter, det ikke leverer, er hullerne, hvor teams ruller en intern combobox selv, og den interne combobox er der, den næste axe-overtrædelse kommer ind i kodebasen. Løsningen er ikke at vælge et andet bibliotek — det er at importere Radix direkte for disse tre mønstre og behandle dem med samme alvor som resten af designsystemet.

Ingeniørteams, der adopterer et bibliotek, bør kombinere valget med resten af udviklertoolkit’et på vores udviklerside, køre en gratis WCAG 2.2-scanning på bibliotekets demo inden det sendes til produktion, og benchmarke resultatet mod den fulde WCAG 2.2 succeskriterier-reference.

»Vælg det bibliotek, hvis forfattere behandlede specifikationen som en kontrakt, og revisionen bliver en formalitet. Vælg det bibliotek, hvis forfattere behandlede specifikationen som en reference, og revisionen bliver et backlog.«

— disabilityworld.org ingeniørredaktion, afsluttende note