ToggleGroup

I denne demoen forsøker jeg å forklare hvor komplisert det kan være å kode noe med wai-aria som ikke her helt identisk med native funksjonalitet. Det kan være greit i løsninger som kan læres, men er mer utfordrende på flater som skal brukes av mange og ikke nødvendigvis ofte. I eksemplet er det egentlig bare skjermlesere som berøres, og litt flåsete sagt er wai-aria primært laget for nettopp skjermlesere (vet at det finnes noen andre bruksområder også).

Bakgrunn / forklaring

Advarsel: dette er i kategorien halv-nerding, og du må kunne litt skjermleser for at dette skal gi mening.

ToggleGroup er en komponent fra NAVs designsystem (Aksel). Komponenten ligner mye på faner. Innhold på siden bestemmes av hvilken knapp som velges, men brukes tastaturet for å flytte til en annen knapp velges ikke knappen (da må du ta Enter eller Mellomrom). Dette kan være hensiktsmessig hvis det tar litt tid å laste innholdet for hver av knappene, og dette kan også være en grunn til å ikke bruke for eksempel en native radioknapp (i tillegg til utseende selvsagt).

Jeg jobber med Aksel og der var ToggleGroup kodet med role=radiogroup og hver knapp med aria-checked. Først da jeg testet en ny løsning med skjermleser la jeg merke til hvor uhensiktsmessig denne kodingen kunne være. Først gjorde jeg et valg i en radioknapp. Så gikk jeg videre til en ToggleGroup, men denne var jo kodet som radio. Jeg pilte og trodde jeg hadde gjort et valg, men det eneste jeg hadde gjort var å flytte fokus. I native radio velger du samtidig som du piler. Etter hvert så jeg med leselist at valget i ToggleGroup ikke ble endret når jeg pilte, og hadde jeg hørt veldig godt etter hadde jeg jo også hørt at valget jeg kom til ikke ble avkrysset. Ganske uheldig at to kontroller som presenteres nøyaktig på samme måte for skjermleser fungerer ulikt. For å se på fordeler og ulemper med ulik koding har jeg laget noen eksempler. I ulike designsystemer gjøres dette på forskjellig måte, så det finnes ikke en standard for ToggleGroup (det samme er tilfelle for andre komponenter med ikke-native funksjonalitet).

En ting som kompliserer dette litt: Hvis du bruker virtuell modus (Jaws) og andre typiske lese-moduser med skjermleser vil både Radio og ToggleGroup fungere helt greit. Problemstillingen er altså hvis du er i skjemamodus og trykker Tab fra felt til felt. Skjemamodus har ulike navn i ulike skjermlesere. I NVDA og Windows skjermleser heter det fokus-modus. Det jeg skriver om skjermlesere nedefor handler om fokus/applikasjonsmodus. Jeg har kun brukt Edge og Safari for iPhone/Mac. Poenget har ikke vært å beskrive alle detaljer, men få fram at her er det ulikheter mellom skjermlesere.

Valg for tastaturnavigering: Piltaster brukes for å flytte fokus i alle variantene nedenfor. Tab flytter ut av kontrollen. Jeg har valgt å beholde fokus på knappen som har fokus når du tabber ut av ToggleGroup-varianten. Jeg tror dette er mest effektivt for brukerne, men et alternativ er selvsagt å la fokus være på den valgte toggle'n.

Aksel

aksel kan du se riktigere visuelle eksempler på ToggleGroup. I mine eksempler nedenfor har jeg ikke jobbet for å få ting til å se flott ut.

I innledningen står dette om bruksområde:

ToggleGroup lar brukeren ta valg som påvirker innholdet på siden. Komponenten består av en gruppe knapper som henger sammen og bare én knapp kan være valgt om gangen.

Egnet til:

Uegnet til:

Aksel bruker altså radiogroup og radio (som i eksempel 2 under).

1: Native radio

En vanlig radioknapp er tatt med for å ha en sammenlikning med ToggleGroup-variantene nedenfor.

Velg boligtype


Kode (radio)

<fieldset>
  <legend>Velg boligtype</legend>
  <input id="telt" type="radio" name="boligtype" value="telt" checked>
  <label for="telt">Telt</label><br>
  <input type="radio" id="leilighet" name="boligtype" value="leilighet">
  <label for="leilighet">Leilighet</label><br>
  <input type="radio" id="enebolig" name="boligtype" value="enebolig">
  <label for="enebolig">Enebolig</label><br>
</fieldset>
      

Jaws og NVDA leser nesten nøyaktig det samme:
Velg boligtype gruppe
Telt alternativknapp avkrysset
1 av 3

VoiceOver (Mac og iOS) leser noe liknende, men i litt annen rekkefølge.

Windows skjermleser leser ikke posisjon / antall.

2: radiogroup, radio, aria-checked

Kode (radiogroup, radio, aria-checked)

<div id="x2" role="radiogroup" aria-label="Velg boligtype">
  <button role="radio" aria-checked="true" tabindex="0">Telt</button>
  <button role="radio" aria-checked="false" tabindex="-1">Leilighet</button>
  <button role="radio" aria-checked="false" tabindex="-1">Enebolig</button>
</div>
        

Jaws, NVDA, Windows skjermleser og VoiceOver (Mac) leser nøyaktig det samme som for native radio.

VoiceOver (iOS) leser ikke aria-label eller posisjon/antall valg.

Kommentar

Ulempen med denne løsningen er at de fleste skjermlesere presenterer denne 100% likt som en native radioknapp. Det er lett for skjermleserbrukere og pile til ønsket valg og så gå videre (slik jeg gjorde da jeg ble oppmerksom på utfordringen). Hører du godt etter, dvs. er mer tålmodig enn meg hører du at valget du piler til ikke er valgt.

3: group, aria-pressed

Kode (group, aria-pressed)

<div aria-live="assertive" id="x3" role="group" aria-label="Velg boligtype">
  <button aria-pressed="true" tabindex="0">Telt</button>
  <button aria-pressed="false" tabindex="-1">Leilighet</button>
  <button aria-pressed="false" tabindex="-1">Enebolig</button>
</div>
      

NVDA, Windows skjermleser og VoiceOver (Mac) leser noe liknende som Jaws:
Velg boligtype gruppe
Telt Vekselknapp Trykket

VoiceOver (iOS) leser ikke aria-label.

Kommentar

Antall knapper leses ikke av noen skjermlesere. Er dette viktig? Jeg tror ikke det siden antall valg i en typisk ToggleButton ikke skal være veldig mange.

Et mye viktigere poeng var det min gode kollega Halvor som påpekte: er det ikke lett å tro at hver enkelt knapp kan skrues av/på. Jo, det er lett. En løsning kan være å inkludere aria-describedby til en forklaring som sier at kun en knapp kan velges av gangen:

Kode (group, aria-pressed, aria-describedby)

<div aria-live="assertive" id="x3b" role="group" aria-label="Velg boligtype" aria-describedby="x3b-desc">
  <button aria-pressed="true" tabindex="0">Telt</button>
  <button aria-pressed="false" tabindex="-1">Leilighet</button>
  <button aria-pressed="false" tabindex="-1">Enebolig</button>
  <div class="sr-only" id="x3b-desc" aria-hidden="true">Kun en knapp kan velges av gangen.</div>
</div>
      

4: group, aria-current

Kode (group, aria-current)

<div aria-liv="assertive" id="x4" role="group" aria-label="Velg boligtype">
  <button aria-current="true" tabindex="0">Telt</button>
  <button aria-current="false" tabindex="-1">Leilighet</button>
  <button aria-current="false" tabindex="-1">Enebolig</button>
</div>
      

Jaws, NVDA og VoiceOver (Mac) leser aria-label og hvilken knapp som er aktiv.

Windows skjermleser leser aria-label, men ikke hvilken knapp som er aktiv.

VoiceOver (iOS) leser valgt knapp, men ikke aria-label.

Kommentar

Muligens er dette den semantisk riktigste løsningen. Det er imidlertid ikke bra at VoiceOver (iOS) ikke leser aria-label eller at Windows skjermleser ikke leser om knapper er valgt eller ikke. Rent taktilt liker jeg løsningen med aria-pressed best for Jaws (den mest brukte skjermleseren). Det er også litt dumt at Jaws ikke leser endringer hvis du står på en ikke-aktiv knapp og trykker Mellomrom (piler du til en annen knapp og så tilbake til den du nettopp valgte leses det at knappen er aktiv).

5: tablist, tab, aria-selected

Kode (tablist, tab, aria-selected)

<div id="x5" role="tablist" aria-label="Velg boligtype">
  <button role="tab" aria-selected="true" tabindex="0">Telt</button>
  <button role="tab" aria-selected="false" tabindex="-1">Leilighet</button>
  <button role="tab" aria-selected="false" tabindex="-1">Enebolig</button>
</div>
      

Jaws, NVDA og VoiceOver (Mac) leser aria-label og hvilken "fane" som er aktiv.

Windows skjermleser leser ikke hvilken "fane" som er aktiv.

VoiceOver (iOS) leser valgt "fane", men ikke aria-label.

Kommentar

Jeg synes faner blir litt feil. Det som er bra er at faner tydelig signaliserer at kun ett valg kan være aktivt. Imidlertid kan ToggleGroup brukes eksempelvis for filtrering, og da synes jeg det blir feil å kode med faner.

Konklusjon

Jeg synes eksemplene over tydelig viser hvor utfordrende det er å tilrettelegge ikke-native kontroller på en god måte. Alt kan læres, og alle løsningene i denne demoen er tilgjengelige. Det jeg synes er uheldig er når kontroller kodes som native kontroller (med WAI-ARIA), men faktisk oppfører seg annerledes.

Jeg har ikke sterke følelser mht. hvilken løsning som fungerer best for ToggleGroup. Ingen er perfekte, men alle fungerer. Brukertesting kunne helt sikkert gitt noen svar, men også da tror jeg resultatet hadde vært ulike meninger. Det er derfor ikke rart at forskjellige designsystemer (som har lagt vekt på universell utforming) koder dette forskjellig. Ulik koding reduserer selvsagt gjenkjennelighet med skjermleser og gjør det dermed enda vanskeligere å bruke løsninger uten å se. I tillegg kommer utfordringene med at skjermlesere håndterer og forstår aria-attributter ulikt.

Jeg liker group med aria-pressed og aria-describedby best. Har ikke andre begrunnelser enn at denne løsningen er annerledes enn en native radioknapp og at den tolkes OK av de skjermleserne jeg har testet med. Unntaket er VoiceOver (iOS), men siden Mac-versjonen takler dette helt fint får vi nesten håpe på forbedringer i Apples mobile skjermleser.