Hopp til hovedinnhold
Tilbake til bloggen
For utviklere

Utviklerens sjekkliste for tilgjengelighet

Denne sjekklisten er skrevet for utviklere som skal levere mot WCAG 2.2 AA. Den dekker markup, tastatur, skjema, dynamisk innhold og testing – med konkrete eksempler.

Alnet12 min lesetid

De aller fleste tilgjengelighetsfeil er ikke eksotiske. De er de samme ti tingene, om og om igjen. Her er de, sortert etter hvor de oppstår i koden.

1. Semantisk markup

Riktig element gir deg rolle, tilstand og tastaturhåndtering gratis. Feil element betyr at du må bygge alt selv – og som regel gjør du det ufullstendig.

<!-- Nei: ingen rolle, ingen tastaturstøtte, ingen fokus -->
<div class="btn" onclick="submit()">Send inn</div>

<!-- Ja: fungerer med tastatur, skjermleser og forms -->
<button type="submit">Send inn</button>
  • Én <h1> per side, og aldri hopp over nivåer nedover
  • Bruk <nav>, <main>, <header>, <footer> og <aside> som landemerker
  • Lister skal være <ul>/<ol>, ikke <div> med kulepunkt-ikoner
  • <button> for handlinger, <a href> for navigasjon – aldri omvendt

2. Tastaturnavigasjon

Test regelen tidlig: legg fra deg musa og gå gjennom siden med Tab, Shift+Tab, Enter, mellomrom og Escape. Alt du kan gjøre med mus, skal kunne gjøres slik.

/* Nei: fjerner fokusmarkeringen for alle */
:focus { outline: none; }

/* Ja: skjuler den bare for musbrukere, og gjør den tydelig */
:focus-visible {
  outline: 2px solid var(--color-brand-600);
  outline-offset: 2px;
}
  • Fokusrekkefølgen skal følge den visuelle rekkefølgen
  • Modaler skal låse fokus inne, og gi det tilbake til utløseren ved lukking
  • Escape skal lukke alt som er åpnet over innholdet
  • Bruk aldri positive tabindex-verdier

3. Skjema

<label for="email">E-post</label>
<input
  id="email"
  name="email"
  type="email"
  autocomplete="email"
  aria-describedby="email-error"
  aria-invalid="true"
/>
<p id="email-error" role="alert">Skriv inn en gyldig e-postadresse.</p>
  • Hvert felt trenger en synlig <label> koblet med for/id
  • Plassholder er ikke en erstatning for ledetekst
  • Feilmeldinger skal knyttes til feltet med aria-describedby
  • Sett autocomplete på felter som gjelder brukerens egne opplysninger
  • Ikke nullstill utfylte felter når validering feiler

4. Bilder og media

<!-- Informasjonsbærende: beskriv innholdet -->
<img src="/graf.png" alt="Omsetning økte fra 4 til 6,2 millioner i 2025" />

<!-- Dekorativt: tom alt, slik at skjermleseren hopper over -->
<img src="/moenster.svg" alt="" />

5. Farge og kontrast

ElementMinstekravMerknad
Brødtekst4,5:1Under 18,66 px, eller under 24 px hvis ikke fet
Stor tekst3:1Fra 24 px, eller 18,66 px i fet skrift
Grensesnittkomponenter3:1Knappekanter, ikoner, fokusmarkering
Fokusmarkering3:1Mot bakgrunnen den vises på

Farge alene skal aldri være eneste bærer av informasjon. En rød ramme rundt et felt må følges av tekst som forklarer feilen.

6. Dynamisk innhold

<!-- Statusmeldinger som ikke skal avbryte brukeren -->
<div role="status" aria-live="polite">3 treff</div>

<!-- Kritiske feil som skal leses umiddelbart -->
<div role="alert">Betalingen ble avvist.</div>
  • Flytt fokus til nytt innhold når brukeren har utløst en navigasjon i en SPA
  • Oppdater dokumenttittelen ved ruteendring
  • Bruk aria-live sparsomt – for mye støy er verre enn for lite
  • Kunngjør laste- og feiltilstander, ikke bare vis en spinner

7. Testing i praksis

  1. Automatisk i CIaxe-core via Playwright eller jest-axe fanger omtrent en tredjedel av avvikene, og hindrer regresjoner.
  2. Tastatur manueltGå gjennom hovedflytene uten mus. Dette avdekker mest per minutt brukt.
  3. SkjermleserNVDA på Windows eller VoiceOver på Mac. Du trenger ikke være ekspert for å høre at noe er galt.
  4. Zoom og reflow200 % tekstforstørrelse og 320 px bredde uten horisontal scrolling.
  5. Redusert bevegelseTest at prefers-reduced-motion faktisk slår av animasjonene dine.
import { test, expect } from "@playwright/test";
import AxeBuilder from "@axe-core/playwright";

test("forsiden har ingen alvorlige avvik", async ({ page }) => {
  await page.goto("/");
  const results = await new AxeBuilder({ page })
    .withTags(["wcag2a", "wcag2aa", "wcag21aa", "wcag22aa"])
    .analyze();
  expect(results.violations).toEqual([]);
});
  • #Utvikling
  • #WCAG
  • #HTML
  • #ARIA
  • #Testing