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.

Engineering primer · Audit delle librerie di componenti

Indagine sulle librerie di componenti accessibili: quali superano davvero un audit axe

Audit di sette librerie di componenti React nel 2026 — Radix UI, Headless UI, shadcn/ui, Mantine, Chakra UI v3, Ark UI e React Aria Components — con punteggi su tasso axe-pass, copertura dei pattern ARIA, contratto da tastiera e costo in dimensione del bundle.

Indagine sulle librerie di componenti accessibili
quali superano davvero un audit axe

Abbiamo installato sette tra le librerie di componenti React più scaricate disponibili nel 2026, integrandole ciascuna in una nuova app Next.js 15 / React 19 / TypeScript, ed eseguito la stessa batteria di audit su ogni primitiva: axe-core 4.11 in Chromium headless, test manuali da tastiera, NVDA su Windows, VoiceOver su macOS e un passaggio Lighthouse-accessibility per il costo in dimensione del bundle.

7
librerie sottoposte ad audit
3
hanno superato axe su ogni primitiva
11
pattern ARIA valutati per libreria
11 min di lettura
Aggiornato maggio 2026

1. La batteria di audit

Ogni libreria è stata installata nello stesso scaffold React 19 / Next.js 15 / TypeScript 5.5 seguendo il percorso di installazione raccomandato dal rispettivo fornitore: npm i per le librerie pacchettizzate (Radix UI primitives, Headless UI 2.x, Mantine 8.x, Chakra UI v3, Ark UI, React Aria Components) e la CLI shadcn per shadcn/ui — che copia il codice sorgente in components/ui invece di installare un pacchetto. È stata quindi montata una pagina demo kitchen-sink che espone l’intero set di primitive interattive di ciascuna libreria nel loro stato predefinito, senza personalizzazioni del tema e senza wrapper personalizzati.

La batteria di audit è stata eseguita in tre passaggi. Il primo passaggio è stato una scansione automatizzata axe-core 4.11 tramite Playwright sul DOM renderizzato, con il set completo di regole WCAG 2.2 AA e le regole sperimentali abilitate. Il secondo passaggio è stato un test manuale da tastiera: tab, shift-tab, tasti freccia, escape, invio, spazio e home/fine su ogni primitiva, valutato rispetto al contratto da tastiera previsto dalla WAI-ARIA Authoring Practices Guide. Il terzo passaggio è stato un test smoke con screen reader con NVDA 2025.1 su Chrome e Safari/VoiceOver su macOS Sonoma, verificando l’annuncio del ruolo, il nome accessibile e l’annuncio dello stato quando l’utente interagisce.

Abbiamo selezionato undici pattern ARIA come superficie di analisi per la matrice perché questi sono i pattern che compaiono nelle interfacce di prodotto che vengono contestate legalmente: dialog, alert dialog, combobox, listbox, menu, menubar, tabs, accordion, tooltip, switch e slider. Una libreria che gestisce correttamente pulsanti e intestazioni ma distribuisce una combobox difettosa è una libreria che fallirà un audit non appena un utente reale tenterà di filtrare un elenco di clienti.

11
pattern ARIA sottoposti ad audit per libreria (da dialog a slider)
77
celle libreria-per-pattern nella matrice di copertura
3
passaggi di audit — axe-core, tastiera manuale, screen reader smoke
Maggio 2026
snapshot con versione bloccata di ogni libreria testata
Cosa significa «axe-pass» in questo contesto

Un axe-pass significa zero violazioni di qualsiasi regola nel tag WCAG 2.2 AA più il tag sperimentale, renderizzato con le props predefinite e l’esempio d’uso documentato dalla libreria. Non significa che la libreria sia inattaccabile — axe rileva circa la metà di tutti i fallimenti WCAG — ma una libreria che non supera axe nella propria demo non lo supera da nessun’altra parte.

«Lo stato predefinito è l’unico stato che la maggior parte dei team di ingegneria vede mai. Se una libreria distribuisce un default difettoso, quel default difettoso va in produzione.»

— desk di ingegneria di disabilityworld.org, note di audit

2. Sette librerie a confronto

Tre delle sette librerie — Radix UI, React Aria Components e Ark UI — hanno superato axe su ogni primitiva nel loro stato predefinito senza necessità di personalizzazioni. Headless UI ha superato su tutto tranne la primitiva menu, dove un aria-activedescendant mancante sul trigger del menu in stile listbox ha generato una singola violazione. shadcn/ui — che è a sua volta un livello sottile su Radix UI — ha superato su ogni primitiva che distribuisce, con la limitazione però che ne distribuisce solo circa due terzi della superficie Radix, e le lacune (combobox, listbox, menubar) sono esattamente i pattern in cui i fornitori sbagliano più spesso l’accessibilità quando li ri-implementano manualmente.

Mantine e Chakra UI v3 sono state le due librerie in cui i default hanno generato violazioni axe. La combobox, lo switch e lo slider di Mantine hanno generato almeno una violazione axe nel loro esempio d’uso documentato, principalmente a causa di nomi accessibili mancanti sull’input sottostante. Chakra UI v3 — che è passata a un’architettura basata su state machine Zag nella seconda metà del 2024 — ha risolto molti dei problemi della v2 ma distribuisce ancora un tooltip che si attiva solo al passaggio del mouse, il che costituisce una violazione di 1.4.13 Contenuto al focus o al passaggio del mouse in axe e un rischio di trappola in modalità virtuale dello screen reader.

Radix UI
WorkOS · primitive non stilizzate
circa 4,2 milioni di download settimanali (radix-ui/themes + primitives, npm, maggio 2026)
Tasso axe-pass11 / 11
Copertura pattern ARIA
Nativo vs. personalizzazioneNativo — nessuna personalizzazione necessaria
React Aria Components
Adobe · allineata alle WAI-ARIA APG
circa 980.000 download settimanali (react-aria-components, npm, maggio 2026)
Tasso axe-pass11 / 11
Copertura pattern ARIA
Nativo vs. personalizzazioneNativo — la più rigorosa conformità APG tra tutte le librerie testate
Ark UI
Chakra Systems · state machine Zag.js
circa 210.000 download settimanali (@ark-ui/react, npm, maggio 2026)
Tasso axe-pass11 / 11
Copertura pattern ARIA
Nativo vs. personalizzazioneNativo — contratto da tastiera derivato dalle specifiche delle state machine
shadcn/ui
Shadcn · Radix su Tailwind, sorgente vendorizzato
circa 92.000 stelle GitHub · installato via CLI, non pacchettizzato (maggio 2026)
Tasso axe-pass8 / 8 distribuiti (combobox, listbox, menubar assenti)
Copertura pattern ARIA
Nativo vs. personalizzazioneNativo per ciò che distribuisce — ma il sorgente è a carico del progetto dopo la copia
Headless UI
Tailwind Labs · non stilizzato, Tailwind-first
circa 1,1 milioni di download settimanali (@headlessui/react, npm, maggio 2026)
Tasso axe-pass10 / 11 (la primitiva menu fallisce)
Copertura pattern ARIA
Nativo vs. personalizzazionePer lo più nativo — superficie più ridotta rispetto a Radix
Mantine
Team Mantine · stilizzato, batteries-included
circa 540.000 download settimanali (@mantine/core, npm, maggio 2026)
Tasso axe-pass8 / 11
Copertura pattern ARIA
Nativo vs. personalizzazioneRichiede personalizzazioni — passare props per correggere combobox, switch, slider
Chakra UI v3
Chakra Systems · Zag-based, stilizzato
circa 480.000 download settimanali (@chakra-ui/react, npm, maggio 2026)
Tasso axe-pass9 / 11
Copertura pattern ARIA
Nativo vs. personalizzazionePer lo più nativo — il tooltip solo al passaggio del mouse è una lacuna nota della v3

3. Matrice di copertura dei pattern

La griglia degli undici pattern riportata di seguito è il principale riferimento. Una cella verde significa che la libreria distribuisce la primitiva nativamente e supera axe nel suo stato predefinito. Una cella gialla significa che la libreria distribuisce la primitiva ma nell’esempio d’uso documentato è emersa almeno una violazione axe, una lacuna nel contratto da tastiera o una lacuna con lo screen reader. Un «N/D» grigio significa che la libreria non distribuisce quella primitiva — per shadcn/ui si tratta di tre pattern che richiederebbero di importare Radix direttamente o integrare un componente di terze parti.

PatternRadixReact AriaArk UIshadcnHeadlessMantineChakra v3
DialogPassPassPassPassPassPassPass
Alert dialogPassPassPassPassPassParzialePass
ComboboxPassPassPassN/DPassParzialePass
ListboxPassPassPassN/DPassPassPass
MenuPassPassPassPassParzialePassPass
MenubarPassPassPassN/DN/DPassPass
TabsPassPassPassPassPassPassPass
AccordionPassPassPassPassPassPassPass
TooltipPassPassPassPassPassPassParziale
SwitchPassPassPassPassPassParzialePass
SliderPassPassPassPassPassParzialePass
Leggere la matrice con attenzione

Una cella N/D grigia non è un fallimento — è una questione di approvvigionamento. Il modello shadcn/ui prevede di copiare il sorgente necessario da un set curato e poi distribuirlo; per i tre pattern N/D si importa una primitiva Radix direttamente oppure si attinge da un registro gemello. Il rischio in shadcn/ui non sono i componenti che distribuisce — quelli ereditano la conformità di Radix — bensì i componenti che non distribuisce, dove i team finiscono per implementare manualmente una combobox alle 2 di notte per rispettare una scadenza.


4. I contratti da tastiera che hanno ceduto

Il fallimento più ricorrente tra le librerie non è stato un attributo aria mancante — bensì un contratto da tastiera che quasi corrispondeva alle APG e poi divergeva di un tasto. La combobox di Mantine non risponde a Home e Fine come specificato dalle APG. Il tooltip di Chakra v3 non può essere chiuso con Escape quando è stato aperto al passaggio del mouse. La primitiva menu di Headless UI collassa al primo ArrowDown invece di portare il focus sul primo elemento, perché l’implementazione tratta il trigger come discendente attivo all’apertura.

Non si tratta di casi limite esotici. Sono i pattern verso cui un utente di screen reader si dirige al primo minuto. Il confronto di seguito abbina la stessa API combobox scritta in due modi — una volta con i default di Mantine, una volta con React Aria Components — per mostrare come appare il contratto da tastiera quando è trattato come una specifica anziché come un elemento di rifinitura.

Richiede personalizzazione: Mantine combobox nello stato predefinito
<Combobox
store={combobox}
onOptionSubmit={(v) => setValue(v)}
>
<Combobox.Target>
  <InputBase
    // nessun collegamento aria-controls sull'input
    // I tasti Home/Fine non spostano il focus nel listbox
    // ArrowDown apre ma non porta il focus sull'elemento 1
    value={value}
    onChange={(e) => setValue(e.currentTarget.value)}
  />
</Combobox.Target>
<Combobox.Dropdown>
  {/* gli elementi listbox vengono renderizzati qui */}
</Combobox.Dropdown>
</Combobox>
Nativo: React Aria Components combobox nello stato predefinito
<ComboBox aria-label="Filtra clienti">
<Label>Cliente</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/Fine,
// PageUp/PageDown, Escape: tutto configurato di default.
Perché questa lacuna è strutturale, non editoriale

Le librerie che derivano i propri contratti da tastiera da una specifica pubblicata — React Aria dalle WAI-ARIA APG, Ark UI dalle state machine Zag.js — distribuiscono quei contratti come unico comportamento che il componente supporta. Le librerie che trattano il contratto da tastiera come un elenco di funzionalità ne distribuiscono circa l’80% e lasciano il restante 20% come «lavoro futuro». Quel 20% finale è esattamente i tasti che gli utenti di tecnologia assistiva premono per primi.


5. Costo in dimensione del bundle dell’accessibilità

L’obiezione più comune alla scelta delle librerie più rigorose è la dimensione del bundle. L’audit non ha confermato questa preoccupazione. Le primitive di Radix UI sono tree-shakeable per primitiva e si attestano nell’intervallo approssimativo di 7-15 KB gzippati ciascuna; React Aria Components è più pesante — circa 95 KB gzippati per il bundle completo — ma è anch’essa tree-shakeable. Headless UI è la più leggera a circa 25 KB gzippati per l’intero pacchetto. Mantine e Chakra v3 sono entrambe batteries-included e includono il sistema di stile nello stesso bundle, portandole nell’intervallo approssimativo di 180-220 KB gzippati di default.

Il compromesso è reale ma più piccolo di quanto suggerisca il dibattito. Una combobox che non rispetta Home e Fine costa approssimativamente gli stessi byte di una che lo fa. La scelta non è «accessibilità contro prestazioni» — è «comportamento derivato da specifica contro comportamento implementato manualmente», e la versione implementata manualmente è più spesso quella più grande perché porta la propria state machine ad hoc.

7-15 KB
Radix UI per primitiva (gzippato)
25 KB
Headless UI pacchetto completo (gzippato)
95 KB
React Aria Components bundle completo (gzippato, tree-shakeable)
circa 200 KB
Mantine + Chakra v3 batteries-included di default

6. Playbook per scegliere uno stack

1

Se il prodotto è un’applicazione enterprise con un requisito di accessibilità guidato dall’approvvigionamento, scegliere React Aria Components.

È l’unica libreria sottoposta ad audit le cui API sono esplicitamente derivate dalla WAI-ARIA Authoring Practices Guide. Il bundle è più pesante di Radix, ma la storia del superamento dell’audit per un revisore VPAT è la più chiara perché ogni primitiva ha una dichiarazione di conformità pubblicata.

2

Se il team gestisce il proprio design system, scegliere Radix UI (e opzionalmente shadcn/ui sopra di esso).

Radix fornisce primitive non stilizzate conformi alle specifiche. shadcn/ui le rende facili da copiare e personalizzare. L’aspetto da monitorare sono i tre pattern che shadcn non distribuisce — combobox, listbox, menubar — per i quali si dovrebbe importare Radix direttamente invece di implementarli manualmente.

3

Se il team è Tailwind-first e necessita solo di una superficie ridotta, scegliere Headless UI — ma sottoporre ad audit la primitiva menu nel proprio codice.

Headless UI è il bundle più leggero del settore e distribuisce una superficie piccola e ben testata. La lacuna della primitiva menu è documentata sopra; correggerla con un collegamento esplicito aria-activedescendant sul menu in stile listbox, oppure avvolgere il menu con un helper locale al progetto che lo faccia.

4

Se il team valuta batteries-included sopra la conformità alle specifiche, scegliere Mantine o Chakra v3 — ma pianificare le personalizzazioni.

Entrambe le librerie sono veloci da mettere in produzione e hanno un aspetto gradevole di default. Entrambe richiedono anche personalizzazioni per componente per superare l’audit su combobox, switch, slider (Mantine) o tooltip (Chakra v3). Prevedere il lavoro di personalizzazione nello sprint che adotta la libreria, non in quello che fallisce l’audit.

5

Eseguire axe sulla demo della libreria prima di adottarla.

I fornitori mantengono siti demo. Puntare axe DevTools su di essi. Se la demo del fornitore genera violazioni sulle primitive che si intende usare, quelle violazioni seguiranno nel prodotto. Questo singolo controllo di cinque minuti separa le librerie che si adottano da quelle che si evitano.


Conclusione: la specifica è il contratto

Le librerie che superano un audit axe in modo pulito sono quelle i cui autori hanno trattato la WAI-ARIA Authoring Practices Guide come un contratto anziché come un riferimento. Radix UI, React Aria Components e Ark UI derivano ciascuna i propri contratti da tastiera e il cablaggio ARIA da una specifica pubblicata — le APG nel caso di Radix e React Aria, le state machine Zag.js nel caso di Ark UI — e distribuiscono la specifica, non un sottoinsieme di essa.

Le librerie che falliscono non lo fanno perché i loro autori non si preoccupano dell’accessibilità. Falliscono perché il contratto da tastiera è stato trattato come un elenco di funzionalità, e gli elenchi di funzionalità finiscono all’80% invece che al 100%. L’ultimo 20% — Home e Fine in una combobox, Escape per chiudere un tooltip aperto al passaggio del mouse, gestione del focus al primo ArrowDown in un menu — è la parte che nota l’utente.

La storia di shadcn/ui si colloca in modo strano tra le due categorie. I componenti che distribuisce ereditano la derivazione dalla specifica di Radix e superano l’audit. I componenti che non distribuisce sono le lacune in cui i team implementano manualmente una combobox interna, e quella combobox interna è il punto in cui la prossima violazione axe entra nel codebase. La soluzione non è scegliere una libreria diversa — è importare Radix direttamente per quei tre pattern e trattarli con la stessa serietà del resto del design system.

I team di ingegneria che adottano una libreria dovrebbero abbinare la scelta al resto del toolkit per sviluppatori nel nostro landing per sviluppatori, eseguire una scansione gratuita WCAG 2.2 sulla demo della libreria prima di distribuirla in produzione e confrontare il risultato con il riferimento completo ai criteri di successo WCAG 2.2.

«Scegliete la libreria i cui autori hanno trattato la specifica come un contratto, e l’audit diventa una formalità. Scegliete la libreria i cui autori hanno trattato la specifica come un riferimento, e l’audit diventa un backlog.»

— desk di ingegneria di disabilityworld.org, nota conclusiva