Tillbaka till bloggen
Utveckling
11 min

Mönster för tangentbordsnavigering

Tryck Tab och försök navigera din sajt utan mus. Om du tappar fokus eller fastnar har du upplevt det miljontals användare möter varje dag.

Testa din egen sajt. Tryck Tab och försök navigera genom sidan utan att röra musen. Om du tappar bort fokus, fastnar i ett element du inte kommer ur, eller inte kan se var du befinner dig, har du precis upplevt det som miljontals användare möter varje dag. Tangentbordsnavigering är inte en nischfunktion. Det är grunden för all interaktivitet på webben, och WCAG 2.1.1 kräver att all funktionalitet ska gå att nå och använda med enbart tangentbordet. Det gäller inte bara personer vid en laptop. Det gäller alla som kopplar ett externt tangentbord till en iPad, alla som använder switch-kontroller, och alla som av motoriska skäl inte kan eller vill använda en pekskärm.


Det första en tangentbordsanvändare stöter på borde vara en “Hoppa till innehåll”-länk. WCAG 2.4.1, Bypass Blocks, kräver att det finns en mekanism för att hoppa förbi upprepat innehåll som navigeringsmenyer och sidhuvuden. Utan den tvingas användaren tabba igenom tjugo, trettio, ibland femtio menyalternativ och ikoner innan hen når sidans faktiska innehåll. På varje ny sida. Om och om igen.

En skip link är vanligtvis en <a>-tagg som pekar på ett ankare vid huvudinnehållet och som är visuellt dold tills den får fokus. Den dyker upp som en synlig länk när användaren börjar tabba, och försvinner igen för musanvändare. Det är en enkel komponent att bygga, men den gör enorm skillnad för alla som navigerar sekventiellt.


Hur fokus fungerar

När en användare trycker Tab flyttar webbläsaren fokus till nästa interaktiva element i dokumentets ordning. Shift+Tab flyttar bakåt. Den ordningen bestäms av DOM-strukturen, och den bör matcha den visuella ordningen på sidan. Om det visuella flödet går från vänster till höger och uppifrån och ner, ska fokusordningen göra detsamma.

Nativa HTML-element som <button>, <a>, <input> och <select> är fokuserbara som standard och ingår automatiskt i tabbordningen. Icke-interaktiva element som <div> och <span> gör det inte. Det är med avsikt. Att lägga till tabindex="0" på ett element gör det fokusbart och placerar det i den naturliga tabbordningen, men det gör det inte interaktivt. Du måste fortfarande lägga till tangentbordslyssnare och rätt ARIA-roller för att elementet ska fungera.

En modern fälla är CSS-egenskapen order i Flexbox och Grid. Den ändrar den visuella ordningen utan att röra DOM:en, och därmed tabbordningen. Resultatet kan bli att fokus hoppar från det tredje visuella elementet till det första, sedan till det femte, utan logik för den som inte ser layouten. WCAG 1.3.2, Meaningful Sequence, kräver att den programmatiska ordningen ska matcha den visuella. Det säkraste är att låta DOM-ordningen styra och undvika order för interaktiva element helt.

En annan vanlig fälla är tabindex med positiva värden. tabindex="1" placerar elementet först i tabbordningen, före allt annat på sidan. Det låter praktiskt, men i verkligheten skapar det kaos. Så fort fler element får positiva tabindex-värden blir ordningen omöjlig att förutsäga och underhålla. W3C:s ARIA Authoring Practices Guide rekommenderar att aldrig använda positiva tabindex-värden.


Synlig fokusindikator

WCAG 2.4.7, Focus Visible, kräver att det ska finnas en synlig indikator som visar vilket element som har tangentbordsfokus. Utan den navigerar användaren i blindo.

Den vanligaste synden är outline: none utan ersättning. Webbläsarnas standardfokusringar anses ofta fula, och designers tar bort dem. Men att ta bort fokusindikatorn utan att erbjuda ett alternativ är att stänga ute alla som navigerar med tangentbordet.

En bra fokusindikator har tillräcklig kontrast, minst 3:1 mot angränsande färger, och är tydligt synlig oavsett var på sidan elementet befinner sig. CSS-pseudoklassen :focus-visible är nyckeln: den visar fokusindikatorn när användaren navigerar med tangentbordet men döljer den vid musklick, vilket ger en ren upplevelse för alla. Ett enkelt mönster som outline: 3px solid #1a73e8; outline-offset: 2px:focus-visible löser problemet med minimal ansträngning.

WCAG 2.2 introducerade dessutom kriterium 2.4.13, Focus Appearance, som ställer mer detaljerade krav på fokusindikatorns storlek och kontrast för nivå AAA.


Fokushantering i modaler

Modaler är det mönster som oftast går fel. W3C:s APG beskriver fyra krav för en tangentbordstillgänglig modal dialog.

Först ska fokus flytta till modalen när den öppnas. Om modalen har ett formulär kan fokus riktas mot det första fältet. Om innehållet är informativt kan ett statiskt element i början av modalen få tabindex="-1" och ta emot fokus, vilket gör det lättare för skärmläsaranvändare att börja läsa.

Sedan ska fokus fångas inuti modalen. Det kallas focus trap, och det innebär att Tab och Shift+Tab bara cyklar mellan elementen i modalen. Utan det hamnar användaren plötsligt bakom modalen, navigerar i innehåll de inte ser, och har ingen aning om vad som händer. En enkel implementation lyssnar på Tab-händelser: om fokus är på det sista elementet och användaren trycker Tab, flyttas fokus till det första, och tvärtom.

Escape ska stänga modalen. Det är en konvention som skärmläsaranvändare förväntar sig, och att bryta den skapar förvirring.

Sist ska fokus återvända till det element som öppnade modalen när den stängs. Om användaren klickade på en “Redigera”-knapp och en modal öppnades, ska fokus hamna tillbaka på den knappen efteråt. Utan det tappar användaren sin plats på sidan.


Komposita komponenter: piltangenter tar över

Enklare komponenter som knappar och länkar hanteras helt av Tab. Men komposita komponenter, saker som flikgrupper, menyer, trädvyer och verktygsrader, har ett annat mönster: Tab tar dig in i och ut ur komponenten, medan piltangenterna hanterar navigering inuti den.

Logiken är att en flikgrupp med fem flikar inte ska kräva fem Tab-tryck för att passera. Användaren trycker Tab för att nå flikgruppen, navigerar mellan flikarna med vänster- och högerpil, och trycker Tab igen för att gå vidare till nästa del av sidan.

Det finns två sätt att implementera det. Roving tabindex innebär att den aktiva fliken har tabindex="0" och alla övriga har tabindex="-1". När användaren trycker på en piltangent flyttas tabindex="0" till den nya fliken och den får fokus via JavaScript. Fördelen är att webbläsaren scrollar det fokuserade elementet till synligt läge automatiskt.

Alternativet är aria-activedescendant. Då behåller behållarelementet fokus och meddelar skärmläsaren vilket barnelement som är aktivt genom attributets värde. Det är enklare att implementera men har kända kompatibilitetsproblem med VoiceOver, som inte alltid annonserar det aktiva elementet korrekt.

W3C:s APG dokumenterar båda mönstren med fungerande kodexempel för flikar, menyer, trädvyer och andra komposita widgets. Att utgå från dessa referensimplementationer sparar timmar jämfört med att bygga från grunden.


Vanliga fallgropar

Några av de vanligaste tangentbordsproblemen dyker upp om och om igen. Anpassade dropdown-menyer som öppnas med mus men inte med Enter eller Space. Karuseller som inte går att pausa eller stega igenom med tangentbordet. Tooltip-innehåll som bara visas vid hover men aldrig vid fokus, och därmed är osynligt för tangentbordsanvändare.

Ett grundläggande test tar två minuter. Börja längst upp på sidan och tabba igenom varje interaktivt element. Kontrollera att du ser var fokus är i varje steg, att du kan aktivera allt du ska kunna aktivera, och att du aldrig fastnar. Öppna en modal och kontrollera att du inte kan tabba ut ur den. Stäng den och kontrollera att fokus hamnar rätt. Navigera en flikgrupp med piltangenter.

Gör det här till en del av utvecklarens vardag, inte bara QA:s checklista. Två minuter med tangentbordet avslöjar problem som inget automatiserat verktyg kan hitta, och det är exakt den typen av manuell kontroll som gör skillnaden mellan en sajt som klarar en revision och en som faktiskt fungerar.

Låt oss bygga något tillgängligt.

Oavsett om det gäller en fullständig granskning, åtgärdning av en befintlig app eller teamutbildning. Hör av dig så hittar vi en lösning.